您的位置:首页 > 运维架构 > Linux

Linux设备模型分析之bus(基于3.10.1内核)

2013-11-01 15:48 423 查看
作者:刘昊昱
博客:http://blog.csdn.net/liuhaoyutz

内核版本:3.10.1


一、bus定义

Linux设备驱动模型中的bus,即可以是物理总线(如PCI、I2C总线)的抽象,也可以是出于设备驱动模型架构需要而定义的虚拟的“platform”总线。一个符合Linux设备驱动模型的device或device_driver必须挂靠在一个bus上,无论这个bus是物理的还是虚拟的。

Linux内核使用bus_type结构体来描述bus,该结构体定义在include/linux/device.h文件中,其内容如下:

56/**
57*structbus_type-Thebustypeofthedevice
58*
59*@name:Thenameofthebus.
60*@dev_name:Usedforsubsystemstoenumeratedeviceslike("foo%u",dev->id).
61*@dev_root:Defaultdevicetouseastheparent.
62*@bus_attrs:Defaultattributesofthebus.
63*@dev_attrs:Defaultattributesofthedevicesonthebus.
64*@drv_attrs:Defaultattributesofthedevicedriversonthebus.
65*@match:Called,perhapsmultipletimes,wheneveranewdeviceordriver
66*isaddedforthisbus.Itshouldreturnanonzerovalueifthe
67*givendevicecanbehandledbythegivendriver.
68*@uevent:Calledwhenadeviceisadded,removed,orafewotherthings
69*thatgenerateueventstoaddtheenvironmentvariables.
70*@probe:Calledwhenanewdeviceordriveraddtothisbus,andcallback
71*thespecificdriver'sprobetoinitialthematcheddevice.
72*@remove:Calledwhenadeviceremovedfromthisbus.
73*@shutdown:Calledatshut-downtimetoquiescethedevice.
74*@suspend:Calledwhenadeviceonthisbuswantstogotosleepmode.
75*@resume:Calledtobringadeviceonthisbusoutofsleepmode.
76*@pm:Powermanagementoperationsofthisbus,callbackthespecific
77*devicedriver'spm-ops.
78*@iommu_ops:IOMMUspecificoperationsforthisbus,usedtoattachIOMMU
79*driverimplementationstoabusandallowthedrivertodo
80*bus-specificsetup
81*@p:Theprivatedataofthedrivercore,onlythedrivercorecan
82*touchthis.
83*
84*Abusisachannelbetweentheprocessorandoneormoredevices.Forthe
85*purposesofthedevicemodel,alldevicesareconnectedviaabus,evenif
86*itisaninternal,virtual,"platform"bus.Busescanplugintoeachother.
87*AUSBcontrollerisusuallyaPCIdevice,forexample.Thedevicemodel
88*representstheactualconnectionsbetweenbusesandthedevicestheycontrol.
89*Abusisrepresentedbythebus_typestructure.Itcontainsthename,the
90*defaultattributes,thebus'methods,PMoperations,andthedrivercore's
91*privatedata.
92*/
93structbus_type{
94constchar*name;
95constchar*dev_name;
96structdevice*dev_root;
97structbus_attribute*bus_attrs;
98structdevice_attribute*dev_attrs;
99structdriver_attribute*drv_attrs;
100
101int(*match)(structdevice*dev,structdevice_driver*drv);
102int(*uevent)(structdevice*dev,structkobj_uevent_env*env);
103int(*probe)(structdevice*dev);
104int(*remove)(structdevice*dev);
105void(*shutdown)(structdevice*dev);
106
107int(*suspend)(structdevice*dev,pm_message_tstate);
108int(*resume)(structdevice*dev);
109
110conststructdev_pm_ops*pm;
111
112structiommu_ops*iommu_ops;
113
114structsubsys_private*p;
115structlock_class_keylock_key;
116};



下面我们来看一下bus_type各个成员的作用:

name,代表bus的名字。

dev_name,用来遍历bus上的device。

dev_root,bus上device的根节点。

bus_attrs,bus的属性。

dev_attrs,bus上device的属性。

drv_attrs,bus上device_driver的属性。

match,当一个新的device或devicedriver被加入到该bus时,match函数会被调用进行匹配操作。更详细的说,当一个device被加入时,会和bus上的所有device_driver进行匹配操作,如果有device_driver能支持这个device,则匹配成功,match返回非0值。当一个device_driver被加入到bus时,会和bus上的所有device进行匹配操作,如果有它能支持的device,则匹配成功,match返回非0值。

uevent,当某个device加入或被删除,或者其它一些会发送uevnt消息的事件发生时,该函数会被调用。

probe,当一个新的device或device_driver被加入bus时,该函数会被调用,它又会进而调用相应device_driver的probe函数对匹配的device进行初始化。

remove,当一个device被删除时,这个函数会被调用。

shutdown,当系统关机时,该函数会被调用。

suspend,当bus的某个device要进入休眠状态时,这个函数会被调用。

resume,当bus上的某个休眠device被唤醒时,这个函数会被调用。

pm,bus上的电源管理操作函数,会进而调用相应device_driver的pm函数。

iommu_ops,bus的iommu相关操作。

p,bus私有数据,只有busdriver本身能访问这些数据,其类型是structsubsys_private,该结构体定义在drivers/base/base.h文件中,其内容如下:

3/**
4*structsubsys_private-structuretoholdtheprivatetothedrivercoreportionsofthebus_type/classstructure.
5*
6*@subsys-thestructksetthatdefinesthissubsystem
7*@devices_kset-thesubsystem's'devices'directory
8*@interfaces-listofsubsysteminterfacesassociated
9*@mutex-protectthedevices,andinterfaceslists.
10*
11*@drivers_kset-thelistofdriversassociated
12*@klist_devices-theklisttoiterateoverthe@devices_kset
13*@klist_drivers-theklisttoiterateoverthe@drivers_kset
14*@bus_notifier-thebusnotifierlistforanythingthatcaresaboutthings
15*onthisbus.
16*@bus-pointerbacktothestructbus_typethatthisstructureisassociated
17*with.
18*
19*@glue_dirs-"glue"directorytoputin-betweentheparentdeviceto
20*avoidnamespaceconflicts
21*@class-pointerbacktothestructclassthatthisstructureisassociated
22*with.
23*
24*Thisstructureistheonethatistheactualkobjectallowingstruct
25*bus_type/classtobestaticallyallocatedsafely.Nothingoutsideofthe
26*drivercoreshouldevertouchthesefields.
27*/
28structsubsys_private{
29structksetsubsys;
30structkset*devices_kset;
31structlist_headinterfaces;
32structmutexmutex;
33
34structkset*drivers_kset;
35structklistklist_devices;
36structklistklist_drivers;
37structblocking_notifier_headbus_notifier;
38unsignedintdrivers_autoprobe:1;
39structbus_type*bus;
40
41structksetglue_dirs;
42structclass*class;
43};



subsys,代表bus对应的kset,即bus对应子系统。bus对应一个kset,而device和device_driver都对应一个kobject,由此也可以看出它们的区别。在Linux设备模型中,bus对应的kset是subsys,bus对应的kobject是subsys.kobj,bus对应的父kset是subsys.kobj.kset。所有通过bus_register注册进系统的bus的父kset均为bus_kset,它对应/sys/bus目录,所以,bus对应的目录都在/sys/bus目录下,如/sys/bus/usb。

devices_kset,该bus上所有device的集合。

interfaces,子系统interfaces链表。

drivers_kset,该bus上所有device_driver的集合。

klist_devices,该bus上所有device的链表。

klist_drivers,该bus上所有device_driver的链表。

bus_notifier,busnotifier列表。

drivers_autoprobe,表明当向bus加入一个device或device_driver时,是否自动进行匹配操作。

bus,指向相关联的bus_type。


二、bus初始化

bus的初始化操作是在buses_init函数中完成的,该函数定义在drivers/base/bus.c文件中,其内容如下:

1300int__initbuses_init(void)
1301{
1302bus_kset=kset_create_and_add("bus",&bus_uevent_ops,NULL);
1303if(!bus_kset)
1304return-ENOMEM;
1305
1306system_kset=kset_create_and_add("system",NULL,&devices_kset->kobj);
1307if(!system_kset)
1308return-ENOMEM;
1309
1310return0;
1311}



1302行,调用kset_create_and_add创建bus_kset,它是所有bus的容器,对应/sys/bus目录。这里指定了当bus_kset中的成员状态有变化时,用来通知用户空间的uevent操作函数集为bus_uevent_ops,它定义在drivers/base/bus.c文件中,其内容如下:

161staticconststructkset_uevent_opsbus_uevent_ops={
162.filter=bus_uevent_filter,
163};



这个操作函数集中只定义了filter函数,用来决定状态发生变化时是否通知用户空间,其定义如下:

152staticintbus_uevent_filter(structkset*kset,structkobject*kobj)
153{
154structkobj_type*ktype=get_ktype(kobj);
155
156if(ktype==&bus_ktype)
157return1;
158return0;
159}



如果ktype不是“bus_ktype”,则不通知用户空间。

回到buses_init函数,1306行,调用kset_create_and_add创建system_kset,对应/sys/devices/system目录。


三、bus的注册

注册一个bus是通过调用bus_register函数完成的,该函数定义在drivers/base/bus.c文件中,其内容如下:

900/**
901*bus_register-registeradriver-coresubsystem
902*@bus:bustoregister
903*
904*Oncewehavethat,weregisterthebuswiththekobject
905*infrastructure,thenregisterthechildrensubsystemsithas:
906*thedevicesanddriversthatbelongtothesubsystem.
907*/
908intbus_register(structbus_type*bus)
909{
910intretval;
911structsubsys_private*priv;
912structlock_class_key*key=&bus->lock_key;
913
914priv=kzalloc(sizeof(structsubsys_private),GFP_KERNEL);
915if(!priv)
916return-ENOMEM;
917
918priv->bus=bus;
919bus->p=priv;
920
921BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
922
923retval=kobject_set_name(&priv->subsys.kobj,"%s",bus->name);
924if(retval)
925gotoout;
926
927priv->subsys.kobj.kset=bus_kset;
928priv->subsys.kobj.ktype=&bus_ktype;
929priv->drivers_autoprobe=1;
930
931retval=kset_register(&priv->subsys);
932if(retval)
933gotoout;
934
935retval=bus_create_file(bus,&bus_attr_uevent);
936if(retval)
937gotobus_uevent_fail;
938
939priv->devices_kset=kset_create_and_add("devices",NULL,
940&priv->subsys.kobj);
941if(!priv->devices_kset){
942retval=-ENOMEM;
943gotobus_devices_fail;
944}
945
946priv->drivers_kset=kset_create_and_add("drivers",NULL,
947&priv->subsys.kobj);
948if(!priv->drivers_kset){
949retval=-ENOMEM;
950gotobus_drivers_fail;
951}
952
953INIT_LIST_HEAD(&priv->interfaces);
954__mutex_init(&priv->mutex,"subsysmutex",key);
955klist_init(&priv->klist_devices,klist_devices_get,klist_devices_put);
956klist_init(&priv->klist_drivers,NULL,NULL);
957
958retval=add_probe_files(bus);
959if(retval)
960gotobus_probe_files_fail;
961
962retval=bus_add_attrs(bus);
963if(retval)
964gotobus_attrs_fail;
965
966pr_debug("bus:'%s':registered\n",bus->name);
967return0;
968
969bus_attrs_fail:
970remove_probe_files(bus);
971bus_probe_files_fail:
972kset_unregister(bus->p->drivers_kset);
973bus_drivers_fail:
974kset_unregister(bus->p->devices_kset);
975bus_devices_fail:
976bus_remove_file(bus,&bus_attr_uevent);
977bus_uevent_fail:
978kset_unregister(&bus->p->subsys);
979out:
980kfree(bus->p);
981bus->p=NULL;
982returnretval;
983}



914行,为structsubsys_private指针priv分配内存空间。

923行,设置priv->subsys.kobj的名字为bus->name,对应sysfs文件系统中该bus的目录名。

927行,设置priv->subsys.kobj.kset为bus_kset,即所有通过bus_register函数注册的bus本身是一个kset(即priv->subsys),该kset对应的kobject是priv->subsys.kobj,该kset的父kset是priv->subsys.kobj.kset,这里设置为bus_kset。因为bus_kset在buses_init函数中初始化,对应/sys/bus目录,所以后面我们注册的bus的根目录都在/sys/bus目录下,如/sys/bus/usb。

928行,设置priv->subsys.kobj.ktype为bus_ktype。因为对kobject属性文件的读写操作将会调用kobject.ktype.sysfs_ops指定的show和store函数,所以,读写bus对应的属性文件,将会调用bus_ktype.sysfs_ops指定的show和store函数。bus_ktype定义在drivers/base/bus.c文件中,其内容如下:

148staticstructkobj_typebus_ktype={
149.sysfs_ops=&bus_sysfs_ops,
150};



bus_sysfs_ops定义在drivers/base/bus.c文件中,其内容如下:

122staticconststructsysfs_opsbus_sysfs_ops={
123.show=bus_attr_show,
124.store=bus_attr_store,
125};



同样是在drivers/base/bus.c文件中,bus_attr_show和bus_attr_store函数定义如下:

95/*
96*sysfsbindingsforbuses
97*/
98staticssize_tbus_attr_show(structkobject*kobj,structattribute*attr,
99char*buf)
100{
101structbus_attribute*bus_attr=to_bus_attr(attr);
102structsubsys_private*subsys_priv=to_subsys_private(kobj);
103ssize_tret=0;
104
105if(bus_attr->show)
106ret=bus_attr->show(subsys_priv->bus,buf);
107returnret;
108}
109
110staticssize_tbus_attr_store(structkobject*kobj,structattribute*attr,
111constchar*buf,size_tcount)
112{
113structbus_attribute*bus_attr=to_bus_attr(attr);
114structsubsys_private*subsys_priv=to_subsys_private(kobj);
115ssize_tret=0;
116
117if(bus_attr->store)
118ret=bus_attr->store(subsys_priv->bus,buf,count);
119returnret;
120}



to_bus_attr是一个宏,定义在drivers/base/bus.c文件中,如下:

26#defineto_bus_attr(_attr)container_of(_attr,structbus_attribute,attr)



bus_attribute定义在include/linux/device.h文件中,其内容如下:

43structbus_attribute{
44structattributeattr;
45ssize_t(*show)(structbus_type*bus,char*buf);
46ssize_t(*store)(structbus_type*bus,constchar*buf,size_tcount);
47};



to_subsys_private定义在drivers/base/base.h文件中,如下:

44#defineto_subsys_private(obj)container_of(obj,structsubsys_private,subsys.kobj)



至此,我们可以回顾一下整个调用流程:

调用bus_register注册任意一个bus时(如usb、i2c等等),都指定了该bus对应的bus_type.p.subsys.kobj.ktype为bus_ktype,所以对任意一个bus对应的属性文件进行读写操作时,都会调用bus_ktype.sysfs_ops.bus_attr_show和bus_ktype.sysfs_ops.bus_attr_store函数。注意,读写usb总线属性文件时是调用这两个函数,读写i2c总线属性文件时也是调用这两个函数,所有的总线调用的是同一个函数。那么怎么区分不同总线的操作呢?关键点是在bus_attr_show和bus_attr_store函数内部,通过to_bus_attr将参数传递过来的structattribute变量转换为包含它的structbus_attribute对象,进而调用bus_attribute.show和bus_attribute.store函数。而bus_attribute对象定义在bus_type结构体中,即bus_type.bus_attrs,每个bus有不同的bus_type定义,也就有不同的bus_type.bus_attrs,也就有不同的bus_type.bus_attrs.show和bus_type.bus_attrs.store实现。

回到bus_register函数中:

929行,设置priv->drivers_autoprobe为1,指示当向bus加入一个device或device_driver时,自动进行匹配操作。

931行,调用kset_register注册priv->subsys,这时在/sys/bus下创建了相应bus目录结构。

935行,调用bus_create_file创建bus_attr_uevent对应的属性文件。

bus_create_file函数定义在drivers/base/bus.c文件中,其内容如下:

127intbus_create_file(structbus_type*bus,structbus_attribute*attr)
128{
129interror;
130if(bus_get(bus)){
131error=sysfs_create_file(&bus->p->subsys.kobj,&attr->attr);
132bus_put(bus);
133}else
134error=-EINVAL;
135returnerror;
136}



bus_attr_uevent定义在drivers/base/bus.c文件中:

898staticBUS_ATTR(uevent,S_IWUSR,NULL,bus_uevent_store);



BUS_ATTR定义在include/linux/device.h文件中:

49#defineBUS_ATTR(_name,_mode,_show,_store)\
50structbus_attributebus_attr_##_name=__ATTR(_name,_mode,_show,_store)



__ATTR定义在include/linux/sysfs.h文件中:

66/**
67*Usethesemacrostomakedefiningattributeseasier.Seeinclude/linux/device.h
68*forexamples..
69*/
70
71#define__ATTR(_name,_mode,_show,_store){\
72.attr={.name=__stringify(_name),.mode=_mode},\
73.show=_show,\
74.store=_store,\
75}



由bus_attr_uevent的定义可以看出,它没有定义show函数,而是只定义了store函数bus_uevent_store,该函数定义在drivers/base/bus.c文件中,其内容如下:

889staticssize_tbus_uevent_store(structbus_type*bus,
890constchar*buf,size_tcount)
891{
892enumkobject_actionaction;
893
894if(kobject_action_type(buf,count,&action)==0)
895kobject_uevent(&bus->p->subsys.kobj,action);
896returncount;
897}



回到bus_register函数:

939-944行,调用kset_create_and_add创建名为devices的kset,赋值给priv->devices_kset,并指定该kset的parentkobject为priv->subsys.kobj。所以该kset对应于用户空间/sys/bus/bus_name/devices目录。

946-951行,调用kset_create_and_add创建名为devices的kset,赋值给priv->drivers_kset,并指定该kset的parentkobject为priv->subsys.kobj。所以该kset对应于用户空间/sys/bus/bus_name/drivers目录。

953行,初始化priv->interfaces

955行,初始化priv->klist_devices,klist_devices_get和klist_devices_put函数定义在drivers/base/bus.c文件中,其内容如下:

873staticvoidklist_devices_get(structklist_node*n)
874{
875structdevice_private*dev_prv=to_device_private_bus(n);
876structdevice*dev=dev_prv->device;
877
878get_device(dev);
879}
880
881staticvoidklist_devices_put(structklist_node*n)
882{
883structdevice_private*dev_prv=to_device_private_bus(n);
884structdevice*dev=dev_prv->device;
885
886put_device(dev);
887}



956行,初始化priv->klist_drivers。

958行,调用add_probe_files函数,该函数定义在drivers/base/bus.c文件中,其内容如下:

641staticintadd_probe_files(structbus_type*bus)
642{
643intretval;
644
645retval=bus_create_file(bus,&bus_attr_drivers_probe);
646if(retval)
647gotoout;
648
649retval=bus_create_file(bus,&bus_attr_drivers_autoprobe);
650if(retval)
651bus_remove_file(bus,&bus_attr_drivers_probe);
652out:
653returnretval;
654}



该函数创建了两个属性文件:/sys/bus/bus_name/drivers_probe和/sys/bus/bus_name/drivers_autoprobe

属性bus_attr_drivers_probe和bus_attr_drivers_autoprobe定义在drivers/base/bus.c文件中,如下:

637staticBUS_ATTR(drivers_probe,S_IWUSR,NULL,store_drivers_probe);
638staticBUS_ATTR(drivers_autoprobe,S_IWUSR|S_IRUGO,
639show_drivers_autoprobe,store_drivers_autoprobe);



可见bus_attr_drivers_probe只定义了store函数,bus_attr_drivers_autoprobe定义了show和store两个函数,它们都定义在drivers/base/bus.c文件中,如下:

226staticssize_tshow_drivers_autoprobe(structbus_type*bus,char*buf)
227{
228returnsprintf(buf,"%d\n",bus->p->drivers_autoprobe);
229}
230
231staticssize_tstore_drivers_autoprobe(structbus_type*bus,
232constchar*buf,size_tcount)
233{
234if(buf[0]=='0')
235bus->p->drivers_autoprobe=0;
236else
237bus->p->drivers_autoprobe=1;
238returncount;
239}
240
241staticssize_tstore_drivers_probe(structbus_type*bus,
242constchar*buf,size_tcount)
243{
244structdevice*dev;
245
246dev=bus_find_device_by_name(bus,NULL,buf);
247if(!dev)
248return-ENODEV;
249if(bus_rescan_devices_helper(dev,NULL)!=0)
250return-EINVAL;
251returncount;
252}



show_drivers_autoprobe函数只是显示当前bus->p->drivers_autoprobe的值。

store_drivers_autoprobe函数根据用户空间传递过来的参数设置bus->p->drivers_autoprobe的值。

store_drivers_probe函数在246行调用bus_find_device_by_name函数根据用户空间传递进来的设备名(保存在buf中)查找对应的device。249行,调用bus_rescan_devices_helper函数为该设备查找对应的device_driver。

回到bus_register函数:

962行,调用bus_add_attrs,该函数定义在drivers/base/bus.c文件中,其内容如下:

838/**
839*bus_add_attrs-Adddefaultattributesforthisbus.
840*@bus:Busthathasjustbeenregistered.
841*/
842
843staticintbus_add_attrs(structbus_type*bus)
844{
845interror=0;
846inti;
847
848if(bus->bus_attrs){
849for(i=0;attr_name(bus->bus_attrs[i]);i++){
850error=bus_create_file(bus,&bus->bus_attrs[i]);
851if(error)
852gotoerr;
853}
854}
855done:
856returnerror;
857err:
858while(--i>=0)
859bus_remove_file(bus,&bus->bus_attrs[i]);
860gotodone;
861}



如果指定了bus的默认属性,即bus->bus_attrs不为NULL,则创建对应的属性文件。

至此,bus_register函数我们就分析完了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: