misc类设备
一. misc类设备简介
1.1. 什么是misc设备
1.1.1. misc中文名就是杂项设备\杂散设备,因为现在的硬件设备多种多样,有好些设备不好对他们进行一个单独的分类,所以就将这些设备全部归属于杂散设备,也就是misc设备,例如像adc、buzzer等这些设备一般都归属于misc中
1.1.2. 由于官方没有规定什么设备是misc设备,由于驱动工程师的差异可能导致虽然这些设备归属于杂散设备中,但不把设备放在这个misc类中,这都是驱动工程师按照自己的想法做的,你想把他们写在misc类设备中也可以,自己单独建立一个类也是可以的,只不过是否标准化而已,因为人家既然建立了这个类,那你就把这个设备放在这个类下,不是很好吗?你还自己单独搞一个类,虽然这也没错,只不过是说你不按套路出牌。
1.1.3. 所有的misc类设备都是字符设备,也就是misc类设备(主设备号为10)其实是字符设备中分出来的一个小类。
1.2. misc设备框架
1.2.1 misc类设备在应用层的操作接口:/dev/xxxx, 设备类对应在 /sys/class/misc
1.2.2. misc类设备有自己的一套驱动框架,所以我们写一个misc设备的驱动直接利用的是内核中提供的驱动框架来实现的(linux/drivers/char/misc.c)。misc驱动框架是对内核提供的原始的字符设备
1.3.3. 在内核中,misc驱动框架的源码实现在: driver/char/misc.c 相应的头文件在:include/linux/miscdevice.h
1.3.4. 但是如果我们自己添加的misc类设备,那么驱动源文件最好放在 driver/misc 这个目录下,这个目录是官方推荐的目录。misc驱动框架和之前的led的驱动框架都是实现为一个模块的形式,在内核配置的时候可以进行动态的编译或者是不编译进内核当中。这样做的一个好处就是能够对内核进行一个最大化的裁剪,将不需要的模块统统拿掉,能够使得内核在满足要求的情况下实现最小化
二.源码分析
2.1. misc_init函数
a. 当我们在make menuconfig开启misc设备时,系统启动时就会调用此函数来初始化misc类
b. 该函数主要完成在sys文件系统中创建misc类以及注册misc字符设备,主设备号=10
c. proc文件系统在2.4版本中用的比较流行,现在主要用的就是sys文件系统,因为sys文件系统比proc文件系统做的更好,功能更加齐全,目录层次设计的很好所以现在proc文件系统成为了一个可以选择添加或者删除的一个选项了,可以通过在内核配置的时候进行相应的配置
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 字符设备 主设备号10 misc_fops*/ 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; }
2.2. miscdevice结构体
a. 这个结构体是内核提供给驱动开发者使用的,当我们register & unregister 具体的misc设备是均要用到该结构体
b. 驱动开发者开发时,定义file_operations成员定义出的相关函数提供给APP调用
struct miscdevice { int minor; // 次设备号 const char *name; // 名字 const struct file_operations *fops; // file_operations 结构体指针 struct list_head list; // 作为一个链表节点挂接到misc设备维护的一个链表头上去 misc_list struct device *parent; // 次设备的父设备 struct device *this_device; // 本设备的device 结构体指针 const char *nodename; mode_t mode; };
2.3. misc_register函数
2.3.1. 驱动开发者通过内核提供的misc_register来注册具体的misc设备
/** * misc_register - register a miscellaneous device * @misc: device structure * * Register a miscellaneous device with the kernel. If the minor * number is set to %MISC_DYNAMIC_MINOR a minor number is assigned * and placed in the minor field of the structure. For other cases * the minor number requested is used. * * The structure passed is linked into the kernel and may not be * destroyed until it has been unregistered. * * A zero is returned on success and a negative errno code for * failure. */ 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; // 如果存在直接退出 } } if (misc->minor == MISC_DYNAMIC_MINOR) { // misc->minor == 255 表示 自动分配次设备号 int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); // 在我们的misc类设备的驱动框架中使用了一种位来表示次设备号是否被占用的情况 if (i >= DYNAMIC_MINORS) { // 使用8个字节的一个变量来表示,这个数据的每一位表示一个次设备号, mutex_unlock(&misc_mtx); // 第一位代表次设备号0 第二位代表次设备号1 ....... 如果这个位被置1表示已经被分配出去了,置0表示没有被分配出去 return -EBUSY; // 所以这段代码就是在找一个最小的没有被使用被置1的位, } // 由此可知misc类设备最多只有64个 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, // 创建设备 /sys/devices/virtual/misc/xxx misc, "%s", misc->name); 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; }
2.4. misc_deregister函数
2.4.1. 驱动开发者通过内核提供的misc_deregister来注销具体的misc设备
/** * misc_deregister - unregister a miscellaneous device * @misc: device to unregister * * Unregister a miscellaneous device that was previously * successfully registered with misc_register(). Success * is indicated by a zero return, a negative errno code * indicates an error. */ 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; }