前面看过了设备驱动模型中的bus、device、driver,这三种都是有迹可循的。其中bus代表实际的总线,device代表实际的设备和接口,而driver则对应存在的驱动。但本节要介绍的class,是设备类,完全是抽象出来的概念,没有对应的实体。所谓设备类,是指提供的用户接口相似的一类设备的集合,常见的设备类的有block、tty、input、usb等等。

class对应的代码在drivers/base/class.c中,对应的头文件在include/linux/device.h和drivers/base/base.h中。

还是先来看class涉及的结构。

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

struct class就是设备驱动模型中通用的设备类结构。

name代表类名称,但和bus/device/driver中的名称一样,是初始名称,实际使用的是内部kobj包含的动态创建的名称。

owner是class所属的模块,虽然class是涉及一类设备,但也是由相应的模块注册的。比如usb类就是由usb模块注册的。

class_attrs是class给自己添加的属性,dev_attrs是class给所包含的设备添加的属性。这里就像bus中一样,只是bus是bus、driver、device全部包含的。

dev_kobj是一个kobject指针。如果你的记性很好(至少要比我好得多),你应该记得在device注册时,会在/sys/dev下创建名为自己设备号的软链接。但设备不知道自己属于块设备还是字符设备,所以会请示自己所属的class,class就是用dev_kobj记录本类设备应属于的哪种设备。

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 plaincopy
  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 plaincopy
  1. struct class_interface {
  2. struct list_head    node;
  3. struct class        *class;
  4. int (*add_dev)      (struct device *, struct class_interface *);
  5. void (*remove_dev)  (struct device *, struct class_interface *);
  6. };

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 plaincopy
  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. #define CLASS_ATTR(_name, _mode, _show, _store)         \
  7. 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 plaincopy
  1. #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
  2. static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr,
  3. char *buf)
  4. {
  5. struct class_attribute *class_attr = to_class_attr(attr);
  6. struct class_private *cp = to_class(kobj);
  7. ssize_t ret = -EIO;
  8. if (class_attr->show)
  9. ret = class_attr->show(cp->class, buf);
  10. return ret;
  11. }
  12. static ssize_t class_attr_store(struct kobject *kobj, struct attribute *attr,
  13. const char *buf, size_t count)
  14. {
  15. struct class_attribute *class_attr = to_class_attr(attr);
  16. struct class_private *cp = to_class(kobj);
  17. ssize_t ret = -EIO;
  18. if (class_attr->store)
  19. ret = class_attr->store(cp->class, buf, count);
  20. return ret;
  21. }
  22. static struct sysfs_ops class_sysfs_ops = {
  23. .show   = class_attr_show,
  24. .store  = class_attr_store,
  25. };

class_sysfs_ops就是class定义的sysfs读写函数集合。

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

class_release()是在class引用计数降为零时调用的释放函数。因为class在结构中提供了class_release的函数指针,所以可以由具体的class调用相应的处理方法。

class_ktype是为class对应的kobject(也可以说kset)定义的kobj_type。

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

class_kset代表了/sys/class对应的kset,在classes_init()中创建。

classes_init()的作用,和之前见到的buses_init()、devices_init()作用相似,都是构建/sys下的主要目录结构。

[cpp] view plaincopy
  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);
  7. else
  8. error = -EINVAL;
  9. return error;
  10. }
  11. void class_remove_file(struct class *cls, const struct class_attribute *attr)
  12. {
  13. if (cls)
  14. sysfs_remove_file(&cls->p->class_subsys.kobj, &attr->attr);
  15. }

class_create_file()创建class的属性文件。

class_remove_files()删除class的属性文件。这两个都是对外提供的API。

[cpp] view plaincopy
  1. static struct class *class_get(struct class *cls)
  2. {
  3. if (cls)
  4. kset_get(&cls->p->class_subsys);
  5. return cls;
  6. }
  7. static void class_put(struct class *cls)
  8. {
  9. if (cls)
  10. kset_put(&cls->p->class_subsys);
  11. }

class_get()增加对cls的引用计数,class_put()减少对cls的引用计数,并在计数降为零时调用相应的释放函数,也就是之前见过的class_release函数。

class的引用计数是由class_private结构中的kset来管的,kset又是由其内部kobject来管的,kobject又是调用其结构中的kref来管的。这是一种嵌套的封装技术。

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

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

remove_class_attrs()把cls->class_attrs中的属性删除。

到了class这个级别,就和bus一样,除了自己,没有其它结构能为自己添加属性。

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

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 plaincopy
  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. int __class_register(struct class *cls, struct lock_class_key *key)
  9. {
  10. struct class_private *cp;
  11. int error;
  12. pr_debug("device class '%s': registering\n", cls->name);
  13. cp = kzalloc(sizeof(*cp), GFP_KERNEL);
  14. if (!cp)
  15. return -ENOMEM;
  16. klist_init(&cp->class_devices, klist_class_dev_get, klist_class_dev_put);
  17. INIT_LIST_HEAD(&cp->class_interfaces);
  18. kset_init(&cp->class_dirs);
  19. __mutex_init(&cp->class_mutex, "struct class mutex", key);
  20. error = kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name);
  21. if (error) {
  22. kfree(cp);
  23. return error;
  24. }
  25. /* set the default /sys/dev directory for devices of this class */
  26. if (!cls->dev_kobj)
  27. cls->dev_kobj = sysfs_dev_char_kobj;
  28. #if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK)
  29. /* let the block class directory show up in the root of sysfs */
  30. if (cls != &block_class)
  31. cp->class_subsys.kobj.kset = class_kset;
  32. #else
  33. cp->class_subsys.kobj.kset = class_kset;
  34. #endif
  35. cp->class_subsys.kobj.ktype = &class_ktype;
  36. cp->class = cls;
  37. cls->p = cp;
  38. error = kset_register(&cp->class_subsys);
  39. if (error) {
  40. kfree(cp);
  41. return error;
  42. }
  43. error = add_class_attrs(class_get(cls));
  44. class_put(cls);
  45. return error;
  46. }

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 plaincopy
  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 plaincopy
  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. * class_create - create a struct class structure
  10. * @owner: pointer to the module that is to "own" this struct class
  11. * @name: pointer to a string for the name of this class.
  12. * @key: the lock_class_key for this class; used by mutex lock debugging
  13. *
  14. * This is used to create a struct class pointer that can then be used
  15. * in calls to device_create().
  16. *
  17. * Note, the pointer created here is to be destroyed when finished by
  18. * making a call to class_destroy().
  19. */
  20. struct class *__class_create(struct module *owner, const char *name,
  21. struct lock_class_key *key)
  22. {
  23. struct class *cls;
  24. int retval;
  25. cls = kzalloc(sizeof(*cls), GFP_KERNEL);
  26. if (!cls) {
  27. retval = -ENOMEM;
  28. goto error;
  29. }
  30. cls->name = name;
  31. cls->owner = owner;
  32. cls->class_release = class_create_release;
  33. retval = __class_register(cls, key);
  34. if (retval)
  35. goto error;
  36. return cls;
  37. error:
  38. kfree(cls);
  39. return ERR_PTR(retval);
  40. }

class_create()是提供给外界快速创建class的API。应该说,class中可以提供的一系列函数,这里都没有提供,或许可以在创建后再加上。

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

[cpp] view plaincopy
  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. * class_destroy - destroys a struct class structure
  8. * @cls: pointer to the struct class that is to be destroyed
  9. *
  10. * Note, the pointer to be destroyed must have been created with a call
  11. * to class_create().
  12. */
  13. void class_destroy(struct class *cls)
  14. {
  15. if ((cls == NULL) || (IS_ERR(cls)))
  16. return;
  17. class_unregister(cls);
  18. }

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 plaincopy
  1. struct class_dev_iter {
  2. struct klist_iter       ki;
  3. const struct device_type    *type;
  4. };
  5. /**
  6. * class_dev_iter_init - initialize class device iterator
  7. * @iter: class iterator to initialize
  8. * @class: the class we wanna iterate over
  9. * @start: the device to start iterating from, if any
  10. * @type: device_type of the devices to iterate over, NULL for all
  11. *
  12. * Initialize class iterator @iter such that it iterates over devices
  13. * of @class.  If @start is set, the list iteration will start there,
  14. * otherwise if it is NULL, the iteration starts at the beginning of
  15. * the list.
  16. */
  17. void class_dev_iter_init(struct class_dev_iter *iter, struct class *class,
  18. struct device *start, const struct device_type *type)
  19. {
  20. struct klist_node *start_knode = NULL;
  21. if (start)
  22. start_knode = &start->knode_class;
  23. klist_iter_init_node(&class->p->class_devices, &iter->ki, start_knode);
  24. iter->type = type;
  25. }
  26. struct device *class_dev_iter_next(struct class_dev_iter *iter)
  27. {
  28. struct klist_node *knode;
  29. struct device *dev;
  30. while (1) {
  31. knode = klist_next(&iter->ki);
  32. if (!knode)
  33. return NULL;
  34. dev = container_of(knode, struct device, knode_class);
  35. if (!iter->type || iter->type == dev->type)
  36. return dev;
  37. }
  38. }
  39. void class_dev_iter_exit(struct class_dev_iter *iter)
  40. {
  41. klist_iter_exit(&iter->ki);
  42. }

之所以要如此费一番周折,在klist_iter外面加上这一层封装,完全是为了对链表进行选择性遍历。选择的条件就是device_type。device_type是在device结构中使用的类型,其中定义了相似设备使用的一些处理操作,可以说比class的划分还要小一层。class对设备链表如此遍历,也是用心良苦啊。

[cpp] view plaincopy
  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. if (!class)
  8. return -EINVAL;
  9. if (!class->p) {
  10. WARN(1, "%s called for class '%s' before it was initialized",
  11. __func__, class->name);
  12. return -EINVAL;
  13. }
  14. class_dev_iter_init(&iter, class, start, NULL);
  15. while ((dev = class_dev_iter_next(&iter))) {
  16. error = fn(dev, data);
  17. if (error)
  18. break;
  19. }
  20. class_dev_iter_exit(&iter);
  21. return error;
  22. }<pre class="cpp" name="code">struct device *class_find_device(struct class *class, struct device *start,
  23. void *data,
  24. int (*match)(struct device *, void *))
  25. {
  26. struct class_dev_iter iter;
  27. struct device *dev;
  28. if (!class)
  29. return NULL;
  30. if (!class->p) {
  31. WARN(1, "%s called for class '%s' before it was initialized",
  32. __func__, class->name);
  33. return NULL;
  34. }
  35. class_dev_iter_init(&iter, class, start, NULL);
  36. while ((dev = class_dev_iter_next(&iter))) {
  37. if (match(dev, data)) {
  38. get_device(dev);
  39. break;
  40. }
  41. }
  42. class_dev_iter_exit(&iter);
  43. return dev;
  44. }</pre>
  45. <pre></pre>
  46. <p class="cpp" name="code">class_for_each_device()是对class的设备链表上的每个设备调用指定的函数。</p>
  47. <p class="cpp" name="code">class_find_device()查找class设备链表上的某个设备,使用指定的匹配函数。</p>
  48. <p class="cpp" name="code"> </p>
  49. <pre class="cpp" name="code">int class_interface_register(struct class_interface *class_intf)
  50. {
  51. struct class *parent;
  52. struct class_dev_iter iter;
  53. struct device *dev;
  54. if (!class_intf || !class_intf->class)
  55. return -ENODEV;
  56. parent = class_get(class_intf->class);
  57. if (!parent)
  58. return -EINVAL;
  59. mutex_lock(&parent->p->class_mutex);
  60. list_add_tail(&class_intf->node, &parent->p->class_interfaces);
  61. if (class_intf->add_dev) {
  62. class_dev_iter_init(&iter, parent, NULL, NULL);
  63. while ((dev = class_dev_iter_next(&iter)))
  64. class_intf->add_dev(dev, class_intf);
  65. class_dev_iter_exit(&iter);
  66. }
  67. mutex_unlock(&parent->p->class_mutex);
  68. return 0;
  69. }</pre>
  70. <p class="cpp" name="code">class_interface_register()把class_interface添加到指定的class上。</p>
  71. <p class="cpp" name="code">调用class_get()获取class的引用计数。</p>
  72. <p class="cpp" name="code">使用class->class_mutex进行保护。</p>
  73. <p class="cpp" name="code">将classs_intf添加到class的接口列表中。</p>
  74. <p class="cpp" name="code">对已经添加到class上的设备补上add_dev()操作。</p>
  75. <p class="cpp" name="code">这里使用的class->class_mutex是用来保护class的类接口链表。对于简单的list_head来说,这种mutex保护是应该的。但对于武装到牙齿的klist来说,就完全不必要了,因为klist内置了spinlock来完成互斥的操作。所以之前其它的klist链表操作都没有mutex保护。</p>
  76. <p class="cpp" name="code">比较spinlock和mutex的话,spinlock操作要比mutex快很多,因为对mutex的操作本身就需要spinlock来保护。但mutex的好处是它可以阻塞。使用spinlock时间太长的话,一是浪费cpu时间,二是禁止了任务抢占。klist是使用spinlock来保护的,这适合大部分情况,但在klist遍历时也可能调用一些未知的操作,它们可能很耗时,甚至可能阻塞,这时最好能使用mutex加以替换。</p>
  77. <p class="cpp" name="code"> </p>
  78. <pre class="cpp" name="code">void class_interface_unregister(struct class_interface *class_intf)
  79. {
  80. struct class *parent = class_intf->class;
  81. struct class_dev_iter iter;
  82. struct device *dev;
  83. if (!parent)
  84. return;
  85. mutex_lock(&parent->p->class_mutex);
  86. list_del_init(&class_intf->node);
  87. if (class_intf->remove_dev) {
  88. class_dev_iter_init(&iter, parent, NULL, NULL);
  89. while ((dev = class_dev_iter_next(&iter)))
  90. class_intf->remove_dev(dev, class_intf);
  91. class_dev_iter_exit(&iter);
  92. }
  93. mutex_unlock(&parent->p->class_mutex);
  94. class_put(parent);
  95. }</pre>
  96. <p class="cpp" name="code">class_interface_unregister()从class中去除指定的class_interface。对于这些class_interface来说,自己注销和设备注销效果是一样的,都会调用相应的remove_dev()。</p>
  97. <p class="cpp" name="code"><br>
  98. <br>
  99. </p>
  100. <pre class="cpp" name="code">struct class_compat {
  101. struct kobject *kobj;
  102. };
  103. /**
  104. * class_compat_register - register a compatibility class
  105. * @name: the name of the class
  106. *
  107. * Compatibility class are meant as a temporary user-space compatibility
  108. * workaround when converting a family of class devices to a bus devices.
  109. */
  110. struct class_compat *class_compat_register(const char *name)
  111. {
  112. struct class_compat *cls;
  113. cls = kmalloc(sizeof(struct class_compat), GFP_KERNEL);
  114. if (!cls)
  115. return NULL;
  116. cls->kobj = kobject_create_and_add(name, &class_kset->kobj);
  117. if (!cls->kobj) {
  118. kfree(cls);
  119. return NULL;
  120. }
  121. return cls;
  122. }
  123. void class_compat_unregister(struct class_compat *cls)
  124. {
  125. kobject_put(cls->kobj);
  126. kfree(cls);
  127. }</pre>
  128. <p class="cpp" name="code">在/sys/class下面,除了class类型的,还有表现起来和class相同的class_compat类型。</p>
  129. <p class="cpp" name="code">其实class_compat就是单单为了显示一个目录,不会定义对应的属性或者函数。</p>
  130. <p class="cpp" name="code"> </p>
  131. <pre class="cpp" name="code">/**
  132. * class_compat_create_link - create a compatibility class device link to
  133. *                a bus device
  134. * @cls: the compatibility class
  135. * @dev: the target bus device
  136. * @device_link: an optional device to which a "device" link should be created
  137. */
  138. int class_compat_create_link(struct class_compat *cls, struct device *dev,
  139. struct device *device_link)
  140. {
  141. int error;
  142. error = sysfs_create_link(cls->kobj, &dev->kobj, dev_name(dev));
  143. if (error)
  144. return error;
  145. /*
  146. * Optionally add a "device" link (typically to the parent), as a
  147. * class device would have one and we want to provide as much
  148. * backwards compatibility as possible.
  149. */
  150. if (device_link) {
  151. error = sysfs_create_link(&dev->kobj, &device_link->kobj,
  152. "device");
  153. if (error)
  154. sysfs_remove_link(cls->kobj, dev_name(dev));
  155. }
  156. return error;
  157. }
  158. void class_compat_remove_link(struct class_compat *cls, struct device *dev,
  159. struct device *device_link)
  160. {
  161. if (device_link)
  162. sysfs_remove_link(&dev->kobj, "device");
  163. sysfs_remove_link(cls->kobj, dev_name(dev));
  164. }</pre>
  165. <p class="cpp" name="code">class_compat_create_link()的目的是在class_compat目录下建立类似于class目录下的,对设备的软链接。这个不是在标准的设备注册时调用的。</p>
  166. <p class="cpp" name="code"> </p>
  167. <p class="cpp" name="code"> </p>
  168. <p class="cpp" name="code">本节我们分析完了设备驱动模型中的class,对设备驱动模型的分析也告一段落。虽然只有五个文件,但已经基本上描绘了设备驱动模型的框架。要加深对它的认识,就要在此基础上不断充实细节,用具体的设备驱动来理解。<br>
  169. <br>
  170. </p>

linux内核部件分析之——设备驱动模型之class相关推荐

  1. linux内核部件分析(十)——设备驱动模型之class,linux内核部件分析(十)——设备驱动模型之class...

    前面看过了设备驱动模型中的bus.device.driver,这三种都是有迹可循的.其中bus代表实际的总线,device代表实际的设备和接口,而driver则对应存在的驱动.但本节要介绍的class ...

  2. linux内核组件分析之--设备驱动模型之bus

    前面我们分析了设备驱动模型中的device和driver,device和driver本来是不相关的东西,只因为bus的存在,才被联系到了一起.本节就来看看设备驱动模型中起枢纽作用的bus.本节的头文件 ...

  3. Linux内核部件分析 设备驱动模型之driver ---mark 详细

    Linux内核部件分析 设备驱动模型之driver 转载:https://www.linuxidc.com/Linux/2011-10/44627p7.htm 上节我们分析设备驱动模型中的device ...

  4. Linux内核学习-字符设备驱动学习(二)

    在Linux内核学习-字符设备驱动学习(一)中编写字符设备驱动的一种方法,但是需要手动创建设备节点. 有没有能够自动的创建设备节点的呢? 有!使用class_create()和device_creat ...

  5. linux下camera驱动分析_LINUX设备驱动模型分析之三 驱动模块相关(DRIVER)接口分析...

    本系列前几篇文章链接如下: <LINUX设备驱动模型分析之一 总体概念说明> <LINUX设备驱动模型分析之二 总线(BUS)接口分析> 上一章我们分析了bus-driver- ...

  6. Linux内核自带SPI设备驱动测试程序分析:spidev_test.c

        在Linux系统中,SPI 的用户模式设备接口的驱动源码位于 drivers/spi/spidev.c,在应用层生成 /dev/spidev* 的节点,可以通过 read. write 达到与 ...

  7. Linux I2C子系统分析-I2C设备驱动

    接下来以一个实际的例子来看I2C设备驱动,就以drivers/i2c/i2c-dev.c为例. 先看它的初始化和注销函数 [cpp] view plaincopy static int __init  ...

  8. 五.linux设备驱动模型

    站在设备驱动这个角度分析,设备驱动模型是如何构建出来,起到什么作用,认识它并在写驱动的时候去利用设备驱动模型 目录 一.linux 设备驱动模型简介 1.1. 什么是设备驱动模型 1.2. 为什么需要 ...

  9. 整理--linux设备驱动模型

    知识整理–linux设备驱动模型 以kobject为底层,组织类class.总线bus.设备device.驱动driver等高级数据结构,同时实现对象引用计数.维护对象链表.对象上锁.对用户空间的表示 ...

最新文章

  1. AOSuite V2.7 发布,JavaEE快速开发平台
  2. SAP 物料订单创建、下达、报工、收货与投料(ABAP代码)
  3. shape的各种获取、更改以及设置方式辨析
  4. TCP的三次握手与四次挥手图文
  5. 【论文知识点笔记】GNN流程到GCN流程
  6. C语言标准库函数qsort具体解释
  7. 安卓平板排行榜_shopee虾皮台湾安卓市场, shopee虾皮直播下载
  8. centos7中yum源安装mysql_centos7下使用yum安装mysql
  9. 多选框中的选中的值和未选中值的获取
  10. 应用机器学习(八):线性模型
  11. 乒乓球单循环赛_乒乓球单循环赛积分表(比赛规则、对比表)
  12. 公众号H5跳转小程序
  13. 教的好、口碑好的Web前端培训机构有哪些?
  14. 信息系统安全等级保护 备案表
  15. html+支付宝+全套页面,【支付源码】全新免签支付系统 仿码支付系统支持 微信 支付宝 QQ 全开源LPays全套源码...
  16. 高清HDMI高清编码器(HDMI网络传输器)使用及前景
  17. Python爬虫项目实战(欧美音乐网站)
  18. 防火墙单向访问控制_使用防火墙实现安全的访问控制
  19. 项目难做,程序员难当,软件开发中的 9 大难题
  20. Qt 编译报错 error: invalid use of incomplete type 'class QXxx'

热门文章

  1. boost::python::register_ptr_to_python相关的测试程序
  2. 2021-06-12
  3. boost::hana::sort用法的测试程序
  4. boost::hana::arg用法的测试程序
  5. boost::geometry::model::d2::point_xy用法的测试程序
  6. boost::fusion::as_map用法的测试程序
  7. boost::dynamic_bitset模块boost::hash的测试程序
  8. gdcm::DummyValueGenerator的测试程序
  9. 基于Boost::beast模块的HTTP客户端协程
  10. Boost:基于Boost的异步UDP回显服务器