1. 程式人生 > >LINUX裝置驅動模型之class

LINUX裝置驅動模型之class

 

轉自 https://blog.csdn.net/qq_20678703/article/details/52754661

1、LINUX裝置驅動模型中的bus、device、driver,。其中bus:實際的匯流排,device:實際的裝置和介面,而driver:對應存在的驅動。

2、但本節要介紹的class,是裝置類,完全是抽象出來的概念,沒有對應的實體。所謂裝置類,是指提供的使用者介面相似的一類裝置的集合,常見的裝置類的有block、tty、input、usb等等。

3、class對應的程式碼在drivers/base/class.c中,對應的標頭檔案在include/linux/device.h和drivers/base/base.h

中。

還是先來看class涉及的結構。

[cpp]  view plain  copy  
  1. struct class {  
  2.     const char      *name;  
  3.     struct module       *owner;  
  4.   
  5.     struct class_attribute      *class_attrs;  
  6.     struct device_attribute     *dev_attrs;  
  7.     struct kobject          *dev_kobj;  
  8.   
  9.     int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);  
  10.     char *(*devnode)(struct device *dev, mode_t *mode);  
  11.   
  12.     void (*class_release)(struct class *class);  
  13.     void (*dev_release)(struct device *dev);  
  14.   
  15.     int (*suspend)(struct device *dev, pm_message_t state);  
  16.     int (*resume)(struct device *dev);  
  17.   
  18.     const struct dev_pm_ops *pm;  
  19.   
  20.     struct class_private *p;  
  21. };  

struct class就是裝置驅動模型中通用的裝置類結構。

name代表類名稱,會在“/sys/class/”目錄下體現,例如:sys/class/p但和bus/device/driver中的名稱一樣,是初始名稱,實際使用的是內部kobj包含的動態建立的名稱。

owner是class所屬的模組,雖然class是涉及一類裝置,但也是由相應的模組註冊的。比如usb類就是由usb模組註冊的。

class_atrrs,該class的預設attribute,會在class註冊到核心時,自動在“/sys/class/xxx_class”下建立對應的attribute檔案

dev_attrs,該class下每個裝置的attribute,會在設備註冊到核心時,自動在該裝置的sysfs目錄下建立對應的attribute檔案。

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結構的指標。

[cpp]  view plain  copy  
  1. /** 
  2.  * struct class_private - structure to hold the private to the driver core portions of the class structure. 
  3.  * 
  4.  * @class_subsys - the struct kset that defines this class.  This is the main kobject 
  5.  * @class_devices - list of devices associated with this class 
  6.  * @class_interfaces - list of class_interfaces associated with this class 
  7.  * @class_dirs - "glue" directory for virtual devices associated with this class 
  8.  * @class_mutex - mutex to protect the children, devices, and interfaces lists. 
  9.  * @class - pointer back to the struct class that this structure is associated 
  10.  * with. 
  11.  * 
  12.  * This structure is the one that is the actual kobject allowing struct 
  13.  * class to be statically allocated safely.  Nothing outside of the driver 
  14.  * core should ever touch these fields. 
  15.  */  
  16. struct class_private {  
  17.     struct kset class_subsys;  
  18.     struct klist class_devices;  
  19.     struct list_head class_interfaces;  
  20.     struct kset class_dirs;  
  21.     struct mutex class_mutex;  
  22.     struct class *class;  
  23. };  
  24. #define to_class(obj)   \  
  25.     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的指標。

[cpp]  view plain  copy  
  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. };  

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。

[cpp]  view plain  copy  
  1. struct class_attribute {  
  2.     struct attribute attr;  
  3.     ssize_t (*show)(struct class *class, char *buf);  
  4.     ssize_t (*store)(struct class *class, const char *buf, size_t count);  
  5. };  
  6.   
  7. #define CLASS_ATTR(_name, _mode, _show, _store)         \  
  8. struct class_attribute class_attr_##_name = __ATTR(_name, _mode, _show, _store)  

從bus_attribute,到driver_attribute,到device_attribute,當然也少不了這裡的class_attribute。struct attribute封裝這種東西,既簡單又耐用,何樂而不為?

 

結構講完了,下面看看class.c中的實現,還是我喜歡的自上而下式。

[cpp]  view plain  copy  
  1. #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)  
  2.   
  3. static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr,  
  4.                    char *buf)  
  5. {  
  6.     struct class_attribute *class_attr = to_class_attr(attr);  
  7.     struct class_private *cp = to_class(kobj);  
  8.     ssize_t ret = -EIO;  
  9.   
  10.     if (class_attr->show)  
  11.         ret = class_attr->show(cp->class, buf);  
  12.     return ret;  
  13. }  
  14.   
  15. static ssize_t class_attr_store(struct kobject *kobj, struct attribute *attr,  
  16.                 const char *buf, size_t count)  
  17. {  
  18.     struct class_attribute *class_attr = to_class_attr(attr);  
  19.     struct class_private *cp = to_class(kobj);  
  20.     ssize_t ret = -EIO;  
  21.   
  22.     if (class_attr->store)  
  23.         ret = class_attr->store(cp->class, buf, count);  
  24.     return ret;  
  25. }  
  26.   
  27. static struct sysfs_ops class_sysfs_ops = {  
  28.     .show   = class_attr_show,  
  29.     .store  = class_attr_store,  
  30. };  

class_sysfs_ops就是class定義的sysfs讀寫函式集合。

[cpp]  view plain  copy  
  1. static void class_release(struct kobject *kobj)  
  2. {  
  3.     struct class_private *cp = to_class(kobj);  
  4.     struct class *class = cp->class;  
  5.   
  6.     pr_debug("class '%s': release.\n", class->name);  
  7.   
  8.     if (class->class_release)  
  9.         class->class_release(class);  
  10.     else  
  11.         pr_debug("class '%s' does not have a release() function, "  
  12.              "be careful\n", class->name);  
  13. }  
  14.   
  15. static struct kobj_type class_ktype = {  
  16.     .sysfs_ops  = &class_sysfs_ops,  
  17.     .release    = class_release,  
  18. };  

class_release()是在class引用計數降為零時呼叫的釋放函式。因為class在結構中提供了class_release的函式指標,所以可以由具體的class呼叫相應的處理方法。

class_ktype是為class對應的kobject(也可以說kset)定義的kobj_type。

[cpp]  view plain  copy  
  1. /* Hotplug events for classes go to the class class_subsys */  
  2. static struct kset *class_kset;  
  3.   
  4. int __init classes_init(void)  
  5. {  
  6.     class_kset = kset_create_and_add("class", NULL, NULL);   //class_kset代表了/sys/class
  7.     if (!class_kset)  
  8.         return -ENOMEM;  
  9.     return 0;  
  10. }  

class_kset代表了/sys/class對應的kset,在classes_init()中建立。

classes_init()的作用,和之前見到的buses_init()、devices_init()作用相似,都是構建/sys下的主要目錄結構。

[cpp]  view plain  copy  
  1. int class_create_file(struct class *cls, const struct class_attribute *attr)  
  2. {  
  3.     int error;  
  4.     if (cls)  
  5.         error = sysfs_create_file(&cls->p->class_subsys.kobj,  
  6.                       &attr->attr);  //cls->p->class_subsys.kobj 代表sys/class/下的目錄  例如:sys/class/video4linux
  7.     else  
  8.         error = -EINVAL;  
  9.     return error;  
  10. }  
  11.   
  12. void class_remove_file(struct class *cls, const struct class_attribute *attr)  
  13. {  
  14.     if (cls)  
  15.         sysfs_remove_file(&cls->p->class_subsys.kobj, &attr->attr);  
  16. }  

class_create_file()建立class的屬性檔案。

class_remove_files()刪除class的屬性檔案。這兩個都是對外提供的API。

[cpp]  view plain  copy  
  1. static struct class *class_get(struct class *cls)  
  2. {  
  3.     if (cls)  
  4.         kset_get(&cls->p->class_subsys);  //class_subsys是一個容器,kset,其容器本身也是一個 kobj
  5.     return cls;  
  6. }  
  7.   
  8. static void class_put(struct class *cls)  
  9. {  
  10.     if (cls)  
  11.         kset_put(&cls->p->class_subsys);  
  12. }  

class_get()增加對cls的引用計數,class_put()減少對cls的引用計數,並在計數降為零時呼叫相應的釋放函式,也就是之前見過的class_release函式。

class的引用計數是由class_private結構中的kset來管的,kset又是由其內部kobject來管的,kobject又是呼叫其結構中的kref來管的。這是一種巢狀的封裝技術。

[cpp]  view plain  copy  
  1. static int add_class_attrs(struct class *cls)  
  2. {  
  3.     int i;  
  4.     int error = 0;  
  5.   
  6.     if (cls->class_attrs) {  
  7.         for (i = 0; attr_name(cls->class_attrs[i]); i++) {  
  8.             error = class_create_file(cls, &cls->class_attrs[i]);  
  9.             if (error)  
  10.                 goto error;  
  11.         }  
  12.     }  
  13. done:  
  14.     return error;  
  15. error:  
  16.     while (--i >= 0)  
  17.         class_remove_file(cls, &cls->class_attrs[i]);  
  18.     goto done;  
  19. }  
  20.   
  21. static void remove_class_attrs(struct class *cls)  
  22. {  
  23.     int i;  
  24.   
  25.     if (cls->class_attrs) {  
  26.         for (i = 0; attr_name(cls->class_attrs[i]); i++)  
  27.             class_remove_file(cls, &cls->class_attrs[i]);  
  28.     }  
  29. }  

add_class_attrs()把cls->class_attrs中的屬性加入sysfs。

remove_class_attrs()把cls->class_attrs中的屬性刪除。

到了class這個級別,就和bus一樣,除了自己,沒有其它結構能為自己新增屬性。

[cpp]  view plain  copy  
  1. static void klist_class_dev_get(struct klist_node *n)  
  2. {  
  3.     struct device *dev = container_of(n, struct device, knode_class);  
  4.   
  5.     get_device(dev);  
  6. }  
  7.   
  8. static void klist_class_dev_put(struct klist_node *n)  
  9. {  
  10.     struct device *dev = container_of(n, struct device, knode_class);  
  11.   
  12.     put_device(dev);  
  13. }  

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時設定的,也由建立方負責實現。

[cpp]  view plain  copy  
  1. /* This is a #define to keep the compiler from merging different 
  2.  * instances of the __key variable */  
  3. #define class_register(class)           \  
  4. ({                      \  
  5.     static struct lock_class_key __key; \  
  6.     __class_register(class, &__key);    \  
  7. })  
  8.   
  9. int __class_register(struct class *cls, struct lock_class_key *key)    //就是填充class_private 私有資料結構體。。然後註冊到核心中
  10. {  
  11.     struct class_private *cp;  
  12.     int error;  
  13.   
  14.     pr_debug("device class '%s': registering\n", cls->name);  
  15.   
  16.     cp = kzalloc(sizeof(*cp), GFP_KERNEL);  
  17.     if (!cp)  
  18.         return -ENOMEM;  
  19.     klist_init(&cp->class_devices, klist_class_dev_get, klist_class_dev_put);  //裝置節點列表初始化,初始化klist的結構。如果klist_node結構將要被嵌入引用計數的對       象(所必需的安全的刪除),則獲得/放參數用於初始化該採取的功能並釋放嵌入物件的引用。
  20.     INIT_LIST_HEAD(&cp->class_interfaces);  //初始化關聯的子系統介面列表
  21.  
  22.     kset_init(&cp->class_dirs);  
  23.     __mutex_init(&cp->class_mutex, "struct class mutex", key);  
  24.     error = kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name);   //設定類的名字,例如video4linux ,sys/class/video4linux
  25.     if (error) {  
  26.         kfree(cp);  
  27.         return error;  
  28.     }  
  29.   
  30.     /* set the default /sys/dev directory for devices of this class */   //裝置預設目錄sys/dev 為類裝置
  31.     if (!cls>dev_kobj) //表示該class下的裝置在/sys/dev/下的目錄,現在一般有char和block兩個,如果dev_kobj為NULL,則預設選擇char。
  32.         cls->dev_kobj = sysfs_dev_char_kobj;  
  33.   
  34. #if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK)  
  35.     /* let the block class directory show up in the root of sysfs */  
  36.     if (cls != &block_class)  
  37.         cp->class_subsys.kobj.kset = class_kset;   //
  38. #else  
  39.     cp->class_subsys.kobj.kset = class_kset;              //設定 例如:video4linux的頂級容器,sys/class
  40. #endif  
  41.     cp->class_subsys.kobj.ktype = &class_ktype;   //設zhi 例如:video4linux的型別
  42.     cp->class = cls;   //將類class 賦給 私有資料結構體class_private
  43.     cls->p = cp;   //將私有資料結構體class_private 賦給類class的私有資料結構體class_private
  44.   
  45.     error = kset_register(&cp->class_subsys);   //註冊進入核心,建立目錄 sys/class/video4linux
  46.     if (error) {  
  47.         kfree(cp);  
  48.         return error;  
  49.     }  
  50.     error = add_class_attrs(class_get(cls));                                                           //新增類屬性,並增加模組引用計數 
  51.     class_put(cls);   //減少模組引用計數
  52.     return error;  
  53. }  

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上面。

[cpp]  view plain  copy  
  1. void class_unregister(struct class *cls)  
  2. {  
  3.     pr_debug("device class '%s': unregistering\n", cls->name);  
  4.     remove_class_attrs(cls);  
  5.     kset_unregister(&cls->p->class_subsys);  
  6. }  

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()這種函式,估計只有在關機時才可能呼叫得到,實在是無關緊要。

[cpp]  view plain  copy  
  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. })  
  8.   
  9. /** 
  10.  * class_create - create a struct class structure 
  11.  * @owner: pointer to the module that is to "own" this struct class 
  12.  * @name: pointer to a string for the name of this class. 
  13.  * @key: the lock_class_key for this class; used by mutex lock debugging 
  14.  * 
  15.  * This is used to create a struct class pointer that can then be used 
  16.  * in calls to device_create(). 
  17.  * 
  18.  * Note, the pointer created here is to be destroyed when finished by 
  19.  * making a call to class_destroy(). 
  20.  */  
  21. struct class *__class_create(struct module *owner, const char *name,  
  22.                  struct lock_class_key *key)  
  23. {  
  24.     struct class *cls;  
  25.     int retval;  
  26.   
  27.     cls = kzalloc(sizeof(*cls), GFP_KERNEL);                   //分配類結構體記憶體
  28.     if (!cls) {  
  29.         retval = -ENOMEM;  
  30.         goto error;  
  31.     }  
  32.   
  33.     cls->name = name;   //填充類的名字,例如:video4linux gpio i2c等等。
  34.     cls->owner = owner;   //填充類所屬模組
  35.     cls->class_release = class_create_release;   //類的釋放函式
  36.   
  37.     retval = __class_register(cls, key);   //在核心中註冊一個類
  38.  
  39.     if (retval)  
  40.         goto error;  
  41.   
  42.     return cls;  
  43.   
  44. error:  
  45.     kfree(cls);  
  46.     return ERR_PTR(retval);  
  47. }  

class_create()是提供給外界快速建立class的API。應該說,class中可以提供的一系列函式,這裡都沒有提供,或許可以在建立後再加上。

相似的函式是在core.c中的device_create(),那是提供一種快速建立device的API。

[cpp]  view plain  copy  
  1. static void class_create_release(struct class *cls)  
  2. {  
  3.     pr_debug("%s called for %s\n", __func__, cls->name);  
  4.     kfree(cls);  
  5. }  
  6.   
  7. /** 
  8.  * class_destroy - destroys a struct class structure 
  9.  * @cls: pointer to the struct class that is to be destroyed 
  10.  * 
  11.  * Note, the pointer to be destroyed must have been created with a call 
  12.  * to class_create(). 
  13.  */  
  14. void class_destroy(struct class *cls)  
  15. {  
  16.     if ((cls == NULL) || (IS_ERR(cls)))  
  17.         return;  
  18.   
  19.     class_unregister(cls);  
  20. }  

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為了遍歷裝置連結串列,特意定義了專門的結構和遍歷函式,實現如下。

[cpp]  view plain  copy  
  1. struct class_dev_iter {  
  2.     struct klist_iter       ki;  
  3.     const struct device_type    *type;  
  4. };  
  5.   
  6. /** 
  7.  * class_dev_iter_init - initialize class device iterator  初始化類裝置遍歷表
  8.  * @iter: class iterator to initialize 
  9.  * @class: the class we wanna iterate over 
  10.  * @start: the device to start iterating from, if any 
  11.  * @type: device_type of the devices to iterate over, NULL for all 
  12.  * 
  13.  * Initialize class iterator @iter such that it iterates over devices 
  14.  * of @class.  If @start is set, the list iteration will start there, 
  15.  * otherwise if it is NULL, the iteration starts at the beginning of    start被設定,列表遍歷從start開始;否則,從列表的表頭開始。。
  16.  * the list. 
  17.  */  
  18. void class_dev_iter_init(struct class_dev_iter *iter, struct class *class,  
  19.              struct device *start, const struct device_type *type)  
  20. {  
  21.     struct klist_node *start_knode = NULL;  
  22.   
  23.     if (start)  
  24.         start_knode = &start->knode_class;  
  25.     klist_iter_init_node(&class->p->class_devices, &iter->ki, start_knode);  
  26.     iter->type = type;  
  27. }  
  28.   
  29. struct device *class_dev_iter_next(struct class_dev_iter *iter)  
  30. {  
  31.     struct klist_node *knode;  
  32.     struct device *dev;  
  33.   
  34.     while (1) {  
  35.         knode = klist_next(&iter->ki);  
  36.         if (!knode)  
  37.             return NULL;  
  38.         dev = container_of(knode, struct device, knode_class);  
  39.         if (!iter->type || iter->type == dev->type)  
  40.             return dev;  
  41.     }  
  42. }  
  43.   
  44. void class_dev_iter_exit(struct class_dev_iter *iter)  
  45. {  
  46.     klist_iter_exit(&iter->ki);  
  47. }  

之所以要如此費一番周折,在klist_iter外面加上這一層封裝,完全是為了對連結串列進行選擇性遍歷。選擇的條件就是device_type。device_type是在device結構中使用的型別,其中定義了相似裝置使用的一些處理操作,可以說比class的劃分還要小一層。class對裝置連結串列如此遍歷,也是用心良苦啊。

[cpp]  view plain  copy  
    1. int class_for_each_device(struct class *class, struct device *start,  
    2.               void *data, int (*fn)(struct device *, void *))  
    3. {  
    4.     struct class_dev_iter iter;  
    5.     struct device *dev;  
    6.     int error = 0;  
    7.   
    8.     if (!class)  
    9.         return -EINVAL;  
    10.     if (!class->p) {  
    11.         WARN(1, "%s called for class '%s' before it was initialized",  
    12.              __func__, class->name);  
    13.         return -EINVAL;  
    14.     }  
    15.   
    16.     class_dev_iter_init(&iter, class, start, NULL);  
    17.     while ((dev = class_dev_iter_next(&iter))) {  
    18.         error = fn(dev, data);  
    19.         if (error)  
    20.           &