/************************************************************************************html
*本文为我的学习记录,若有错误,欢迎指正。node
* http://www.javashuo.com/article/p-hfnxtfsk-ck.html
数据结构
* https://blog.csdn.net/armwind/article/details/52166139
框架
************************************************************************************/ide
misc类设备,即杂项设备,全部的misc类设备都是字符设备,其主设备号固定为10。由于如今的硬件设备多种多样,有好些设备很差对他们进行一个单独的分类,因此就将这些设备所有归属于misc类设备,譬如adc、buzzer等这些设备通常都归属于misc类设备中。misc类设备在应用层的操做接口:/dev/xxxx,设备类对应在 /sys/class/misc。函数
Linux内核中提供了一套misc类设备驱动框架,因此咱们写一个misc设备的驱动直接利用的是内核中提供的驱动框架来实现的;misc类设备驱动一般嵌套在platform 总线驱动中,配合总线驱动达到更复杂,多功能的效果。misc类设备驱动框架和以前的LED设备驱动框架都是实现为一个模块的形式,在内核配置的时候能够进行动态的编译或者是不编译进内核当中。使用misc类设备驱动框架以前,需确保Linux内核支持misc类设备驱动框架。进入Linux内核的配置界面menuconfig进行设置,具体配置以下:学习
Device Drivers --->this
[*] Misc devices ---> spa
misc类设备驱动框架的核心文件:
/kernel/ drivers/char/misc.c
/kernel/include/linux/miscdevice.h
misc类设备驱动框架使用subsys_initcall宏修饰misc_init()函数,所以misc_init()函数在内核启动阶段被调用。
misc_init函数是misc类设备驱动框架模块注册时的一个初始化函数,只有执行了初始化,咱们才可以利用misc类设备驱动框架来进行编写misc类设备驱动程序和管理misc类设备。
static const struct file_operations misc_fops = { .owner = THIS_MODULE, .open = misc_open, }; static int __init misc_init(void) { int err; #ifdef CONFIG_PROC_FS //CONFIG_PROC_FS用来控制咱们的系统中是否须要proc虚拟文件系统 proc_create("misc", 0, NULL, &misc_proc_fops); //在proc文件系统下建立一个名为 misc 的文件 #endif misc_class = class_create(THIS_MODULE, "misc"); //在sys文件系统下建立misc设备类 err = PTR_ERR(misc_class); if (IS_ERR(misc_class)) goto fail_remove; err = -EIO; if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))//注册misc字符设备,主设备号为MISC_MAJOR = 10 goto fail_printk; misc_class->devnode = misc_devnode; return 0; fail_printk: printk("unable to get major %d for misc devices\n", MISC_MAJOR); class_destroy(misc_class); fail_remove: remove_proc_entry("misc", NULL); return err; } subsys_initcall(misc_init);
register_chrdev(MISC_MAJOR,"misc",&misc_fops),从这里能够看出来 misc_fops 就是传入的一个file_operations结构体;misc_fops结构体中只实现了open函数,而没有实现其余的函数,由于具体的驱动实现的open、read、write函数在具体的misc类设备中的file_operations结构体中,并不在这里实现。系统经过这里的open函数去找到具体的要打开的硬件设备,而后找到该设备下的的file_operations结构体,调用结构体中实现的open函数,而且将要打开的设备的file_operations结构体替换当前要操做的这个结构体,以后咱们就能够经过这个结构体来调用设备的其余操做函数,例如read、write....等函数。
在注册misc类设备以前,须要先定义并初始化一个struct miscdevice结构体变量,该结构体包含了该misc类设备的全部信息。
struct miscdevice { int minor; //次设备号 const char *name; //设备名称 const struct file_operations *fops; //设备操做集 struct list_head list; //做为一个链表节点挂接到misc设备维护的一个链表头上去 struct device *parent; //父设备 struct device *this_device; //本设备的device基类 const char *nodename; mode_t mode; };
struct miscdevice结构体变量的一个实例。
static struct miscdevice buzzer_device = { .minor = BUZZER_MINOR, //次设备号 .name = BUZZER_NAME, //设备名字宏定义 .fops = &buzzer_fops, //文件操做指针集合 };
misc_register()函数是misc驱动框架提供给驱动工程师编写misc类设备时的注册函数。
int misc_register(struct miscdevice * misc) { struct miscdevice *c; //定义一个 miscdevice结构体指针 dev_t dev; //设备号 int err = 0; INIT_LIST_HEAD(&misc->list); //初始化链表 mutex_lock(&misc_mtx); //上锁 list_for_each_entry(c, &misc_list, list) { //遍历misc_list 链表,查找是否存在次设备号与当前注册的设备的次设备号相同的 if (c->minor == misc->minor) { mutex_unlock(&misc_mtx); return -EBUSY; //若是存在直接退出 } } /*在咱们的misc类设备的驱动框架中使用了一种位来表示次设备号是否被占用的状况。使用8个字节的一个变量来表示,这个数据的每一位表示一个次设备号,第一位表明次设备号0,
第二位表明次设备号1,以此类推。
若是这个位被置1表示已经被分配出去了,置0表示没有被分配出去。因此这段代码就是在找一个最小的没有被使用被置1的位,由此可知misc类设备最多只有64个。*/ if (misc->minor == MISC_DYNAMIC_MINOR) // misc->minor == 255 表示 自动分配次设备号 { int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); if (i >= DYNAMIC_MINORS) { mutex_unlock(&misc_mtx); return -EBUSY; } misc->minor = DYNAMIC_MINORS - i - 1; //咱们这里的意思就是咱们是从小到大去寻找,那么分配就是从大到小,例如: i=0 ,minor=63 i =1,minor=62 set_bit(i, misc_minors); //而后将该位置1 } dev = MKDEV(MISC_MAJOR, misc->minor); //使用主次设备号合成设备号 misc->this_device = device_create(misc_class, misc->parent, dev, misc, "%s", misc->name); //建立设备/sys/devices/virtual/misc/xxx if (IS_ERR(misc->this_device)) { int i = DYNAMIC_MINORS - misc->minor - 1; if (i < DYNAMIC_MINORS && i >= 0) clear_bit(i, misc_minors); err = PTR_ERR(misc->this_device); goto out; } /* * Add it to the front, so that later devices can "override" * earlier defaults */ list_add(&misc->list, &misc_list); //将 misc->list 做为节点挂接到 misc_list 链表上去 out: mutex_unlock(&misc_mtx); return err; }
misc_deregister就是相对应的misc类设备的注销函数。
int misc_deregister(struct miscdevice *misc) { int i = DYNAMIC_MINORS - misc->minor - 1; if (list_empty(&misc->list)) return -EINVAL; mutex_lock(&misc_mtx); list_del(&misc->list); device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor)); if (i < DYNAMIC_MINORS && i >= 0) clear_bit(i, misc_minors); mutex_unlock(&misc_mtx); return 0; }