tiny6410 Miscdevice driver<1>

来源:互联网 发布:js点击显示div 编辑:程序博客网 时间:2024/06/06 06:31
MiscDev.c文件的头文件介绍:
#include <linux/module.h>   /*驱动编译成模块形式,而不是编译进内核。同时MODULE_*()声明的头文件*/
#include <asm/io.h>  /*内核不能直接使用物理地址,要用ioremap()函数进行物理地址到虚拟地址的映射*/
#include <linux/delay.h>  /**/
#include <linux/kernel.h>  /*printk()函数的头文件*/
#include <linux/init.h>   /*定义了module_init()、module_exit()函数*/
#include <linux/fs.h>   /*file_operations、inode_operations、super_operations结构体*/
#include <linux/cdev.h>  /*字符设备操作*/
#include <linux/mm.h>  /*内存管理头文件,含有页面大小定义和一些页面释放函数原型*/
#include <linux/slab.h>  /*kmalloc()函数*/
#include <linux/list.h>   /*链表操作*/
#include <linux/miscdevice.h>   /*misc_register()、misc_deregister()函数*/
#include <asm/uaccess.h>   /*copy_to_user()、copy_from_user()函数*/

ioctl()函数的cmd参数的选取不应该任意选择,如果A驱动和B驱动有相同的有效cmd参数,本来发给A驱动的cmd也可能会意外的作用于B驱动。可以查看ioctl-number.txt文件。这个文件给出了一些推荐和已经被使用的“幻数”,新设备驱动与这些“幻数”避免冲突。这里定义“幻数”MAGIC=0x05。

#define MAGIC 0x05

ioctl()的命令码由设备类型、序列号、方向、数据尺寸组成,如下表所示:设备类型序列号方向数据尺寸8bit8bit2bit13/14bit

“幻数”就是其中的设备类型,在ioctl函数中多用switch语句进行驱动的io操作,因此用序列号字段做case条件。数据的传输方向用内核定义的宏来处理。数据的传输方向是从应用程序的角度来看的(这个数据方向还是很疑惑,在写led驱动时,没有数据要传送)主要有:

_IO():无数据传输方向

_IOR():读

_IOW():写

_IOWR():双向

#define CMD0 _IO(MAGIC,0)
#define CMD1 _IO(MAGIC,1)
#define CMD2 _IO(MAGIC,2)
#define CMD3 _IO(MAGIC,3)
#define CMD4 _IO(MAGIC,4)

struct file_operations 结构体中的成员函数时设备驱动程序设计的主体内容。定义了TestRead函数和TestWrite函数以及led_ioctl函数。

struct file_operations test_fops=
{
.owner = THIS_MODULE, /*struct module *ower拥有该结构的模块的指针,一般为THIS_MODULE*/
.read = TestRead,   /*从设备中同步读取数据*/
.write = TestWrite,  /*向设备发送数据*/
.unlocked_ioctl = led_ioctl,   /*不使用BLK文件,使用这个函数指针代替ioctl*/
};

杂项设备结构的初始化,杂项设备的主设备号为10,动态分配次设备号。设置杂项设备的文件操作。my_led_device将用来进行杂项设备的注册和注销。

struct miscdevice my_led_device=
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEV_NAME,
.fops = &test_fops,
};

驱动程序设计的框架就是:设备的初始化包括设备注册,初始化设备操作,产生设备结点;设备的注销包括设备结点删除,注销设备。杂项设备的注册与注销函数与字符设备的不同。这里主要考虑的是对led物理地址的映射处理。内核不能直接读取设备的物理地址,需要将物理地址映射为相应的虚拟地址。对led控制寄存器的设置和led数据寄存器的读写就需要转化为对寄存器虚拟地址的操作。ioremap()第二个参数是要映射空间的大小,寄存器的地址空间为4个字节。

static int __init TestChar_init(void)
{
int ret;
printk("<1>TestMisc initing...\n");

ret = misc_register(&my_led_device);
if(ret < 0)
{
printk("<1>Register failed!\n");
}
else
{
printk("<1>Misc device registered!\n");
}

pledCtrlAddr = ioremap(GPKCON,4);
pledDataAddr = ioremap(GPKDAT,4);
unContrlReg = ioread32(pledCtrlAddr);
unContrlReg &= REG_AND_VAL;
unContrlReg |= REG_OR_VAL;  
iowrite32(unContrlReg,pledCtrlAddr);

return ret;
}

static void __exit TestChar_exit(void)
{
printk("<1>Misc Device exit!\n");
misc_deregister(&my_led_device);
return;
}

驱动中进行设备的读写是用户空间和内核空间的数据交换。因此在驱动设计中用copy_to_user函数从内核空间读取数据到用户空间,copy_from_user函数将用户空间数据写入内核空间。

还有两点需要注意的是驱动的信息就是MODULE_*()函数用来表明驱动lisence、作者、别名、说明等信息。还有就是要用module_init()、module_exit()函数执行模块的注册函数和注销函数。

函数代码如下:


测试代码的编写。一开始没有弄明白,还是陷在C编程中main函数调用其他函数文件的思想来考虑。后来发现,linux中一切设备皆文件。led驱动insmod进内核后,操作led驱动也就是打开led设备文件,用读写文件的方式操作驱动的自身的fops。在led_ioctl操作中,应为传递的是在驱动中抵用的CMD指令,因此在测试函数中又重新定义了,在参考其他的测试代码时发现时用简单的任意取得数做为指令传递的,感觉还是不可靠。同样的也对led_test.c中头文件做一下描述,感觉还是需要挊清除:
#include <stdio.h>  /*标准的输入输出*/
#include <stdlib.h>  /*常用的函数如malloc()、calloc()、realloc()、free()、system()、atoi()、atol()、rand()、srand()、exit()等等*/
#include <unistd.h> /*接口操作read、write、close*/
#include <sys/ioctl.h>  /*I/O控制函数*/
#include <sys/stat.h> /*文件状态*/
#include <sys/types.h>  /*原系统数据类型*/
#include <fcntl.h>   /*文件控制操作*/

led_test.c代码如下:

0 0
原创粉丝点击