zoukankan      html  css  js  c++  java
  • 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. ", 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 ", 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 ", 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 ", 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 ", __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.             break;  
      21.     }  
      22.     class_dev_iter_exit(&iter);  
      23.   
      24.     return error;  
      25. }<pre class="cpp" name="code">struct device *class_find_device(struct class *class, struct device *start,  
      26.                  void *data,  
      27.                  int (*match)(struct device *, void *))  
      28. {  
      29.     struct class_dev_iter iter;  
      30.     struct device *dev;  
      31.   
      32.     if (!class)  
      33.         return NULL;  
      34.     if (!class->p) {  
      35.         WARN(1, "%s called for class '%s' before it was initialized",  
      36.              __func__, class->name);  
      37.         return NULL;  
      38.     }  
      39.   
      40.     class_dev_iter_init(&iter, class, start, NULL);  
      41.     while ((dev = class_dev_iter_next(&iter))) {  
      42.         if (match(dev, data)) {  
      43.             get_device(dev);  
      44.             break;  
      45.         }  
      46.     }  
      47.     class_dev_iter_exit(&iter);  
      48.   
      49.     return dev;  
      50. }</pre>  
      51. <pre></pre>  
      52. <p class="cpp" name="code">class_for_each_device()是对class的设备链表上的每个设备调用指定的函数。</p>  
      53. <p class="cpp" name="code">class_find_device()查找class设备链表上的某个设备,使用指定的匹配函数。</p>  
      54. <p class="cpp" name="code"> </p>  
      55. <pre class="cpp" name="code">int class_interface_register(struct class_interface *class_intf)  
      56. {  
      57.     struct class *parent;  
      58.     struct class_dev_iter iter;  
      59.     struct device *dev;  
      60.   
      61.     if (!class_intf || !class_intf->class)  
      62.         return -ENODEV;  
      63.   
      64.     parent = class_get(class_intf->class);  
      65.     if (!parent)  
      66.         return -EINVAL;  
      67.   
      68.     mutex_lock(&parent->p->class_mutex);  
      69.     list_add_tail(&class_intf->node, &parent->p->class_interfaces);  
      70.     if (class_intf->add_dev) {  
      71.         class_dev_iter_init(&iter, parent, NULL, NULL);  
      72.         while ((dev = class_dev_iter_next(&iter)))  
      73.             class_intf->add_dev(dev, class_intf);  
      74.         class_dev_iter_exit(&iter);  
      75.     }  
      76.     mutex_unlock(&parent->p->class_mutex);  
      77.   
      78.     return 0;  
      79. }</pre>  
      80. <p class="cpp" name="code">class_interface_register()把class_interface添加到指定的class上。</p>  
      81. <p class="cpp" name="code">调用class_get()获取class的引用计数。</p>  
      82. <p class="cpp" name="code">使用class->class_mutex进行保护。</p>  
      83. <p class="cpp" name="code">将classs_intf添加到class的接口列表中。</p>  
      84. <p class="cpp" name="code">对已经添加到class上的设备补上add_dev()操作。</p>  
      85. <p class="cpp" name="code">这里使用的class->class_mutex是用来保护class的类接口链表。对于简单的list_head来说,这种mutex保护是应该的。但对于武装到牙齿的klist来说,就完全不必要了,因为klist内置了spinlock来完成互斥的操作。所以之前其它的klist链表操作都没有mutex保护。</p>  
      86. <p class="cpp" name="code">比较spinlock和mutex的话,spinlock操作要比mutex快很多,因为对mutex的操作本身就需要spinlock来保护。但mutex的好处是它可以阻塞。使用spinlock时间太长的话,一是浪费cpu时间,二是禁止了任务抢占。klist是使用spinlock来保护的,这适合大部分情况,但在klist遍历时也可能调用一些未知的操作,它们可能很耗时,甚至可能阻塞,这时最好能使用mutex加以替换。</p>  
      87. <p class="cpp" name="code"> </p>  
      88. <pre class="cpp" name="code">void class_interface_unregister(struct class_interface *class_intf)  
      89. {  
      90.     struct class *parent = class_intf->class;  
      91.     struct class_dev_iter iter;  
      92.     struct device *dev;  
      93.   
      94.     if (!parent)  
      95.         return;  
      96.   
      97.     mutex_lock(&parent->p->class_mutex);  
      98.     list_del_init(&class_intf->node);  
      99.     if (class_intf->remove_dev) {  
      100.         class_dev_iter_init(&iter, parent, NULL, NULL);  
      101.         while ((dev = class_dev_iter_next(&iter)))  
      102.             class_intf->remove_dev(dev, class_intf);  
      103.         class_dev_iter_exit(&iter);  
      104.     }  
      105.     mutex_unlock(&parent->p->class_mutex);  
      106.   
      107.     class_put(parent);  
      108. }</pre>  
      109. <p class="cpp" name="code">class_interface_unregister()从class中去除指定的class_interface。对于这些class_interface来说,自己注销和设备注销效果是一样的,都会调用相应的remove_dev()。</p>  
      110. <p class="cpp" name="code"><br>  
      111. <br>  
      112.  </p>  
      113. <pre class="cpp" name="code">struct class_compat {  
      114.     struct kobject *kobj;  
      115. };  
      116.   
      117. /** 
      118.  * class_compat_register - register a compatibility class 
      119.  * @name: the name of the class 
      120.  * 
      121.  * Compatibility class are meant as a temporary user-space compatibility 
      122.  * workaround when converting a family of class devices to a bus devices. 
      123.  */  
      124. struct class_compat *class_compat_register(const char *name)  
      125. {  
      126.     struct class_compat *cls;  
      127.   
      128.     cls = kmalloc(sizeof(struct class_compat), GFP_KERNEL);  
      129.     if (!cls)  
      130.         return NULL;  
      131.     cls->kobj = kobject_create_and_add(name, &class_kset->kobj);  
      132.     if (!cls->kobj) {  
      133.         kfree(cls);  
      134.         return NULL;  
      135.     }  
      136.     return cls;  
      137. }  
      138.   
      139. void class_compat_unregister(struct class_compat *cls)  
      140. {  
      141.     kobject_put(cls->kobj);  
      142.     kfree(cls);  
      143. }</pre>  
      144. <p class="cpp" name="code">在/sys/class下面,除了class类型的,还有表现起来和class相同的class_compat类型。</p>  
      145. <p class="cpp" name="code">其实class_compat就是单单为了显示一个目录,不会定义对应的属性或者函数。</p>  
      146. <p class="cpp" name="code"> </p>  
      147. <pre class="cpp" name="code">/** 
      148.  * class_compat_create_link - create a compatibility class device link to 
      149.  *                a bus device 
      150.  * @cls: the compatibility class 
      151.  * @dev: the target bus device 
      152.  * @device_link: an optional device to which a "device" link should be created 
      153.  */  
      154. int class_compat_create_link(struct class_compat *cls, struct device *dev,  
      155.                  struct device *device_link)  
      156. {  
      157.     int error;  
      158.   
      159.     error = sysfs_create_link(cls->kobj, &dev->kobj, dev_name(dev));  
      160.     if (error)  
      161.         return error;  
      162.   
      163.     /* 
      164.      * Optionally add a "device" link (typically to the parent), as a 
      165.      * class device would have one and we want to provide as much 
      166.      * backwards compatibility as possible. 
      167.      */  
      168.     if (device_link) {  
      169.         error = sysfs_create_link(&dev->kobj, &device_link->kobj,  
      170.                       "device");  
      171.         if (error)  
      172.             sysfs_remove_link(cls->kobj, dev_name(dev));  
      173.     }  
      174.   
      175.     return error;  
      176. }  
      177.   
      178. void class_compat_remove_link(struct class_compat *cls, struct device *dev,  
      179.                   struct device *device_link)  
      180. {  
      181.     if (device_link)  
      182.         sysfs_remove_link(&dev->kobj, "device");  
      183.     sysfs_remove_link(cls->kobj, dev_name(dev));  
      184. }</pre>  
      185. <p class="cpp" name="code">class_compat_create_link()的目的是在class_compat目录下建立类似于class目录下的,对设备的软链接。这个不是在标准的设备注册时调用的。</p>  
      186. <p class="cpp" name="code"> </p>  
      187. <p class="cpp" name="code"> </p>  
      188. <p class="cpp" name="code">本节我们分析完了设备驱动模型中的class,对设备驱动模型的分析也告一段落。虽然只有五个文件,但已经基本上描绘了设备驱动模型的框架。要加深对它的认识,就要在此基础上不断充实细节,用具体的设备驱动来理解。<br>  
      189. <br>  
      190. </p>  
      191.     
  • 相关阅读:
    使用Navicat for Oracle工具连接oracle
    ArcGIS中的坐标系统定义与投影转换(转)
    随鼠标移动在状态栏实时显示地图坐标(转载)
    SQL Server 2014 中废止的数据库引擎功能
    sql server 对已有数据的表,添加核查约束 [失败],请使用with nocheck 子句
    一次性预览多张图片时遇到的问题
    vue中注册多个全局过滤器
    关于vue-cli本地项目启动,手机端无法访问题(有可能是360安全卫士作的妖)
    关于vuex中mapActions传参小tips
    javaScript知识梳理String篇
  • 原文地址:https://www.cnblogs.com/terrytian88/p/10037832.html
Copyright © 2011-2022 走看看