Linux设备模型——设备驱动模型和sysfs文件系统解读

本文将对Linux系统中的sysfs进行简单的分析,要分析sysfs就必须分析内核的driver-model(驱动模型),两者是紧密联系的。在分析过程中,本文将以platform总线和spi主控制器的platform驱动为例来进行讲解。其实,platform机制是基于driver-model的,通过本文,也会对platform机制有个简单的了解。

内核版本:2.6.30

1. What is sysfs?

个人理解:sysfs向用户空间展示了驱动设备的层次结构。我们都知道设备和对应的驱动都是由内核管理的,这些对于用户空间是不可见的。现在通过sysfs,可以在用户空间直观的了解设备驱动的层次结构。

我们来看看sysfs的文件结构:

[root@yj423 /sys]#lsblock class devices fs modulebus dev firmware kernel power

block:块设备

bus:系统中的总线

class: 设备类型,比如输入设备

dev:系统中已注册的设备节点的视图,有两个子目录char和block。

devices:系统中所有设备拓扑结构视图

fireware:固件

fs:文件系统

kernel:内核配置选项和状态信息

module:模块

power:系统的电源管理数据

2. kobject ,kset和ktype

要分析sysfs,首先就要分析kobject和kset,因为驱动设备的层次结构的构成就是由这两个东东来完成的。

2.1 kobject

kobject是一个对象的抽象,它用于管理对象。每个kobject对应着sysfs中的一个目录。

kobject用struct kobject来描述。

[cpp]view plaincopy

    structkobject{constchar*name;/*在sysfs建立目录的名字*/structlist_headentry;/*用于连接到所属kset的链表中*/structkobject*parent;/*父对象*/structkset*kset;/*属于哪个kset*/structkobj_type*ktype;/*类型*/structsysfs_dirent*sd;/*sysfs中与该对象对应的文件节点*/structkrefkref;/*对象的应用计数*/unsignedintstate_initialized:1;unsignedintstate_in_sysfs:1;unsignedintstate_add_uevent_sent:1;unsignedintstate_remove_uevent_sent:1;unsignedintuevent_suppress:1;};

2.2 kset

kset是一些kobject的集合,这些kobject可以有相同的ktype,也可以不同。同时,kset自己也包含一个kobject。在sysfs中,kset也是对应这一个目录,但是目录下面包含着其他的kojbect。

kset使用struct kset来描述。

[cpp]view plaincopy

    /***structkset-asetofkobjectsofaspecifictype,belongingtoaspecificsubsystem.**Aksetdefinesagroupofkobjects.Theycanbeindividually*different"types"butoverallthesekobjectsallwanttobegrouped*togetherandoperatedoninthesamemanner.ksetsareusedto*definetheattributecallbacksandothercommoneventsthathappento*akobject.**@list:thelistofallkobjectsforthiskset*@list_lock:alockforiteratingoverthekobjects*@kobj:theembeddedkobjectforthiskset(recursion,isn’titfun…)*@uevent_ops:thesetofueventoperationsforthiskset.Theseare*calledwheneverakobjecthassomethinghappentoitsothatthekset*canaddnewenvironmentvariables,orfilterouttheueventsifso*desired.*/structkset{structlist_headlist;/*属于该kset的kobject链表*/spinlock_tlist_lock;structkobjectkobj;/*该kset内嵌的kobj*/structkset_uevent_ops*uevent_ops;};

2.3 ktype

每个kobject对象都内嵌有一个ktype,该结构定义了kobject在创建和删除时所采取的行为。

[cpp]view plaincopy

    structkobj_type{void(*release)(structkobject*kobj);structsysfs_ops*sysfs_ops;structattribute**default_attrs;};structsysfs_ops{ssize_t(*show)(structkobject*,structattribute*,char*);ssize_t(*store)(structkobject*,structattribute*,constchar*,size_t);};/*FIXME*The*ownerfieldisnolongerused.*x86treehasbeencleanedup.Theowner*attributeisstillleftforotherarches.*/structattribute{constchar*name;structmodule*owner;mode_tmode;};

当kobject的引用计数为0时,通过release方法来释放相关的资源。

attribute为属性,每个属性在sysfs中都有对应的属性文件。

sysfs_op的两个方法用于实现读取和写入属性文件时应该采取的行为。

2.4 kobject与kset的关系

下面这张图非常经典。最下面的kobj都属于一个kset,同时这些kobj的父对象就是kset内嵌的kobj。通过链表,kset可以获取所有属于它的kobj。

从sysfs角度而言,kset代表一个文件夹,而下面的kobj就是这个文件夹里面的内容,而内容有可能是文件也有可能是文件夹。

3.举例

在上一节中,我们知道sys下有一个bus目录,这一将分析如何通过kobject创建bus目录。

下面代码位于drivers/base/bus.c

[cpp]view plaincopy

    int__initbuses_init(void){bus_kset=kset_create_and_add("bus",&bus_uevent_ops,NULL);if(!bus_kset)return-ENOMEM;return0;}staticstructkset_uevent_opsbus_uevent_ops={.filter=bus_uevent_filter,};staticintbus_uevent_filter(structkset*kset,structkobject*kobj){structkobj_type*ktype=get_ktype(kobj);if(ktype==&bus_ktype)return1;return0;}

这里直接调用kset_create_and_add,第一个参数为要创建的目录的名字,而第三个参数表示没有父对象。

下面代码位于drivers/base/kobject.c

[cpp]view plaincopy

    /***kset_create_and_add-createastructksetdynamicallyandaddittosysfs**@name:thenameforthekset*@uevent_ops:astructkset_uevent_opsforthekset*@parent_kobj:theparentkobjectofthiskset,ifany.**Thisfunctioncreatesaksetstructuredynamicallyandregistersit*withsysfs.Whenyouarefinishedwiththisstructure,call*kset_unregister()andthestructurewillbedynamicallyfreedwhenit*isnolongerbeingused.**Iftheksetwasnotabletobecreated,NULLwillbereturned.*/structkset*kset_create_and_add(constchar*name,structkset_uevent_ops*uevent_ops,structkobject*parent_kobj){structkset*kset;interror;kset=kset_create(name,uevent_ops,parent_kobj);/*建立kset,设置某些字段*/if(!kset)returnNULL;error=kset_register(kset);/*添加kset到sysfs*/if(error){kfree(kset);returnNULL;}returnkset;}

这里主要调用了两个函数,接下分别来看下。

3.1 kset_create函数

下面代码位于drivers/base/kobject.c

[cpp]view plaincopy

    /***kset_create-createastructksetdynamically**@name:thenameforthekset*@uevent_ops:astructkset_uevent_opsforthekset*@parent_kobj:theparentkobjectofthiskset,ifany.**Thisfunctioncreatesaksetstructuredynamically.Thisstructurecan*thenberegisteredwiththesystemandshowupinsysfswithacallto*kset_register().Whenyouarefinishedwiththisstructure,if*kset_register()hasbeencalled,callkset_unregister()andthe*structurewillbedynamicallyfreedwhenitisnolongerbeingused.**Iftheksetwasnotabletobecreated,NULLwillbereturned.*/staticstructkset*kset_create(constchar*name,structkset_uevent_ops*uevent_ops,structkobject*parent_kobj){structkset*kset;kset=kzalloc(sizeof(*kset),GFP_KERNEL);/*分配kset*/if(!kset)returnNULL;kobject_set_name(&kset->kobj,name);/*设置kobj->name*/kset->uevent_ops=uevent_ops;kset->kobj.parent=parent_kobj;/*设置父对象*//**Thekobjectofthisksetwillhaveatypeofkset_ktypeandbelongto*noksetitself.Thatwaywecanproperlyfreeitwhenitis*finishedbeingused.*/kset->kobj.ktype=&kset_ktype;kset->kobj.kset=NULL;/*本keset不属于任何kset*/returnkset;}

这个函数中,动态分配了kset结构,调用kobject_set_name设置kset->kobj->name为bus,也就是我们要创建的目录bus。同时这里kset->kobj.parent为NULL,

也就是没有父对象。因为要创建的bus目录是在sysfs所在的根目录创建的,自然没有父对象。

随后简要看下由kobject_set_name函数调用引发的一系列调用。

[cpp]view plaincopy

    /***kobject_set_name-Setthenameofakobject*@kobj:structkobjecttosetthenameof*@fmt:formatstringusedtobuildthename**Thissetsthenameofthekobject.Ifyouhavealreadyaddedthe*kobjecttothesystem,youmustcallkobject_rename()inorderto*changethenameofthekobject.*/intkobject_set_name(structkobject*kobj,constchar*fmt,…){va_listvargs;intretval;va_start(vargs,fmt);retval=kobject_set_name_vargs(kobj,fmt,vargs);va_end(vargs);returnretval;}/***kobject_set_name_vargs-Setthenameofankobject*@kobj:structkobjecttosetthenameof*@fmt:formatstringusedtobuildthename*@vargs:vargstoformatthestring.*/intkobject_set_name_vargs(structkobject*kobj,constchar*fmt,va_listvargs){constchar*old_name=kobj->name;char*s;if(kobj->name&&!fmt)return0;kobj->name=kvasprintf(GFP_KERNEL,fmt,vargs);if(!kobj->name)return-ENOMEM;/*ewww…someofthesebuggershave’/’inthename…*/while((s=strchr(kobj->name,’/’)))s[0]=’!’;kfree(old_name);return0;}/*Simplifiedasprintf.*/char*kvasprintf(gfp_tgfp,constchar*fmt,va_listap){unsignedintlen;char*p;va_listaq;va_copy(aq,ap);len=vsnprintf(NULL,0,fmt,aq);va_end(aq);p=kmalloc(len+1,gfp);if(!p)returnNULL;vsnprintf(p,len+1,fmt,ap);returnp;}

3.2 kset_register

下面代码位于drivers/base/kobject.c。

[cpp]view plaincopy

    /***kset_register-initializeandaddakset.*@k:kset.*/intkset_register(structkset*k){interr;if(!k)return-EINVAL;kset_init(k);/*初始化kset*/err=kobject_add_internal(&k->kobj);/*在sysfs中建立目录*/if(err)returnerr;kobject_uevent(&k->kobj,KOBJ_ADD);return0;}

这里面调用了3个函数。这里先介绍前两个函数。

3.2.1 kset_init

该函数用于初始化kset。

下面代码位于drivers/base/kobject.c。

[cpp]view plaincopy

    /***kset_init-initializeaksetforuse*@k:kset*/voidkset_init(structkset*k){kobject_init_internal(&k->kobj);/*初始化kobject的某些字段*/INIT_LIST_HEAD(&k->list);/*初始化链表头*/spin_lock_init(&k->list_lock);/*初始化自旋锁*/}staticvoidkobject_init_internal(structkobject*kobj){if(!kobj)return;kref_init(&kobj->kref);/*初始化引用基计数*/INIT_LIST_HEAD(&kobj->entry);/*初始化链表头*/kobj->state_in_sysfs=0;kobj->state_add_uevent_sent=0;kobj->state_remove_uevent_sent=0;kobj->state_initialized=1;}

3.2.2 kobject_add_internal

该函数将在sysfs中建立目录。

下面代码位于drivers/base/kobject.c。

[cpp]view plaincopy

    staticintkobject_add_internal(structkobject*kobj){interror=0;structkobject*parent;if(!kobj)return-ENOENT;/*检查name字段是否存在*/if(!kobj->name||!kobj->name[0]){WARN(1,"kobject:(%p):attemptedtoberegisteredwithempty""name!\n",kobj);return-EINVAL;}parent=kobject_get(kobj->parent);/*有父对象则增加父对象引用计数*//*joinksetifset,useitasparentifwedonotalreadyhaveone*/if(kobj->kset){if(!parent)/*kobj属于某个kset,但是该kobj没有父对象,则以kset的kobj作为父对象*/parent=kobject_get(&kobj->kset->kobj);kobj_kset_join(kobj);/*将kojbect添加到kset结构中的链表当中*/kobj->parent=parent;}pr_debug("kobject:’%s'(%p):%s:parent:’%s’,set:’%s’\n",kobject_name(kobj),kobj,__func__,parent?kobject_name(parent):"<NULL>",kobj->kset?kobject_name(&kobj->kset->kobj):"<NULL>");error=create_dir(kobj);/*根据kobj->name在sys中建立目录*/if(error){kobj_kset_leave(kobj);/*删除链表项*/kobject_put(parent);/*减少引用计数*/kobj->parent=NULL;/*benoisyonerrorissues*/if(error==-EEXIST)printk(KERN_ERR"%sfailedfor%swith""-EEXIST,don’ttrytoregisterthingswith""thesamenameinthesamedirectory.\n",__func__,kobject_name(kobj));elseprintk(KERN_ERR"%sfailedfor%s(%d)\n",__func__,kobject_name(kobj),error);dump_stack();}elsekobj->state_in_sysfs=1;returnerror;}

在上面的kset_create中有kset->kobj.kset = NULL,因此if (kobj->kset)条件不满足。因此在这个函数中,对name进行了必要的检查之后,调用了create_dir在sysfs中创建目录。

在create_dir执行完成以后会在sysfs的根目录(/sys/)建立文件夹bus。该函数的详细分析将在后面给出。

至此,对bus目录的建立有了简单而直观的了解。我们可以看出kset其实就是表示一个文件夹,而kset本身也含有一个kobject,而该kobject的name字段即为该目录的名字,本例中为bus。

4. driver model

第2节所介绍的是最底层,最核心的内容。下面开始将描述较为高层的内容。

Linux设备模型使用了三个数据结构分别来描述总线、设备和驱动。所有的设备和对应的驱动都必须挂载在某一个总线上,通过总线,可以绑定设备和驱动。

这个属于分离的思想,将设备和驱动分开管理。

同时驱动程序可以了解到所有它所支持的设备,同样的,设备也能知道它对应驱动程序。

4.1 bus

总线是处理器与一个设备或者多个设备之间的通道。在设备模型中,所有的设备都挂载在某一个总线上。总线使用struct bus_type来表述。

下列代码位于include/linux/device.h。

[cpp]view plaincopy

    <strong><spanstyle="font-family:System;font-size:12px;">structbus_type{constchar*name;structbus_attribute*bus_attrs;structdevice_attribute*dev_attrs;structdriver_attribute*drv_attrs;int(*match)(structdevice*dev,structdevice_driver*drv);int(*uevent)(structdevice*dev,structkobj_uevent_env*env);int(*probe)(structdevice*dev);int(*remove)(structdevice*dev);void(*shutdown)(structdevice*dev);int(*suspend)(structdevice*dev,pm_message_tstate);int(*suspend_late)(structdevice*dev,pm_message_tstate);int(*resume_early)(structdevice*dev);int(*resume)(structdevice*dev);structdev_pm_ops*pm;structbus_type_private*p;};/***structbus_type_private-structuretoholdtheprivatetothedrivercoreportionsofthebus_typestructure.**@subsys-thestructksetthatdefinesthisbus.Thisisthemainkobject*@drivers_kset-thelistofdriversassociatedwiththisbus*@devices_kset-thelistofdevicesassociatedwiththisbus*@klist_devices-theklisttoiterateoverthe@devices_kset*@klist_drivers-theklisttoiterateoverthe@drivers_kset*@bus_notifier-thebusnotifierlistforanythingthatcaresaboutthings*onthisbus.*@bus-pointerbacktothestructbus_typethatthisstructureisassociated*with.**Thisstructureistheonethatistheactualkobjectallowingstruct*bus_typetobestaticallyallocatedsafely.Nothingoutsideofthedriver*coreshouldevertouchthesefields.*/structbus_type_private{structksetsubsys;structkset*drivers_kset;structkset*devices_kset;structklistklist_devices;structklistklist_drivers;structblocking_notifier_headbus_notifier;unsignedintdrivers_autoprobe:1;structbus_type*bus;};</span></strong>

我们看到每个bus_type都包含一个kset对象subsys,该kset在/sys/bus/目录下有着对应的一个目录,目录名即为字段name。后面我们将看到platform总线的建立。

drivers_kset和devices_kset对应着两个目录,该两个目录下将包含该总线上的设备和相应的驱动程序。

同时总线上的设备和驱动将分别保存在两个链表中:klist_devices和klist_drivers。

4.2 device

设备对象在driver-model中使用struct device来表示。

下列代码位于include/linux/device.h。[cpp]view plaincopy

    structdevice{structdevice*parent;structdevice_private*p;structkobjectkobj;constchar*init_name;/*initialnameofthedevice*/structdevice_type*type;structsemaphoresem;/*semaphoretosynchronizecallsto*itsdriver.*/structbus_type*bus;/*typeofbusdeviceison*/structdevice_driver*driver;/*whichdriverhasallocatedthisdevice*/void*driver_data;/*dataprivatetothedriver*/void*platform_data;/*Platformspecificdata,devicecoredoesn’ttouchit*/structdev_pm_infopower;#ifdefCONFIG_NUMAintnuma_node;/*NUMAnodethisdeviceiscloseto*/#endifu64*dma_mask;/*dmamask(ifdma’abledevice)*/u64coherent_dma_mask;/*Likedma_mask,butforalloc_coherentmappingsasnotallhardwaresupports64bitaddressesforconsistentallocationssuchdescriptors.*/structdevice_dma_parameters*dma_parms;structlist_headdma_pools;/*dmapools(ifdma’ble)*/structdma_coherent_mem*dma_mem;/*internalforcoherentmemoverride*//*archspecificadditions*/structdev_archdataarchdata;dev_tdevt;/*dev_t,createsthesysfs"dev"*/spinlock_tdevres_lock;structlist_headdevres_head;structklist_nodeknode_class;structclass*class;structattribute_group**groups;/*optionalgroups*/void(*release)(structdevice*dev);};/***structdevice_private-structuretoholdtheprivatetothedrivercoreportionsofthedevicestructure.**@klist_children-klistcontainingallchildrenofthisdevice*@knode_parent-nodeinsiblinglist*@knode_driver-nodeindriverlist*@knode_bus-nodeinbuslist*@device-pointerbacktothestructclassthatthisstructureis*associatedwith.**Nothingoutsideofthedrivercoreshouldevertouchthesefields.*/structdevice_private{structklistklist_children;structklist_nodeknode_parent;structklist_nodeknode_driver;structklist_nodeknode_bus;structdevice*device;};

device本身包含一个kobject,也就是说这个device在sysfs的某个地方有着一个对应的目录。

该device所挂载的bus由knode_bus指定。

该device所对应的设备驱动由knode_driver指定。

4.3 driver

设备设备对象在driver-model中使用struct device_driver来表示。

下列代码位于include/linux/device.h。

[cpp]view plaincopy

    structdevice_driver{constchar*name;structbus_type*bus;structmodule*owner;constchar*mod_name;/*usedforbuilt-inmodules*/int(*probe)(structdevice*dev);int(*remove)(structdevice*dev);void(*shutdown)(structdevice*dev);int(*suspend)(structdevice*dev,pm_message_tstate);int(*resume)(structdevice*dev);structattribute_group**groups;structdev_pm_ops*pm;structdriver_private*p;};structdriver_private{structkobjectkobj;structklistklist_devices;structklist_nodeknode_bus;structmodule_kobject*mkobj;structdevice_driver*driver;};

device_driver本身包含一个kobject,也就是说这个device_driver在sysfs的某个地方有着一个对应的目录。

该设备驱动所支持的设备由klist_devices指定。

该设备驱动所挂载的总线由knode_bus制定。

5. Bus举例

本节我们将以platform总线为例,来看看,/sys/bus/platform是如何建立的。

platform总线的注册是由platform_bus_init函数完成的。该函数在内核启动阶段被调用,我们来简单看下调用过程:

start_kernel() -> rest_init() ->kernel_init() -> do_basic_setup() -> driver_init() -> platform_bus_init()。

注:kernel_init()是在rest_init函数中创建内核线程来执行的。

[cpp]view plaincopy

    int__initplatform_bus_init(void){interror;early_platform_cleanup();error=device_register(&platform_bus);if(error)returnerror;error=bus_register(&platform_bus_type);if(error)device_unregister(&platform_bus);returnerror;}structbus_typeplatform_bus_type={.name="platform",.dev_attrs=platform_dev_attrs,.match=platform_match,.uevent=platform_uevent,.pm=PLATFORM_PM_OPS_PTR,};EXPORT_SYMBOL_GPL(platform_bus_type);

从bus_type,我们看到该总线的名字为platform。

调用了两个函数,我们只关注bus_register函数。

[cpp]view plaincopy

    /***bus_register-registerabuswiththesystem.*@bus:bus.**Oncewehavethat,weregisteredthebuswiththekobject*infrastructure,thenregisterthechildrensubsystemsithas:*thedevicesanddriversthatbelongtothebus.*/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(&priv->bus_notifier);/*设定kobject->name*/retval=kobject_set_name(&priv->subsys.kobj,"%s",bus->name);if(retval)gotoout;priv->subsys.kobj.kset=bus_kset;priv->subsys.kobj.ktype=&bus_ktype;priv->drivers_autoprobe=1;/*注册kset,在bus/建立目录XXX,XXX为bus->name*/retval=kset_register(&priv->subsys);if(retval)gotoout;/*创建属性,在bus/XXX/建立文件uevent*/retval=bus_create_file(bus,&bus_attr_uevent);if(retval)gotobus_uevent_fail;/*创建kset,在bus/XXX/建立目录devices*/priv->devices_kset=kset_create_and_add("devices",NULL,&priv->subsys.kobj);if(!priv->devices_kset){retval=-ENOMEM;gotobus_devices_fail;}/*创建kset,在bus/XXX/建立目录drivers*/priv->drivers_kset=kset_create_and_add("drivers",NULL,&priv->subsys.kobj);if(!priv->drivers_kset){retval=-ENOMEM;gotobus_drivers_fail;}/*初始化2个内核链表,*/klist_init(&priv->klist_devices,klist_devices_get,klist_devices_put);klist_init(&priv->klist_drivers,NULL,NULL);/*创建属性,在bus/XXX/建立文件drivers_autoprobe和drivers_probe*/retval=add_probe_files(bus);if(retval)gotobus_probe_files_fail;/*根据bus->bus_attribute创建属性,在bus/XXX/下建立相应的文件d*/retval=bus_add_attrs(bus);if(retval)gotobus_attrs_fail;pr_debug("bus:’%s’:registered\n",bus->name);return0;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_attr_uevent);bus_uevent_fail:kset_unregister(&bus->p->subsys);kfree(bus->p);out:bus->p=NULL;returnretval;}EXPORT_SYMBOL_GPL(bus_register);

函数中,首先调用kobject_set_name设置了bus对象的subsys.kobject->name 为 platform,也就是说会建立一个名为platform的目录。kobject_set_name函数在3.1小节中已经给出。

在这里还用到了bus_kset这个变量,这个变量就是在第3节buses_init函数中建立bus目录所对应的kset对象。

接着,priv->subsys.kobj.kset = bus_kset,设置subsys的kobj在bus_kset对象包含的集合中,也就是说bus目录下将包含subsys对象所对应的目录,即platform。

紧接着调用了kset_register,参数为&priv->subsys。该函数在3.2节中以给出。在该函数的调用过程中,将调用kobj_kset_join函数,该函数将kobject添加到kobject->kset的链表中。

[cpp]view plaincopy

    /*addthekobjecttoitskset’slist*/staticvoidkobj_kset_join(structkobject*kobj){if(!kobj->kset)return;kset_get(kobj->kset);/*增加kset引用计数*/spin_lock(&kobj->kset->list_lock);list_add_tail(&kobj->entry,&kobj->kset->list);/*将kojbect添加到kset结构中的链表当中*/spin_unlock(&kobj->kset->list_lock);}

kset_register函数执行完成后,将在/sys/bus/下建立目录platform。此刻,我们先来看下kset和kobject之间的关系。

然后,调用了bus_create_file函数在/sys/bus/platform/下建立文件uevent。

[cpp]view plaincopy

    intbus_create_file(structbus_type*bus,structbus_attribute*attr){interror;if(bus_get(bus)){error=sysfs_create_file(&bus->p->subsys.kobj,&attr->attr);bus_put(bus);}elseerror=-EINVAL;returnerror;}EXPORT_SYMBOL_GPL(bus_create_file);

有关底层的sysfs将在后面叙述,这里只要关注参数&bus->p->subsys.kobj,表示在该kset下建立文件,也就是platform下建立。

接着调用了2次kset_create_and_add,分别在/sys/bus/platform/下建立了文件夹devices和drivers。该函数位于第3节开始处。

这里和第3节调用kset_create_and_add时的最主要一个区别就是:此时的parent参数不为NULL,而是&priv->subsys.kobj。

也就是说,将要创建的kset的kobject->parent = &priv->subsys.kobj,也即新建的kset被包含在platform文件夹对应的kset中。

我们来看下关系图:

随后,调用了add_probe_files创建了属性文件drivers_autoprobe和drivers_probe。

[cpp]view plaincopy

    staticintadd_probe_files(structbus_type*bus){intretval;retval=bus_create_file(bus,&bus_attr_drivers_probe);if(retval)gotoout;retval=bus_create_file(bus,&bus_attr_drivers_autoprobe);if(retval)bus_remove_file(bus,&bus_attr_drivers_probe);out:returnretval;}

该函数只是简单的调用了两次bus_create_file,该函数已在前面叙述过。

最后调用bus_add_attrs创建总线相关的属性文件。

[cpp]view plaincopy

    /***bus_add_attrs-Adddefaultattributesforthisbus.*@bus:Busthathasjustbeenregistered.*/staticintbus_add_attrs(structbus_type*bus){interror=0;inti;if(bus->bus_attrs){for(i=0;attr_name(bus->bus_attrs[i]);i++){error=bus_create_file(bus,&bus->bus_attrs[i]);if(error)gotoerr;}}done:returnerror;err:while(–i>=0)bus_remove_file(bus,&bus->bus_attrs[i]);gotodone;}

我们可以看到这个函数将根据bus_type->bus_arrts来创建属性文件。不过,在本例中,bus_arrts从未给出定义,因此次函数不做任何工作。

好了,整个bus_register调用完成了,我们来看下sysfs中实际的情况。

[root@yj423 platform]#pwd/sys/bus/platform[root@yj423 platform]#lsdevices drivers drivers_autoprobe drivers_probe uevent

最后,我们对整个bus_register的过程进行一个小结。

6. device举例

本节将首先讲述如何在/sys/devices下建立虚拟的platform设备,然后再讲述如何在/sys/devices/platform/下建立子设备。

6.1 虚拟的platform设备

之所以叫虚拟是因为这个platform并不代表任何实际存在的设备,但是platform将是所有具体设备的父设备。

在第5节,platform_bus_init函数中还调用了device_register,现在对其做出分析。

[cpp]view plaincopy

    int__initplatform_bus_init(void){interror;early_platform_cleanup();error=device_register(&platform_bus);if(error)returnerror;error=bus_register(&platform_bus_type);if(error)device_unregister(&platform_bus);returnerror;}structdeviceplatform_bus={.init_name="platform",};EXPORT_SYMBOL_GPL(platform_bus)

下列函数位于drivers/base/core.c。[cpp]view plaincopy

    /***device_register-registeradevicewiththesystem.*@dev:pointertothedevicestructure**Thishappensintwocleansteps-initializethedevice*andaddittothesystem.Thetwostepscanbecalled*separately,butthisistheeasiestandmostcommon.*I.e.youshouldonlycallthetwohelpersseparatelyif*haveaclearlydefinedneedtouseandrefcountthedevice*beforeitisaddedtothehierarchy.**NOTE:_Never_directlyfree@devaftercallingthisfunction,even*ifitreturnedanerror!Alwaysuseput_device()togiveupthe*referenceinitializedinthisfunctioninstead.*/intdevice_register(structdevice*dev){device_initialize(dev);/*初始化dev的某些字段*/returndevice_add(dev);/*将设备添加到系统中*/}

一个设备的注册分成两部,每步通过调用一个函数函数。首先先看第一步:

下列函数位于drivers/base/core.c。[cpp]view plaincopy

    /***device_initialize-initdevicestructure.*@dev:device.**Thispreparesthedeviceforusebyotherlayersbyinitializing*itsfields.*Itisthefirsthalfofdevice_register(),ifcalledby*thatfunction,thoughitcanalsobecalledseparately,soone*mayuse@dev’sfields.Inparticular,get_device()/put_device()*maybeusedforreferencecountingof@devaftercallingthis*function.**NOTE:Useput_device()togiveupyourreferenceinsteadoffreeing*@devdirectlyonceyouhavecalledthisfunction.*/voiddevice_initialize(structdevice*dev){dev->kobj.kset=devices_kset;/*设置kobj属于哪个kset,/sys/devices/*/kobject_init(&dev->kobj,&device_ktype);/*初始化dev->kobj*/INIT_LIST_HEAD(&dev->dma_pools);/*初始化链表头*/init_MUTEX(&dev->sem);/*初始化互斥体*/spin_lock_init(&dev->devres_lock);/*初始化自旋锁*/INIT_LIST_HEAD(&dev->devres_head);/*初始化链表头*/device_init_wakeup(dev,0);/*设置该device不能唤醒*/device_pm_init(dev);/*设置该device可操作*/set_dev_node(dev,-1);/*设置NUMA节点*/}

6.1.1 有关devices_kset

首先其中用到了devices_kset对象,这个对象和第3节当中的bus_kset是同样的性质,也就是说该对象表示一个目录。

该对象的建立是在devices_init函数中完成的。

[cpp]view plaincopy

    int__initdevices_init(void){devices_kset=kset_create_and_add("devices",&device_uevent_ops,NULL);if(!devices_kset)return-ENOMEM;dev_kobj=kobject_create_and_add("dev",NULL);if(!dev_kobj)gotodev_kobj_err;sysfs_dev_block_kobj=kobject_create_and_add("block",dev_kobj);if(!sysfs_dev_block_kobj)gotoblock_kobj_err;sysfs_dev_char_kobj=kobject_create_and_add("char",dev_kobj);if(!sysfs_dev_char_kobj)gotochar_kobj_err;return0;char_kobj_err:kobject_put(sysfs_dev_block_kobj);block_kobj_err:kobject_put(dev_kobj);dev_kobj_err:kset_unregister(devices_kset);return-ENOMEM;}

由此可见,devices_kset对象表示的目录为/sys下的devices目录。6.1.2 kobject_init

下列函数位于lib/kojbect.c。

[cpp]view plaincopy

    /***kobject_init-initializeakobjectstructure*@kobj:pointertothekobjecttoinitialize*@ktype:pointertothektypeforthiskobject.**Thisfunctionwillproperlyinitializeakobjectsuchthatitcanthen*bepassedtothekobject_add()call.**Afterthisfunctioniscalled,thekobjectMUSTbecleanedupbyacall*tokobject_put(),notbyacalltokfreedirectlytoensurethatallof*thememoryiscleanedupproperly.*/voidkobject_init(structkobject*kobj,structkobj_type*ktype){char*err_str;if(!kobj){err_str="invalidkobjectpointer!";gotoerror;}if(!ktype){err_str="musthaveaktypetobeinitializedproperly!\n";gotoerror;}if(kobj->state_initialized){/*donoterroroutassometimeswecanrecover*/printk(KERN_ERR"kobject(%p):triedtoinitaninitialized""object,somethingisseriouslywrong.\n",kobj);dump_stack();}kobject_init_internal(kobj);kobj->ktype=ktype;return;error:printk(KERN_ERR"kobject(%p):%s\n",kobj,err_str);dump_stack();}EXPORT_SYMBOL(kobject_init);staticvoidkobject_init_internal(structkobject*kobj){if(!kobj)return;kref_init(&kobj->kref);/*初始化引用基计数*/INIT_LIST_HEAD(&kobj->entry);/*初始化链表头*/kobj->state_in_sysfs=0;kobj->state_add_uevent_sent=0;kobj->state_remove_uevent_sent=0;kobj->state_initialized=1;}

该函数在做了一系列的必要检查后,调用kobject_init_internal初始化了kobject的某些字段。6.1.3 device_init_wakeup

参数val为0,设置该device不能够唤醒。

[cpp]view plaincopy

    #ifdefCONFIG_PM/*changestodevice_may_wakeuptakeeffectonthenextpmstatechange.*bydefault,devicesshouldwakeupiftheycan.*/staticinlinevoiddevice_init_wakeup(structdevice*dev,intval){dev->power.can_wakeup=dev->power.should_wakeup=!!val;}。。。。。。#else/*!CONFIG_PM*//*ForsomereasonthenexttworoutinesworkevenwithoutCONFIG_PM*/staticinlinevoiddevice_init_wakeup(structdevice*dev,intval){dev->power.can_wakeup=!!val;}。。。。。。#endif

6.1.4 device_pm_init

设置电源的状态。

[cpp]view plaincopy

    staticinlinevoiddevice_pm_init(structdevice*dev){dev->power.status=DPM_ON;/*该device被认为可操作*/}

6.1.5 set_dev_node

如果使用NUMA,则设置NUMA节点。

[cpp]view plaincopy

    #ifdefCONFIG_NUMA。。。。。。staticinlinevoidset_dev_node(structdevice*dev,intnode){dev->numa_node=node;}#else。。。。。。staticinlinevoidset_dev_node(structdevice*dev,intnode){}#endif

6.2 device_add

接下来是注册的第二步:调用device_add。

[cpp]view plaincopy

    /***device_add-adddevicetodevicehierarchy.*@dev:device.**Thisispart2ofdevice_register(),thoughmaybecalled*separately_iff_device_initialize()hasbeencalledseparately.**Thisadds@devtothekobjecthierarchyviakobject_add(),addsit*totheglobalandsiblinglistsforthedevice,then*addsittotheotherrelevantsubsystemsofthedrivermodel.**NOTE:_Never_directlyfree@devaftercallingthisfunction,even*ifitreturnedanerror!Alwaysuseput_device()togiveupyour*referenceinstead.*/intdevice_add(structdevice*dev){structdevice*parent=NULL;structclass_interface*class_intf;interror=-EINVAL;dev=get_device(dev);/*增加引用计数*/if(!dev)gotodone;dev->p=kzalloc(sizeof(*dev->p),GFP_KERNEL);/*分配device_private结构*/if(!dev->p){error=-ENOMEM;gotodone;}dev->p->device=dev;/*保存dev*/klist_init(&dev->p->klist_children,klist_children_get,/*初始化内核链表*/klist_children_put);/**forstaticallyallocateddevices,whichshouldallbeconverted*someday,weneedtoinitializethename.Wepreventreadingback*thename,andforcetheuseofdev_name()*/if(dev->init_name){dev_set_name(dev,dev->init_name);/*dev->kobject->name=dev->init_name*/dev->init_name=NULL;}if(!dev_name(dev))/*检查dev->kobject->name*/gotoname_error;pr_debug("device:’%s’:%s\n",dev_name(dev),__func__);parent=get_device(dev->parent);/*增加父设备引用计数*/setup_parent(dev,parent);/*设置dev->kobject->parent*//*useparentnuma_node*/if(parent)set_dev_node(dev,dev_to_node(parent));/*first,registerwithgenericlayer.*//*werequirethenametobesetbefore,andpassNULL*//*执行完以后,将在/sys/devices/下建立目录XXX,目录名XXX为dev->kobj->name*/error=kobject_add(&dev->kobj,dev->kobj.parent,NULL);if(error)gotoError;/*notifyplatformofdeviceentry*/if(platform_notify)platform_notify(dev);/*在XXX下建立文件uevent*/error=device_create_file(dev,&uevent_attr);if(error)gotoattrError;if(MAJOR(dev->devt)){/*主设备号不为0*/error=device_create_file(dev,&devt_attr);/*创建属性文件dev*/if(error)gotoueventattrError;/*在sys/dev/char/下建立symlink,名字为主设备号:次设备号,该链接指向XXX*/error=device_create_sys_dev_entry(dev);if(error)gotodevtattrError;}error=device_add_class_symlinks(dev);if(error)gotoSymlinkError;error=device_add_attrs(dev);/*添加类设备属型文件和属性组*/if(error)gotoAttrsError;error=bus_add_device(dev);/*添加3个symlink*/if(error)gotoBusError;error=dpm_sysfs_add(dev);/*创建power子目录,并在其下添加电源管理的属性组文件*/if(error)gotoDPMError;device_pm_add(dev);/*将该device添加到电源管理链表中*//*Notifyclientsofdeviceaddition.Thiscallmustcome*afterdpm_sysf_add()andbeforekobject_uevent().*/if(dev->bus)blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE,dev);kobject_uevent(&dev->kobj,KOBJ_ADD);/*通知用户层*/bus_attach_device(dev);/*将设备添加到总线的设备链表中,并尝试获取驱动*/if(parent)klist_add_tail(&dev->p->knode_parent,/*有父设备,则将该设备添加到父设备的儿子链表中*/&parent->p->klist_children);if(dev->class){/*该设备属于某个设备类*/mutex_lock(&dev->class->p->class_mutex);/*tietheclasstothedevice*/klist_add_tail(&dev->knode_class,/*将device添加到class的类设备链表中*/&dev->class->p->class_devices);/*notifyanyinterfacesthatthedeviceishere*/list_for_each_entry(class_intf,&dev->class->p->class_interfaces,node)if(class_intf->add_dev)class_intf->add_dev(dev,class_intf);mutex_unlock(&dev->class->p->class_mutex);}done:put_device(dev);returnerror;DPMError:bus_remove_device(dev);BusError:device_remove_attrs(dev);AttrsError:device_remove_class_symlinks(dev);SymlinkError:if(MAJOR(dev->devt))device_remove_sys_dev_entry(dev);devtattrError:if(MAJOR(dev->devt))device_remove_file(dev,&devt_attr);ueventattrError:device_remove_file(dev,&uevent_attr);attrError:kobject_uevent(&dev->kobj,KOBJ_REMOVE);kobject_del(&dev->kobj);Error:cleanup_device_parent(dev);if(parent)put_device(parent);name_error:kfree(dev->p);dev->p=NULL;gotodone;}

该函数调用了非常多的其他函数,接下来对主要的函数做出分析。6.2.1 setup_parent函数

下列代码位于drivers/base/core.c。

[cpp]view plaincopy

    staticvoidsetup_parent(structdevice*dev,structdevice*parent){structkobject*kobj;kobj=get_device_parent(dev,parent);if(kobj)dev->kobj.parent=kobj;}staticstructkobject*get_device_parent(structdevice*dev,structdevice*parent){/*classdeviceswithoutaparentlivein/sys/class/<classname>/*/if(dev->class&&(!parent||parent->class!=dev->class))return&dev->class->p->class_subsys.kobj;/*allotherdeviceskeeptheirparent*/elseif(parent)return&parent->kobj;returnNULL;}

该函数将设置dev对象的parent。在这里实际传入的parent为NULL,同时dev->class也没有定义过。因此这个函数什么都没有做。6.2.2 kobject_add函数

下列代码位于lib/kobject.c。

[cpp]view plaincopy

    /***kobject_add-themainkobjectaddfunction*@kobj:thekobjecttoadd*@parent:pointertotheparentofthekobject.*@fmt:formattonamethekobjectwith.**Thekobjectnameissetandaddedtothekobjecthierarchyinthis*function.**If@parentisset,thentheparentofthe@kobjwillbesettoit.*If@parentisNULL,thentheparentofthe@kobjwillbesettothe*kobjectassocitedwiththeksetassignedtothiskobject.Ifnokset*isassignedtothekobject,thenthekobjectwillbelocatedinthe*rootofthesysfstree.**Ifthisfunctionreturnsanerror,kobject_put()mustbecalledto*properlycleanupthememoryassociatedwiththeobject.*Undernoinstanceshouldthekobjectthatispassedtothisfunction*bedirectlyfreedwithacalltokfree(),thatcanleakmemory.**Note,no"add"ueventwillbecreatedwiththiscall,thecallershouldset*upallofthenecessarysysfsfilesfortheobjectandthencall*kobject_uevent()withtheUEVENT_ADDparametertoensurethat*userspaceisproperlynotifiedofthiskobject’screation.*/intkobject_add(structkobject*kobj,structkobject*parent,constchar*fmt,…){va_listargs;intretval;if(!kobj)return-EINVAL;if(!kobj->state_initialized){printk(KERN_ERR"kobject’%s'(%p):triedtoaddan""uninitializedobject,somethingisseriouslywrong.\n",kobject_name(kobj),kobj);dump_stack();return-EINVAL;}va_start(args,fmt);retval=kobject_add_varg(kobj,parent,fmt,args);va_end(args);returnretval;}EXPORT_SYMBOL(kobject_add);staticintkobject_add_varg(structkobject*kobj,structkobject*parent,constchar*fmt,va_listvargs){intretval;retval=kobject_set_name_vargs(kobj,fmt,vargs);if(retval){printk(KERN_ERR"kobject:cannotsetnameproperly!\n");returnretval;}kobj->parent=parent;returnkobject_add_internal(kobj);}staticintkobject_add_internal(structkobject*kobj){interror=0;structkobject*parent;if(!kobj)return-ENOENT;/*检查name字段是否存在*/if(!kobj->name||!kobj->name[0]){WARN(1,"kobject:(%p):attemptedtoberegisteredwithempty""name!\n",kobj);return-EINVAL;}parent=kobject_get(kobj->parent);/*有父对象则增加父对象引用计数*//*joinksetifset,useitasparentifwedonotalreadyhaveone*/if(kobj->kset){if(!parent)/*kobj属于某个kset,但是该kobj没有父对象,则以kset的kobj作为父对象*/parent=kobject_get(&kobj->kset->kobj);kobj_kset_join(kobj);/*将kojbect添加到kset结构中的链表当中*/kobj->parent=parent;}pr_debug("kobject:’%s'(%p):%s:parent:’%s’,set:’%s’\n",kobject_name(kobj),kobj,__func__,parent?kobject_name(parent):"<NULL>",kobj->kset?kobject_name(&kobj->kset->kobj):"<NULL>");error=create_dir(kobj);/*根据kobj->name在sys中建立目录*/if(error){kobj_kset_leave(kobj);/*删除链表项*/kobject_put(parent);/*减少引用计数*/kobj->parent=NULL;/*benoisyonerrorissues*/if(error==-EEXIST)printk(KERN_ERR"%sfailedfor%swith""-EEXIST,don’ttrytoregisterthingswith""thesamenameinthesamedirectory.\n",__func__,kobject_name(kobj));elseprintk(KERN_ERR"%sfailedfor%s(%d)\n",__func__,kobject_name(kobj),error);dump_stack();}elsekobj->state_in_sysfs=1;returnerror;}

在调用时,参数parent为NULL,且dev->kobj.kset在6.1节device_initialize函数中设置为devices_kset。

而devices_kset对应着/sys/devices目录,因此该函数调用完成后将在/sys/devices目录下生成目录platform。

但是这里比较奇怪的是,为什么platform目录没有对应的kset对象???

6.2.3 device_create_sys_dev_entry函数在调用该函数之前,会在/sys/devices/platform/下生成属性文件。接着如果该device的设备号不为0,则创建属性文件dev,并调用本函数。

但是,在本例中设备号devt从未设置过,显然为0,那么本函数实际并未执行。

下列代码位于drivers/base/core.c。

[cpp]view plaincopy

    staticintdevice_create_sys_dev_entry(structdevice*dev){structkobject*kobj=device_to_dev_kobj(dev);interror=0;chardevt_str[15];if(kobj){format_dev_t(devt_str,dev->devt);error=sysfs_create_link(kobj,&dev->kobj,devt_str);}returnerror;}/***device_to_dev_kobj-selecta/sys/dev/directoryforthedevice*@dev:device**Bydefaultweselectchar/fornewentries.Settingclass->dev_obj*toNULLpreventsanentryfrombeingcreated.class->dev_kobjmust*beset(orcleared)beforeanydevicesareregisteredtotheclass*otherwisedevice_create_sys_dev_entry()and*device_remove_sys_dev_entry()willdisagreeaboutthethepresence*ofthelink.*/staticstructkobject*device_to_dev_kobj(structdevice*dev){structkobject*kobj;if(dev->class)kobj=dev->class->dev_kobj;elsekobj=sysfs_dev_char_kobj;returnkobj;}

6.2.4 device_add_class_symlinks函数

由于dev->class为NULL,本函数其实没做任何工作。

下列代码位于drivers/base/core.c。

[cpp]view plaincopy

    staticintdevice_add_class_symlinks(structdevice*dev){interror;if(!dev->class)return0;error=sysfs_create_link(&dev->kobj,&dev->class->p->class_subsys.kobj,"subsystem");if(error)gotoout;#ifdefCONFIG_SYSFS_DEPRECATED/*stackedclassdevicesneedasymlinkintheclassdirectory*/if(dev->kobj.parent!=&dev->class->p->class_subsys.kobj&&device_is_not_partition(dev)){error=sysfs_create_link(&dev->class->p->class_subsys.kobj,&dev->kobj,dev_name(dev));if(error)gotoout_subsys;}if(dev->parent&&device_is_not_partition(dev)){structdevice*parent=dev->parent;char*class_name;/**stackedclassdeviceshavethe’device’link*pointingtothebusdeviceinsteadoftheparent*/while(parent->class&&!parent->bus&&parent->parent)parent=parent->parent;error=sysfs_create_link(&dev->kobj,&parent->kobj,"device");if(error)gotoout_busid;class_name=make_class_name(dev->class->name,&dev->kobj);if(class_name)error=sysfs_create_link(&dev->parent->kobj,&dev->kobj,class_name);kfree(class_name);if(error)gotoout_device;}return0;out_device:if(dev->parent&&device_is_not_partition(dev))sysfs_remove_link(&dev->kobj,"device");out_busid:if(dev->kobj.parent!=&dev->class->p->class_subsys.kobj&&device_is_not_partition(dev))sysfs_remove_link(&dev->class->p->class_subsys.kobj,dev_name(dev));#else/*linkintheclassdirectorypointingtothedevice*/error=sysfs_create_link(&dev->class->p->class_subsys.kobj,&dev->kobj,dev_name(dev));if(error)gotoout_subsys;if(dev->parent&&device_is_not_partition(dev)){error=sysfs_create_link(&dev->kobj,&dev->parent->kobj,"device");if(error)gotoout_busid;}return0;out_busid:sysfs_remove_link(&dev->class->p->class_subsys.kobj,dev_name(dev));#endifout_subsys:sysfs_remove_link(&dev->kobj,"subsystem");out:returnerror;}

6.2.5 device_add_attrs函数同样dev->class为空,什么都没干。

下列代码位于drivers/base/core.c。

[cpp]view plaincopy

    staticintdevice_add_attrs(structdevice*dev){structclass*class=dev->class;structdevice_type*type=dev->type;interror;if(class){error=device_add_attributes(dev,class->dev_attrs);if(error)returnerror;}if(type){error=device_add_groups(dev,type->groups);if(error)gotoerr_remove_class_attrs;}error=device_add_groups(dev,dev->groups);if(error)gotoerr_remove_type_groups;return0;err_remove_type_groups:if(type)device_remove_groups(dev,type->groups);err_remove_class_attrs:if(class)device_remove_attributes(dev,class->dev_attrs);returnerror;}

6.2.6 bus_add_device函数

由于dev->bus未指定,因此这个函数什么都没干。

该函数将创建三个symlink,在sysfs中建立总线和设备间的关系。

下列代码位于drivers/base/bus.c。

[cpp]view plaincopy

    /***bus_add_device-adddevicetobus*@dev:devicebeingadded**-Addthedevicetoitsbus’slistofdevices.*-Createlinktodevice’sbus.*/intbus_add_device(structdevice*dev){structbus_type*bus=bus_get(dev->bus);interror=0;if(bus){pr_debug("bus:’%s’:adddevice%s\n",bus->name,dev_name(dev));error=device_add_attrs(bus,dev);if(error)gotoout_put;/*在sys/bus/XXX/devices下建立symlink,名字为设备名,该链接指向/sys/devices/下的某个目录*/error=sysfs_create_link(&bus->p->devices_kset->kobj,&dev->kobj,dev_name(dev));if(error)gotoout_id;/*在sys/devices/的某个目录下建立symlink,名字为subsystem,该链接指向/sys/bus/下的某个目录*/error=sysfs_create_link(&dev->kobj,&dev->bus->p->subsys.kobj,"subsystem");if(error)gotoout_subsys;/*在sys/devices/的某个目录下建立symlink,名字为bus,该链接指向/sys/bus/下的某个目录*/error=make_deprecated_bus_links(dev);if(error)gotoout_deprecated;}return0;out_deprecated:sysfs_remove_link(&dev->kobj,"subsystem");out_subsys:sysfs_remove_link(&bus->p->devices_kset->kobj,dev_name(dev));out_id:device_remove_attrs(bus,dev);out_put:bus_put(dev->bus);returnerror;}

6.2.7 dpm_sysfs_add函数

下列代码位于drivers/base/power/sysfs.c。

[cpp]view plaincopy

    intdpm_sysfs_add(structdevice*dev){returnsysfs_create_group(&dev->kobj,&pm_attr_group);}staticDEVICE_ATTR(wakeup,0644,wake_show,wake_store);staticstructattribute*power_attrs[]={&dev_attr_wakeup.attr,NULL,};staticstructattribute_grouppm_attr_group={.name="power",.attrs=power_attrs,};

该函数将在XXX目录下建立power子目录,并在该子目录下建立属性文件wakeup。

在本例中,将在/sys/bus/platform下建立子目录power并在子目录下建立wakeup文件。

6.2.8 device_pm_add函数

下列代码位于drivers/base/power/main.c。

[cpp]view plaincopy

    /***device_pm_add-addadevicetothelistofactivedevices*@dev:Devicetobeaddedtothelist*/voiddevice_pm_add(structdevice*dev){pr_debug("PM:Addinginfofor%s:%s\n",dev->bus?dev->bus->name:"NoBus",kobject_name(&dev->kobj));mutex_lock(&dpm_list_mtx);if(dev->parent){if(dev->parent->power.status>=DPM_SUSPENDING)dev_warn(dev,"parent%sshouldnotbesleeping\n",dev_name(dev->parent));}elseif(transition_started){/**WerefusetoregisterparentlessdeviceswhileaPM*transitionisinprogressinordertoavoidleavingthem*unhandleddowntheroad*/dev_WARN(dev,"ParentlessdeviceregisteredduringaPMtransaction\n");}list_add_tail(&dev->power.entry,&dpm_list);/*将该设备添加到链表中*/mutex_unlock(&dpm_list_mtx);}

该函数只是将设备添加到电源管理链表中。6.2.9 bus_attach_device函数

在本例中,由于bus未指定,该函数实际不做任何工作。

下列代码位于drivers/base/bus.c。

[cpp]view plaincopy

    /***bus_attach_device-adddevicetobus*@dev:devicetriedtoattachtoadriver**-Adddevicetobus’slistofdevices.*-Trytoattachtodriver.*/voidbus_attach_device(structdevice*dev){structbus_type*bus=dev->bus;intret=0;if(bus){if(bus->p->drivers_autoprobe)ret=device_attach(dev);/*尝试获取驱动*/WARN_ON(ret<0);if(ret>=0)/*将设备挂在到总线中*/klist_add_tail(&dev->p->knode_bus,&bus->p->klist_devices);}}/***device_attach-trytoattachdevicetoadriver.*@dev:device.**Walkthelistofdriversthatthebushasandcall*driver_probe_device()foreachpair.Ifacompatible*pairisfound,breakoutandreturn.**Returns1ifthedevicewasboundtoadriver;*0ifnomatchingdevicewasfound;*-ENODEVifthedeviceisnotregistered.**WhencalledforaUSBinterface,@dev->parent->semmustbeheld.*/intdevice_attach(structdevice*dev){intret=0;down(&dev->sem);if(dev->driver){/*如果已指定驱动,即已绑定*/ret=device_bind_driver(dev);/*在sysfs中建立链接关系*/if(ret==0)ret=1;else{dev->driver=NULL;ret=0;}}else{/*尚未绑定,尝试绑定,遍历该总线上的所有驱动*/ret=bus_for_each_drv(dev->bus,NULL,dev,__device_attach);}up(&dev->sem);returnret;}EXPORT_SYMBOL_GPL(device_attach);

如果bus存在的话,将会调用device_attach函数进行绑定工作。该函数首先判断dev->driver,如果非0,表示该设备已经绑定了驱动,只要在sysfs中建立链接关系即可。

为0表示没有绑定,接着调用bus_for_each_drv,注意作为参数传入的__device_attach,这是个函数,后面会调用它。

我们来看下bus_for_each_drv:

[cpp]view plaincopy

    /***bus_for_each_drv-driveriterator*@bus:buswe’redealingwith.*@start:drivertostartiteratingon.*@data:datatopasstothecallback.*@fn:functiontocallforeachdriver.**Thisisnearlyidenticaltothedeviceiteratorabove.*Weiterateovereachdriverthatbelongsto@bus,andcall*@fnforeach.If@fnreturnsanythingbut0,webreakout*andreturnit.If@startisnotNULL,weuseitasthehead*ofthelist.**NOTE:wedon’treturnthedriverthatreturnsanon-zero*value,nordoweleavethereferencecountincrementedforthat*driver.Ifthecallerneedstoknowthatinfo,itmustsetit*inthecallback.Itmustalsobesuretoincrementtherefcount*soitdoesn’tdisappearbeforereturningtothecaller.*/intbus_for_each_drv(structbus_type*bus,structdevice_driver*start,void*data,int(*fn)(structdevice_driver*,void*)){structklist_iteri;structdevice_driver*drv;interror=0;if(!bus)return-EINVAL;klist_iter_init_node(&bus->p->klist_drivers,&i,start?&start->p->knode_bus:NULL);while((drv=next_driver(&i))&&!error)error=fn(drv,data);klist_iter_exit(&i);returnerror;}EXPORT_SYMBOL_GPL(bus_for_each_drv);

该函数将遍历总线的drivers目录下的所有驱动,也就是/sys/bus/XXX/drivers/下的目录,为该driver调用fn函数,也就是__device_attach。我们来看下:[cpp]view plaincopy

    staticint__device_attach(structdevice_driver*drv,void*data){structdevice*dev=data;if(!driver_match_device(drv,dev))/*进行匹配工作*/return0;returndriver_probe_device(drv,dev);}staticinlineintdriver_match_device(structdevice_driver*drv,structdevice*dev){returndrv->bus->match?drv->bus->match(dev,drv):1;}/***driver_probe_device-attempttobinddevice&drivertogether*@drv:drivertobindadeviceto*@dev:devicetotrytobindtothedriver**Thisfunctionreturns-ENODEVifthedeviceisnotregistered,*1ifthedeviceisboundsucessfullyand0otherwise.**Thisfunctionmustbecalledwith@dev->semheld.Whencalledfora*USBinterface,@dev->parent->semmustbeheldaswell.*/intdriver_probe_device(structdevice_driver*drv,structdevice*dev){intret=0;if(!device_is_registered(dev))/*该device是否已在sysfs中*/return-ENODEV;pr_debug("bus:’%s’:%s:matcheddevice%swithdriver%s\n",drv->bus->name,__func__,dev_name(dev),drv->name);ret=really_probe(dev,drv);/*device已在sysfs,调用really_probe*/returnret;}

该函数首先调用driver_match_device函数,后者将会调用总线的match方法,如果有的话,来进行匹配工作。如果没有该方法,则返回1,表示匹配成功。

我们这里是针对platform总线,该总线的方法将在7.6.2节中看到。

随后,又调用了driver_probe_device函数。该函数将首先判断该device是否已在sysfs中,如果在则调用really_probe,否则返回出错。

really_probe将会调用驱动的probe并完成绑定的工作。该函数将在7.6.2节中分析。

6.2.10 小结

在本例中,当device_register调用完成以后,将在/sys/devices/下建立目录platform,并在platfrom下建立属性文件uevent和子目录power,最后在power子目录下建立wakeup属性文件。

最后以函数调用过程的总结来结束第6.2小结。

6.3 spi主控制器的平台设备

本节对一个特定的platform设备进行讲解,那就是spi主控制器的平台设备。

在内核的启动阶段,platform设备将被注册进内核。我们来看下。

下列代码位于arch/arm/mach-s3c2440/mach-smdk2440.c

[cpp]view plaincopy

    staticstructresources3c_spi0_resource[]={[0]={.start=S3C24XX_PA_SPI,.end=S3C24XX_PA_SPI+0x1f,.flags=IORESOURCE_MEM,},[1]={.start=IRQ_SPI0,.end=IRQ_SPI0,.flags=IORESOURCE_IRQ,}};staticu64s3c_device_spi0_dmamask=0xffffffffUL;structplatform_devices3c_device_spi0={.name="s3c2410-spi",.id=0,.num_resources=ARRAY_SIZE(s3c_spi0_resource),.resource=s3c_spi0_resource,.dev={.dma_mask=&s3c_device_spi0_dmamask,.coherent_dma_mask=0xffffffffUL}};staticstructplatform_device*smdk2440_devices[]__initdata={&s3c_device_usb,&s3c_device_lcd,&s3c_device_wdt,&s3c_device_i2c0,&s3c_device_iis,&s3c_device_spi0,};staticvoid__initsmdk2440_machine_init(void){s3c24xx_fb_set_platdata(&smdk2440_fb_info);s3c_i2c0_set_platdata(NULL);platform_add_devices(smdk2440_devices,ARRAY_SIZE(smdk2440_devices));smdk_machine_init();}

在smdk2440_machine_init函数中,通过调用platform_add_devices将设备注册到内核中。接着来看下该函数。6.3.1 platform_add_devices[cpp]view plaincopy

    /***platform_add_devices-addanumbersofplatformdevices*@devs:arrayofplatformdevicestoadd*@num:numberofplatformdevicesinarray*/intplatform_add_devices(structplatform_device**devs,intnum){inti,ret=0;for(i=0;i<num;i++){ret=platform_device_register(devs[i]);if(ret){while(–i>=0)platform_device_unregister(devs[i]);break;}}returnret;}EXPORT_SYMBOL_GPL(platform_add_devices);

该函数将根据devs指针数组,调用platform_device_register将platform设备逐一注册进内核。6.3.2 platform_device_register[cpp]view plaincopy

    /***platform_device_register-addaplatform-leveldevice*@pdev:platformdevicewe’readding*/intplatform_device_register(structplatform_device*pdev){device_initialize(&pdev->dev);returnplatform_device_add(pdev);}EXPORT_SYMBOL_GPL(platform_device_register);

调用了两个函数,第一个函数在6.1节已经分析过。我们来看下第二个函数。6.3.2 platform_device_register[cpp]view plaincopy

    /***platform_device_add-addaplatformdevicetodevicehierarchy*@pdev:platformdevicewe’readding**Thisispart2ofplatform_device_register(),thoughmaybecalled*separately_iff_pdevwasallocatedbyplatform_device_alloc().*/intplatform_device_add(structplatform_device*pdev){inti,ret=0;if(!pdev)return-EINVAL;if(!pdev->dev.parent)pdev->dev.parent=&platform_bus;/*该设备的父设备是platform设备,/sys/devices/platform*/pdev->dev.bus=&platform_bus_type;/*设备挂载到platform总线上*/if(pdev->id!=-1)dev_set_name(&pdev->dev,"%s.%d",pdev->name,pdev->id);elsedev_set_name(&pdev->dev,pdev->name);/*pdev->dev->kobj->name=pdev->name*//*遍历平台设备的资源,并将资源添加到资源树中*/for(i=0;i<pdev->num_resources;i++){structresource*p,*r=&pdev->resource[i];if(r->name==NULL)r->name=dev_name(&pdev->dev);/*获取dev->kobject->name*/p=r->parent;if(!p){/*p空*/if(resource_type(r)==IORESOURCE_MEM)p=&iomem_resource;elseif(resource_type(r)==IORESOURCE_IO)p=&ioport_resource;}if(p&&insert_resource(p,r)){/*将资源添加到资源树中*/printk(KERN_ERR"%s:failedtoclaimresource%d\n",dev_name(&pdev->dev),i);ret=-EBUSY;gotofailed;}}pr_debug("Registeringplatformdevice’%s’.Parentat%s\n",dev_name(&pdev->dev),dev_name(pdev->dev.parent));ret=device_add(&pdev->dev);/*添加设备*/if(ret==0)returnret;failed:while(–i>=0){structresource*r=&pdev->resource[i];unsignedlongtype=resource_type(r);if(type==IORESOURCE_MEM||type==IORESOURCE_IO)release_resource(r);}returnret;}EXPORT_SYMBOL_GPL(platform_device_add);

在这个函数的最后赫然出现了device_add函数。我们回忆下在6.1节中device_register的注册过程,该函数只调用了两个函数,一个是device_initialize函数,另一个就是device_add。

本节的platform_device_register函数,首先也是调用了device_initialize,但是随后他做了一些其他的工作,最后调用了device_add。

那么这个"其他的工作"干了些什么呢?

首先,它将该SPI主控制对应的平台设备的父设备设为虚拟的platform设备(platform_bus),然后将该平台设备挂在至platform总线(platform_bus_type)上,这两步尤为重要,后面我们将看到。

然后,调用了dev_set_name设置了pdev->dev-kobj.name,也就是该设备对象的名字,这里的名字为s3c2410-spi.0,这个名字将被用来建立一个目录。

最后,将平台的相关资源添加到资源树中。这不是本篇文章讨论的重点所在,所以不做过多说明。

在"其他的工作""干完之后,调用了device_add函数。那么后面的函数调用过程将和6.2小结的一致。

由于“其他的工作”的原因,实际执行的过程和结果将有所区别。我们来分析下。

6.3.3 不一样device_add调用结果

首先,在device_add被调用之前,有若干个非常重要的条件已经被设置了。如下:

pdev->dev->kobj.kset = devices_kset

pdev->dev-.parent = &platform_bus

pdev->dev.bus = &platform_bus_type

set_up函数执行时,由于参数parent为&platform_bus,因此最后将设置pdev->dev->kobj.parent = platform_bus.kobj。平台设备对象的父对象为虚拟的platform设备。

kobject_add函数执行时,由于参数parent的存在,将在parent对象所对应的目录下创建另一个目录。parent对象代表目录/sys/devices/下的platform,因此将在/sys/devices/platform下建立目录s3c2410-spi.0。

device_create_file建立属性文件uevent。

bus_add_device函数执行时,由于dev.bus 为&platform_bus_type,因此将建立三个symlink。

/sys/devices/platform/s3c2410-spi.0下建立链接subsystem和bus,他们指向/sys/bus/platform。

/sys/bus/platform/devices/下建立链接s3c2410-spi.0,指向/sys/devices/platform/s3c2410-spi.0。

dpm_sysfs_add函数在/sys/devices/platform/s3c2410-spi.0下建立子目录power,并在该子目录下建立属性文件wakeup。

执行到这里时,sysfs已将内核中新添加的SPI主控制器平台设备呈现出来了,我们来验证下。

[root@yj423 s3c2410-spi.0]#pwd/sys/devices/platform/s3c2410-spi.0[root@yj423 s3c2410-spi.0]#lllrwxrwxrwx 1 root root 0 Jan 1 00:29 bus -> ../../../bus/platformlrwxrwxrwx 1 root root 0 Jan 1 00:29 driver -> ../../../bus/platform/drivers/s3c2410-spi-r–r–r– 1 root root 4096 Jan 1 00:29 modaliasdrwxr-xr-x 2 root root 0 Jan 1 00:29 powerdrwxr-xr-x 3 root root 0 Jan 1 00:00 spi0.0drwxr-xr-x 3 root root 0 Jan 1 00:00 spi0.1lrwxrwxrwx 1 root root 0 Jan 1 00:29 spi_master:spi0 -> ../../../class/spi_master/spi0lrwxrwxrwx 1 root root 0 Jan 1 00:29 subsystem -> ../../../bus/platform-rw-r–r– 1 root root 4096 Jan 1 00:29 uevent

[root@yj423 devices]#pwd/sys/bus/platform/devices[root@yj423 devices]#ll s3c2410-spi.0lrwxrwxrwx 1 root root 0 Jan 1 00:44 s3c2410-spi.0 -> ../../../devices/platform/s3c2410-spi.0

通过sysfs将设备驱动的模型层次呈现在用户空间以后,将更新内核的设备模型之间的关系,这是通过修改链表的指向来完成的。

bus_attach_device函数执行时,将设备添加到总线的设备链表中,同时也会尝试绑定驱动,不过会失败。

接着,由于dev->parent的存在,将SPI主控制器设备添加到父设备platform虚拟设备的儿子链表中。

7. driver举例

我们已经介绍过platform总线的注册,也讲述了SPI主控制器设备作为平台设备的注册过程,在本节,将描述SPI主控制器的platform驱动是如何注册的。

7.1 s3c24xx_spi_init

下列代码位于drivers/spi/spi_s3c24xx.c。

[cpp]view plaincopy

    MODULE_ALIAS("platform:s3c2410-spi");staticstructplatform_drivers3c24xx_spi_driver={.remove=__exit_p(s3c24xx_spi_remove),.suspend=s3c24xx_spi_suspend,.resume=s3c24xx_spi_resume,.driver={.name="s3c2410-spi",.owner=THIS_MODULE,},};staticint__inits3c24xx_spi_init(void){returnplatform_driver_probe(&s3c24xx_spi_driver,s3c24xx_spi_probe);//设备不可热插拔,所以使用该函数,而不是platform_driver_register}

驱动注册通过调用platform_driver_probe来完成。

注意:driver.name字段使用来匹配设备的,该字段必须和6.3节一开始给出的pdev.name字段相同。

7.2 platform_driver_probe

下列代码位于drivers/base/platform.c。

[cpp]view plaincopy

    /***platform_driver_probe-registerdriverfornon-hotpluggabledevice*@drv:platformdriverstructure*@probe:thedriverproberoutine,probablyfroman__initdiv**Usethisinsteadofplatform_driver_register()whenyouknowthedevice*isnothotpluggableandhasalreadybeenregistered,andyouwantto*removeitsrun-onceprobe()infrastructurefrommemoryafterthedriver*hasboundtothedevice.**Onetypicaluseforthiswouldbewithdriversforcontrollersintegrated*intosystem-on-chipprocessors,wherethecontrollerdeviceshavebeen*configuredaspartofboardsetup.**Returnszeroifthedriverregisteredandboundtoadevice,elsereturns*anegativeerrorcodeandwiththedrivernotregistered.*/int__init_or_moduleplatform_driver_probe(structplatform_driver*drv,int(*probe)(structplatform_device*)){intretval,code;/*temporarydivviolationduringprobe()*/drv->probe=probe;retval=code=platform_driver_register(drv);/*注册platform驱动*//*Fixupthatdivviolation,beingparanoidaboutcodescanning*thelistofdriversinordertoprobenewdevices.Checktosee*iftheprobewassuccessful,andmakesureanyforcedprobesof*newdevicesfail.*/spin_lock(&platform_bus_type.p->klist_drivers.k_lock);drv->probe=NULL;if(code==0&&list_empty(&drv->driver.p->klist_devices.k_list))retval=-ENODEV;drv->driver.probe=platform_drv_probe_fail;spin_unlock(&platform_bus_type.p->klist_drivers.k_lock);if(code!=retval)platform_driver_unregister(drv);returnretval;}EXPORT_SYMBOL_GPL(platform_driver_probe);

这里的重点是platform_driver_register,由它来完成了platform驱动的注册。7.3 platform_driver_register[cpp]view plaincopy

    /***platform_driver_register*@drv:platformdriverstructure*/intplatform_driver_register(structplatform_driver*drv){drv->driver.bus=&platform_bus_type;if(drv->probe)drv->driver.probe=platform_drv_probe;if(drv->remove)drv->driver.remove=platform_drv_remove;if(drv->shutdown)drv->driver.shutdown=platform_drv_shutdown;if(drv->suspend)drv->driver.suspend=platform_drv_suspend;if(drv->resume)drv->driver.resume=platform_drv_resume;returndriver_register(&drv->driver);/*驱动注册*/}EXPORT_SYMBOL_GPL(platform_driver_register);

driver_register函数就是driver注册的核心函数。需要注意的是,在调用函数之前,将该驱动所挂载的总线设置为platform总线(platform_bus_type)。7.4 driver_register

下列代码位于drivers/base/driver.c。

[cpp]view plaincopy

    /***driver_register-registerdriverwithbus*@drv:drivertoregister**Wepassoffmostoftheworktothebus_add_driver()call,*sincemostofthethingswehavetododealwiththebus*structures.*/intdriver_register(structdevice_driver*drv){intret;structdevice_driver*other;BUG_ON(!drv->bus->p);if((drv->bus->probe&&drv->probe)||(drv->bus->remove&&drv->remove)||(drv->bus->shutdown&&drv->shutdown))printk(KERN_WARNING"Driver’%s’needsupdating-pleaseuse""bus_typemethods\n",drv->name);other=driver_find(drv->name,drv->bus);/*用驱动名字来搜索在该总线上驱动是否已经存在*/if(other){/*存在则报错*/put_driver(other);printk(KERN_ERR"Error:Driver’%s’isalreadyregistered,""aborting…\n",drv->name);return-EEXIST;}ret=bus_add_driver(drv);/*将驱动添加到一个总线中*/if(ret)returnret;ret=driver_add_groups(drv,drv->groups);/*建立属性组文件*/if(ret)bus_remove_driver(drv);returnret;}EXPORT_SYMBOL_GPL(driver_register);

这里主要调用两个函数driver_find和bus_add_driver。前者将通过总线来搜索该驱动是否存在,后者将添加驱动到总线中。

接下来就分析这两个函数。

7.5 driver_find

下列代码位于drivers/base/driver.c。

[cpp]view plaincopy

    /***driver_find-locatedriveronabusbyitsname.*@name:nameofthedriver.*@bus:bustoscanforthedriver.**Callkset_find_obj()toiterateoverlistofdriverson*abustofinddriverbyname.Returndriveriffound.**Notethatkset_find_objincrementsdriver’sreferencecount.*/structdevice_driver*driver_find(constchar*name,structbus_type*bus){structkobject*k=kset_find_obj(bus->p->drivers_kset,name);structdriver_private*priv;if(k){priv=to_driver(k);returnpriv->driver;}returnNULL;}EXPORT_SYMBOL_GPL(driver_find);

[cpp]view plaincopy

    /***kset_find_obj-searchforobjectinkset.*@kset:ksetwe’relookingin.*@name:object’sname.**Lockksetvia@kset->subsys,anditerateover@kset->list,*lookingforamatchingkobject.Ifmatchingobjectisfound*takeareferenceandreturntheobject.*/structkobject*kset_find_obj(structkset*kset,constchar*name){structkobject*k;structkobject*ret=NULL;spin_lock(&kset->list_lock);list_for_each_entry(k,&kset->list,entry){if(kobject_name(k)&&!strcmp(kobject_name(k),name)){ret=kobject_get(k);break;}}spin_unlock(&kset->list_lock);returnret;}

这里调用了kset_find_obj函数,传入的实参bus->p->drivers_kset,它对应的就是/sys/bus/platform/下的drivers目录,然后通过链表,它将搜索该目录下的所有文件,来寻找是否有名为s3c2410-spi的文件。还记得吗? kobject就是一个文件对象。如果没有找到将返回NULL,接着将调用bus_add_driver把驱动注册进内核。7.6 bus_add_driver

下列代码位于drivers/base/bus.c

[cpp]view plaincopy

    /***bus_add_driver-Addadrivertothebus.*@drv:driver.*/intbus_add_driver(structdevice_driver*drv){structbus_type*bus;structdriver_private*priv;interror=0;bus=bus_get(drv->bus);/*增加引用计数获取bus_type*/if(!bus)return-EINVAL;pr_debug("bus:’%s’:adddriver%s\n",bus->name,drv->name);priv=kzalloc(sizeof(*priv),GFP_KERNEL);/*分配driver_private结构体*/if(!priv){error=-ENOMEM;gotoout_put_bus;}/*初始化内核链表*/klist_init(&priv->klist_devices,NULL,NULL);/*相互保存*/priv->driver=drv;drv->p=priv;/*设置该kobj属于那个kset*/priv->kobj.kset=bus->p->drivers_kset;error=kobject_init_and_add(&priv->kobj,&driver_ktype,NULL,/*parent=NULL*/"%s",drv->name);/*执行完以后,会在bus/总线名/drivers/下建立名为drv->name的目录*/if(error)gotoout_unregister;if(drv->bus->p->drivers_autoprobe){error=driver_attach(drv);/*尝试绑定驱动和设备*/if(error)gotoout_unregister;}/*添加该驱动到bus的内核链表中*/klist_add_tail(&priv->knode_bus,&bus->p->klist_drivers);module_add_driver(drv->owner,drv);/*?????????*//*创建属性,在bus/总线名/drivers/驱动名/下建立文件uevent*/error=driver_create_file(drv,&driver_attr_uevent);if(error){printk(KERN_ERR"%s:ueventattr(%s)failed\n",__func__,drv->name);}/*利用bus->drv_attrs创建属性,位于bus/总线名/drivers/驱动名/*/error=driver_add_attrs(bus,drv);if(error){/*Howthehelldowegetoutofthispickle?Giveup*/printk(KERN_ERR"%s:driver_add_attrs(%s)failed\n",__func__,drv->name);}/*创建属性,在bus/总线名/drivers/驱动名/下建立文件bind和unbind*/error=add_bind_files(drv);if(error){/*Ditto*/printk(KERN_ERR"%s:add_bind_files(%s)failed\n",__func__,drv->name);}/*通知用户空间???*/kobject_uevent(&priv->kobj,KOBJ_ADD);return0;out_unregister:kfree(drv->p);drv->p=NULL;kobject_put(&priv->kobj);out_put_bus:bus_put(bus);returnerror;}

在设置driver的kobj.kset为drivers目录所对应的kset之后,调用了kobject_init_and_add,我们来看下。7.6.1 kobject_init_and_add

下列代码位于lib/kobject.c。

[cpp]view plaincopy

    /***kobject_init_and_add-initializeakobjectstructureandaddittothekobjecthierarchy*@kobj:pointertothekobjecttoinitialize*@ktype:pointertothektypeforthiskobject.*@parent:pointertotheparentofthiskobject.*@fmt:thenameofthekobject.**Thisfunctioncombinesthecalltokobject_init()and*kobject_add().Thesametypeoferrorhandlingafteracallto*kobject_add()andkobjectlifetimerulesarethesamehere.*/intkobject_init_and_add(structkobject*kobj,structkobj_type*ktype,structkobject*parent,constchar*fmt,…){va_listargs;intretval;kobject_init(kobj,ktype);va_start(args,fmt);retval=kobject_add_varg(kobj,parent,fmt,args);va_end(args);returnretval;}EXPORT_SYMBOL_GPL(kobject_init_and_add);

该函数中调用了两个函数,这两个函数分别在6.1.2和6.2.2中讲述过,这里不再赘述。

调用该函数时由于parent为NULL,但kobj.kset为drivers目录,所以将在/sys/bus/platform/drivers/下建立目录,名为s3c2410-spi。

我们来验证下:

[root@yj423 s3c2410-spi]#pwd/sys/bus/platform/drivers/s3c2410-spi

接着由于drivers_autoprobe在bus_register执行的时候已经置1,将调用driver_attach。

7.6.2 driver_attach

下列代码位于drivers/base/dd.c。

[cpp]view plaincopy

    /***driver_attach-trytobinddrivertodevices.*@drv:driver.**Walkthelistofdevicesthatthebushasonitandtryto*matchthedriverwitheachone.Ifdriver_probe_device()*returns0andthe@dev->driverisset,we’vefounda*compatiblepair.*/intdriver_attach(structdevice_driver*drv){returnbus_for_each_dev(drv->bus,NULL,drv,__driver_attach);}EXPORT_SYMBOL_GPL(driver_attach);

该函数将调用bus_for_each_dev来寻找总线上的每个设备,这里的总线即为platform总线,然后尝试绑定设备。

这里需要注意的是最后一个参数__driver_attach,这是一个函数名,后面将会调用它。

[cpp]view plaincopy

    /***bus_for_each_dev-deviceiterator.*@bus:bustype.*@start:devicetostartiteratingfrom.*@data:dataforthecallback.*@fn:functiontobecalledforeachdevice.**Iterateover@bus’slistofdevices,andcall@fnforeach,*passingit@data.If@startisnotNULL,weusethatdeviceto*beginiteratingfrom.**Wecheckthereturnof@fneachtime.Ifitreturnsanything*otherthan0,webreakoutandreturnthatvalue.**NOTE:Thedevicethatreturnsanon-zerovalueisnotretained*inanyway,norisitsrefcountincremented.Ifthecallerneeds*toretainthisdata,itshoulddo,andincrementthereference*countinthesuppliedcallback.*/intbus_for_each_dev(structbus_type*bus,structdevice*start,void*data,int(*fn)(structdevice*,void*)){structklist_iteri;structdevice*dev;interror=0;if(!bus)return-EINVAL;klist_iter_init_node(&bus->p->klist_devices,&i,(start?&start->p->knode_bus:NULL));while((dev=next_device(&i))&&!error)error=fn(dev,data);klist_iter_exit(&i);returnerror;}EXPORT_SYMBOL_GPL(bus_for_each_dev);

通过klist将遍历该总线上的所有设备,并为其调用__driver_attach函数。[cpp]view plaincopy

    staticint__driver_attach(structdevice*dev,void*data){structdevice_driver*drv=data;/**Lockdeviceandtrytobindtoit.Wedroptheerror*hereandalwaysreturn0,becauseweneedtokeeptrying*tobindtodevicesandsomedriverswillreturnanerror*simplyifitdidn’tsupportthedevice.**driver_probe_device()willspitawarningifthere*isanerror.*/if(!driver_match_device(drv,dev))return0;if(dev->parent)/*NeededforUSB*/down(&dev->parent->sem);down(&dev->sem);if(!dev->driver)driver_probe_device(drv,dev);up(&dev->sem);if(dev->parent)up(&dev->parent->sem);return0;}

首先调用了driver_match_device函数,该函数进会进行匹配,如果匹配成功将返回1。我们看下这个函数:[cpp]view plaincopy

    staticinlineintdriver_match_device(structdevice_driver*drv,structdevice*dev){returndrv->bus->match?drv->bus->match(dev,drv):1;}

这里直接调用了platform总线的match方法,我们来看下这个方法。[cpp]view plaincopy

    /***platform_match-bindplatformdevicetoplatformdriver.*@dev:device.*@drv:driver.**PlatformdeviceIDsareassumedtobeencodedlikethis:*"<name><instance>",where<name>isashortdescriptionofthetypeof*device,like"pci"or"floppy",and<instance>istheenumerated*instanceofthedevice,like’0’or’42’.DriverIDsaresimply*"<name>".So,extractthe<name>fromtheplatform_devicestructure,*andcompareitagainstthenameofthedriver.Returnwhethertheymatch*ornot.*/staticintplatform_match(structdevice*dev,structdevice_driver*drv){structplatform_device*pdev=to_platform_device(dev);structplatform_driver*pdrv=to_platform_driver(drv);/*matchagainsttheidtablefirst*/if(pdrv->id_table)returnplatform_match_id(pdrv->id_table,pdev)!=NULL;/*fall-backtodrivernamematch*/return(strcmp(pdev->name,drv->name)==0);}

该方法的核心其实就是使用stcmp进行字符匹配,判断pdev->name和drv->name是否相等。

在本例中两者同为s3c2410-spi。因此匹配完成,返回1。

返回后,由于dev->driver为NULL,将调用driver_probe_device函数。我们来看下:

[cpp]view plaincopy

    /***driver_probe_device-attempttobinddevice&drivertogether*@drv:drivertobindadeviceto*@dev:devicetotrytobindtothedriver**Thisfunctionreturns-ENODEVifthedeviceisnotregistered,*1ifthedeviceisboundsucessfullyand0otherwise.**Thisfunctionmustbecalledwith@dev->semheld.Whencalledfora*USBinterface,@dev->parent->semmustbeheldaswell.*/intdriver_probe_device(structdevice_driver*drv,structdevice*dev){intret=0;if(!device_is_registered(dev))return-ENODEV;pr_debug("bus:’%s’:%s:matcheddevice%swithdriver%s\n",drv->bus->name,__func__,dev_name(dev),drv->name);ret=really_probe(dev,drv);returnret;}staticinlineintdevice_is_registered(structdevice*dev){returndev->kobj.state_in_sysfs;}

该函数将调用really_probe来绑定设备和它的驱动。[cpp]view plaincopy

    staticintreally_probe(structdevice*dev,structdevice_driver*drv){intret=0;atomic_inc(&probe_count);pr_debug("bus:’%s’:%s:probingdriver%swithdevice%s\n",drv->bus->name,__func__,drv->name,dev_name(dev));WARN_ON(!list_empty(&dev->devres_head));dev->driver=drv;if(driver_sysfs_add(dev)){/*创建两个symlink,更新sysfs*/printk(KERN_ERR"%s:driver_sysfs_add(%s)failed\n",__func__,dev_name(dev));gotoprobe_failed;}if(dev->bus->probe){ret=dev->bus->probe(dev);/*调用总线的probe方法*/if(ret)gotoprobe_failed;}elseif(drv->probe){ret=drv->probe(dev);/*调用驱动的probe方法*/if(ret)gotoprobe_failed;}driver_bound(dev);/*绑定设备和驱动*/ret=1;pr_debug("bus:’%s’:%s:bounddevice%stodriver%s\n",drv->bus->name,__func__,dev_name(dev),drv->name);gotodone;probe_failed:devres_release_all(dev);driver_sysfs_remove(dev);dev->driver=NULL;if(ret!=-ENODEV&&ret!=-ENXIO){/*drivermatchedbuttheprobefailed*/printk(KERN_WARNING"%s:probeof%sfailedwitherror%d\n",drv->name,dev_name(dev),ret);}/**Ignoreerrorsreturnedby->probesothatthenextdrivercantry*itsluck.*/ret=0;done:atomic_dec(&probe_count);wake_up(&probe_waitqueue);returnret;}

在这个函数中调用4个函数。

第一个函数driver_sysfs_add将更新sysfs。

[cpp]view plaincopy

    staticintdriver_sysfs_add(structdevice*dev){intret;/*在/sys/bus/XXX/drivers/XXX目录下建立symlink,链接名为kobj->name,链接指向/sys/devices/platform/XXX*/ret=sysfs_create_link(&dev->driver->p->kobj,&dev->kobj,kobject_name(&dev->kobj));if(ret==0){/*在/sys/devices/platform/XXX/下建立symlink,链接名为driver,指向/sys/bus/xxx/drivers目录下的某个目录*/ret=sysfs_create_link(&dev->kobj,&dev->driver->p->kobj,"driver");if(ret)sysfs_remove_link(&dev->driver->p->kobj,kobject_name(&dev->kobj));}returnret;}

执行完以后,建立了两个链接。

在/sys/bus/platform/drivers/s3c2410-spi下建立链接,指向/sys/devices/platform/s3c2410-spi.0

在/sys/devices/platform/s3c2410-spi.0下建立链接,指向/sys/devices/platform/s3c2410-spi.0。

这样就在用户空间呈现出驱动和设备的关系了。我们来验证下。

[root@yj423 s3c2410-spi]#pwd/sys/bus/platform/drivers/s3c2410-spi[root@yj423 s3c2410-spi]#ll s3c2410-spi.0lrwxrwxrwx 1 root root 0 Jan 1 02:28 s3c2410-spi.0 -> ../../../../devices/platform/s3c2410-spi.0

[root@yj423 s3c2410-spi.0]#pwd/sys/devices/platform/s3c2410-spi.0[root@yj423 s3c2410-spi.0]#ll driverlrwxrwxrwx 1 root root 0 Jan 1 02:26 driver -> ../../../bus/platform/drivers/s3c2410-spi

第2个函数执行总线的probe方法,由于platform总线没有提供probe方法,因此不执行。

第3个函数执行驱动的probe方法,驱动提供了probe,因此调用它,该函数的细节超过了本文的讨论内容,所以略过。

第4个函数执行driver_bound,用来绑定设备和驱动,来看下这个函数。

[cpp]view plaincopy

    staticvoiddriver_bound(structdevice*dev){if(klist_node_attached(&dev->p->knode_driver)){printk(KERN_WARNING"%s:device%salreadybound\n",__func__,kobject_name(&dev->kobj));return;}pr_debug("driver:’%s’:%s:boundtodevice’%s’\n",dev_name(dev),__func__,dev->driver->name);if(dev->bus)blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_BOUND_DRIVER,dev);klist_add_tail(&dev->p->knode_driver,&dev->driver->p->klist_devices);}

其实,所谓的绑定,就是将设备的驱动节点添加到驱动支持的设备链表中。

至此,通过内核链表,这个platform device 和platform driver 已经绑定完成,将继续遍历内核链表尝试匹配和绑定,直到链表结束。

在driver_attach执行完毕以后,bus_add_driver函数还有些剩余工作要完成。

首先,将驱动添加到总线的驱动列表中。

接着,如果定义了驱动属性文件,则创建。

最后,在/sys/bus/platform/drivers/s3c2410-spi/下建立属性文件uevent,并在同一目录下建立文件bind和unbind。

我们来验证下:

[root@yj423 s3c2410-spi]#pwd/sys/bus/platform/drivers/s3c2410-spi[root@yj423 s3c2410-spi]#lsbind s3c2410-spi.0 uevent unbind

7.7 小结

在本节中,我们看到了platform driver是如何注册到内核中,在注册过程中,通过更新了sysfs,向用户空间展示总线,设备和驱动之间的关系。

同时,还更新了链表的指向,在内核中体现了同样的关系。

最后以platform driver的注册过程结束本章。

8. sysfs底层函数

下面讲述的内容将基于VFS,有关VFS的基本内容超过本文的范围,请参考<<深入理解Linux内核>>一书的第12章。

在前面讲述的过程中,我们知道设备驱动模型是如何通过kobject将总线,设备和驱动间的层次关系在用户空间呈现出来的。事实上,就是通过目录,文件和symlink来呈现相互之间的关系。在前面的叙述中,我们并没有对目录,文件和symlink的创建进行 讲解,本章就对这些底层函数进行讲解。在讲解这些函数之前,我们先来看下,sysfs文件系统是如何注册的。

8.1 注册sysfs文件系统

sysfs文件系统的注册是调用sysfs_init函数来完成的,该函数在内核启动阶段被调用,我们来看下大致函数调用流程,这里不作分析。

start_kernel( ) -> vfs_caches_init( ) -> mnt_init( ) -> mnt_init( ) -> sysfs_init( )。


[cpp]view plaincopy

    int__initsysfs_init(void){interr=-ENOMEM;/*建立cache,名字为sysfs_dir_cache*/sysfs_dir_cachep=kmem_cache_create("sysfs_dir_cache",sizeof(structsysfs_dirent),0,0,NULL);if(!sysfs_dir_cachep)gotoout;err=sysfs_inode_init();if(err)gotoout_err;/*注册文件系统*/err=register_filesystem(&sysfs_fs_type);if(!err){/*注册成功,加载文件系统*/sysfs_mount=kern_mount(&sysfs_fs_type);if(IS_ERR(sysfs_mount)){printk(KERN_ERR"sysfs:couldnotmount!\n");err=PTR_ERR(sysfs_mount);sysfs_mount=NULL;unregister_filesystem(&sysfs_fs_type);gotoout_err;}}elsegotoout_err;out:returnerr;out_err:kmem_cache_destroy(sysfs_dir_cachep);sysfs_dir_cachep=NULL;gotoout;}staticstructfile_system_typesysfs_fs_type={.name="sysfs",.get_sb=sysfs_get_sb,.kill_sb=kill_anon_super,};

8.1.1 register_filesystem下列代码位于fs/filesystems.c。[cpp]view plaincopy

    /***register_filesystem-registeranewfilesystem*@fs:thefilesystemstructure**Addsthefilesystempassedtothelistoffilesystemsthekernel*isawareofformountandothersyscalls.Returns0onsuccess,*oranegativeerrnocodeonanerror.**The&structfile_system_typethatispassedislinkedintothekernel*structuresandmustnotbefreeduntilthefilesystemhasbeen*unregistered.*/intregister_filesystem(structfile_system_type*fs){intres=0;structfile_system_type**p;BUG_ON(strchr(fs->name,’.’));if(fs->next)return-EBUSY;INIT_LIST_HEAD(&fs->fs_supers);write_lock(&file_systems_lock);p=find_filesystem(fs->name,strlen(fs->name));/*查找要住的文件是同是否存在,返回位置*/if(*p)res=-EBUSY;/*该文件系统已存在,返回error*/else*p=fs;/*将新的文件系统加入到链表中*/write_unlock(&file_systems_lock);returnres;}

[cpp]view plaincopy

    staticstructfile_system_type**find_filesystem(constchar*name,unsignedlen){structfile_system_type**p;for(p=&file_systems;*p;p=&(*p)->next)if(strlen((*p)->name)==len&&strncmp((*p)->name,name,len)==0)break;returnp;}


该函数将调用函数file_system_type,此函数根据name字段(sysfs)来查找要注册的文件系统是否已经存在。

如果不存在,表示还未注册,则将新的fs添加到链表中,链表的第一项为全局变量file_systems。

该全局变量为单项链表,所有已注册的文件系统都被插入到这个链表当中。

8.1.2 kern_mount函数

下列代码位于include/linux/fs.h

[cpp]view plaincopy

    #definekern_mount(type)kern_mount_data(type,NULL)

下列代码位于fs/sysfs/mount.c[cpp]view plaincopy

    structvfsmount*kern_mount_data(structfile_system_type*type,void*data){returnvfs_kern_mount(type,MS_KERNMOUNT,type->name,data);}EXPORT_SYMBOL_GPL(kern_mount_data);

kern_mount实际上最后是调用了vfs_kern_mount函数。我们来看下:

[cpp]view plaincopy

    structvfsmount*vfs_kern_mount(structfile_system_type*type,intflags,constchar*name,void*data){structvfsmount*mnt;char*secdata=NULL;interror;if(!type)returnERR_PTR(-ENODEV);error=-ENOMEM;mnt=alloc_vfsmnt(name);/*分配structvfsmount*/if(!mnt)gotoout;if(data&&!(type->fs_flags&FS_BINARY_MOUNTDATA)){secdata=alloc_secdata();if(!secdata)gotoout_mnt;error=security_sb_copy_data(data,secdata);if(error)gotoout_free_secdata;}/*get_sb方法,分配superblock对象,并初始化*/error=type->get_sb(type,flags,name,data,mnt);if(error<0)gotoout_free_secdata;BUG_ON(!mnt->mnt_sb);error=security_sb_kern_mount(mnt->mnt_sb,flags,secdata);if(error)gotoout_sb;mnt->mnt_mountpoint=mnt->mnt_root;/*设置挂载点的dentry*/mnt->mnt_parent=mnt;/*设置所挂载的fs为自己本身*/up_write(&mnt->mnt_sb->s_umount);free_secdata(secdata);returnmnt;out_sb:dput(mnt->mnt_root);deactivate_locked_super(mnt->mnt_sb);out_free_secdata:free_secdata(secdata);out_mnt:free_vfsmnt(mnt);out:returnERR_PTR(error);}

该函数在首先调用alloc_vfsmnt来分配struct vfsmount结构,并做了一些初试化工作。

下列函数位于fs/super.c

[cpp]view plaincopy

    structvfsmount*alloc_vfsmnt(constchar*name){structvfsmount*mnt=kmem_cache_zalloc(mnt_cache,GFP_KERNEL);if(mnt){interr;err=mnt_alloc_id(mnt);/*设置mnt->mnt_id*/if(err)gotoout_free_cache;if(name){mnt->mnt_devname=kstrdup(name,GFP_KERNEL);/*拷贝name,并赋值*/if(!mnt->mnt_devname)gotoout_free_id;}atomic_set(&mnt->mnt_count,1);INIT_LIST_HEAD(&mnt->mnt_hash);INIT_LIST_HEAD(&mnt->mnt_child);INIT_LIST_HEAD(&mnt->mnt_mounts);INIT_LIST_HEAD(&mnt->mnt_list);INIT_LIST_HEAD(&mnt->mnt_expire);INIT_LIST_HEAD(&mnt->mnt_share);INIT_LIST_HEAD(&mnt->mnt_slave_list);INIT_LIST_HEAD(&mnt->mnt_slave);atomic_set(&mnt->__mnt_writers,0);}returnmnt;out_free_id:mnt_free_id(mnt);out_free_cache:kmem_cache_free(mnt_cache,mnt);returnNULL;}

分配好结构体以后,由于参数data为NULL,将直接调用文件系统类型提供的get_sb方法,该方法就是函数sysfs_get_sb。我们来看下:

下列函数位于fs/sysfs/mount.c。

[cpp]view plaincopy

    staticintsysfs_get_sb(structfile_system_type*fs_type,intflags,constchar*dev_name,void*data,structvfsmount*mnt){returnget_sb_single(fs_type,flags,data,sysfs_fill_super,mnt);}

这里直接调用了get_sb_single函数,注意这里的第4个实参sysfs_fill_super,该参数是函数名,后面将会调用该函数。

该函数将分配sysfs文件系统的superblock,获取文件系统根目录的inode和dentry。

该函数的执行过程相当复杂,在下一节单独讲述。

8.2 get_sb_single函数

下列函数位于fs/sysfs/mount.c。

[cpp]view plaincopy

    intget_sb_single(structfile_system_type*fs_type,intflags,void*data,int(*fill_super)(structsuper_block*,void*,int),structvfsmount*mnt){structsuper_block*s;interror;/*查找或者创建super_block*/s=sget(fs_type,compare_single,set_anon_super,NULL);if(IS_ERR(s))returnPTR_ERR(s);if(!s->s_root){/*没有根目录dentry*/s->s_flags=flags;/*获取root(/)的inode和dentry*/error=fill_super(s,data,flags&MS_SILENT?1:0);if(error){deactivate_locked_super(s);returnerror;}s->s_flags|=MS_ACTIVE;}do_remount_sb(s,flags,data,0);simple_set_mnt(mnt,s);/*设置vfsmount的superblock和根dentry*/return0;}EXPORT_SYMBOL(get_sb_single);

8.2.1 sget函数

首先调用了sget函数来查找是否

下列函数位于fs/super.c。

[cpp]view plaincopy

    /***sget-findorcreateasuperblock*@type:filesystemtypesuperblockshouldbelongto*@test:comparisoncallback*@set:setupcallback*@data:argumenttoeachofthem*/structsuper_block*sget(structfile_system_type*type,int(*test)(structsuper_block*,void*),int(*set)(structsuper_block*,void*),void*data){structsuper_block*s=NULL;structsuper_block*old;interr;retry:spin_lock(&sb_lock);if(test){/*遍历所有属于该文件系统的super_block*/list_for_each_entry(old,&type->fs_supers,s_instances){if(!test(old,data))continue;if(!grab_super(old))gotoretry;if(s){up_write(&s->s_umount);destroy_super(s);}returnold;}}if(!s){spin_unlock(&sb_lock);s=alloc_super(type);/*创建新的super_block并初始化*/if(!s)returnERR_PTR(-ENOMEM);gotoretry;}err=set(s,data);/*设置s->s_dev*/if(err){spin_unlock(&sb_lock);up_write(&s->s_umount);destroy_super(s);returnERR_PTR(err);}s->s_type=type;strlcpy(s->s_id,type->name,sizeof(s->s_id));/*拷贝name*/list_add_tail(&s->s_list,&super_blocks);/*将新的super_block添加到链表头super_blocks中*/list_add(&s->s_instances,&type->fs_supers);/*将新的super_block添加到相应的文件系统类型的链表中*/spin_unlock(&sb_lock);get_filesystem(type);returns;}EXPORT_SYMBOL(sget);

该函数将遍历属于sysfs文件系统的所有superblock,本例中由于之前没有任何superblock创建,遍历立即结束。

然后调用alloc_super函数来创建新的struct super_block。

下列函数位于fs/super.c。

[cpp]view plaincopy

    /***alloc_super-createnewsuperblock*@type:filesystemtypesuperblockshouldbelongto**Allocatesandinitializesanew&structsuper_block.alloc_super()*returnsapointernewsuperblockor%NULLifallocationhadfailed.*/staticstructsuper_block*alloc_super(structfile_system_type*type){structsuper_block*s=kzalloc(sizeof(structsuper_block),GFP_USER);/*分配并清0super_block*/staticstructsuper_operationsdefault_op;if(s){if(security_sb_alloc(s)){kfree(s);s=NULL;gotoout;}INIT_LIST_HEAD(&s->s_dirty);INIT_LIST_HEAD(&s->s_io);INIT_LIST_HEAD(&s->s_more_io);INIT_LIST_HEAD(&s->s_files);INIT_LIST_HEAD(&s->s_instances);INIT_HLIST_HEAD(&s->s_anon);INIT_LIST_HEAD(&s->s_inodes);INIT_LIST_HEAD(&s->s_dentry_lru);INIT_LIST_HEAD(&s->s_async_list);init_rwsem(&s->s_umount);mutex_init(&s->s_lock);lockdep_set_class(&s->s_umount,&type->s_umount_key);/**Thelockingrulesfors_lockareuptothe*filesystem.Forexampleext3fshasdifferent*lockorderingthanusbfs:*/lockdep_set_class(&s->s_lock,&type->s_lock_key);/**sget()canhaves_umountrecursion.**Whenitcannotfindasuitablesb,itallocatesanew*one(thisone),andtriesagaintofindasuitableold*one.**Incasethatsucceeds,itwillacquirethes_umount*lockoftheoldone.Sincetheseareclearlydistrinct*locks,andthisobjectisn’texposedyet,there’sno*riskofdeadlocks.**Annotatethisbyputtingthislockinadifferent*subclass.*/down_write_nested(&s->s_umount,SINGLE_DEPTH_NESTING);s->s_count=S_BIAS;atomic_set(&s->s_active,1);mutex_init(&s->s_vfs_rename_mutex);mutex_init(&s->s_dquot.dqio_mutex);mutex_init(&s->s_dquot.dqonoff_mutex);init_rwsem(&s->s_dquot.dqptr_sem);init_waitqueue_head(&s->s_wait_unfrozen);s->s_maxbytes=MAX_NON_LFS;s->dq_op=sb_dquot_ops;s->s_qcop=sb_quotactl_ops;s->s_op=&default_op;s->s_time_gran=1000000000;}out:returns;}

分配完以后,调用作为参数传入的函数指针set,也就是set_anon_super函数,该函数用来设置s->s_dev。

下列函数位于fs/super.c。

[cpp]view plaincopy

    intset_anon_super(structsuper_block*s,void*data){intdev;interror;retry:if(ida_pre_get(&unnamed_dev_ida,GFP_ATOMIC)==0)/*分配ID号*/return-ENOMEM;spin_lock(&unnamed_dev_lock);error=ida_get_new(&unnamed_dev_ida,&dev);/*获取ID号,保存在dev中*/spin_unlock(&unnamed_dev_lock);if(error==-EAGAIN)/*WeracedandlostwithanotherCPU.*/gotoretry;elseif(error)return-EAGAIN;if((dev&MAX_ID_MASK)==(1<<MINORBITS)){spin_lock(&unnamed_dev_lock);ida_remove(&unnamed_dev_ida,dev);spin_unlock(&unnamed_dev_lock);return-EMFILE;}s->s_dev=MKDEV(0,dev&MINORMASK);/*构建设备号*/return0;}

8.2.2 sysfs_fill_super函数

分配了super_block之后,将判断该super_block是否有root dentry。本例中,显然没有。然后调用形参fill_super指向的函数,也就是sysfs_fill_super函数。

下列函数位于fs/sysfs/mount.c。

[cpp]view plaincopy

    structsuper_block*sysfs_sb=NULL;staticintsysfs_fill_super(structsuper_block*sb,void*data,intsilent){structinode*inode;structdentry*root;sb->s_blocksize=PAGE_CACHE_SIZE;/*4KB*/sb->s_blocksize_bits=PAGE_CACHE_SHIFT;/*4KB*/sb->s_magic=SYSFS_MAGIC;/*0x62656572*/sb->s_op=&sysfs_ops;sb->s_time_gran=1;sysfs_sb=sb;/*sysfs_sb即为sysfs的super_block*//*getrootinode,initializeandunlockit*/mutex_lock(&sysfs_mutex);inode=sysfs_get_inode(&sysfs_root);/*sysfs_root即为sysfs所在的根目录的dirent,,获取inode*/mutex_unlock(&sysfs_mutex);if(!inode){pr_debug("sysfs:couldnotgetrootinode\n");return-ENOMEM;}/*instantiateandlinkrootdentry*/root=d_alloc_root(inode);/*为获得的根inode分配root(/)dentry*/if(!root){pr_debug("%s:couldnotgetrootdentry!\n",__func__);iput(inode);return-ENOMEM;}root->d_fsdata=&sysfs_root;sb->s_root=root;/*保存superblock的根dentry*/return0;}structsysfs_direntsysfs_root={/*sysfs_root即为sysfs所在的根目录的dirent*/.s_name="",.s_count=ATOMIC_INIT(1),.s_flags=SYSFS_DIR,.s_mode=S_IFDIR|S_IRWXU|S_IRUGO|S_IXUGO,.s_ino=1,};

在设置了一些字段后,设置了sysfs_sb这个全局变量,该全局变量表示的就是sysfs的super_block。

随后,调用了sysfs_get_inode函数,来获取sysfs的根目录的dirent。该函数的参数sysfs_root为全局变量,表示sysfs的根目录的sysfs_dirent。

我们看些这个sysfs_dirent数据结构:

[cpp]view plaincopy

    /**sysfs_dirent-thebuildingblockofsysfshierarchy.Eachand*everysysfsnodeisrepresentedbysinglesysfs_dirent.**Aslongass_countreferenceisheld,thesysfs_direntitselfis*accessible.Dereferencings_elemoranyotherouterentity*requiress_activereference.*/structsysfs_dirent{atomic_ts_count;atomic_ts_active;structsysfs_dirent*s_parent;structsysfs_dirent*s_sibling;constchar*s_name;union{structsysfs_elem_dirs_dir;structsysfs_elem_symlinks_symlink;structsysfs_elem_attrs_attr;structsysfs_elem_bin_attrs_bin_attr;};unsignedints_flags;ino_ts_ino;umode_ts_mode;structiattr*s_iattr;};

其中比较关键的就是那个联合体,针对不同的形式(目录,symlink,属性文件和可执行文件)将使用不同的数据结构。

另外,sysfs_dirent将最为dentry的fs专有数据被保存下来,这一点会在下面中看到。

接着,在来看下sysfs_get_inode函数:

下列函数位于fs/sysfs/inode.c。

[cpp]view plaincopy

    /***sysfs_get_inode-getinodeforsysfs_dirent*@sd:sysfs_direnttoallocateinodefor**Getinodefor@sd.Ifsuchinodedoesn’texist,anewinode*isallocatedandbasicsareinitialized.Newinodeis*returnedlocked.**LOCKING:*Kernelthreadcontext(maysleep).**RETURNS:*Pointertoallocatedinodeonsuccess,NULLonfailure.*/structinode*sysfs_get_inode(structsysfs_dirent*sd){structinode*inode;inode=iget_locked(sysfs_sb,sd->s_ino);/*在inodecache查找inode是否存在,不存在侧创建一个*/if(inode&&(inode->i_state&I_NEW))/*如果是新创建的inode,则包含I_NEW*/sysfs_init_inode(sd,inode);returninode;}/***iget_locked-obtainaninodefromamountedfilesystem*@sb:superblockoffilesystem*@ino:inodenumbertoget**iget_locked()usesifind_fast()tosearchfortheinodespecifiedby@inoin*theinodecacheandifpresentitisreturnedwithanincreasedreference*count.Thisisforfilesystemswheretheinodenumberissufficientfor*uniqueidentificationofaninode.**Iftheinodeisnotincache,get_new_inode_fast()iscalledtoallocatea*newinodeandthisisreturnedlocked,hashed,andwiththeI_NEWflagset.*Thefilesystemgetstofillitinbeforeunlockingitvia*unlock_new_inode().*/structinode*iget_locked(structsuper_block*sb,unsignedlongino){structhlist_head*head=inode_hashtable+hash(sb,ino);structinode*inode;inode=ifind_fast(sb,head,ino);/*在inodecache查找该inode*/if(inode)returninode;/*找到了该inode*//**get_new_inode_fast()willdotherightthing,re-tryingthesearch*incaseithadtoblockatanypoint.*/returnget_new_inode_fast(sb,head,ino);/*分配一个新的inode*/}EXPORT_SYMBOL(iget_locked);staticvoidsysfs_init_inode(structsysfs_dirent*sd,structinode*inode){structbin_attribute*bin_attr;inode->i_private=sysfs_get(sd);inode->i_mapping->a_ops=&sysfs_aops;inode->i_mapping->backing_dev_info=&sysfs_backing_dev_info;inode->i_op=&sysfs_inode_operations;inode->i_ino=sd->s_ino;lockdep_set_class(&inode->i_mutex,&sysfs_inode_imutex_key);if(sd->s_iattr){/*sysfs_direnthasnon-defaultattributes*getthemforthenewinodefrompersistentcopy*insysfs_dirent*/set_inode_attr(inode,sd->s_iattr);}elseset_default_inode_attr(inode,sd->s_mode);/*设置inode属性*//*initializeinodeaccordingtotype*/switch(sysfs_type(sd)){caseSYSFS_DIR:inode->i_op=&sysfs_dir_inode_operations;inode->i_fop=&sysfs_dir_operations;inode->i_nlink=sysfs_count_nlink(sd);break;caseSYSFS_KOBJ_ATTR:inode->i_size=PAGE_SIZE;inode->i_fop=&sysfs_file_operations;break;caseSYSFS_KOBJ_BIN_ATTR:bin_attr=sd->s_bin_attr.bin_attr;inode->i_size=bin_attr->size;inode->i_fop=&bin_fops;break;caseSYSFS_KOBJ_LINK:inode->i_op=&sysfs_symlink_inode_operations;break;default:BUG();}unlock_new_inode(inode);}

该函数首先调用了,iget_locked来查找该inode是否已存在,如果不存在则创建。如果是新创建的inode,则对inode进行初始化。再获取了根目录的inode和sysfs_dirent后,调用d_alloc_root来获得dirent。[cpp]view plaincopy

    /***d_alloc_root-allocaterootdentry*@root_inode:inodetoallocatetherootfor**Allocatearoot("/")dentryfortheinodegiven.Theinodeis*instantiatedandreturned.%NULLisreturnedifthereisinsufficient*memoryortheinodepassedis%NULL.*/structdentry*d_alloc_root(structinode*root_inode){structdentry*res=NULL;if(root_inode){staticconststructqstrname={.name="/",.len=1};res=d_alloc(NULL,&name);/*分配structdentry,没有父dentry*/if(res){res->d_sb=root_inode->i_sb;res->d_parent=res;d_instantiated_instantiate(res,root_inode);/*绑定inode和dentry之间的关系*/}}returnres;}/***d_alloc-allocateadcacheentry*@parent:parentofentrytoallocate*@name:qstrofthename**Allocatesadentry.Itreturns%NULLifthereisinsufficientmemory*available.Onasuccessthedentryisreturned.Thenamepassedinis*copiedandthecopypassedinmaybereusedafterthiscall.*/structdentry*d_alloc(structdentry*parent,conststructqstr*name){structdentry*dentry;char*dname;dentry=kmem_cache_alloc(dentry_cache,GFP_KERNEL);/*分配structdentry*/if(!dentry)returnNULL;if(name->len>DNAME_INLINE_LEN-1){dname=kmalloc(name->len+1,GFP_KERNEL);if(!dname){kmem_cache_free(dentry_cache,dentry);returnNULL;}}else{dname=dentry->d_iname;}dentry->d_name.name=dname;dentry->d_name.len=name->len;dentry->d_name.hash=name->hash;memcpy(dname,name->name,name->len);dname[name->len]=0;atomic_set(&dentry->d_count,1);dentry->d_flags=DCACHE_UNHASHED;spin_lock_init(&dentry->d_lock);dentry->d_inode=NULL;dentry->d_parent=NULL;dentry->d_sb=NULL;dentry->d_op=NULL;dentry->d_fsdata=NULL;dentry->d_mounted=0;INIT_HLIST_NODE(&dentry->d_hash);INIT_LIST_HEAD(&dentry->d_lru);INIT_LIST_HEAD(&dentry->d_subdirs);INIT_LIST_HEAD(&dentry->d_alias);if(parent){/*有父目录,则设置指针来表示关系*/dentry->d_parent=dget(parent);dentry->d_sb=parent->d_sb;/*根dentry的父对象为自己*/}else{INIT_LIST_HEAD(&dentry->d_u.d_child);}spin_lock(&dcache_lock);if(parent)/*有父目录,则添加到父目录的儿子链表中*/list_add(&dentry->d_u.d_child,&parent->d_subdirs);dentry_stat.nr_dentry++;spin_unlock(&dcache_lock);returndentry;}/***d_instantiate-fillininodeinformationforadentry*@entry:dentrytocomplete*@inode:inodetoattachtothisdentry**Fillininodeinformationintheentry.**Thisturnsnegativedentriesintoproductivefullmembers*ofsociety.**NOTE!Thisassumesthattheinodecounthasbeenincremented*(orotherwiseset)bythecallertoindicatethatitisnow*inusebythedcache.*/voidd_instantiate(structdentry*entry,structinode*inode){BUG_ON(!list_empty(&entry->d_alias));spin_lock(&dcache_lock);__d_instantiate(entry,inode);spin_unlock(&dcache_lock);security_d_instantiate(entry,inode);}/*thecallermustholddcache_lock*/staticvoid__d_instantiate(structdentry*dentry,structinode*inode){if(inode)list_add(&dentry->d_alias,&inode->i_dentry);/*将dentry添加到inode的链表中*/dentry->d_inode=inode;/*保存dentry对应的inode*/fsnotify_d_instantiate(dentry,inode);}

该函数首先调用了d_alloc来创建struct dentry,参数parent为NULL,既然是为根( / )建立dentry,自然没有父对象。

接着调用d_instantiate来绑定inode和dentry之间的关系。

在sysfs_fill_super函数执行的最后,将sysfs_root保存到了dentry->d_fsdata。

可见,在sysfs中用sysfs_dirent来表示目录,但是对于VFS,还是要使用dentry来表示目录。

8.2.3 do_remount_sb

下列代码位于fs/super.c。

[cpp]view plaincopy

    /***do_remount_sb-asksfilesystemtochangemountoptions.*@sb:superblockinquestion*@flags:numericpartofoptions*@data:therestofoptions*@force:whetherornottoforcethechange**Altersthemountoptionsofamountedfilesystem.*/intdo_remount_sb(structsuper_block*sb,intflags,void*data,intforce){intretval;intremount_rw;#ifdefCONFIG_BLOCKif(!(flags&MS_RDONLY)&&bdev_read_only(sb->s_bdev))return-EACCES;#endifif(flags&MS_RDONLY)acct_auto_close(sb);shrink_dcache_sb(sb);fsync_super(sb);/*IfweareremountingRDONLYandcurrentsbisread/write,makesuretherearenorwfilesopened*/if((flags&MS_RDONLY)&&!(sb->s_flags&MS_RDONLY)){if(force)mark_files_ro(sb);elseif(!fs_may_remount_ro(sb))return-EBUSY;retval=vfs_dq_off(sb,1);if(retval<0&&retval!=-ENOSYS)return-EBUSY;}remount_rw=!(flags&MS_RDONLY)&&(sb->s_flags&MS_RDONLY);if(sb->s_op->remount_fs){lock_super(sb);retval=sb->s_op->remount_fs(sb,&flags,data);unlock_super(sb);if(retval)returnretval;}sb->s_flags=(sb->s_flags&~MS_RMT_MASK)|(flags&MS_RMT_MASK);if(remount_rw)vfs_dq_quota_on_remount(sb);return0;}

这个函数用来修改挂在选项,这个函数就不分析了,不是重点。8.2.4simple_set_mnt

下列函数位于fs/namespace.c。

[cpp]view plaincopy

    voidsimple_set_mnt(structvfsmount*mnt,structsuper_block*sb){mnt->mnt_sb=sb;mnt->mnt_root=dget(sb->s_root);}

该函数设置了vfsmount的superblock和根dentry。8.2.5 小结

这里,对sysfs的注册过程做一个总结。

sysfs_init函数调用过程示意图如下:

在整个过程中,先后使用和创建了许多struct

第一,根据file_system_type表示的sysfs文件系统的类型注册了sysfs。

第二,建立了vfsmount。

第三,创建了超级块super_block。

第四,根据sysfs_dirent表示的根目录,建立了inode。

最后,根据刚才建立的inode创建了dentry。

除了sysfs_dirent,其他5个结构体都是VFS中基本的数据结构,而sysfs_dirent则是特定于sysfs文件系统的数据结构。

8.3 创建目录

在前面的描述中,使用sysfs_create_dir在sysfs下建立一个目录。我们来看下这个函数是如何来建立目录的。

下列代码位于fs/sysfs/dir.c。

[cpp]view plaincopy

    /***sysfs_create_dir-createadirectoryforanobject.*@kobj:objectwe’recreatingdirectoryfor.*/intsysfs_create_dir(structkobject*kobj){structsysfs_dirent*parent_sd,*sd;interror=0;BUG_ON(!kobj);if(kobj->parent)/*如果有parent,获取parent对应的sys目录*/parent_sd=kobj->parent->sd;else/*没有则是在sys根目录*/parent_sd=&sysfs_root;error=create_dir(kobj,parent_sd,kobject_name(kobj),&sd);if(!error)kobj->sd=sd;returnerror;}

函数中,首先获取待建目录的父sysfs_dirent,然后将它作为参数 来调用create_dir函数。

很明显,就是要在父sysfs_dirent下建立新的sysfs_dirent,新建立的sysfs_dirent将保存到参数sd中。

下列代码位于fs/sysfs/dir.c。

[cpp]view plaincopy

    staticintcreate_dir(structkobject*kobj,structsysfs_dirent*parent_sd,constchar*name,structsysfs_dirent**p_sd){umode_tmode=S_IFDIR|S_IRWXU|S_IRUGO|S_IXUGO;structsysfs_addrm_cxtacxt;structsysfs_dirent*sd;intrc;/*allocate*//*分配sysfs_dirent并初始化*/sd=sysfs_new_dirent(name,mode,SYSFS_DIR);if(!sd)return-ENOMEM;sd->s_dir.kobj=kobj;/*保存kobject对象*//*linkin*/sysfs_addrm_start(&acxt,parent_sd);/*寻找父sysfs_dirent对应的inode*/rc=sysfs_add_one(&acxt,sd);/*检查父sysfs_dirent下是否已有有该sysfs_dirent,没有则添加到父sysfs_dirent中*/sysfs_addrm_finish(&acxt);/*收尾工作*/if(rc==0)/*rc为0表示创建成功*/*p_sd=sd;elsesysfs_put(sd);/*增加引用计数*/returnrc;}

这里要注意一下mode变量,改变了使用了宏定义SYSFS_DIR,这个就表示要创建的是一个目录。

mode还有几个宏定义可以使用,如下:

[cpp]view plaincopy

    #defineSYSFS_KOBJ_ATTR0x0002#defineSYSFS_KOBJ_BIN_ATTR0x0004#defineSYSFS_KOBJ_LINK0x0008#defineSYSFS_COPY_NAME(SYSFS_DIR|SYSFS_KOBJ_LINK)

8.3.1 sysfs_new_dirent

在create_dir函数中,首先调用了sysfs_new_dirent来建立一个新的sysfs_dirent结构体。

下列代码位于fs/sysfs/dir.c。

[cpp]view plaincopy

    structsysfs_dirent*sysfs_new_dirent(constchar*name,umode_tmode,inttype){char*dup_name=NULL;structsysfs_dirent*sd;if(type&SYSFS_COPY_NAME){name=dup_name=kstrdup(name,GFP_KERNEL);if(!name)returnNULL;}/*分配sysfs_dirent并清0*/sd=kmem_cache_zalloc(sysfs_dir_cachep,GFP_KERNEL);if(!sd)gotoerr_out1;if(sysfs_alloc_ino(&sd->s_ino))/*分配ID号*/gotoerr_out2;atomic_set(&sd->s_count,1);atomic_set(&sd->s_active,0);sd->s_name=name;sd->s_mode=mode;sd->s_flags=type;returnsd;err_out2:kmem_cache_free(sysfs_dir_cachep,sd);err_out1:kfree(dup_name);returnNULL;}

8.3.2 有关sysfs_dirent中的联合体

分配了sysfs_dirent后,设置了该结构中的联合体数据。先来看下联合体中的四个数据结构。

[cpp]view plaincopy

    /*type-specificstructuresforsysfs_dirent->s_*unionmembers*/structsysfs_elem_dir{structkobject*kobj;/*childrenliststartshereandgoesthroughsd->s_sibling*/structsysfs_dirent*children;};structsysfs_elem_symlink{structsysfs_dirent*target_sd;};structsysfs_elem_attr{structattribute*attr;structsysfs_open_dirent*open;};structsysfs_elem_bin_attr{structbin_attribute*bin_attr;structhlist_headbuffers;};

根据sysfs_dirent所代表的类型不同,也就是目录,synlink,属性文件和bin文件,将分别使用该联合体中相应的struct。

在本例中要创建的是目录,自然使用sysfs_elem_dir结构体,然后保存了kobject对象。

在8.4和8.5中我们将分别看到sysfs_elem_attr和sysfs_elem_symlink的使用。

8.3.3 sysfs_addrm_start

在获取了父sysfs_dirent,调用sysfs_addrm_start来获取与之对应的inode。

下列代码位于fs/sysfs/dir.c。

[cpp]view plaincopy

    /***sysfs_addrm_start-prepareforsysfs_direntadd/remove*@acxt:pointertosysfs_addrm_cxttobeused*@parent_sd:parentsysfs_dirent**Thisfunctioniscalledwhenthecallerisabouttoaddor*removesysfs_direntunder@parent_sd.Thisfunctionacquires*sysfs_mutex,grabsinodefor@parent_sdifavailableandlock*i_mutexofit.@acxtisusedtokeepandpasscontextto*otheraddrmfunctions.**LOCKING:*Kernelthreadcontext(maysleep).sysfs_mutexislockedon*return.i_mutexofparentinodeislockedonreturnif*available.*/voidsysfs_addrm_start(structsysfs_addrm_cxt*acxt,structsysfs_dirent*parent_sd){structinode*inode;memset(acxt,0,sizeof(*acxt));acxt->parent_sd=parent_sd;/*Lookupparentinode.inodeinitializationisprotectedby*sysfs_mutex,soinodeexistencecanbedeterminedby*lookingupinodewhileholdingsysfs_mutex.*/mutex_lock(&sysfs_mutex);/*根据parent_sd来寻找父inode*/inode=ilookup5(sysfs_sb,parent_sd->s_ino,sysfs_ilookup_test,parent_sd);if(inode){WARN_ON(inode->i_state&I_NEW);/*parentinodeavailable*/acxt->parent_inode=inode;/*保存找到的父inode*//*sysfs_mutexisbelowi_mutexinlockhierarchy.*First,trylocki_mutex.Iffails,unlock*sysfs_mutexandlocktheminorder.*/if(!mutex_trylock(&inode->i_mutex)){mutex_unlock(&sysfs_mutex);mutex_lock(&inode->i_mutex);mutex_lock(&sysfs_mutex);}}}/**Contextstructuretobeusedwhileadding/removingnodes.*/structsysfs_addrm_cxt{structsysfs_dirent*parent_sd;structinode*parent_inode;structsysfs_dirent*removed;intcnt;};

注意形参sysfs_addrm_cxt,该结构作用是临时存放数据。8.3.4 sysfs_add_one

下列代码位于fs/sysfs/dir.c。

[cpp]view plaincopy

    /***sysfs_add_one-addsysfs_direnttoparent*@acxt:addrmcontexttouse*@sd:sysfs_direnttobeadded**Get@acxt->parent_sdandsetsd->s_parenttoitandincrement*nlinkofparentinodeif@sdisadirectoryandlinkintothe*childrenlistoftheparent.**Thisfunctionshouldbecalledbetweencallsto*sysfs_addrm_start()andsysfs_addrm_finish()andshouldbe*passedthesame@acxtaspassedtosysfs_addrm_start().**LOCKING:*Determinedbysysfs_addrm_start().**RETURNS:*0onsuccess,-EEXISTifentrywiththegivennamealready*exists.*/intsysfs_add_one(structsysfs_addrm_cxt*acxt,structsysfs_dirent*sd){intret;ret=__sysfs_add_one(acxt,sd);if(ret==-EEXIST){char*path=kzalloc(PATH_MAX,GFP_KERNEL);WARN(1,KERN_WARNING"sysfs:cannotcreateduplicatefilename’%s’\n",(path==NULL)?sd->s_name:strcat(strcat(sysfs_pathname(acxt->parent_sd,path),"/"),sd->s_name));kfree(path);}returnret;}/***__sysfs_add_one-addsysfs_direnttoparentwithoutwarning*@acxt:addrmcontexttouse*@sd:sysfs_direnttobeadded**Get@acxt->parent_sdandsetsd->s_parenttoitandincrement*nlinkofparentinodeif@sdisadirectoryandlinkintothe*childrenlistoftheparent.**Thisfunctionshouldbecalledbetweencallsto*sysfs_addrm_start()andsysfs_addrm_finish()andshouldbe*passedthesame@acxtaspassedtosysfs_addrm_start().**LOCKING:*Determinedbysysfs_addrm_start().**RETURNS:*0onsuccess,-EEXISTifentrywiththegivennamealready*exists.*/int__sysfs_add_one(structsysfs_addrm_cxt*acxt,structsysfs_dirent*sd){/*查找该parent_sd下有无将要建立的sd,没有返回NULL*/if(sysfs_find_dirent(acxt->parent_sd,sd->s_name))return-EEXIST;sd->s_parent=sysfs_get(acxt->parent_sd);/*设置父sysfs_dirent,增加父sysfs_dirent的引用计数*/if(sysfs_type(sd)==SYSFS_DIR&&acxt->parent_inode)/*如果要创建的是目录或文件,并且有父inode*/inc_nlink(acxt->parent_inode);/*inode->i_nlink加1*/acxt->cnt++;sysfs_link_sibling(sd);return0;}/***sysfs_find_dirent-findsysfs_direntwiththegivenname*@parent_sd:sysfs_direnttosearchunder*@name:nametolookfor**Lookforsysfs_direntwithname@nameunder@parent_sd.**LOCKING:*mutex_lock(sysfs_mutex)**RETURNS:*Pointertosysfs_direntiffound,NULLifnot.*/structsysfs_dirent*sysfs_find_dirent(structsysfs_dirent*parent_sd,constunsignedchar*name){structsysfs_dirent*sd;for(sd=parent_sd->s_dir.children;sd;sd=sd->s_sibling)if(!strcmp(sd->s_name,name))returnsd;returnNULL;}/***sysfs_link_sibling-linksysfs_direntintosiblinglist*@sd:sysfs_direntofinterest**Link@sdintoitssiblinglistwhichstartsfrom*sd->s_parent->s_dir.children.**Locking:*mutex_lock(sysfs_mutex)*/staticvoidsysfs_link_sibling(structsysfs_dirent*sd){structsysfs_dirent*parent_sd=sd->s_parent;structsysfs_dirent**pos;BUG_ON(sd->s_sibling);/*Storedirectoryentriesinorderbyino.Thisallows*readdirtoproperlyrestartwithouthavingtoadda*cursorintothes_dir.childrenlist.*//*children链表根据s_ino按升序排列,现在将sd插入到正确的儿子链表中*/for(pos=&parent_sd->s_dir.children;*pos;pos=&(*pos)->s_sibling){if(sd->s_ino<(*pos)->s_ino)break;}/*插入链表*/sd->s_sibling=*pos;*pos=sd;}

该函数直接调用了__sysfs_add_one,后者先调用sysfs_find_dirent来查找该parent_sd下有无该的sysfs_dirent,如果没有,则设置创建好的新的sysfs_dirent的s_parent字段。也就是将新的sysfs_dirent添加到父sys_dirent中。接着调用sysfs_link_sibling函数,将新建的sysfs_dirent添加到sd->s_parent->s_dir.children链表中。8.3.5 sysfs_addrm_finish

下列代码位于fs/sysfs/dir.c。

[cpp]view plaincopy

    /***sysfs_addrm_finish-finishupsysfs_direntadd/remove*@acxt:addrmcontexttofinishup**Finishupsysfs_direntadd/remove.Resourcesacquiredby*sysfs_addrm_start()arereleasedandremovedsysfs_direntsare*cleanedup.Timestampsontheparentinodeareupdated.**LOCKING:*Allmutexesacquiredbysysfs_addrm_start()arereleased.*/voidsysfs_addrm_finish(structsysfs_addrm_cxt*acxt){/*releaseresourcesacquiredbysysfs_addrm_start()*/mutex_unlock(&sysfs_mutex);if(acxt->parent_inode){structinode*inode=acxt->parent_inode;/*ifadded/removed,updatetimestampsontheparent*/if(acxt->cnt)inode->i_ctime=inode->i_mtime=CURRENT_TIME;/*更新父inode的时间*/mutex_unlock(&inode->i_mutex);iput(inode);}/*killremovedsysfs_dirents*/while(acxt->removed){structsysfs_dirent*sd=acxt->removed;acxt->removed=sd->s_sibling;sd->s_sibling=NULL;sysfs_drop_dentry(sd);sysfs_deactivate(sd);unmap_bin_file(sd);sysfs_put(sd);}}

该函数结束了添加sysfs_dirent的工作,这个就不多做说明了。

至此,添加一个目录的工作已经完成了,添加目录的工作其实就是创建了一个新的sysfs_dirent,并把它添加到父sysfs_dirent中。

下面我们看下如何添加属性文件。

8.4 创建属性文件

添加属性文件使用sysfs_create_file函数。

下列函数位于fs/sysfs/file.c。

[cpp]view plaincopy

    /***sysfs_create_file-createanattributefileforanobject.*@kobj:objectwe’recreatingfor.*@attr:attributedescriptor.*/intsysfs_create_file(structkobject*kobj,conststructattribute*attr){BUG_ON(!kobj||!kobj->sd||!attr);returnsysfs_add_file(kobj->sd,attr,SYSFS_KOBJ_ATTR);}intsysfs_add_file(structsysfs_dirent*dir_sd,conststructattribute*attr,inttype){returnsysfs_add_file_mode(dir_sd,attr,type,attr->mode);}intsysfs_add_file_mode(structsysfs_dirent*dir_sd,conststructattribute*attr,inttype,mode_tamode){umode_tmode=(amode&S_IALLUGO)|S_IFREG;structsysfs_addrm_cxtacxt;structsysfs_dirent*sd;intrc;/*分配sysfs_dirent并初始化*/sd=sysfs_new_dirent(attr->name,mode,type);if(!sd)return-ENOMEM;sd->s_attr.attr=(void*)attr;sysfs_addrm_start(&acxt,dir_sd);/*寻找父sysfs_dirent对应的inode*/rc=sysfs_add_one(&acxt,sd);/*检查父sysfs_dirent下是否已有有该sysfs_dirent,没有则创建*/sysfs_addrm_finish(&acxt);/*收尾工作*/if(rc)/*0表示创建成功*/sysfs_put(sd);returnrc;}

sysfs_create_file用参数SYSFS_KOBJ_ATTR(表示建立属性文件)来调用了sysfs_add_file,后者又直接调用了sysfs_add_file_mode。

sysfs_add_file_mode函数的执行和8.3节的create_dir函数非常类似,只不过它并没有保存kobject对象,也就是说该sysfs_dirent并没有一个对应的kobject对象。

需要注意的是,这里是建立属性文件,因此使用了联合体中的结构体s_attr。

8.5 创建symlink最后,来看下symlink的建立。[cpp]view plaincopy

    /***sysfs_create_link-createsymlinkbetweentwoobjects.*@kobj:objectwhosedirectorywe’recreatingthelinkin.*@target:objectwe’repointingto.*@name:nameofthesymlink.*/intsysfs_create_link(structkobject*kobj,structkobject*target,constchar*name){returnsysfs_do_create_link(kobj,target,name,1);}staticintsysfs_do_create_link(structkobject*kobj,structkobject*target,constchar*name,intwarn){structsysfs_dirent*parent_sd=NULL;structsysfs_dirent*target_sd=NULL;structsysfs_dirent*sd=NULL;structsysfs_addrm_cxtacxt;interror;BUG_ON(!name);if(!kobj)/*kobj为空,表示在sysyfs跟目录下建立symlink*/parent_sd=&sysfs_root;else/*有父sysfs_dirent*/parent_sd=kobj->sd;error=-EFAULT;if(!parent_sd)gotoout_put;/*target->sdcangoawaybeneathusbutisprotectedwith*sysfs_assoc_lock.Fetchtarget_sdfromit.*/spin_lock(&sysfs_assoc_lock);if(target->sd)target_sd=sysfs_get(target->sd);、/*获取目标对象的sysfs_dirent*/spin_unlock(&sysfs_assoc_lock);error=-ENOENT;if(!target_sd)gotoout_put;error=-ENOMEM;/*分配sysfs_dirent并初始化*/sd=sysfs_new_dirent(name,S_IFLNK|S_IRWXUGO,SYSFS_KOBJ_LINK);if(!sd)gotoout_put;sd->s_symlink.target_sd=target_sd;/*保存目标sysfs_dirent*/target_sd=NULL;/*referenceisnowownedbythesymlink*/sysfs_addrm_start(&acxt,parent_sd);/*寻找父sysfs_dirent对应的inode*/if(warn)error=sysfs_add_one(&acxt,sd);/*检查父sysfs_dirent下是否已有有该sysfs_dirent,没有则创建*/elseerror=__sysfs_add_one(&acxt,sd);sysfs_addrm_finish(&acxt);/*收尾工作*/if(error)gotoout_put;return0;out_put:sysfs_put(target_sd);sysfs_put(sd);returnerror;}

这个函数的执行也和8.3节的create_dir函数非常类似。其次,symlink同样没有对应的kobject对象。

因为sysfs_dirent表示的是symlink,这里使用了联合体中的s_symlink。同时设置了s_symlink.target_sd指向的目标sysfs_dirent为参数targed_sd。

8.6 小结

本节首先对syfs这一特殊的文件系统的注册过程进行了分析。接着对目录,属性文件和symlink的建立进行了分析。这三者的建立过程基本一致,但是目录

有kobject对象,而剩余两个没有。其次,这三者的每个sysfs_dirent中,都使用了自己的联合体数据。

9 总结

本文首先对sysfs的核心数据kobject,kset等数据结构做出了分析,正是通过它们才能向用户空间呈现出设备驱动模型。

接着,以/sys/bus目录的建立为例,来说明如何通过kobject和kset来建立该bus目录。

随后,介绍了驱动模型中表示总线,设备和驱动的三个数据结构。

然后,介绍了platform总线(bus/platform)的注册,再介绍了虚拟的platform设备(devices/platform)的添加过程。

之后 ,以spi主控制器的platform设备为例,介绍了该platform设备和相应的驱动的注册过程。

最后,介绍了底层sysfs文件系统的注册过程和如何建立目录,属性文件和symlink的过程。

更新说明:2012.09.14 在6.2.9中,添加分析 bus_for_each_drv。

怠惰是贫穷的制造厂。

Linux设备模型——设备驱动模型和sysfs文件系统解读

相关文章:

你感兴趣的文章:

标签云: