轉自 https://blog.csdn.net/qq_20678703/article/details/52754661node
一、LINUX設備驅動模型中的bus、device、driver,。其中bus:實際的總線,device:實際的設備和接口,而driver:對應存在的驅動。linux
2、但本節要介紹的class,是設備類,徹底是抽象出來的概念,沒有對應的實體。所謂設備類,是指提供的用戶接口類似的一類設備的集合,常見的設備類的有block、tty、input、usb等等。安全
三、class對應的代碼在drivers/base/class.c中,對應的頭文件在include/linux/device.h和drivers/base/base.h中。數據結構
仍是先來看class涉及的結構。框架
- struct class {
- const char *name;
- struct module *owner;
-
- struct class_attribute *class_attrs;
- struct device_attribute *dev_attrs;
- struct kobject *dev_kobj;
-
- int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
- char *(*devnode)(struct device *dev, mode_t *mode);
-
- void (*class_release)(struct class *class);
- void (*dev_release)(struct device *dev);
-
- int (*suspend)(struct device *dev, pm_message_t state);
- int (*resume)(struct device *dev);
-
- const struct dev_pm_ops *pm;
-
- struct class_private *p;
- };
struct class就是設備驅動模型中通用的設備類結構。ide
name表明類名稱,會在「/sys/class/」目錄下體現,例如:sys/class/p但和bus/device/driver中的名稱同樣,是初始名稱,實際使用的是內部kobj包含的動態建立的名稱。函數
owner是class所屬的模塊,雖然class是涉及一類設備,但也是由相應的模塊註冊的。好比usb類就是由usb模塊註冊的。this
class_atrrs,該class的默認attribute,會在class註冊到內核時,自動在「/sys/class/xxx_class」下建立對應的attribute文件
spa
dev_attrs,該class下每一個設備的attribute,會在設備註冊到內核時,自動在該設備的sysfs目錄下建立對應的attribute文件。
.net
dev_bin_attrs,相似dev_attrs,只不過是二進制類型attribute。
class_attrs是class給本身添加的屬性,dev_attrs是class給所包含的設備添加的屬性。這裏就像bus中同樣,只是bus是bus、driver、device所有包含的。
dev_kobj是一個kobject指針。若是你的記性很好(至少要比我好得多),你應該記得在device註冊時,會在/sys/dev下建立名爲本身設備號的軟連接。但設備不知道本身屬於塊設備仍是字符設備,因此會請示本身所屬的class,class就是用dev_kobj記錄本類設備應屬於的哪一種設備。表示該class下的設備在/sys/dev/下的目錄,如今通常有char和block兩個,若是dev_kobj爲NULL,則默認選擇char。
dev_uevent()是在設備發出uevent消息時添加環境變量用的。還記得在core.c中的dev_uevent()函數,其中就包含對設備所屬bus或class中dev_uevent()方法的調用,只是bus結構中定義方法用的函數名是uevent。
devnode()返回設備節點的相對路徑名。在core.c的device_get_devnode()中有調用到。
class_release()是在class釋放時調用到的。相似於device在結構中爲本身定義的release函數。
dev_release()天然是在設備釋放時調用到的。具體在core.c的device_release()函數中調用。
suspend()是在設備休眠時調用。
resume()是恢復設備時調用。
pm是電源管理用的函數集合,在bus、driver、class中都有看到,只是在device中換成了dev_pm_info結構,但device_type中仍是隱藏着dev_pm_ops的指針。可見電源管理仍是很重要的,只是這些東西都要結合具體的設備來分析,這裏的設備驅動模型能給的,最可能是一個函數指針與通用數據的框架。
p是指向class_private結構的指針。
- struct class_private {
- struct kset class_subsys;
- struct klist class_devices;
- struct list_head class_interfaces;
- struct kset class_dirs;
- struct mutex class_mutex;
- struct class *class;
- };
- #define to_class(obj) \
- container_of(obj, struct class_private, class_subsys.kobj)
struct class_private,是class鏈接到系統中的重要結構 私有數據。
class_subsys是kset類型,表明class在sysfs中的位置。
class_devices是klist類型,是class下的設備鏈表。
class_interfaces是list_head類型的類接口鏈表,設備類接口稍後會介紹。
class_dirs也是kset類型,它並未實際在sysfs中體現,反而是其下連接了一系列膠水kobject。記得在core.c中的get_device_parent()函數,好像小蝌蚪找媽媽同樣,咱們在爲新註冊的設備尋找sysfs中能夠存放的位置。若是發現dev->class存在,而dev->parent->class不存在,就要創建一個膠水目錄,在sysfs中隔離這兩個實際上有父子關係的設備。linux這麼作也是爲了在sysfs顯示時更清晰一些。但若是父設備下有多個屬於同一類的設備,它們須要放在同一膠水目錄下。怎麼尋找這個膠水目錄有沒有創建過,就要從這裏的class_dirs下的kobject中找了。
class_mutex是互斥信號量,用於保護class內部的數據結構。
class是指回struct class的指針。
- struct class_interface {
- struct list_head node;
- struct class *class;
-
- int (*add_dev) (struct device *, struct class_interface *);
- void (*remove_dev) (struct device *, struct class_interface *);
- };
struct class_interface就是以前被串在class->p->class_interface上的類接口的結構。用於描述設備類對外的一種接口。
node就是class->p->class_interface鏈表上的節點。
class是指向所屬class的指針。
add_dev()是在有設備添加到所屬class時調用的函數。固然,若是class_interface比設備更晚添加到class,也會補上的。
remove_dev()是在設備刪除時調用的。
從結構來看class_interface真是太簡單了。咱們都懷疑其到底有沒有用。但每每看起來簡單的內容實際可能更復雜,好比driver,還有這裏的class_interface。
- struct class_attribute {
- struct attribute attr;
- ssize_t (*show)(struct class *class, char *buf);
- ssize_t (*store)(struct class *class, const char *buf, size_t count);
- };
-
- #define CLASS_ATTR(_name, _mode, _show, _store) \
- struct class_attribute class_attr_##_name = __ATTR(_name, _mode, _show, _store)
從bus_attribute,到driver_attribute,到device_attribute,固然也少不了這裏的class_attribute。struct attribute封裝這種東西,既簡單又耐用,何樂而不爲?
結構講完了,下面看看class.c中的實現,仍是我喜歡的自上而下式。
- #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
-
- static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr,
- char *buf)
- {
- struct class_attribute *class_attr = to_class_attr(attr);
- struct class_private *cp = to_class(kobj);
- ssize_t ret = -EIO;
-
- if (class_attr->show)
- ret = class_attr->show(cp->class, buf);
- return ret;
- }
-
- static ssize_t class_attr_store(struct kobject *kobj, struct attribute *attr,
- const char *buf, size_t count)
- {
- struct class_attribute *class_attr = to_class_attr(attr);
- struct class_private *cp = to_class(kobj);
- ssize_t ret = -EIO;
-
- if (class_attr->store)
- ret = class_attr->store(cp->class, buf, count);
- return ret;
- }
-
- static struct sysfs_ops class_sysfs_ops = {
- .show = class_attr_show,
- .store = class_attr_store,
- };
class_sysfs_ops就是class定義的sysfs讀寫函數集合。
- static void class_release(struct kobject *kobj)
- {
- struct class_private *cp = to_class(kobj);
- struct class *class = cp->class;
-
- pr_debug("class '%s': release.\n", class->name);
-
- if (class->class_release)
- class->class_release(class);
- else
- pr_debug("class '%s' does not have a release() function, "
- "be careful\n", class->name);
- }
-
- static struct kobj_type class_ktype = {
- .sysfs_ops = &class_sysfs_ops,
- .release = class_release,
- };
class_release()是在class引用計數降爲零時調用的釋放函數。由於class在結構中提供了class_release的函數指針,因此能夠由具體的class調用相應的處理方法。
class_ktype是爲class對應的kobject(也能夠說kset)定義的kobj_type。
- static struct kset *class_kset;
-
- int __init classes_init(void)
- {
- class_kset = kset_create_and_add("class", NULL, NULL); //class_kset表明了/sys/class
- if (!class_kset)
- return -ENOMEM;
- return 0;
- }
class_kset表明了/sys/class對應的kset,在classes_init()中建立。
classes_init()的做用,和以前見到的buses_init()、devices_init()做用類似,都是構建/sys下的主要目錄結構。
- int class_create_file(struct class *cls, const struct class_attribute *attr)
- {
- int error;
- if (cls)
- error = sysfs_create_file(&cls->p->class_subsys.kobj,
- &attr->attr); //cls->p->class_subsys.kobj 表明sys/class/下的目錄 例如:sys/class/video4linux
- else
- error = -EINVAL;
- return error;
- }
-
- void class_remove_file(struct class *cls, const struct class_attribute *attr)
- {
- if (cls)
- sysfs_remove_file(&cls->p->class_subsys.kobj, &attr->attr);
- }
class_create_file()建立class的屬性文件。
class_remove_files()刪除class的屬性文件。這兩個都是對外提供的API。
- static struct class *class_get(struct class *cls)
- {
- if (cls)
- kset_get(&cls->p->class_subsys); //class_subsys是一個容器,kset,其容器自己也是一個 kobj
- return cls;
- }
-
- static void class_put(struct class *cls)
- {
- if (cls)
- kset_put(&cls->p->class_subsys);
- }
class_get()增長對cls的引用計數,class_put()減小對cls的引用計數,並在計數降爲零時調用相應的釋放函數,也就是以前見過的class_release函數。
class的引用計數是由class_private結構中的kset來管的,kset又是由其內部kobject來管的,kobject又是調用其結構中的kref來管的。這是一種嵌套的封裝技術。
- static int add_class_attrs(struct class *cls)
- {
- int i;
- int error = 0;
-
- if (cls->class_attrs) {
- for (i = 0; attr_name(cls->class_attrs[i]); i++) {
- error = class_create_file(cls, &cls->class_attrs[i]);
- if (error)
- goto error;
- }
- }
- done:
- return error;
- error:
- while (--i >= 0)
- class_remove_file(cls, &cls->class_attrs[i]);
- goto done;
- }
-
- static void remove_class_attrs(struct class *cls)
- {
- int i;
-
- if (cls->class_attrs) {
- for (i = 0; attr_name(cls->class_attrs[i]); i++)
- class_remove_file(cls, &cls->class_attrs[i]);
- }
- }
add_class_attrs()把cls->class_attrs中的屬性加入sysfs。
remove_class_attrs()把cls->class_attrs中的屬性刪除。
到了class這個級別,就和bus同樣,除了本身,沒有其它結構能爲本身添加屬性。
- static void klist_class_dev_get(struct klist_node *n)
- {
- struct device *dev = container_of(n, struct device, knode_class);
-
- get_device(dev);
- }
-
- static void klist_class_dev_put(struct klist_node *n)
- {
- struct device *dev = container_of(n, struct device, knode_class);
-
- put_device(dev);
- }
klist_class_dev_get()增長節點對應設備的引用計數,klist_class_dev_put()減小節點對應設備的引用計數。
這是class的設備鏈表,在節點添加和刪除時調用的。類似的klist鏈表,還有驅動的設備鏈表,不過因爲linux對驅動不太信任,因此沒有讓驅動佔用設備的引用計數。還有總線的設備鏈表,在添加釋放節點時分別調用klist_devices_get()和list_devices_put(),是在bus.c中定義的。還有設備的子設備鏈表,在添加釋放節點時分別調用klist_children_get()和klist_children_put(),是在device.c中定義的。看來klist中的get()/put()函數,是在初始化klist時設定的,也由建立方負責實現。
- #define class_register(class) \
- ({ \
- static struct lock_class_key __key; \
- __class_register(class, &__key); \
- })
-
- int __class_register(struct class *cls, struct lock_class_key *key) //就是填充class_private 私有數據結構體。。而後註冊到內核中
- {
- struct class_private *cp;
- int error;
-
- pr_debug("device class '%s': registering\n", cls->name);
-
- cp = kzalloc(sizeof(*cp), GFP_KERNEL);
- if (!cp)
- return -ENOMEM;
- klist_init(&cp->class_devices, klist_class_dev_get, klist_class_dev_put); //設備節點列表初始化,初始化klist的結構。若是klist_node結構將要被嵌入引用計數的對 象(所必需的安全的刪除),則得到/放參數用於初始化該採起的功能並釋放嵌入對象的引用。
- INIT_LIST_HEAD(&cp->class_interfaces); //初始化關聯的子系統接口列表
-
- kset_init(&cp->class_dirs);
- __mutex_init(&cp->class_mutex, "struct class mutex", key);
- error = kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name); //設置類的名字,例如video4linux ,sys/class/video4linux
- if (error) {
- kfree(cp);
- return error;
- }
-
-
- if (!cls>dev_kobj) //表示該class下的設備在/sys/dev/下的目錄,如今通常有char和block兩個,若是dev_kobj爲NULL,則默認選擇char。
- cls->dev_kobj = sysfs_dev_char_kobj;
-
- #if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK)
-
- if (cls != &block_class)
- cp->class_subsys.kobj.kset = class_kset; //
- #else
- cp->class_subsys.kobj.kset = class_kset; //設置 例如:video4linux的頂級容器,sys/class
- #endif
- cp->class_subsys.kobj.ktype = &class_ktype; //設zhi 例如:video4linux的類型
- cp->class = cls; //將類class 賦給 私有數據結構體class_private
- cls->p = cp; //將私有數據結構體class_private 賦給類class的私有數據結構體class_private
-
- error = kset_register(&cp->class_subsys); //註冊進入內核,建立目錄 sys/class/video4linux
- if (error) {
- kfree(cp);
- return error;
- }
- error = add_class_attrs(class_get(cls)); //添加類屬性,並增長模塊引用計數
- class_put(cls); //減小模塊引用計數
- return error;
- }
class_register()將class註冊到系統中。之因此把class_register()寫成宏定義的形式,彷佛是爲了__key的不一樣實例合併,在__class_register()中確實使用了__key,可是是爲了調試class中使用的mutex用的。__key的類型lock_class_key是隻有使用LOCKDEP定義時纔會有內容,寫成這樣也許是爲了在lock_class_key定義爲空時減小一些沒必要要的空間消耗。總之這類trick的作法,是不會影響咱們理解代碼邏輯的。
__class_register()中進行實際的class註冊工做:
先是分配和初始化class_private結構。
能夠看到對cp->class_dirs,只是調用kset_init()定義,並未實際註冊到sysfs中。
調用kobject_set_name()建立kobj中實際的類名。
cls->dev_kobj若是未設置,這裏會被設爲sysfs_dev_char_kobj。
調用kset_register()將class註冊到sysfs中,所屬kset爲class_kset,使用類型爲class_ktype。由於沒有設置parent,會以/sys/class爲父目錄。
最後調用add_class_attrs()添加相關的屬性文件。
在bus、device、driver、class中,最簡單的註冊過程就是class的註冊,由於它不只和bus同樣屬於一種頂層結構,並且連通用的屬性文件都不須要,全部的操做就圍繞在class_private的建立初始化與添加到sysfs上面。
- void class_unregister(struct class *cls)
- {
- pr_debug("device class '%s': unregistering\n", cls->name);
- remove_class_attrs(cls);
- kset_unregister(&cls->p->class_subsys);
- }
class_unregister()取消class的註冊。它的操做也簡單到了極點。
只是這裏的class註銷也太懶了些。不管是class_unregister(),仍是在計數徹底釋放時調用的class_release(),都找不到釋放class_private結構的地方。這是bug嗎?
我懷着敬畏的心情,查看了linux-3.0.4中的drivers/base/class.c,發現其中的class_release()函數最後添加了釋放class_private結構的代碼。看來linux-2.6.32仍是有較爲明顯的缺陷。奈何木已成舟,只能先把這個bug在現有代碼裏改正,至少之後本身編譯內核時不會再這個問題上出錯。
不過提及來,像bus_unregister()、class_unregister()這種函數,估計只有在關機時纔可能調用獲得,實在是可有可無。
- #define class_create(owner, name) \
- ({ \
- static struct lock_class_key __key; \
- __class_create(owner, name, &__key); \
- })
-
- struct class *__class_create(struct module *owner, const char *name,
- struct lock_class_key *key)
- {
- struct class *cls;
- int retval;
-
- cls = kzalloc(sizeof(*cls), GFP_KERNEL); //分配類結構體內存
- if (!cls) {
- retval = -ENOMEM;
- goto error;
- }
-
- cls->name = name; //填充類的名字,例如:video4linux gpio i2c等等。
- cls->owner = owner; //填充類所屬模塊
- cls->class_release = class_create_release; //類的釋放函數
-
- retval = __class_register(cls, key); //在內核中註冊一個類
-
- if (retval)
- goto error;
-
- return cls;
-
- error:
- kfree(cls);
- return ERR_PTR(retval);
- }
class_create()是提供給外界快速建立class的API。應該說,class中能夠提供的一系列函數,這裏都沒有提供,或許能夠在建立後再加上。
類似的函數是在core.c中的device_create(),那是提供一種快速建立device的API。
- static void class_create_release(struct class *cls)
- {
- pr_debug("%s called for %s\n", __func__, cls->name);
- kfree(cls);
- }
-
- void class_destroy(struct class *cls)
- {
- if ((cls == NULL) || (IS_ERR(cls)))
- return;
-
- class_unregister(cls);
- }
class_destroy()是與class_create()相對的刪除class的函數。
雖然在class_destroy()中沒有看到釋放class內存的代碼,但這是在class_create_release()中作的。class_create_release()以前已經在class_create()中被做爲class結構中定義的class_release()函數,會在class引用計數降爲零時被調用。
在class中,class結構和class_private結構都是在class引用計數降爲零時才釋放的。這保證了即便class已經被註銷,仍然不會影響其下設備的正常使用。但在bus中,bus_private結構是在bus_unregister()中就被釋放的。沒有了bus_private,bus下面的device和driver想必都沒法正常工做了吧。這或許和bus對應與實際總線有關。總線都沒了,下面的設備天然沒人用了。
class爲了遍歷設備鏈表,特地定義了專門的結構和遍歷函數,實現以下。
- struct class_dev_iter {
- struct klist_iter ki;
- const struct device_type *type;
- };
-
- void class_dev_iter_init(struct class_dev_iter *iter, struct class *class,
- struct device *start, const struct device_type *type)
- {
- struct klist_node *start_knode = NULL;
-
- if (start)
- start_knode = &start->knode_class;
- klist_iter_init_node(&class->p->class_devices, &iter->ki, start_knode);
- iter->type = type;
- }
-
- struct device *class_dev_iter_next(struct class_dev_iter *iter)
- {
- struct klist_node *knode;
- struct device *dev;
-
- while (1) {
- knode = klist_next(&iter->ki);
- if (!knode)
- return NULL;
- dev = container_of(knode, struct device, knode_class);
- if (!iter->type || iter->type == dev->type)
- return dev;
- }
- }
-
- void class_dev_iter_exit(struct class_dev_iter *iter)
- {
- klist_iter_exit(&iter->ki);
- }
之因此要如此費一番周折,在klist_iter外面加上這一層封裝,徹底是爲了對鏈表進行選擇性遍歷。選擇的條件就是device_type。device_type是在device結構中使用的類型,其中定義了類似設備使用的一些處理操做,能夠說比class的劃分還要小一層。class對設備鏈表如此遍歷,也是用心良苦啊。
- int class_for_each_device(struct class *class, struct device *start,
- void *data, int (*fn)(struct device *, void *))
- {
- struct class_dev_iter iter;
- struct device *dev;
- int error = 0;
-
- if (!class)
- return -EINVAL;
- if (!class->p) {
- WARN(1, "%s called for class '%s' before it was initialized",
- __func__, class->name);
- return -EINVAL;
- }
-
- class_dev_iter_init(&iter, class, start, NULL);
- while ((dev = class_dev_iter_next(&iter))) {
- error = fn(dev, data);
- if (error)
- break;
- }
- class_dev_iter_exit(&iter);
-
- return error;
- }<pre class="cpp" name="code">struct device *class_find_device(struct class *class, struct device *start,
- void *data,
- int (*match)(struct device *, void *))
- {
- struct class_dev_iter iter;
- struct device *dev;
-
- if (!class)
- return NULL;
- if (!class->p) {
- WARN(1, "%s called for class '%s' before it was initialized",
- __func__, class->name);
- return NULL;
- }
-
- class_dev_iter_init(&iter, class, start, NULL);
- while ((dev = class_dev_iter_next(&iter))) {
- if (match(dev, data)) {
- get_device(dev);
- break;
- }
- }
- class_dev_iter_exit(&iter);
-
- return dev;
- }</pre>
- <pre></pre>
- <p class="cpp" name="code">class_for_each_device()是對class的設備鏈表上的每一個設備調用指定的函數。</p>
- <p class="cpp" name="code">class_find_device()查找class設備鏈表上的某個設備,使用指定的匹配函數。</p>
- <p class="cpp" name="code"> </p>
- <pre class="cpp" name="code">int class_interface_register(struct class_interface *class_intf)
- {
- struct class *parent;
- struct class_dev_iter iter;
- struct device *dev;
-
- if (!class_intf || !class_intf->class)
- return -ENODEV;
-
- parent = class_get(class_intf->class);
- if (!parent)
- return -EINVAL;
-
- mutex_lock(&parent->p->class_mutex);
- list_add_tail(&class_intf->node, &parent->p->class_interfaces);
- if (class_intf->add_dev) {
- class_dev_iter_init(&iter, parent, NULL, NULL);
- while ((dev = class_dev_iter_next(&iter)))
- class_intf->add_dev(dev, class_intf);
- class_dev_iter_exit(&iter);
- }
- mutex_unlock(&parent->p->class_mutex);
-
- return 0;
- }</pre>
- <p class="cpp" name="code">class_interface_register()把class_interface添加到指定的class上。</p>
- <p class="cpp" name="code">調用class_get()獲取class的引用計數。</p>
- <p class="cpp" name="code">使用class->class_mutex進行保護。</p>
- <p class="cpp" name="code">將classs_intf添加到class的接口列表中。</p>
- <p class="cpp" name="code">對已經添加到class上的設備補上add_dev()操做。</p>
- <p class="cpp" name="code">這裏使用的class->class_mutex是用來保護class的類接口鏈表。對於簡單的list_head來講,這種mutex保護是應該的。但對於武裝到牙齒的klist來講,就徹底沒必要要了,由於klist內置了spinlock來完成互斥的操做。因此以前其它的klist鏈表操做都沒有mutex保護。</p>
- <p class="cpp" name="code">比較spinlock和mutex的話,spinlock操做要比mutex快不少,由於對mutex的操做自己就須要spinlock來保護。但mutex的好處是它能夠阻塞。使用spinlock時間太長的話,一是浪費cpu時間,二是禁止了任務搶佔。klist是使用spinlock來保護的,這適合大部分狀況,但在klist遍歷時也可能調用一些未知的操做,它們可能很耗時,甚至可能阻塞,這時最好能使用mutex加以替換。</p>
- <p class="cpp" name="code"> </p>
- <pre class="cpp" name="code">void class_interface_unregister(struct class_interface *class_intf)
- {
- struct class *parent = class_intf->class;
- struct class_dev_iter iter;
- struct device *dev;
-
- if (!parent)
- return;
-
- mutex_lock(&parent->p->class_mutex);
- list_del_init(&class_intf->node);
- if (class_intf->remove_dev) {
- class_dev_iter_init(&iter, parent, NULL, NULL);
- while ((dev = class_dev_iter_next(&iter)))
- class_intf->remove_dev(dev, class_intf);
- class_dev_iter_exit(&iter);
- }
- mutex_unlock(&parent->p->class_mutex);
-
- class_put(parent);
- }</pre>
- <p class="cpp" name="code">class_interface_unregister()從class中去除指定的class_interface。對於這些class_interface來講,本身註銷和設備註銷效果是同樣的,都會調用相應的remove_dev()。</p>
- <p class="cpp" name="code"><br>
- <br>
- </p>
- <pre class="cpp" name="code">struct class_compat {
- struct kobject *kobj;
- };
-
- struct class_compat *class_compat_register(const char *name)
- {
- struct class_compat *cls;
-
- cls = kmalloc(sizeof(struct class_compat), GFP_KERNEL);
- if (!cls)
- return NULL;
- cls->kobj = kobject_create_and_add(name, &class_kset->kobj);
- if (!cls->kobj) {
- kfree(cls);
- return NULL;
- }
- return cls;
- }
-
- void class_compat_unregister(struct class_compat *cls)
- {
- kobject_put(cls->kobj);
- kfree(cls);
- }</pre>
- <p class="cpp" name="code">在/sys/class下面,除了class類型的,還有表現起來和class相同的class_compat類型。</p>
- <p class="cpp" name="code">其實class_compat就是單單爲了顯示一個目錄,不會定義對應的屬性或者函數。</p>
- <p class="cpp" name="code"> </p>
- <pre class="cpp" name="code">
- int class_compat_create_link(struct class_compat *cls, struct device *dev,
- struct device *device_link)
- {
- int error;
-
- error = sysfs_create_link(cls->kobj, &dev->kobj, dev_name(dev));
- if (error)
- return error;
-
-
- if (device_link) {
- error = sysfs_create_link(&dev->kobj, &device_link->kobj,
- "device");
- if (error)
- sysfs_remove_link(cls->kobj, dev_name(dev));
- }
-
- return error;
- }
-
- void class_compat_remove_link(struct class_compat *cls, struct device *dev,
- struct device *device_link)
- {
- if (device_link)
- sysfs_remove_link(&dev->kobj, "device");
- sysfs_remove_link(cls->kobj, dev_name(dev));
- }</pre>
- <p class="cpp" name="code">class_compat_create_link()的目的是在class_compat目錄下創建相似於class目錄下的,對設備的軟連接。這個不是在標準的設備註冊時調用的。</p>
- <p class="cpp" name="code"> </p>
- <p class="cpp" name="code"> </p>
- <p class="cpp" name="code">本節咱們分析完了設備驅動模型中的class,對設備驅動模型的分析也告一段落。雖然只有五個文件,但已經基本上描繪了設備驅動模型的框架。要加深對它的認識,就要在此基礎上不斷充實細節,用具體的設備驅動來理解。<br>
- <br>
- </p>