转自 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>