The misc subsystem is a simple and flexible device driver framework in the Linux kernel. It can be used to implement some device drivers that do not belong to other subsystems, such as character devices, virtual devices, hybrid devices, etc. The advantage of the misc subsystem is that it is simple and easy to use. It does not require writing a lot of code and only needs to implement some basic operating functions. However, the misc subsystem also has some shortcomings, such as the inability to support multiple device instances, device tree, and hot swapping. In order to solve these problems, this article will introduce a new device driver framework: 3 2 1 device recognition driver framework, which is based on the combination of misc subsystem and platform subsystem, and can achieve more functions and features.
There are three major categories of devices in Linux: character, network, and block devices. Each device is subdivided into many categories. For example, character devices are pre-divided into many categories, and these categories are marked in the file. Which major device number, but even so, there are tens of millions of hardware, and there are always fish that slip through the net. For these character devices that are difficult to classify, Linux uses "mixed" devices to describe them uniformly, and assign them a The common major device number is 10. Only this device number is used to distinguish devices, . These devices mainly include random number generators, LCDs, clock generators, etc. In addition, like many subsystems that also re-encapsulate cdev, misc will also automatically create device files, so as not to have to use class_create() and device_create() every time the cdev interface is written.
misc objects provided in the kernel:
//include/linux/miscdevice.h 55 struct miscdevice { 56 int minor; 57 const char *name; 58 const struct file_operations *fops; 59 struct list_head list; 60 struct device *parent; 61 struct device *this_device; 62 const char *nodename; 63 umode_t mode; 64 };
fops interface like a character device and give it a minor. If the minor uses the macro MISC_DYNAMIC_MINOR (actually 255), the kernel will Automatically assign a minor device number. Other kernels that have implemented the agreed minor device number can refer to **"include/linux/miscdevice.h"**. After everything is ready, just use the following API to register/logout to the kernel
178 int misc_register(struct miscdevice * misc) 238 int misc_deregister(struct miscdevice *misc)
We can learn from the design ideas and improve the quality of our driver. Next, we briefly analyze the internal mechanism of misc.
initializing the data structure, creating the corresponding class, creating, initializing and registering the cdev object to the kernel, etc.. With these foundations, we can program using misc's many benefits.
//drivers/char/misc.c 56 static const struct file_operations misc_fops = { 157 .owner = THIS_MODULE, 158 .open = misc_open, 159 .llseek = noop_llseek, 160 }; 268 static int __init misc_init(void) 269 { 272 #ifdef CONFIG_PROC_FS 273 proc_create("misc", 0, NULL, &misc_proc_fops); 274 #endif 275 misc_class = class_create(THIS_MODULE, "misc"); 281 if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) 282 goto fail_printk; 283 misc_class->devnode = misc_devnode; 284 return 0; 292 } 293 subsys_initcall(misc_init);
“
misc_init() –293–>The misc subsystem will be initialized during system startup
–273–>Depending on the system configuration, the /proc interface may need to be provided
–275–>Create a class in /sysfs named misc
–281–>Using the static major device number (10) and the encapsulated method set misc_fops, register_chrdev() will create a cdev object internally and use these two parameters to initialize it and register it to the kernel. This cdev object will be responsible for all The device number of the promiscuous device. For the relationship between cdev objects and device numbers, see cdev_map.
–158–>fops used by misc’s cdev object. Obviously, the calling process is the same as that of ordinary character devices, chrdev_open()->misc_open().
”
接下来,老规矩,我们从”XXX_register”开始分析,在Linux内核中,这些”XXX_register”往往就是一个设备对象注册到内核的接口,是研究当相应对象注册进去之后内核动作的最佳入口。
178 int misc_register(struct miscdevice * misc) 179 { 180 dev_t dev; 187 if (misc->minor == MISC_DYNAMIC_MINOR) { 188 int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); 193 misc->minor = DYNAMIC_MINORS - i - 1; 194 set_bit(i, misc_minors); 195 } 206 dev = MKDEV(MISC_MAJOR, misc->minor); 208 misc->this_device = device_create(misc_class, misc->parent, dev, 209 misc, "%s", misc->name); 210 if (IS_ERR(misc->this_device)) { 211 int i = DYNAMIC_MINORS - misc->minor - 1; 212 if (i = 0) 213 clear_bit(i, misc_minors); 214 err = PTR_ERR(misc->this_device); 216 } 222 list_add(&misc->list, &misc_list); 226 }
“
misc_register()
–187–> 如果指定的minor是动态分配,那么进入相关语句块。
–188–> 使dev用位图遍历API-find_first_zero_bit找到最小未用的设备号。
–193–> 得到分配好的次设备号。
–208–> (根据设备号)创建设备文件,使用的是misc_init中创建的misc_class,至此就可以实现misc设备文件的自动创建。就相当与我们在纯粹的cdev驱动中使用class_create()+device_create()创建设备文件。一个设备文件和一个设备号相联系,而misc的所有的设备号都和misc_init创建的cdev对象相联系,所以打开的任何一个misc设备文件首先回调的就是(chrdev_open()->)misc_open()。
–222–> 关键,将这个新分配的misc加入到misc链表中,用于管理所有的misc设备,便于misc_open()提取具体设备的fops。”
构建的misc子系统,将设备添加到了该子系统中,接下来我们来看一下应用层程序是如何打开一个misc设备的。由于misc也是一种字符设备,所以其提供的接口也是位于/dev中。但是正如misc的定义,其中的设备五花八门却共用同一个主设备号,这就意味着最终被chrdev_open回调的misc_open一定要具备根据被打开的不同文件为file结构准备不同的操作方法这一能力,即在驱动中实现对子设备的识别,或者称之为”多态”。
112 static int misc_open(struct inode * inode, struct file * file) 113 { 114 int minor = iminor(inode); 115 struct miscdevice *c; 116 int err = -ENODEV; 117 const struct file_operations *new_fops = NULL; 121 list_for_each_entry(c, &misc_list, list) { 122 if (c->minor == minor) { 123 new_fops = fops_get(c->fops); 124 break; 125 } 126 } 144 replace_fops(file, new_fops); 145 if (file->f_op->open) { 146 file->private_data = c; 147 err = file->f_op->open(inode,file); 148 } 152 }
“
misc_open()
–121–>遍历misc设备链表,根据被打开的设备的次设备号找到设备对象。
–123–>存储这个设备对象的操作方法集unique_fops。
–144–>将misc设备具体的操作方法集unique_fops替换到filp中的f_op中,这个位置原来是misc的cdev对象的fops,filp带着这个unique_fops从open()返回,就实现了不同的设备对应不同的操作方法,即面向对象的”多态””
通过上述对misc机制的分析,我们不难总结出一个支持设备识别的3+2+1驱动模型(3个函数+2个数据结构+1个封装):
+
+
至此,我们就可以写一写这个3+2+1驱动模型的模板。
struct multidevice{ struct list_head head; int minor; struct file_operations* fops; void *priv; //私有数据,供read/write等接口识别的信息,以及其他数据都放这里 };
struct list_head multi_dev_list; unsigned int minors_map; //根据设备号数目的不同选数据类型or数组
int major,baseminor = 0,max_dev = sizeof(minors_map)*8; #define DEV_NAME "multi_device" struct class *cls; xxx_open(struct inode *inode,struct file *file){ int minor = iminor(inode); struct multidevice *dp; const struct file_operations *new_fops = NULL; list_for_each_entry(dp, &multi_dev_list, head) { if (dp->minor == minor) { new_fops = fops_get(dp->fops); break; } } replace_fops(file, new_fops); if (file->f_op->open) { file->private_data = dp file->f_op->open(inode,file); } } xxx_init(void){ dev_t devno, INIT_LIST_HEAD(&multi_dev_list); init_map(&minors_map); struct cdev *multi_cdev = cdev_alloc(); cdev_init(multi_cdev, multi_fops); alloc_chrdev_region(&devno, baseminor, count,DEV_NAME); major = MAJOR(devno); cdev_add(multi_cdev , devno, count); cls = class_create(THIS_MODULE, DEV_NAME); } /*---------------下面是给待加驱动用的----------------------*/ xxx_register(struct *multidevice dev){ dev_t dev; if (dev->minor == MISC_DYNAMIC_MINOR) { int i = find_first_zero_bit(minors_map, DYNAMIC_MINORS); dev->minor = DYNAMIC_MINORS - i - 1; set_bit(i, minors_map); } dev_t pri_devno = MKDEV(major, dev->minor); device_create(multi_class, NULL, pri_devno, "%s", misc->name); list_add(dev->head, &multi_dev_list); } EXPORT_SYMBOL(xxx_register)
通过本文,我们了解了misc子系统和3+2+1设备识别驱动框架的原理和方法,它们可以用来实现一些特殊的设备驱动,如识别设备,虚拟设备,混合设备等。我们应该根据实际需求选择合适的框架,并遵循一些基本原则,如使用正确的注册和注销函数,使用正确的文件操作结构体,使用正确的设备树节点等。misc子系统和3+2+1设备识别驱动框架是Linux内核中两个有用而灵活的设备驱动框架,它们可以提升设备驱动的兼容性和可扩展性,也可以提升开发者的效率和质量。希望本文能够对你有所帮助和启发。
The above is the detailed content of Two special device driver frameworks in the Linux kernel: misc subsystem and 3+2+1 device recognition driver framework. For more information, please follow other related articles on the PHP Chinese website!