第六章 混杂设备驱动讲解
在上一章节中讲解了字符设备驱动的相关知识和使用方法,本章节将讲解混杂设备驱动相关框架知识及编写方法。
混杂设备驱动(miscdevice)在Linux驱动中也是很常见的,它是一种特殊的字符设备驱动,混杂设备驱动的设计产生主要是为了解决如下两个问题:
(1)由于在使用常规字符设备驱动时,仅仅需要确定设备号即可确定设备驱动,随着科技的进步外设的种类日益增多,导致主设备号的资源分配越来越紧张,此时混杂设备驱动的出现可以在一定程度上缓解这个问题,因为混杂设备驱动使用的是统一的主设备号(10),然后只要通过次设备号在misc_list链表中即可找到对应的设备。
6.1混杂设备驱动框架
如下图6.1所示为混杂设备驱动模型框架示意图,从图中可以看出混杂设备驱动主要包括四个重要的操作:初始化混杂设备结构体(miscdevice)、注册混杂设备结构体、填充完善文件操作集的相关函数(file_operation)以及在注销设备结构体操作。
图6.1混杂设备驱动框架示意图
6.1.1混杂设备结构体描述
混杂设备结构体struct miscdevice的定义如下:
主要关注三个比较重要的参数是:
(1)minor:此设备号
(2)name:设备名称
(3)fops:文件操作集指针,其中文件操作集中的成员函数的实现与上一章节字符设备驱动的实现方法类似,此处不再展开讲述。
struct miscdevice { int minor; const char *name; const struct file_operations *fops; struct list_head list; struct device *parent; struct device *this_device; };
定义混杂设备结构体的例子:
static struct miscdevice gpio_miscdev = { .minor = MISC_DYNAMIC_MINOR, //次设备 .name = DEVICE_NAME, //设备名 .open调用时的名称 .fops = &gpio_fops, //操作结构体实现功能 };
6.1.2注册混杂设备
在定义好混杂设备结构体和完善好文件操作集(file_operations)即可进行注册混杂设备,注册混杂设备到内核中需要使用如下函数进行注册:
/*函数名:混杂设备注册函数 *函数参数:struct miscdevice * misc为混杂设备结构体指针 *函数返回值:成功返回0,失败返回负值。 */ int misc_register(struct miscdevice * misc);
6.1.3注销混杂设备
在需要卸载设备驱动时,需要调用注销混杂设备函数来进行释放相应的资源,如下为注销混杂设备的函数:
/*函数名:混杂设备注销函数 *函数参数:struct miscdevice * misc为混杂设备结构体指针 *函数返回值:成功返回0,失败返回负值。 */ int misc_deregister(struct miscdevice *misc);
6.2混杂设备驱动示例
在上述讲解了混杂设备驱动的相关知识后,接下来将给出利用混杂设备驱动的示例代码,进一步学习如何编写一个混杂设备驱动进行控制LED灯。
控制LED灯的亮灭的混杂设备驱动实例代码:
#include <linux/init.h> #inlude <linux/module.h> #include <linux/miscdevice.h> #include <linux/fs.h> /*打开函数*/ int misc_open(struct inode *inode,struct file *file) { led_con = ioremap(LEDCON,4);//LEDCON内存动态映射 led_dat = ioremap(lEDDAT,4); writel(0x00000001,led_con); return 0; } /*控制函数*/ long misc_ioctl(struct file *file,unsigned int cmd,unsigned long arg) { switch(cmd) { //点亮led case 1: writel(0x01,led_dat); break; //熄灭led case 0: writel(0x00000000,led_dat); break; default: return -EINVAL; } return 0; } /*关闭函数*/ int misc_close(struct inode *inode, struct file *file) { return 0; } /*定义操作函数集*/ struct file_operations misc_fops = { .open = misc_open, .unlocked_ioctl = misc_ioctl, .release = misc_close, }; /*定义混杂设备驱动描述结构*/ struct miscdevice misc = { .minor = 100, .name = "misc", .fops = misc_fops, }; /*模块加载函数*/ static int misc_init(void) { misc_register(&misc); return 0; } /*模块卸载函数*/ static void misc_exit(void) { misc_deregister(&misc); } MODULE_LICENSE("GPL"); module_init(misc_init); module_exit(misc_exit);
混杂设备驱动测试代码:
#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <stdio.h> int main(int argc,char *argv[]) { int fd; int i=0; fd = open("/dev/misc",O_RDWR);//混杂设备驱动可以在/dev中打开生成的misc节点 if(fd<0) { printf("App open failed\n"); return 0; } while(i++ < 4) { ioctl(fd,0); sleep(1); ioctl(fd,1); sleep(1); } return 0; }
在上述混杂驱动代码中主要完成了如下工作:
(1) 在一开始完成了定义混杂设备驱动描述结构(struct miscdevice misc)的填充和文件操作集(struct file_operations misc_fops)的定义和填充。
(2) 在模块加载函数(misc_init)中完成混杂设备驱动的注册到内核的操作。
(3) 在模块卸载函数(misc_exit)中完成混杂设备驱动的注销释放资源的操作。
针对上面代码中misc_open()函数中出现的ioremap()函数进行简要说明,这个函数是一个驱动常用的IO内存动态映射的函数,完成内存映射的工作,然后再配合misc_ioctl()函数将应用程序传入的控制值传入到驱动里面,才能够实现对LED灯进行实际的操作,关于IO内存映射的讲解将在下一章节展开讲解以及ioctl相关知识将在后续章节进行讲解,本章节不做详细讲解。
在上述驱动测试代码中,由于混杂设备驱动的名字设置为”misc”,所以在混杂设备驱动注册后将在/dev中生成misc节点,只需打开这个节点即可进行操作,来实现LED灯闪烁的效果。
6.3本章总结
本章节讲解了关于混杂设备驱动的知识以及如何编写驱动,混杂设备驱动是字符设备驱动的一种特殊实现方式,它能够实现与字符设备驱动一样的效果,同时能够起到节省主设备号和简化代码的效果,经常应用于日常的驱动开发中,是非常需要认真学习的内容。