版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
本文格式为Word版,下载可任意编辑——linux设备模型深探一:前言
Linux设备模型是一个极其繁杂的结构体系,在编写驱动程序的时候,寻常不会用到这方面的东西,但是。理解这部份内容,对于我们理解linux设备驱动的结构是大有裨益的。我们不但可以在编写程序程序的时候知其然,亦知其所以然。又可以学习到一种极其精致的架构设计方法。由于之前已经详细分析了sysfs文件系统。所以本节的探讨主要集中在设备模型的底层实现上。上层的接口,如pci.,usb,网络设备都可以看成是底层的封装。
二:kobject,kset和ktype
Kobject,kset,kypte这三个结构是设备模型中的下层架构。模型中的每一个元素都对应一个kobject.kset和ktype可以看成是kobject在层次结构与属性结构方面的扩展。将三者之间的关系用图的方示描述如下:
如上图所示:我们知道。在sysfs中每一个目录都对应一个kobject.这些kobject都有自己的parent。在没有指定parent的状况下,都会指向它所属的kset->object。其次,kset也内嵌了kobject.这个kobject又可以指它上一级的parent。就这样。构成了一个空间上面的层次关系。
其实,每个对象都有属性。例如,电源管理,执插拨事性管理等等。由于大部份的同类设备都有一致的属性,因此将这个属性隔离开来,存放在ktype中。这样就可以灵活的管理了.记得在分析sysfs的时候。对于sysfs中的普通文件读写操作都是由kobject->ktype->sysfs_ops来完成的.
经过上面的分析,我们大约了解了kobject.kset与ktype的大约架设与相互之间的关系。下面我们从linux源代码中的分析来详细研究他们的操作。
三:kobject,kset和ktype的操作
为了说明kobject的操作,先写一个测试模块,代码如下:
#include
#include
#include
#include
#include
#include
#include
MODULE_AUTHOR(\
MODULE_LICENSE(\
voidobj_test_release(structkobject*kobject);
ssize_teric_test_show(structkobject*kobject,structattribute*attr,char*buf);
ssize_teric_test_store(structkobject*kobject,structattribute*attr,constchar*buf,size_tcount);
structattributetest_attr={
.name=\
.mode=S_IRWXUGO,};
staticstructattribute*def_attrs[]={
structsysfs_opsobj_test_sysops={
.show=eric_test_show,
.store=eric_test_store,};
structkobj_typektype={
.release=obj_test_release,
.sysfs_ops=
voidobj_test_release(structkobject*kobject){
printk(\}
ssize_teric_test_show(structkobject*kobject,structattribute*attr,char*buf){
printk(\
printk(\
sprintf(buf,\
returnstrlen(attr->name)+2;}
ssize_teric_test_store(structkobject*kobject,structattribute*attr,constchar*buf,size_tcount){
printk(\
printk(\
returncount;}
structkobjectkobj;
staticintkobject_test_init(){
printk(\
kobject_init_and_add(}
staticintkobject_test_exit(){
printk(\
kobject_del(
return0;}
module_init(kobject_test_init);
module_exit(kobject_test_exit);
加载模块之后,会发现,在/sys下多了一个eric_test目录。该目录下有一个叫eric_xiao的文件。如下所示:
[root@localhosteric_test]#ls
eric_xiao
用cat观测此文件:
[root@localhosteric_test]#cateric_xiao
eric_xiao
再用echo往里面写点东西;
[root@localhosteric_test]#echohello>eric_xiao
Dmesg的输出如下:
haveshow.
attrname:eric_xiao.
havestore
write:hello
如上所示。我们看到了kobject的大约建立过程.我们来看一下kobject_init_and_add()的实现。在这个函数里,包含了对kobject的大部份操作。
intkobject_init_and_add(structkobject*kobj,structkobj_type*ktype,
structkobject*parent,constchar*fmt,...){
va_listargs;
intretval;
//初始化kobject
kobject_init(kobj,ktype);
va_start(args,fmt);
//为kobjcet设置名称,在sysfs中建立相关信息
retval=kobject_add_varg(kobj,parent,fmt,args);
va_end(args);
returnretval;}
上面的流程主要分为两部份。一部份是kobject的初始化。在这一部份,它将kobject与给定的ktype关联起来。初始化kobject中的各项结构。另一部份是kobject的名称设置。空间层次关系的设置,具体表现在sysfs文件系统中.
对于第一部份,代码比较简单,这里不再赘述。跟踪其次部份,也就是kobject_add_varg()的实现.
staticintkobject_add_varg(structkobject*kobj,structkobject*parent,
constchar*fmt,va_listvargs){
va_listaq;
intretval;
va_copy(aq,vargs);
//设置kobject的名字。即kobject的name成员
retval=kobject_set_name_vargs(kobj,fmt,aq);
va_end(aq);
if(retval){
printk(KERN_ERR\
returnretval;
}
//设置kobject的parent。在上面的例子中,我们没有给它指定父结点
kobj->parent=parent;
//在sysfs中添加kobjcet信息
returnkobject_add_internal(kobj);}
设置好kobject->name后,转入kobject_add_internal()。在sysfs中创立空间结构.代码如下:
staticintkobject_add_internal(structkobject*kobj){
interror=0;
structkobject*parent;
if(!kobj)
return-ENOENT;
//假使kobject的名字为空.退出
if(!kobj->name||!kobj->name[0]){
pr_debug(\
\
WARN_ON(1);
return-EINVAL;
}
//取kobject的父结点
parent=kobject_get(kobj->parent);
//假使kobject的父结点没有指定,就将kset->kobject做为它的父结点
/*joinksetifset,useitasparentifwedonotalreadyhaveone*/
if(kobj->kset){
if(!parent)
parent=kobject_get(
kobj_kset_join(kobj);
kobj->parent=parent;
}
//调试用
pr_debug(\
kobject_name(kobj),kobj,__FUNCTION__,
parent?kobject_name(parent):\
kobj->kset?kobject_name(
if(error){
//v假使创立失败。减少相关的引用计数
kobj_kset_leave(kobj);
kobject_put(parent);
kobj->parent=NULL;
/*benoisyonerrorissues*/
if(error==-EEXIST)
printk(KERN_ERR\\\__FUNCTION__,kobject_name(kobj));
else
printk(KERN_ERR\
__FUNCTION__,kobject_name(kobj),error);
dump_stack();
}else
//假使创立成功。将state_in_sysfs建为1。表示该object已经在sysfs中了
kobj->state_in_sysfs=1;
returnerror;}
这段代码比较简单,它主要完成kobject父结点的判断和选定,然后再调用create_dir()在sysfs创立相关信息。该函数代码如下:
staticintcreate_dir(structkobject*kobj){
interror=0;
if(kobject_name(kobj)){
//为kobject创立目录
error=sysfs_create_dir(kobj);
if(!error){
//为kobject->ktype中的属性创立文件
error=populate_dir(kobj);
if(error)sysfs_remove_dir(kobj);
}
}
returnerror;}
我们在上面的例如中看到的/sys下的eric_test目录,以及该目录下面的eric_xiao的这个文件就是这里被创立的。我们先看一下kobject所表示的目录创立过程。这是在sysfs_create_dir()中完成的。代码如下:
intsysfs_create_dir(structkobject*kobj){
structsysfs_dirent*parent_sd,*sd;
interror=0;
BUG_ON(!kobj);
/*假使kobject的parnet存在。就在目录点的目录下创立这个目录。假使没有父结点不存在,就在/sys下面创立结点。在上面的流程中,我们可能并没有为其指定父结点,也没有为其指定kset。*/
if(kobj->parent)
parent_sd=kobj->parent->sd;
else
parent_sd=
//在sysfs中创立目录
error=create_dir(kobj,parent_sd,kobject_name(kobj),
if(!error)
kobj->sd=sd;
returnerror;}
在这里,我们就要联系之前分析过的sysfs文件系统的研究了。假使不太明白的,可以在找到那篇文章细心的研读一下。create_dir()就是在sysfs中创立目录的接口,在之前已经详细分析过了。这里不再陈述。
接着看为kobject->ktype中的属性创立文件。这是在populate_dir()中完成的。代码如下:
staticintpopulate_dir(structkobject*kobj){
structkobj_type*t=get_ktype(kobj);
structattribute*attr;
interror=0;
inti;
if(t(attr=t->default_attrs[i])!=NULL;i++){
error=sysfs_create_file(kobj,attr);
if(error)break;
}
}
returnerror;}
这段代码比较简单。它遍历ktype中的属性。然后为其建立文件。请注意:文件的操作最终都会回溯到ktype->sysfs_ops的show和store这两个函数中.
Kobject的创立已经分析完了,接着分析怎么将一个kobject注销掉。注意过程是在kobject_del()中完成的。代码如下:
voidkobject_del(structkobject*kobj){
if(!kobj)
return;
sysfs_remove_dir(kobj);
kobj->state_in_sysfs=0;
kobj_kset_leave(kobj);
kobject_put(kobj->parent);
kobj->parent=NULL;}
该函数会将在sysfs中的kobject对应的目录删除。请注意,属性文件是建立在这个目录下面的。只需要将这个目录删除。属性文件也随之删除。
是后,减少相关的引用计数,假使kobject的引用计数为零。则将其所占空间释放.
Kset的操作与kobject类似,由于kset中内嵌了一个kobject结构,所以,大部份操作都是集中在kset->kobject上.具体分析一下kset_create_and_add()这个接口,类似上面分析的kobject接口,这个接口也包括了kset的大部分操作.代码如下:
structkset*kset_create_and_add(constchar*name,structkset_uevent_ops*uevent_ops,structkobject*parent_kobj){
structkset*kset;
interror;
//创立一个kset
kset=kset_create(name,uevent_ops,parent_kobj);
if(!kset)
returnNULL;
//注册kset
error=kset_register(kset);
if(error){
//假使注册失败,释放kset
kfree(kset);
returnNULL;
}
returnkset;}
Kset_create()用来创立一个structkset结构.代码如下:
staticstructkset*kset_create(constchar*name,structkset_uevent_ops*uevent_ops,structkobject*parent_kobj){
structkset*kset;
kset=kzalloc(sizeof(*kset),GFP_KERNEL);
if(!kset)
returnNULL;
kobject_set_name(
kset->uevent_ops=uevent_ops;
kset->kobj.parent=parent_kobj;
kset->kobj.ktype=
kset->kobj.kset=NULL;
returnkset;}
我们注意,在这里创立kset时.为其内嵌的kobject指定其ktype结构为kset_ktype.这个结构的定义如下:
staticstructkobj_typekset_ktype={
.sysfs_ops=
属性文件的读写操作全部都包含在sysfs_ops成员里.kobj_sysfs_ops的定义如下:
structsysfs_opskobj_sysfs_ops={
.show=kobj_attr_show,
.store=kobj_attr_store,};
Show,store成员对应的函数代码如下所示:
staticssize_tkobj_attr_show(structkobject*kobj,structattribute*attr,char*buf){
structkobj_attribute*kattr;
ssize_tret=-EIO;
kattr=container_of(attr,structkobj_attribute,attr);
if(kattr->show)
ret=kattr->show(kobj,kattr,buf);
returnret;}
staticssize_tkobj_attr_store(structkobject*kobj,structattribute*attr,constchar*buf,size_tcount){
structkobj_attribute*kattr;
ssize_tret=-EIO;
kattr=container_of(attr,structkobj_attribute,attr);
if(kattr->store)
ret=kattr->store(kobj,kattr,buf,count);
returnret;}
从上面的代码看以看出.会将structattribute结构转换为structkobj_attribte结构.也就是说structkobj_attribte内嵌了一个structattribute.实际上,这是和宏__ATTR协同在一起使用的.经常用于group中.在这里并不计划研究group.原理都是一样的.这里列出来只是做个说明而已.
创立好了kset之后,会调用kset_register().这个函数就是kset操作的核心代码了.如下:
intkset_register(structkset*k){
interr;
if(!k)
return-EINVAL;
kset_init(k);
err=kobject_add_internal(
if(err)
returnerr;
kobject_uevent(
return0;}
在kset_init()里会初始化kset中的其它字段.然后调用kobject_add_internal()为其内嵌的
kobject结构建立空间层次结构.之后由于添加了kset.会产生一个事件.这个事件是通过用户空间的hotplug程序处理的.这就是kset明显不同于kobject的地方.详细研究一下这个函数.这对于我们研究hotplug的深层机理是很有帮助的.它的代码如下;
intkobject_uevent(structkobject*kobj,enumkobject_actionaction){
returnkobject_uevent_env(kobj,action,NULL);}
之后,会调用kobject_uevent_env().这个函数中的三个参数含义分别为:引起事件的kobject.事件类型(add,remove,change,move,online,offline等).第三个参数是要添加的环境变量.
代码篇幅较长,我们效仿情景分析上面的做法.分段分析如下:
intkobject_uevent_env(structkobject*kobj,enumkobject_actionaction,char*envp_ext[]){
structkobj_uevent_env*env;
constchar*action_string=kobject_actions[action];
constchar*devpath=NULL;
constchar*subsystem;
structkobject*top_kobj;
structkset*kset;
structkset_uevent_ops*uevent_ops;
u64seq;
inti=0;
intretval=0;
pr_debug(\
kobject_name(kobj),kobj,__FUNCTION__);
/*searchtheksetwebelongto*/
top_kobj=kobj;
while(!top_kobj->kset
if(!top_kobj->kset){
pr_debug(\
\
__FUNCTION__);
return-EINVAL;
}
由于对事件的处理函数包含在kobject->kset->uevent_ops中.要处理事件,就必需要找到上层的一个不为空的kset.上面的代码就是顺着kobject->parent找不到一个不为空的kset.假使不存在这样的kset.就退出
kset=top_kobj->kset;
uevent_ops=kset->uevent_ops;
/*skiptheevent,ifthefilterreturnszero.*/
if(uevent_ops
return0;
}
/*originatingsubsystem*/
if(uevent_ops
else
subsystem=kobject_name(
if(!subsystem){
pr_debug(\
\
__FUNCTION__);
return0;
}
找到了不为空的kset.就跟kset->uevent_ops->filter()匹配.看这个事件是否被过滤.假使没有被过滤掉.就会调用kset->uevent_ops->name()得到子系统的名称,假使不存在kset->uevent_ops->name().就会以kobject->name做为子系统名称.
/*environmentbuffer*/
env=kzalloc(sizeof(structkobj_uevent_env),GFP_KERNEL);
if(!env)
return-ENOMEM;
/*completeobjectpath*/
devpath=kobject_get_path(kobj,GFP_KERNEL);
if(!devpath){
retval=-ENOENT;
gotoexit;
}
/*defaultkeys*/
retval=add_uevent_var(env,\
if(retval)
gotoexit;
retval=add_uevent_var(env,\
if(retval)
gotoexit;
retval=add_uevent_var(env,\
if(retval)
gotoexit;
/*keyspassedinfromthecaller*/
if(envp_ext){
for(i=0;envp_ext[i];i++){
retval=add_uevent_var(env,envp_ext[i]);
if(retval)gotoexit;
}
}
接下来,就应当设置为调用hotplug设置环境变量了.首先,分派一个structkobj_uevent_env结构用来存放环境变量的值.然后调用kobject_get_path()用来获得引起事件的kobject在sysfs中的路径.再调用add_uevent_var()将动作代表的字串,kobject路径,子系统名称填充到structkobj_uevent_env中,假使有指定环境变量,也将其添加进去.kobject_get_path()和add_uevent_var()都比较简单.这里不再详细分析了.请自行查看源代码
/*lettheksetspecificfunctionadditsstuff*/
if(uevent_ops
if(retval){
pr_debug(\\__FUNCTION__,retval);
gotoexit;
}
}
/*
*Mark\
*eventstouserspaceduringautomaticcleanup.Iftheobjectdid
*sendan\
*thecore,ifnotalreadydonebythecaller.
*/
if(action==KOBJ_ADD)
kobj->state_add_uevent_sent=1;
elseif(action==KOBJ_REMOVE)
kobj->state_remove_uevent_sent=1;
/*wewillsendanevent,sorequestanewsequencenumber*/
spin_lock(
seq=++uevent_seqnum;
spin_unlock(
retval=add_uevent_var(env,\
if(retval)
gotoexit;
在这里还会调用kobject->kset->uevent_ops->uevent().让产生事件的kobject添加环境变量.最
后将事件序列添加到环境变量中去.
#ifdefined(CONFIG_NET)
/*sendnetlinkmessage*/
if(uevent_sock){
structsk_buff*skb;
size_tlen;
/*allocatemessagewiththemaximumpossiblesize*/
len=strlen(action_string)+strlen(devpath)+2;
skb=alloc_skb(len+env->buflen,GFP_KERNEL);
if(skb){
char*scratch;
/*addheader*/
scratch=skb_put(skb,len);
sprintf(scratch,\
/*copykeystoourcontinuouseventpayloadbuffer*/
for(i=0;ienvp_idx;i++){len=strlen(env->envp[i])+1;scratch=skb_put(skb,len);strcpy(scratch,env->envp[i]);
}
NETLINK_CB(skb).dst_group=1;
netlink_broadcast(uevent_sock,skb,0,1,GFP_KERNEL);
}
}
#endif
/*calluevent_helper,usuallyonlyenabledduringearlyboot*/
if(uevent_helper[0]){
char*argv[3];
argv[0]=uevent_helper;
argv[1]=(char*)subsystem;
argv[2]=NULL;
retval=add_uevent_var(env,\
if(retval)
gotoexit;
retval=add_uevent_var(env,
\
if(retval)
gotoexit;
call_usermodehelper(argv[0],argv,env->envp,UMH_WAIT_EXEC);
}exit:
kfree(devpath);
kfree(env);
returnretval;}
忽略一段选择编译的代码.再后就是调用用户空间的hotplug了.添加最终两个环境变量.HOME和PATH.然后调用hotplug.以子系统名称为参数.
现在我们终究知道hotplug处理程序中的参数和环境变量是怎么来的了.^_^
使用完了kset.再调用kset_unregister()将其注销.这个函数很简单,请自行查阅代码.
为了印证一下上面的分析,写一个测试模块。如下:
#include
#include
#include
#include
#include
#include
#include
#include
MODULE_AUTHOR(\
MODULE_LICENSE(\
intkset_filter(structkset*kset,structkobject*kobj);
constchar*kset_name(structkset*kset,structkobject*kobj);
intkset_uevent(structkset*kset,structkobject*kobj,
structkobj_uevent_env*env);
structksetkset_p;
structksetkset_c;
structkset_uevent_opsuevent_ops={
.filter=kset_filter,
.name=kset_name,
.uevent=kset_uevent,};
intkset_filter(structkset*kset,structkobject*kobj){
printk(\
return1;}
constchar*kset_name(structkset*kset,structkobject*kobj){
staticcharbuf[20];
printk(\
sprintf(buf,\
returnbuf;}
intkset_uevent(structkset*kset,structkobject*kobj,
structkobj_uevent_env*env){
inti=0;
printk(\
while(ienvp_idx){printk(\i++;
}
return0;}
intkset_test_init(){
printk(\
kobject_set_name(
kset_register(
kobject_set_name(
kset_register(
return0;}
intkset_test_exit(){
printk(\
kset_unregister(
kset_unregister(
return0;}
module_init(kset_test_init);
module_exit(kset_test_exit);
在这里,定义并注册了二个kset.其次个kset的kobj->kset域指向第一个kset.这样,当其次个kset注册或者卸载的时候就会调用第一个kset中的uevent_ops的相关操作.
kset_p.uevent_ops->filter函数中,使其返回1.使其匹配成功。
在kset_p.uevent_ops->name中。使其返回的子系统名为引起事件的kobject的名称,即:kset_c.
最终在kset_p.uevent_ops->uevent中将环境变量全部打印出来。
下面是dmesg的输出结果:
ksettestinit.
UEVENT:filter.kobjkset_c.
UEVENT:name.kobjkset_c.
UEVENT:uevent.kobjkset_c.
ACTION=add.
DEVPATH=/kset_p/kset_c.
SUBSYSTEM=kset_test.
输出结果跟我们的分析是吻合的.
在这里,值得我们注意的是。注册一个kobject不会产生事件,只有注册kset才会.
四:bus,device和device_driver
上面分析了kobject.kset,ktype.这三个结构联合起来一起构成了整个设备模型的基石.而bus.device.device_driver.则是基于kobject.kset.ktype之上的架构.在这里,总线,设备,驱动被有序的组和在一起.
Bus.device.device_driver三者之间的关系如下图所示:
如上图所示.structbus_type的p->drivers_kset指向注册在上面的驱动程序.它的p->device_kset上挂着注册在上面的设备.每次有一个新的设备注册到上面,都会去匹配右边的驱动,看是否能匹配上.假使匹配成功,则将设备结构的is_registerd域置为0.然后将设备添加到驱动的p->klist_devices域.同理,每注册一个驱动,都会去匹配左边的设备,.假使匹配成功,将则设备加到驱动的p->klist_devices域.再将设备的is_registerd置为0/
这就是linux设备模型用来管理设备和驱动的基本架构.我们来跟踪一下代码来看下详细的操作.
注册一个总线的接口为bus_register().我们循例分段分析:
intbus_register(structbus_type*bus){
intretval;
structbus_type_private*priv;
priv=kzalloc(sizeof(structbus_type_private),GFP_KERNEL);
if(!priv)
return-ENOMEM;
priv->bus=bus;
bus->p=priv;
BLOCKING_INIT_NOTIFIER_HEAD(
retval=kobject_set_name(
priv->subsys.kobj.kset=bus_kset;
priv->subsys.kobj.ktype=
priv->drivers_autoprobe=1;
retval=kset_register(
if(retval)
gotoout;
首先,先为structbus_type的私有区分派空间,然后将其和structbus_type关联起来.由于structbus_type也要在sysfs文件中表示一个节点,因此,它也内嵌也一个kset的结构.这就是priv->subsys.
首先,它为这个kset的名称赋值为bus的名称,然后将priv->subsys.kobj.kset指向bus_kset.priv->subsys.kobj.ktype指向bus_ktype;然后调用kset_reqister()将priv->subsys注册.这里涉及到的接口都在之前分析过.注册过后,应当会在bus_kset所表示的目录下创立一个总线名称的目录.并且用户空间的hotplug应当会检测到一个add事件.我们来看一下bus_kset终究指向的是什么:
bus_kset=kset_create_and_add(\
此后可以看出.这个bus_kset在sysfs中的结点就是/sys/bus.在这里注册的structbus_types就
会在/sys/bus/下面出现.
retval=bus_create_file(bus,
if(retval)
gotobus_uevent_fail;
bus_create_file()就是在priv->subsys.kobj的这个kobject上建立一个普通属性的文件.这个文件的属性对应在bus_attr_uevent.读写操作对应在priv->subsys.ktype中.我们到后面才统一分析bus注册时候的文件创立
priv->devices_kset=kset_create_and_add(\
if(!priv->devices_kset){
retval=-ENOMEM;
gotobus_devices_fail;
}
priv->drivers_kset=kset_create_and_add(\
if(!priv->drivers_kset){
retval=-ENOMEM;
gotobus_drivers_fail;
}
klist_init(
klist_init(
这段代码会在bus所在的目录下建立两个目录,分别为devices和drivers.并初始化挂载设备和驱动的链表
retval=add_probe_files(bus);
if(retval)
gotobus_probe_files_fail;
retval=bus_add_attrs(bus);
if(retval)
gotobus_attrs_fail;
pr_debug(\
return0;
在这里,会为bus_attr_drivers_probe,bus_attr_drivers_autoprobe.注册bus_type中的属性建立文件
bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus,
bus_uevent_fail:
kset_unregister(
kfree(bus->p);out:
returnretval;}
这段代码为出错处理
这段代码中比较繁锁的就是bus_type对应目录下的属性文件建立,为了直观的说明,将属性文件的建立统一放到一起分析
从上面的代码中可以看,创立属性文件对应的属性分别为:
bus_attr_ueventbus_attr_drivers_probe,bus_attr_drivers_autoprobe
分别定义如下:
staticBUS_ATTR(uevent,S_IWUSR,NULL,bus_uevent_store);
staticBUS_ATTR(drivers_probe,S_IWUSR,NULL,store_drivers_probe);
staticBUS_ATTR(drivers_autoprobe,S_IWUSR|S_IRUGO,
show_drivers_autoprobe,store_drivers_autoprobe);
BUS_ATTR定义如下:
#defineBUS_ATTR(_name,_mode,_show,_store)\\
structbus_attributebus_attr_##_name=__ATTR(_name,_mode,_show,_store)
#define__ATTR(_name,_mode,_show,_store){\\
.attr={.name=__stringify(_name),.mode=_mode},\\
.show=_show,\\
.store=_store,\\}
由此可见.上面这三个属性对应的名称为别为uevent,drivers_probe,drivers_autoprobe.也就是说,会在bus_types目录下生成三个文件,分别为uevent,probe,autoprobe.
根据之前的分析,我们知道在sysfs文件系统中,对普通属性文件的读写都会回溯到kobject->ktype->sysfs_ops中.在这里,注意到有:
priv->subsys.kobj.kset=bus_kset;
priv->subsys.kobj.ktype=
显然,读写操作就回溯到了bus_ktype中.定义如下:
staticstructkobj_typebus_ktype={
.sysfs_ops=
staticstructsysfs_opsbus_sysfs_ops={
.show=bus_attr_show,
.store=bus_attr_store,};
Show和store函数对应的代码为:
staticssize_tbus_attr_show(structkobject*kobj,structattribute*attr,char*buf){
structbus_attribute*bus_attr=to_bus_attr(attr);
structbus_type_private*bus_priv=to_bus(kobj);
ssize_tret=0;
if(bus_attr->show)
ret=bus_attr->show(bus_priv->bus,buf);
returnret;}
staticssize_tbus_attr_store(structkobject*kobj,structattribute*attr,constchar*buf,size_tcount){
structbus_attribute*bus_attr=to_bus_attr(attr);
structbus_type_private*bus_priv=to_bus(kobj);
ssize_tret=0;
if(bus_attr->store)
ret=bus_attr->store(bus_priv->bus,buf,count);
returnret;}
从代码可以看出.读写操作又会回溯到bus_attribute中的show和store中.在自定义结构里嵌入structattribute,.然后再操作回溯到自定义结构中,这是一种比较高明的架构设计手法.
闲言少叙.我们对应看一下上面三个文件对应的最终操作:
Uevent对应的读写操作为:NULL,bus_uevent_store.对于这个文件没有读操作,只有写操作.用cat命令去查看这个文件的时候,可能会返回〞设备不存在〞的错误.bus_uevent_store()代码如下:
staticssize_tbus_uevent_store(structbus_type*bus,constchar*buf,size_tcount){
enumkobject_actionaction;
if(kobject_action_type(buf,count,
returncount;}
从这里可以看到,可以在用户空间控制事件的发生,如echoadd>event就会产生一个add的事件,
Probe文件对应的读写操作为:NULLstore_drivers_probe.
store_drivers_probe()这个函数的代码涉及到structdevice.等分析完structdevice可以自行回过来看下这个函数的实现.实际上,这个函数是将用户输和的设备名称对应的设备与驱动匹配一次.
Autoprobe文件对应的读写操作为show_drivers_autoprobe,store_drivers_autoprobe.对应读的代码为:
staticssize_tshow_drivers_autoprobe(structbus_type*bus,char*buf){
returnsprintf(buf,\
}
它将总线对应的drivers_autoprobe的值输出到用户空间,这个值为1时,自动将驱动与设备进行匹配.否则,反之.
写操作的代码如下:
staticssize_tstore_drivers_autoprobe(structbus_type*bus,
constchar*buf,size_tcount){
if(buf[0]=='0')
bus->p->drivers_autoprobe=0;
else
bus->p->drivers_autoprobe=1;
returncount;}
写操作就会改变bus->p->drivers_autoprobe的值.
就这样,通过sysfs就可以控制总线是否要进行自动匹配了.
从这里也可以看出.内核开发者的思维是何等的灵活.
我们从sysfs中找个例子来印证一下:
Cd/sys/bus/usb
用ls命令查看:
devicesdriversdrivers_autoprobedrivers_probeuevent
与上面分析的相吻合
设备的注册接口为:device_register().
intdevice_register(structdevice*dev){
device_initialize(dev);
returndevice_add(dev);}
Device_initialize()中有几个很重要的操作,如下:
voiddevice_initialize(structdevice*dev){
dev->kobj.kset=devices_kset;
kobject_init(
klist_init(
INIT_LIST_HEAD(
INIT_LIST_HEAD(
init_MUTEX(
spin_lock_init(
INIT_LIST_HEAD(
device_init_wakeup(dev,0);
set_dev_node(dev,-1);}
在这里,它为device的内嵌kobject指定了ktype和kset.device_kset的值如下:
devices_kset=kset_create_and_add(\
即对应sysfs中的/sys/devices
device_ktype中对属性的读写操作同bus中的类似,被回溯到了structdevice_attribute中的show和store.
接着往下看device_add()的实现.这个函数比较长,分段分析如下:
intdevice_add(structdevice*dev){
structdevice*parent=NULL;
structclass_interface*class_intf;
interror;
dev=get_device(dev);
if(!dev||!strlen(dev->bus_id)){
error=-EINVAL;
gotoDone;
}
pr_debug(\
parent=get_device(dev->parent);
setup_parent(dev,parent);
/*first,registerwithgenericlayer.*/
err
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年工业铝挤压材项目发展计划
- 豪华酒店转让居间合同
- 档案馆电气设备升级合同
- 教育机构装修合同范文参考
- 科技馆装修合同专业文档
- 豪华别墅庭院绿化协议
- 食品安全快速运输合同
- 山东省汶上县2023-2024学年中考数学五模试卷含解析
- 2024年抛弃式温深计项目合作计划书
- 山东潍坊高密市文慧学校2024年中考数学模拟试卷含解析
- 双减背景下初中语文名著导读作业设计策略 论文
- 春江花月夜微课金奖优质获奖课件
- 2023年公司法讲义
- 七年级(4)班家长会课件
- 2023版思想道德与法治专题6 遵守道德规范 锤炼道德品格 第3讲 投身崇德向善的道德实践
- 绿带2023试题(带答案)
- 嵌入式应用开发期末练习题
- 义务教育英语课程标准2022版电子版
- 2023年小学班级管理心得体会 小学班级管理心得体会3000字(三篇)
- be动词的否定句和一般疑问句2
- GB/T 2965-2007钛及钛合金棒材
评论
0/150
提交评论