字符设备驱动框架
一、linux软件系统的层次关系
一个应用程序操作底层驱动程序的过程:
1 . 应用程序使用库提供的open函数打开某一个设备文件。
2. 库根据open函数传入的参数执行swi(软件中断)指令,这会引起CPU异常,进入内核。
3 . 内核的异常处理函数根据这些参数找到相应的驱动程序,返回一个文件句柄给库,进而返回给应用程序。
4. 应用程序的到文件句柄后,使用库函数提供的write或ioctl函数发出控制命令。
5. 库根据write或ioctl函数传入的参数执行swi(软件中断)指令,这会引起CPU异常,进入内核。
6. 内核的异常处理函数根据这些参数调用驱动程序的相应函数,实现对设备的控制。
以上内容参照《嵌入式linux应用开发完全手册 韦东山编著》
二、编写字符设备驱动程序的步骤
1. 定义相应file_operation结构类型的变量,并实现结构中的函数(驱动作者的的主要任务就在于此),如read, write ,ioctl等。
2. 定义变量(以led驱动为例):
/*当led_major为0时,led_init采取动态分配设备号。在此默认采用动态分配,第一个次设备号为0,所请求设备个数DEV_NUM为1*/
static int led_major = 0; //保存主设备号
-
static int led_minor = 0; //保存次设备号
static int DEV_NUM = 1; //请求设备数量
-
static struct cdev led_cdev; //定义一个cdev结构表示字符设备
static struct class_simple*led_class; //创建相应的类,用于自动创建节点
static struct class_device*led_class_dev; /*定义一个类中的设备*/
-
dev_t led_id; //dev_t变量保存设备编号
3.获得一个或多个设备编号(包括主设备号和次设备号),分为静态分配和动态分配:
静态分配
int register_chrdev_region(dev_tfirst, unsigned int count, char *name)
first:要分配的设备编号范围的起始值,其次设备号常被置为0。
count:所请求连续设备编号的个数。
name:和编号范围关联的设备名称,将出现在/proc/deviecs和sysfs中。
分配成功,返回0,失败时,返回负的错误码。
动态分配
intalloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned intcount, char *name)
dev :仅用于输出的参数,成功分配后保存以分配范围的第一个编号。
firstminor:要使用的被请求的第一个次设备号,通常为0。
为了避免静态分配设备编号带来的冲突和麻烦,我们应当使用动态分配机制获取主设备号。即使用
alloc_chrdev_region。也可以同时保留静态分配设备号的余地。
if(led_major) // 如果led_major!=0,采取静态分配。
{
led_id = MKDEV(led_major,led_minor);
ret = register_chrdev_region(led_id,DEV_NUM, "led");
}
else //led_major = 0动态分配
{
ret =alloc_chrdev_region(&led_id,0,1,"led");
led_major = MAJOR(led_id);
}
if ( ret < 0 )
{
printk(KERN_WARNING "led : can'tget major %d\n", led_major);
return ret;
}
else
{
printk(KERN_WARNING"alloc_chrdev_region succeed .\n"); printk(KERN_WARNING "led major = %d ,minor = %d .\n",MAJOR(led_id),MINOR(led_id));
}
4.注册设备
cdev_init(&led_cdev,&led_fops);//初始化设备cdev结构
err = cdev_add(&led_cdev, led_id,1);//向内核注册cdev结构
if(err<0)
printk(KERN_WARNING "Error%d,cdev_addfailed .\n", err);
else
printk(KERN_WARNING "cdev_add succeed .\n");
5.创建类并给类中添加设备
led_class =class_simple_create(THIS_MODULE, "led_clss_drv"); /*创建led_class_drv的类*/
led_class_dev =class_simple_device_add(led_class, led_id, NULL, "led");/*创建类的设备/dev/led*/
6.设备不用时,做一系列撤销动作:
class_simple_device_remove(led_id);/*删除类中的设备 这里提供设备号就足够了,不许要用class_simple_device_add返回的结构*/
class_simple_destroy(led_class); //销毁一个简单的类
cdev_del(&led_cdev); //从系统中移除字符设备
unregister_chrdev_region(led_id, 1); //释放设备编号
步骤3到步骤5,放在init函数中,insmod时调用;
步骤6,放在exit函数中,rmmod时调用。
参考书籍:LDD3《嵌入式linux应用开发完全手册 韦东山编著》