700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > linux内核部件分析之——设备驱动模型之class

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

时间:2024-09-16 05:35:49

相关推荐

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

前面看过了设备驱动模型中的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 structclass{ constchar*name; structmodule*owner; structclass_attribute*class_attrs; structdevice_attribute*dev_attrs; structkobject*dev_kobj; int(*dev_uevent)(structdevice*dev,structkobj_uevent_env*env); char*(*devnode)(structdevice*dev,mode_t*mode); void(*class_release)(structclass*class); void(*dev_release)(structdevice*dev); int(*suspend)(structdevice*dev,pm_message_tstate); int(*resume)(structdevice*dev); conststructdev_pm_ops*pm; structclass_private*p; };

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 /** *structclass_private-structuretoholdtheprivatetothedrivercoreportionsoftheclassstructure. * *@class_subsys-thestructksetthatdefinesthisclass.Thisisthemainkobject *@class_devices-listofdevicesassociatedwiththisclass *@class_interfaces-listofclass_interfacesassociatedwiththisclass *@class_dirs-"glue"directoryforvirtualdevicesassociatedwiththisclass *@class_mutex-mutextoprotectthechildren,devices,andinterfaceslists. *@class-pointerbacktothestructclassthatthisstructureisassociated *with. * *Thisstructureistheonethatistheactualkobjectallowingstruct *classtobestaticallyallocatedsafely.Nothingoutsideofthedriver *coreshouldevertouchthesefields. */ structclass_private{ structksetclass_subsys; structklistclass_devices; structlist_headclass_interfaces; structksetclass_dirs; structmutexclass_mutex; structclass*class; }; #defineto_class(obj)\ container_of(obj,structclass_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 structclass_interface{ structlist_headnode; structclass*class; int(*add_dev)(structdevice*,structclass_interface*); void(*remove_dev)(structdevice*,structclass_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。

[cpp]view plaincopy structclass_attribute{ structattributeattr; ssize_t(*show)(structclass*class,char*buf); ssize_t(*store)(structclass*class,constchar*buf,size_tcount); }; #defineCLASS_ATTR(_name,_mode,_show,_store)\ structclass_attributeclass_attr_##_name=__ATTR(_name,_mode,_show,_store)

从bus_attribute,到driver_attribute,到device_attribute,当然也少不了这里的class_attribute。struct attribute封装这种东西,既简单又耐用,何乐而不为?

结构讲完了,下面看看class.c中的实现,还是我喜欢的自上而下式。

[cpp]view plaincopy #defineto_class_attr(_attr)container_of(_attr,structclass_attribute,attr) staticssize_tclass_attr_show(structkobject*kobj,structattribute*attr, char*buf) { structclass_attribute*class_attr=to_class_attr(attr); structclass_private*cp=to_class(kobj); ssize_tret=-EIO; if(class_attr->show) ret=class_attr->show(cp->class,buf); returnret; } staticssize_tclass_attr_store(structkobject*kobj,structattribute*attr, constchar*buf,size_tcount) { structclass_attribute*class_attr=to_class_attr(attr); structclass_private*cp=to_class(kobj); ssize_tret=-EIO; if(class_attr->store) ret=class_attr->store(cp->class,buf,count); returnret; } staticstructsysfs_opsclass_sysfs_ops={ .show=class_attr_show, .store=class_attr_store, };

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

[cpp]view plaincopy staticvoidclass_release(structkobject*kobj) { structclass_private*cp=to_class(kobj); structclass*class=cp->class; pr_debug("class'%s':release.\n",class->name); if(class->class_release) class->class_release(class); else pr_debug("class'%s'doesnothavearelease()function," "becareful\n",class->name); } staticstructkobj_typeclass_ktype={ .sysfs_ops=&class_sysfs_ops, .release=class_release, };

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

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

[cpp]view plaincopy /*Hotplugeventsforclassesgototheclassclass_subsys*/ staticstructkset*class_kset; int__initclasses_init(void) { class_kset=kset_create_and_add("class",NULL,NULL); if(!class_kset) return-ENOMEM; return0; }

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

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

[cpp]view plaincopy intclass_create_file(structclass*cls,conststructclass_attribute*attr) { interror; if(cls) error=sysfs_create_file(&cls->p->class_subsys.kobj, &attr->attr); else error=-EINVAL; returnerror; } voidclass_remove_file(structclass*cls,conststructclass_attribute*attr) { if(cls) sysfs_remove_file(&cls->p->class_subsys.kobj,&attr->attr); }

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

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

[cpp]view plaincopy staticstructclass*class_get(structclass*cls) { if(cls) kset_get(&cls->p->class_subsys); returncls; } staticvoidclass_put(structclass*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来管的。这是一种嵌套的封装技术。

[cpp]view plaincopy staticintadd_class_attrs(structclass*cls) { inti; interror=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) gotoerror; } } done: returnerror; error: while(--i>=0) class_remove_file(cls,&cls->class_attrs[i]); gotodone; } staticvoidremove_class_attrs(structclass*cls) { inti; 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一样,除了自己,没有其它结构能为自己添加属性。

[cpp]view plaincopy staticvoidklist_class_dev_get(structklist_node*n) { structdevice*dev=container_of(n,structdevice,knode_class); get_device(dev); } staticvoidklist_class_dev_put(structklist_node*n) { structdevice*dev=container_of(n,structdevice,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时设定的,也由创建方负责实现。

[cpp]view plaincopy /*Thisisa#definetokeepthecompilerfrommergingdifferent *instancesofthe__keyvariable*/ #defineclass_register(class)\ ({\ staticstructlock_class_key__key;\ __class_register(class,&__key);\ }) int__class_register(structclass*cls,structlock_class_key*key) { structclass_private*cp; interror; pr_debug("deviceclass'%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); INIT_LIST_HEAD(&cp->class_interfaces); kset_init(&cp->class_dirs); __mutex_init(&cp->class_mutex,"structclassmutex",key); error=kobject_set_name(&cp->class_subsys.kobj,"%s",cls->name); if(error){ kfree(cp); returnerror; } /*setthedefault/sys/devdirectoryfordevicesofthisclass*/ if(!cls->dev_kobj) cls->dev_kobj=sysfs_dev_char_kobj; #ifdefined(CONFIG_SYSFS_DEPRECATED)&&defined(CONFIG_BLOCK) /*lettheblockclassdirectoryshowupintherootofsysfs*/ if(cls!=&block_class) cp->class_subsys.kobj.kset=class_kset; #else cp->class_subsys.kobj.kset=class_kset; #endif cp->class_subsys.kobj.ktype=&class_ktype; cp->class=cls; cls->p=cp; error=kset_register(&cp->class_subsys); if(error){ kfree(cp); returnerror; } error=add_class_attrs(class_get(cls)); class_put(cls); returnerror; }

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 voidclass_unregister(structclass*cls) { pr_debug("deviceclass'%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()这种函数,估计只有在关机时才可能调用得到,实在是无关紧要。

[cpp]view plaincopy /*Thisisa#definetokeepthecompilerfrommergingdifferent *instancesofthe__keyvariable*/ #defineclass_create(owner,name)\ ({\ staticstructlock_class_key__key;\ __class_create(owner,name,&__key);\ }) /** *class_create-createastructclassstructure *@owner:pointertothemodulethatisto"own"thisstructclass *@name:pointertoastringforthenameofthisclass. *@key:thelock_class_keyforthisclass;usedbymutexlockdebugging * *Thisisusedtocreateastructclasspointerthatcanthenbeused *incallstodevice_create(). * *Note,thepointercreatedhereistobedestroyedwhenfinishedby *makingacalltoclass_destroy(). */ structclass*__class_create(structmodule*owner,constchar*name, structlock_class_key*key) { structclass*cls; intretval; cls=kzalloc(sizeof(*cls),GFP_KERNEL); if(!cls){ retval=-ENOMEM; gotoerror; } cls->name=name; cls->owner=owner; cls->class_release=class_create_release; retval=__class_register(cls,key); if(retval) gotoerror; returncls; error: kfree(cls); returnERR_PTR(retval); }

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

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

[cpp]view plaincopy staticvoidclass_create_release(structclass*cls) { pr_debug("%scalledfor%s\n",__func__,cls->name); kfree(cls); } /** *class_destroy-destroysastructclassstructure *@cls:pointertothestructclassthatistobedestroyed * *Note,thepointertobedestroyedmusthavebeencreatedwithacall *toclass_create(). */ voidclass_destroy(structclass*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为了遍历设备链表,特意定义了专门的结构和遍历函数,实现如下。

[cpp]view plaincopy structclass_dev_iter{ structklist_iterki; conststructdevice_type*type; }; /** *class_dev_iter_init-initializeclassdeviceiterator *@iter:classiteratortoinitialize *@class:theclasswewannaiterateover *@start:thedevicetostartiteratingfrom,ifany *@type:device_typeofthedevicestoiterateover,NULLforall * *Initializeclassiterator@itersuchthatititeratesoverdevices *of@class.If@startisset,thelistiterationwillstartthere, *otherwiseifitisNULL,theiterationstartsatthebeginningof *thelist. */ voidclass_dev_iter_init(structclass_dev_iter*iter,structclass*class, structdevice*start,conststructdevice_type*type) { structklist_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; } structdevice*class_dev_iter_next(structclass_dev_iter*iter) { structklist_node*knode; structdevice*dev; while(1){ knode=klist_next(&iter->ki); if(!knode) returnNULL; dev=container_of(knode,structdevice,knode_class); if(!iter->type||iter->type==dev->type) returndev; } } voidclass_dev_iter_exit(structclass_dev_iter*iter) { klist_iter_exit(&iter->ki); }

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

[cpp]view plaincopy intclass_for_each_device(structclass*class,structdevice*start, void*data,int(*fn)(structdevice*,void*)) { structclass_dev_iteriter; structdevice*dev; interror=0; if(!class) return-EINVAL; if(!class->p){ WARN(1,"%scalledforclass'%s'beforeitwasinitialized", __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); returnerror; }<preclass="cpp"name="code">structdevice*class_find_device(structclass*class,structdevice*start, void*data, int(*match)(structdevice*,void*)) { structclass_dev_iteriter; structdevice*dev; if(!class) returnNULL; if(!class->p){ WARN(1,"%scalledforclass'%s'beforeitwasinitialized", __func__,class->name); returnNULL; } 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); returndev; }</pre> <pre></pre> <pclass="cpp"name="code">class_for_each_device()是对class的设备链表上的每个设备调用指定的函数。</p> <pclass="cpp"name="code">class_find_device()查找class设备链表上的某个设备,使用指定的匹配函数。</p> <pclass="cpp"name="code"></p> <preclass="cpp"name="code">intclass_interface_register(structclass_interface*class_intf) { structclass*parent; structclass_dev_iteriter; structdevice*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); return0; }</pre> <pclass="cpp"name="code">class_interface_register()把class_interface添加到指定的class上。</p> <pclass="cpp"name="code">调用class_get()获取class的引用计数。</p> <pclass="cpp"name="code">使用class->class_mutex进行保护。</p> <pclass="cpp"name="code">将classs_intf添加到class的接口列表中。</p> <pclass="cpp"name="code">对已经添加到class上的设备补上add_dev()操作。</p> <pclass="cpp"name="code">这里使用的class->class_mutex是用来保护class的类接口链表。对于简单的list_head来说,这种mutex保护是应该的。但对于武装到牙齿的klist来说,就完全不必要了,因为klist内置了spinlock来完成互斥的操作。所以之前其它的klist链表操作都没有mutex保护。</p> <pclass="cpp"name="code">比较spinlock和mutex的话,spinlock操作要比mutex快很多,因为对mutex的操作本身就需要spinlock来保护。但mutex的好处是它可以阻塞。使用spinlock时间太长的话,一是浪费cpu时间,二是禁止了任务抢占。klist是使用spinlock来保护的,这适合大部分情况,但在klist遍历时也可能调用一些未知的操作,它们可能很耗时,甚至可能阻塞,这时最好能使用mutex加以替换。</p> <pclass="cpp"name="code"></p> <preclass="cpp"name="code">voidclass_interface_unregister(structclass_interface*class_intf) { structclass*parent=class_intf->class; structclass_dev_iteriter; structdevice*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> <pclass="cpp"name="code">class_interface_unregister()从class中去除指定的class_interface。对于这些class_interface来说,自己注销和设备注销效果是一样的,都会调用相应的remove_dev()。</p> <pclass="cpp"name="code"><br> <br> </p> <preclass="cpp"name="code">structclass_compat{ structkobject*kobj; }; /** *class_compat_register-registeracompatibilityclass *@name:thenameoftheclass * *Compatibilityclassaremeantasatemporaryuser-spacecompatibility *workaroundwhenconvertingafamilyofclassdevicestoabusdevices. */ structclass_compat*class_compat_register(constchar*name) { structclass_compat*cls; cls=kmalloc(sizeof(structclass_compat),GFP_KERNEL); if(!cls) returnNULL; cls->kobj=kobject_create_and_add(name,&class_kset->kobj); if(!cls->kobj){ kfree(cls); returnNULL; } returncls; } voidclass_compat_unregister(structclass_compat*cls) { kobject_put(cls->kobj); kfree(cls); }</pre> <pclass="cpp"name="code">在/sys/class下面,除了class类型的,还有表现起来和class相同的class_compat类型。</p> <pclass="cpp"name="code">其实class_compat就是单单为了显示一个目录,不会定义对应的属性或者函数。</p> <pclass="cpp"name="code"></p> <preclass="cpp"name="code">/** *class_compat_create_link-createacompatibilityclassdevicelinkto *abusdevice *@cls:thecompatibilityclass *@dev:thetargetbusdevice *@device_link:anoptionaldevicetowhicha"device"linkshouldbecreated */ intclass_compat_create_link(structclass_compat*cls,structdevice*dev, structdevice*device_link) { interror; error=sysfs_create_link(cls->kobj,&dev->kobj,dev_name(dev)); if(error) returnerror; /* *Optionallyadda"device"link(typicallytotheparent),asa *classdevicewouldhaveoneandwewanttoprovideasmuch *backwardscompatibilityaspossible. */ if(device_link){ error=sysfs_create_link(&dev->kobj,&device_link->kobj, "device"); if(error) sysfs_remove_link(cls->kobj,dev_name(dev)); } returnerror; } voidclass_compat_remove_link(structclass_compat*cls,structdevice*dev, structdevice*device_link) { if(device_link) sysfs_remove_link(&dev->kobj,"device"); sysfs_remove_link(cls->kobj,dev_name(dev)); }</pre> <pclass="cpp"name="code">class_compat_create_link()的目的是在class_compat目录下建立类似于class目录下的,对设备的软链接。这个不是在标准的设备注册时调用的。</p> <pclass="cpp"name="code"></p> <pclass="cpp"name="code"></p> <pclass="cpp"name="code">本节我们分析完了设备驱动模型中的class,对设备驱动模型的分析也告一段落。虽然只有五个文件,但已经基本上描绘了设备驱动模型的框架。要加深对它的认识,就要在此基础上不断充实细节,用具体的设备驱动来理解。<br> <br> </p>

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。