linux设备驱动(10)class详解

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

posted @ 2020-05-19 21:50  Action_er  阅读(2307)  评论(0编辑  收藏  举报