900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > Linux内核部件分析 设备驱动模型之driver ---mark 详细

Linux内核部件分析 设备驱动模型之driver ---mark 详细

时间:2024-07-17 13:00:32

相关推荐

Linux内核部件分析 设备驱动模型之driver    ---mark  详细

Linux内核部件分析

设备驱动模型之driver

转载:/Linux/-10/44627p7.htm 上节我们分析设备驱动模型中的device,主要是drivers/base/core.c,可以说是代码量最大的一个文件。本节要分析的驱动driver,就要相对简单很多。原因也很简单,对于driver,我们能定义的公共部分实在不多,能再sysfs中表达的也很少。本节的分析将围绕drivers/base/driver.c,但头文件仍然是include/linux/device.h和drivers/base/base.h。

先让我们来看看driver的结构。

structdevice_driver{constchar*name;structbus_type*bus;structmodule*owner;constchar*mod_name;/*usedforbuilt-inmodules*/boolsuppress_bind_attrs;/*disablesbind/unbindviasysfs*/int(*probe)(structdevice*dev);int(*remove)(structdevice*dev);void(*shutdown)(structdevice*dev);int(*suspend)(structdevice*dev,pm_message_tstate);int(*resume)(structdevice*dev);conststructattribute_group**groups;conststructdev_pm_ops*pm;structdriver_private*p;};

struct device_driver就是模型定义的通用驱动结构。name是驱动名称,但这个name也只是在静态定义的初始名称,实际使用的名称还是由kobject中保管的。bus执行驱动所在的总线,owner是驱动所在的模块,还有一个所在模块名称mod_name,suppress_bind_attrs定义是否允许驱动通过sysfs决定挂载还是卸载设备。下面是一系列函数指针,probe是在驱动刚与设备挂接时调用的,remove是在设备卸载时调用的,shutdown是在设备关闭时调用的(说实话我现在还不知道remove和shutdown的区别),suspend是设备休眠时调用的,resume是设备恢复时调用的。group是属性集合,pm是电源管理的函数集合,p是指向driver_private的指针。

structdriver_private{structkobjectkobj;structklistklist_devices;structklist_nodeknode_bus;structmodule_kobject*mkobj;structdevice_driver*driver;};#defineto_driver(obj)container_of(obj,structdriver_private,kobj)

与device类似,device_driver把与其它组件联系的大部分结构变量移到struct driver_private中来。首先是kobj,在sysfs中代表driver目录本身。klist_devices是驱动下的设备链表,knode_bus是要挂载在总线的驱动链表上的节点。mkobj是driver与相关module的联系,之前在device_driver结构中已经有指向module的指针,但这还不够,在/sys下你能发现一个module目录,所以驱动所属的模块在sysfs中也有显示,具体留到代码中再看。driver指针自然是从driver_private指回struct device_driver的。

structdriver_attribute{structattributeattr;ssize_t(*show)(structdevice_driver*driver,char*buf);ssize_t(*store)(structdevice_driver*driver,constchar*buf,size_tcount);};#defineDRIVER_ATTR(_name,_mode,_show,_store)\structdriver_attributedriver_attr_##_name=\__ATTR(_name,_mode,_show,_store)

除了以上两个结构,还有struct driver_attribute。driver_attribute是driver对struct attribute的封装,添加了两个特用于device_driver的读写函数。这种封装看似简单重复,工作量很小,但在使用时却会造成巨大的便利。

好,结构介绍完毕,下面看driver.c中的实现。

staticstructdevice*next_device(structklist_iter*i){structklist_node*n=klist_next(i);structdevice*dev=NULL;structdevice_private*dev_prv;if(n){dev_prv=to_device_private_driver(n);dev=dev_prv->device;}returndev;}intdriver_for_each_device(structdevice_driver*drv,structdevice*start,void*data,int(*fn)(structdevice*,void*)){structklist_iteri;structdevice*dev;interror=0;if(!drv)return-EINVAL;klist_iter_init_node(&drv->p->klist_devices,&i,start?&start->p->knode_driver:NULL);while((dev=next_device(&i))&&!error)error=fn(dev,data);klist_iter_exit(&i);returnerror;}structdevice*driver_find_device(structdevice_driver*drv,structdevice*start,void*data,int(*match)(structdevice*dev,void*data)){structklist_iteri;structdevice*dev;if(!drv)returnNULL;klist_iter_init_node(&drv->p->klist_devices,&i,(start?&start->p->knode_driver:NULL));while((dev=next_device(&i)))if(match(dev,data)&&get_device(dev))break;klist_iter_exit(&i);returndev;}

driver_for_each_device()是对drv的设备链表中的每个设备调用一次指定函数。

driver_find_device()是在drv的设备链表中寻找一个设备,寻找使用指定的匹配函数。

这两个函数都不陌生,在之前分析device的core.c中已经见到与它们很类似的函数,只不过那里是遍历设备的子设备链表,这里是遍历驱动的设备链表。next_device()同样是辅助用的内部函数。

intdriver_create_file(structdevice_driver*drv,structdriver_attribute*attr){interror;if(drv)error=sysfs_create_file(&drv->p->kobj,&attr->attr);elseerror=-EINVAL;returnerror;}voiddriver_remove_file(structdevice_driver*drv,structdriver_attribute*attr){if(drv)sysfs_remove_file(&drv->p->kobj,&attr->attr);}

driver_create_file()创建drv下的属性文件,调用sysfs_create_file()实现。

driver_remove_file()删除drv下的属性文件,调用sysfs_remove_file()实现。

staticintdriver_add_groups(structdevice_driver*drv,conststructattribute_group**groups){interror=0;inti;if(groups){for(i=0;groups[i];i++){error=sysfs_create_group(&drv->p->kobj,groups[i]);if(error){while(--i>=0)sysfs_remove_group(&drv->p->kobj,groups[i]);break;}}}returnerror;}staticvoiddriver_remove_groups(structdevice_driver*drv,conststructattribute_group**groups){inti;if(groups)for(i=0;groups[i];i++)sysfs_remove_group(&drv->p->kobj,groups[i]);}

driver_add_groups()在drv目录下添加属性集合,调用sysfs_create_groups()实现。

driver_remove_groups()在drv目录下删除属性集合,调用sysfs_remove_groups()实现。

发现两点问题:第一,是不是觉得driver_add_groups()不太合适,最好改为driver_create_groups()才搭调。但不只是driver用driver_add_groups(),device也使用device_add_groups(),不知一处这样做。第二,有没有发现driver_create_file()是外部���数,driver_add_groups()就是内部函数,也就是说driver只对外提供添加属性的接口,却不提供添加属性集合的接口。理由吗?在struct device_driver()已经专门定义了一个groups变量来添加属性集合,后面就不易再重复提供接口,而且创建属性集合需要的操作远比创建属性费时。在device中也是这样做的。

另外,driver中只提供管理属性文件的方法,却不提供管理二进制属性文件的方法,这是因为驱动本身没有这种需求,只有部分设备才要求二进制文件表示。

structdevice_driver*get_driver(structdevice_driver*drv){if(drv){structdriver_private*priv;structkobject*kobj;kobj=kobject_get(&drv->p->kobj);priv=to_driver(kobj);returnpriv->driver;}returnNULL;}voidput_driver(structdevice_driver*drv){kobject_put(&drv->p->kobj);}

get_driver()增加drv的引用计数,put_driver()减少drv的引用计数。这都是通过drv->p->kobj来做的。

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;}

driver_find()从bus的驱动链表中寻找特定名称的driver。

/***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-EBUSY;}ret=bus_add_driver(drv);if(ret)returnret;ret=driver_add_groups(drv,drv->groups);if(ret)bus_remove_driver(drv);returnret;}

driver_register()将drv注册到系统中。它真是做得难以预料地简单,所有的工作几乎完全是由bus_add_driver()代为完成的。但你要注意,在调用driver_register()前,drv->bus一定要预先设置。device可以不绑定bus,但driver一定要绑定到bus上。

voiddriver_unregister(structdevice_driver*drv){if(!drv||!drv->p){WARN(1,"Unexpecteddriverunregister!\n");return;}driver_remove_groups(drv,drv->groups);bus_remove_driver(drv);}

driver_unregister()将drv从系统中撤销。大部分工作是调用bus_remove_driver()完成的。可以看出bus_add_driver()与bus_remove_driver()相对。driver和bus的联系如此紧密,以至于driver的注册和撤销工作都可以由bus代劳了。我们需要更进一步的分析。

经过调查,我们发现很有一部分driver的代码被移动到了bus.c中。我们本节是以driver为主,所以接下来会尽量在不惊动bus的情况下,分析存在于bus.c中的driver代码。

staticssize_tdrv_attr_show(structkobject*kobj,structattribute*attr,char*buf){structdriver_attribute*drv_attr=to_drv_attr(attr);structdriver_private*drv_priv=to_driver(kobj);ssize_tret=-EIO;if(drv_attr->show)ret=drv_attr->show(drv_priv->driver,buf);returnret;}staticssize_tdrv_attr_store(structkobject*kobj,structattribute*attr,constchar*buf,size_tcount){structdriver_attribute*drv_attr=to_drv_attr(attr);structdriver_private*drv_priv=to_driver(kobj);ssize_tret=-EIO;if(drv_attr->store)ret=drv_attr->store(drv_priv->driver,buf,count);returnret;}staticstructsysfs_opsdriver_sysfs_ops={.show=drv_attr_show,.store=drv_attr_store,};

看到这里,你终于觉得driver开始正常了,它还要定义sysfs读写时操作的函数。

staticvoiddriver_release(structkobject*kobj){structdriver_private*drv_priv=to_driver(kobj);pr_debug("driver:'%s':%s\n",kobject_name(kobj),__func__);kfree(drv_priv);}staticstructkobj_typedriver_ktype={.sysfs_ops=&driver_sysfs_ops,.release=driver_release,};

与device的释放函数device_release不同,driver_release没有提供外界代码运行的机会,只是简单地释放drv_priv函数。

/*Manuallydetachadevicefromitsassociateddriver.*/staticssize_tdriver_unbind(structdevice_driver*drv,constchar*buf,size_tcount){structbus_type*bus=bus_get(drv->bus);structdevice*dev;interr=-ENODEV;dev=bus_find_device_by_name(bus,NULL,buf);if(dev&&dev->driver==drv){if(dev->parent)/*NeededforUSB*/down(&dev->parent->sem);device_release_driver(dev);if(dev->parent)up(&dev->parent->sem);err=count;}put_device(dev);bus_put(bus);returnerr;}staticDRIVER_ATTR(unbind,S_IWUSR,NULL,driver_unbind);/**Manuallyattachadevicetoadriver.*Note:thedrivermustwanttobindtothedevice,*itisnotpossibletooverridethedriver'sidtable.*/staticssize_tdriver_bind(structdevice_driver*drv,constchar*buf,size_tcount){structbus_type*bus=bus_get(drv->bus);structdevice*dev;interr=-ENODEV;dev=bus_find_device_by_name(bus,NULL,buf);if(dev&&dev->driver==NULL&&driver_match_device(drv,dev)){if(dev->parent)/*NeededforUSB*/down(&dev->parent->sem);down(&dev->sem);err=driver_probe_device(drv,dev);up(&dev->sem);if(dev->parent)up(&dev->parent->sem);if(err>0){/*success*/err=count;}elseif(err==0){/*driverdidn'tacceptdevice*/err=-ENODEV;}}put_device(dev);bus_put(bus);returnerr;}staticDRIVER_ATTR(bind,S_IWUSR,NULL,driver_bind);

上面描述了driver下两个只写的属性文件,unbind和bind。应该是提供用户空间命令是否将设备与驱动挂接的接口。

staticintdriver_add_attrs(structbus_type*bus,structdevice_driver*drv){interror=0;inti;if(bus->drv_attrs){for(i=0;attr_name(bus->drv_attrs[i]);i++){error=driver_create_file(drv,&bus->drv_attrs[i]);if(error)gotoerr;}}done:returnerror;err:while(--i>=0)driver_remove_file(drv,&bus->drv_attrs[i]);gotodone;}staticvoiddriver_remove_attrs(structbus_type*bus,structdevice_driver*drv){inti;if(bus->drv_attrs){for(i=0;attr_name(bus->drv_attrs[i]);i++)driver_remove_file(drv,&bus->drv_attrs[i]);}}

driver_add_attrs()向drv目录下添加属性,只是这些属性都是在bus中定义的drv_attrs[]。

driver_remove_attrs()从drv目录中删除相应的bus->drv_attrs[]。

staticint__must_checkadd_bind_files(structdevice_driver*drv){intret;ret=driver_create_file(drv,&driver_attr_unbind);if(ret==0){ret=driver_create_file(drv,&driver_attr_bind);if(ret)driver_remove_file(drv,&driver_attr_unbind);}returnret;}staticvoidremove_bind_files(structdevice_driver*drv){driver_remove_file(drv,&driver_attr_bind);driver_remove_file(drv,&driver_attr_unbind);}

add_bind_files()在drv目录下增加bind和unbind属性。

remove_bind_files()从drv目录下删除bind和unbind属性。

staticssize_tdriver_uevent_store(structdevice_driver*drv,constchar*buf,size_tcount){enumkobject_actionaction;if(kobject_action_type(buf,count,&action)==0)kobject_uevent(&drv->p->kobj,action);returncount;}staticDRIVER_ATTR(uevent,S_IWUSR,NULL,driver_uevent_store);

这是drv目录下地uevent属性文件,提供了从drv发送uevent的方法。

/***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);if(!bus)return-EINVAL;pr_debug("bus:'%s':adddriver%s\n",bus->name,drv->name);priv=kzalloc(sizeof(*priv),GFP_KERNEL);if(!priv){error=-ENOMEM;gotoout_put_bus;}klist_init(&priv->klist_devices,NULL,NULL);priv->driver=drv;drv->p=priv;priv->kobj.kset=bus->p->drivers_kset;error=kobject_init_and_add(&priv->kobj,&driver_ktype,NULL,"%s",drv->name);if(error)gotoout_unregister;if(drv->bus->p->drivers_autoprobe){error=driver_attach(drv);if(error)gotoout_unregister;}klist_add_tail(&priv->knode_bus,&bus->p->klist_drivers);module_add_driver(drv->owner,drv);error=driver_create_file(drv,&driver_attr_uevent);if(error){printk(KERN_ERR"%s:ueventattr(%s)failed\n",__func__,drv->name);}error=driver_add_attrs(bus,drv);if(error){/*Howthehelldowegetoutofthispickle?Giveup*/printk(KERN_ERR"%s:driver_add_attrs(%s)failed\n",__func__,drv->name);}if(!drv->suppress_bind_attrs){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;}

bus_add_driver()看似是把drv与bus联系起来,其实是完成driver加入系统的大部分操作。

首先调用bus_get(drv->bus)增加对bus的引用。

分配并初始化drv->p,即driver_private结构。

调用kobject_init_and_add()将drv加入sysfs,之前只是设置了priv->obj.kset为bus->p->drivers_kset,所以drv目录会出现在bus目录的drivers子目录中。如果总线允许自动probe,就会调用driver_attach()将驱动和总线上的设备进行匹配,这个过程先略过。

然后调用klist_add_tail()将drv挂入总线的驱动链表。

调用module_add_driver()创建driver相关的模块在sysfs中的表示。后面专门描述。

调用driver_create_file()在drv目录下创建uevent属性文件。

调用driver_add_attrs()在drv目录下添加bus->driver_attrs[]中定义的属性。

如果drv->suppress_bind_attrs为零,即允许用户空间决定驱动何时链接和卸载设备,则调用add_bind_files()添加bind和unbind属性文件。

调用kobject_uevent()向用户空间发布KOBJ_ADD消息。

从bus_add_driver()的处理过程来看,driver只在bus的drivers目录下出现,没什么软链接,需要的属性也不多。

/***bus_remove_driver-deletedriverfrombus'sknowledge.*@drv:driver.**Detachthedriverfromthedevicesitcontrols,andremove*itfromitsbus'slistofdrivers.Finally,wedropthereference*tothebuswetookinbus_add_driver().*/voidbus_remove_driver(structdevice_driver*drv){if(!drv->bus)return;if(!drv->suppress_bind_attrs)remove_bind_files(drv);driver_remove_attrs(drv->bus,drv);driver_remove_file(drv,&driver_attr_uevent);klist_remove(&drv->p->knode_bus);pr_debug("bus:'%s':removedriver%s\n",drv->bus->name,drv->name);driver_detach(drv);module_remove_driver(drv);kobject_put(&drv->p->kobj);bus_put(drv->bus);}

bus_remove_driver()将drv从系统中撤销,与bus_add_driver()相对应。

driver真正精彩的地方在于probe函数,对设备的操作,对用户空间提供的接口,可惜这些都是特定的。这里只能将driver与bus联系起来,并在以后与device联系起来。

不过不必失望,下面我们分析下drivers/base/module.c,它显示了与驱动有关的module,在sysfs中的表现情况。

首先介绍使用到的结构。应该说module.c的代码实现很简单,但使用到的结构不简单。

structmodule_attribute{structattributeattr;ssize_t(*show)(structmodule_attribute*,structmodule*,char*);ssize_t(*store)(structmodule_attribute*,structmodule*,constchar*,size_tcount);void(*setup)(structmodule*,constchar*);int(*test)(structmodule*);void(*free)(structmodule*);};structparam_attribute{structmodule_attributemattr;structkernel_param*param;};structmodule_param_attrs{unsignedintnum;structattribute_groupgrp;structparam_attributeattrs[0];};structmodule_kobject{structkobjectkobj;structmodule*mod;structkobject*drivers_dir;structmodule_param_attrs*mp;};

可以看到module_attribute结构除了包含struct attribute,还多增加了好几条函数指针。而这只是最简单的,struct param_attribute除了包含module_attribute,还有一个指向kernel_param的指针param。这个kernel_param就太复杂了,是外界向module提供参数用的窗口,这里忽略。后面还有struct module_param_attrs和struct module_kobject。

staticchar*make_driver_name(structdevice_driver*drv){char*driver_name;driver_name=kmalloc(strlen(drv->name)+strlen(drv->bus->name)+2,GFP_KERNEL);if(!driver_name)returnNULL;sprintf(driver_name,"%s:%s",drv->bus->name,drv->name);returndriver_name;}

make_driver_name()将drv的名字和drv->bus的名字合起来,不过这是一个内部函数,具体使用还要看后面。

staticvoidmodule_create_drivers_dir(structmodule_kobject*mk){if(!mk||mk->drivers_dir)return;mk->drivers_dir=kobject_create_and_add("drivers",&mk->kobj);}

module_create_drivers_dir()在mk所在的目录下创建一个drivers的目录。不过因为是使用kobject_create_and_add(),所以这个kobject使用默认的dynamic_kobj_ktype。

voidmodule_add_driver(structmodule*mod,structdevice_driver*drv){char*driver_name;intno_warn;structmodule_kobject*mk=NULL;if(!drv)return;if(mod)mk=&mod->mkobj;elseif(drv->mod_name){structkobject*mkobj;/*Lookupbuilt-inmoduleentryin/sys/modules*/mkobj=kset_find_obj(module_kset,drv->mod_name);if(mkobj){mk=container_of(mkobj,structmodule_kobject,kobj);/*rememberourmodulestructure*/drv->p->mkobj=mk;/*kset_find_objtookareference*/kobject_put(mkobj);}}if(!mk)return;/*Don'tcheckreturncodes;thesecallsareidempotent*/no_warn=sysfs_create_link(&drv->p->kobj,&mk->kobj,"module");driver_name=make_driver_name(drv);if(driver_name){module_create_drivers_dir(mk);no_warn=sysfs_create_link(mk->drivers_dir,&drv->p->kobj,driver_name);kfree(driver_name);}}

module_add_drivers()在module下添加与driver的联系。

开始调用kset_find_obj()从module_kset下寻找drv所属的module对应的kobj。说明每个module在加载时都会在/sys/module中创建一个kobject目录。这里找到后只是将其赋给drv->p->kmobj,并调用kobject_put()释放找到时加上的引用计数。至于为什么driver不保留对module的引用计数,或许是不需要,或许是已经存在了。

接下来调用sysfs_create_link()在驱动目录中添加指向module目录的软链接,名称就是module。

调用module_create_drivers_dir()在module目录下建立drivers子目录。

调用sysfs_create_link()在drivers子目录下建立指向驱动目录的软链接,名称使用make_driver_name()的返回结果。

voidmodule_remove_driver(structdevice_driver*drv){structmodule_kobject*mk=NULL;char*driver_name;if(!drv)return;sysfs_remove_link(&drv->p->kobj,"module");if(drv->owner)mk=&drv->owner->mkobj;elseif(drv->p->mkobj)mk=drv->p->mkobj;if(mk&&mk->drivers_dir){driver_name=make_driver_name(drv);if(driver_name){sysfs_remove_link(mk->drivers_dir,driver_name);kfree(driver_name);}}}

module_remove_driver()消除driver与相应module之间的软链接关系。

对于module,应该是另一个议题了,这里只是简单涉及,下节我们将涉及到总线bus,并深入分析device和driver的关系。

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