1. 概述
在设备模型中,bus、device、device driver等等,都比较好理解,因为它们对应了实实在在的东西,所有的逻辑都是围绕着这些实体展开的。而本文所要描述的class就有些不同了,因为它是虚拟出来的,只是为了抽象设备的共性。
举个例子,一些年龄相仿、需要获取的知识相似的人,聚在一起学习,就构成了一个班级(Class)。这个班级可以有自己的名称(如295),但如果离开构成它的学生(device),它就没有任何存在意义。另外,班级存在的最大意义是什么呢?是由老师讲授的每一个课程!因为老师只需要讲一遍,一个班的学生都可以听到。不然的话(例如每个学生都在家学习),就要为每人请一个老师,讲授一遍。而讲的内容,大多是一样的,这就是极大的浪费。
设备模型中的class所提供的功能也一样了,例如一些相似的device(学生),需要向用户空间提供相似的接口(课程),如果每个设备的驱动都实现一遍的话,就会导致内核有大量的冗余代码,这就是极大的浪费。所以,class说了,我帮你们实现吧,你们会用就行了。
这就是设备模型中Class的功能,再结合内核的注释:A class is a higher-level view of a device that abstracts out low-level implementation details(include/linux/device.h line326类是抽象出低级实现细节的设备的更高级视图),就容易理解了。
2. 数据结构描述
2.1 struct class
struct class是class的抽象,它的定义include\linux\device.h,如下
1 /** 2 * struct class - device classes 3 * @name: Name of the class. 4 * @owner: The module owner. 5 * @class_attrs: Default attributes of this class. 6 * @dev_attrs: Default attributes of the devices belong to the class. 7 * @dev_bin_attrs: Default binary attributes of the devices belong to the class. 8 * @dev_kobj: The kobject that represents this class and links it into the hierarchy. 9 * @dev_uevent: Called when a device is added, removed from this class, or a 10 * few other things that generate uevents to add the environment 11 * variables. 12 * @devnode: Callback to provide the devtmpfs. 13 * @class_release: Called to release this class. 14 * @dev_release: Called to release the device. 15 * @suspend: Used to put the device to sleep mode, usually to a low power 16 * state. 17 * @resume: Used to bring the device from the sleep mode. 18 * @ns_type: Callbacks so sysfs can detemine namespaces. 19 * @namespace: Namespace of the device belongs to this class. 20 * @pm: The default device power management operations of this class. 21 * @p: The private data of the driver core, no one other than the 22 * driver core can touch this. 23 * 24 * A class is a higher-level view of a device that abstracts out low-level 25 * implementation details. Drivers may see a SCSI disk or an ATA disk, but, 26 * at the class level, they are all simply disks. Classes allow user space 27 * to work with devices based on what they do, rather than how they are 28 * connected or how they work. 29 */ 30 struct class { 31 const char *name;class的名字,在sys/class下的名字 32 struct module *owner; 33 34 struct class_attribute *class_attrs;//class的默认attribute,class注册到内核时,会自动在“/sys/class/xxx_class”下创建对应的attribute文件。 35 struct device_attribute *dev_attrs;//class下每个设备的attribute,会在设备注册到内核时,自动在该设备的sysfs目录下创建对应的attribute文件。 36 struct bin_attribute *dev_bin_attrs;//类似dev_attrs,只不过是二进制类型attribute 37 struct kobject *dev_kobj;//表示该class下的设备在/sys/dev/下的目录,现在一般有char和block两个,如果dev_kobj为NULL,则默认选择char。 38 39 int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);上报事件的回调函数,当该class下有设备发生变化时,会调用class的uevent回调函数 40 char *(*devnode)(struct device *dev, umode_t *mode); 41 42 void (*class_release)(struct class *class);//释放类的回调函数 43 void (*dev_release)(struct device *dev);//用于release class内设备的回调函数。在device_release接口中,会依次检查Device、Device Type以及Device所在的class,是否注册release接口,如果有则调用相应的release接口release设备指针 44 45 int (*suspend)(struct device *dev, pm_message_t state); 46 int (*resume)(struct device *dev); 47 48 const struct kobj_ns_type_operations *ns_type; 49 const void *(*namespace)(struct device *dev); 50 51 const struct dev_pm_ops *pm; 52 53 struct subsys_private *p;//类私有数据,同bus的私有数据 54 }
2.2 struct class_interface
struct class_interface是这样的一个结构:它允许class driver在class下有设备添加或移除的时候,调用预先设置好的回调函数(add_dev和remove_dev)。那调用它们做什么呢?想做什么都行(),由具体的class driver实现。
该结构的定义如下:
1 struct class_interface { 2 struct list_head node; 3 struct class *class; 4 5 int (*add_dev) (struct device *, struct class_interface *); 6 void (*remove_dev) (struct device *, struct class_interface *); 7 };
在device_add的最后面有使用到class_interface
1 int device_add(struct device *dev) 2 { 3 ... 4 if (dev->class) { 5 mutex_lock(&dev->class->p->mutex); 6 /* tie the class to the device */ 7 klist_add_tail(&dev->knode_class, 8 &dev->class->p->klist_devices); 9 10 /* notify any interfaces that the device is here */ 11 list_for_each_entry(class_intf, 12 &dev->class->p->interfaces, node) 13 if (class_intf->add_dev) 14 class_intf->add_dev(dev, class_intf); 15 mutex_unlock(&dev->class->p->mutex); 16 } 17 ... 18 }
可以看到在注册完一个设备后,都要通知所有interface,这个事件,具体的通知函数还是由具体的class来实现。
3 struct class嵌入的变量
3.1 device
1 struct device { 2 ... 3 struct class *class; 4 const struct attribute_group **groups; /* optional groups */ 5 6 ... 7 }
4. 功能及内部逻辑解析
4.1 class的功能
看完上面的东西,class到底提供了什么功能?怎么使用呢?让我们先看一下现有Linux系统中有关class的状况(这里以input class为例):
可以看到class下面的所有设备都是device设备下面的符号链接。class只是一个管理者和分类着。
看上面的例子,发现input class也没做什么实实在在的事儿,它(input class)的功能,仅仅是:
在/sys/class/目录下,创建一个本class的目录(input)
在本目录下,创建每一个属于该class的设备的符号链接,这样就可以在本class目录下,访问该设备的所有特性(即attribute)
另外,device在sysfs的目录下,也会创建一个subsystem的符号链接,链接到本class的目录。
4.2 class相关的API
source code 位于:drivers/base/class.c
4.2.1class_create
1 /* This is a #define to keep the compiler from merging different 2 * instances of the __key variable */ 3 #define class_create(owner, name) \ 4 ({ \ 5 static struct lock_class_key __key; \ 6 __class_create(owner, name, &__key); \ 7 })
class_create是一个宏定义,直接call 函数_class_create()创建一个class结构体
1 /** 2 * class_create - create a struct class structure 3 * @owner: pointer to the module that is to "own" this struct class 4 * @name: pointer to a string for the name of this class. 5 * @key: the lock_class_key for this class; used by mutex lock debugging 6 * 7 * This is used to create a struct class pointer that can then be used 8 * in calls to device_create(). 9 * 10 * Returns &struct class pointer on success, or ERR_PTR() on error. 11 * 12 * Note, the pointer created here is to be destroyed when finished by 13 * making a call to class_destroy(). 14 */ 15 struct class *__class_create(struct module *owner, const char *name, 16 struct lock_class_key *key) 17 { 18 struct class *cls; 19 int retval; 20 21 cls = kzalloc(sizeof(*cls), GFP_KERNEL); 22 if (!cls) { 23 retval = -ENOMEM; 24 goto error; 25 } 26 27 cls->name = name; 28 cls->owner = owner; 29 cls->class_release = class_create_release; 30 31 retval = __class_register(cls, key); 32 if (retval) 33 goto error; 34 35 return cls; 36 37 error: 38 kfree(cls); 39 return ERR_PTR(retval); 40 }
紧接着call _class_register()
1 int __class_register(struct class *cls, struct lock_class_key *key) 2 { 3 struct subsys_private *cp; 4 int error; 5 6 pr_debug("device class '%s': registering\n", cls->name); 7 8 cp = kzalloc(sizeof(*cp), GFP_KERNEL);//类同bus,分配一个私有数据 9 if (!cp) 10 return -ENOMEM; 11 klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);//初始化class 链表 12 INIT_LIST_HEAD(&cp->interfaces); 13 kset_init(&cp->glue_dirs); 14 __mutex_init(&cp->mutex, "subsys mutex", key); 15 error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name); 16 if (error) { 17 kfree(cp); 18 return error; 19 } 20 21 /* set the default /sys/dev directory for devices of this class */ 22 if (!cls->dev_kobj) 23 cls->dev_kobj = sysfs_dev_char_kobj; 24 25 #if defined(CONFIG_BLOCK) 26 /* let the block class directory show up in the root of sysfs */ 27 if (!sysfs_deprecated || cls != &block_class) 28 cp->subsys.kobj.kset = class_kset; 29 #else 30 cp->subsys.kobj.kset = class_kset; 31 #endif 32 cp->subsys.kobj.ktype = &class_ktype; 33 cp->class = cls; 34 cls->p = cp; 35 36 error = kset_register(&cp->subsys);//在/sys/class/下面创建一个目录,同时发送一个uenent时间给上层 37 if (error) { 38 kfree(cp); 39 return error; 40 } 41 error = add_class_attrs(class_get(cls)); 42 class_put(cls); 43 return error; 44 }
class的注册,是由__class_register接口(它的实现位于"drivers/base/class.c, line 609")实现的,它的处理逻辑和bus的注册类似,主要包括:
(1)为class结构中的struct subsys_private类型的指针(cp)分配空间,并初始化其中的字段,包括cp->subsys.kobj.kset、cp->subsys.kobj.ktype等等
(2)调用kset_register,注册该class(一个class就是一个子系统,因此注册class也是注册子系统)。该过程结束后,在/sys/class/目录下,就会创建对应该class(子系统)的目录
(3)调用add_class_attrs接口,将class结构中class_attrs指针所指向的attribute,添加到内核中。执行完后,在/sys/class/xxx_class/目录下,就会看到这些attribute对应的文件
4.3 回忆一下device注册时,和class有关的动作
在 linux设备驱动device和device_driver,其中struct device结构会包含一个struct class指针(这从侧面说明了class是device的集合,甚至,class可以是device的driver)。当某个class driver向内核注册了一个class后,需要使用该class的device,通过把自身的class指针指向该class即可,剩下的事情,就由内核在注册device时处理了。
在device注册时,和class有关的动作:
(1)device的注册最终是由device_add接口(drivers/base/core.c)实现了,该接口中和class有关的动作包括:
(2)调用device_add_class_symlinks接口,创建各种符号链接,即:在对应class的目录下,创建指向device的符号链接;在device的目录下,创建名称为subsystem、指向对应class目录的符号链接。
(3)调用device_add_attrs,添加由class指定的attributes(class->dev_attrs)
(4)如果存在对应该class的add_dev回调函数,调用该回调函数
上面说了很多class的具体做了哪些事,但真正有效只有一件就算是对设备分类,即以字符设备为例,一个类代表一类字符设备即代表一个主设备号,即输入类设备有共同的主设备号,led类有共同的主设备号,framebuffer类有共同的主设备号,杂散类有共同的主设备号。类和总线是同不用角度来管理设备的。
参考博文:https://blog.csdn.net/qq_16777851/java/article/details/81474374