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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM