




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
详解Linux2.6内核中基于platform机制的驱动模型【摘要】本文以Linux2.6.25内核为例,分析了基于platform总线的驱动模型。首先介绍了Platform总线的基本概念,接着介绍了platformdevice和platformdriver的定义和加载过程,分析了其与基类device和driver的派生关系及在此过程中面向对象的设计思想。最后以ARMS3C2440中I2C控制器为例介绍了基于platform总线的驱动开发流程。【关键字】platform_bus,platform_device,resource,platform_driver,file_operations目录1
何谓platformbus?
2
2
device和platform_device
3
3
device_register和platform_device_register
5
4
device_driver和platformdriver
8
5
driver_register和platform_driver_register
10
6
bus、device及driver三者之间的关系
17
7
哪些适用于plarform驱动?
18
8
基于platform总线的驱动开发流程
18
8.1
初始化platform_bus
19
8.2
定义platform_device
22
8.3
注册platform_device
22
8.4
定义platform_driver
28
8.5
注册platform_driver
29
8.6
操作设备
321
何谓platformbus?
Linux系统中许多部分对设备是如何链接的并不感兴趣,但是他们需要知道哪些类型的设备是可以使用的。设备模型提供了一种机制来对设备进行分类,在更高的功能层面上描述这些设备,并使得这些设备对用户空间可见。因此从2.6内核开始引入了设备模型。总线是处理器和一个或多个设备之间的通道,在设备模型中,所有的设备都通过总线相连。总线可以相互插入。设备模型展示了总线和它们所控制的设备之间的实际连接。Platform总线是2.6kernel中最近引入的一种虚拟总线,主要用来管理CPU的片上资源,具有更好的移植性,因此在2.6kernel中,很多驱动都用platform改写了。platform_bus_type的定义如下:
http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L609
609structbus_typeplatform_bus_type={
610
.name
="platform",
611
.dev_attrs
=platform_dev_attrs,
612
.match
=platform_match,
613
.uevent
=platform_uevent,
614
.suspend
=platform_suspend,
615
.suspend_late
=platform_suspend_late,
616
.resume_early
=platform_resume_early,
617
.resume
=platform_resume,
618};
619EXPORT_SYMBOL_GPL(platform_bus_type);http://lxr.linux.no/#linux+v2.6.25/include/linux/device.h#L55
55structbus_type{
56
constchar
*name;
57
structbus_attribute
*bus_attrs;
58
structdevice_attribute*dev_attrs;
59
structdriver_attribute*drv_attrs;
60
61
int(*match)(structdevice*dev,structdevice_driver*drv);
62
int(*uevent)(structdevice*dev,structkobj_uevent_env*env);
63
int(*probe)(structdevice*dev);
64
int(*remove)(structdevice*dev);
65
void(*shutdown)(structdevice*dev);
66
67
int(*suspend)(structdevice*dev,pm_message_tstate);
68
int(*suspend_late)(structdevice*dev,pm_message_tstate);
69
int(*resume_early)(structdevice*dev);
70
int(*resume)(structdevice*dev);
71
72
structbus_type_private*p;
73};总线名称是"platform",其只是bus_type的一种,定义了总线的属性,同时platform_bus_type还有相关操作方法,如挂起、中止、匹配及hotplug事件等。总线bus是联系driver和device的中间枢纽。Device通过所属的bus找到driver,由match操作方法进行匹配。
Bus、driver及devices的连接关系2
device和platform_device
Plarformdevice会有一个名字用于driverbinding(在注册driver的时候会查找driver的目标设备的bus位置,这个过程称为driverbinding),另外IRQ以及地址空间等资源也要给出。platform_device结构体用来描述设备的名称、资源信息等。该结构被定义在http://lxr.linux.no/#linux+v2.6.25/include/linux/platform_device.h#L16中,定义原型如下:
16structplatform_device{
17
constchar
*name;//定义平台设备的名称,此处设备的命名应和相应驱动程序命名一致
18
int
id;
19
structdevice
dev;
20
u32
num_resources;
21
structresource*resource;
//定义平台设备的资源
22};在这个结构里封装了structdevice及structresource。可知:platform_device由device派生而来,是一种特殊的device。下面来看一下platform_device结构体中最重要的一个成员structresource*resource。structresource被定义在http://lxr.linux.no/#linux+v2.6.25/include/linux/ioport.h#L18中,定义原型如下:
14/*
15*Resourcesaretree-like,allowing
16*nestingetc..
17*/
18structresource{
19
resource_size_tstart;
//定义资源的起始地址
20
resource_size_tend;
//定义资源的结束地址
21
constchar*name;//定义资源的名称
22
unsignedlongflags;定义资源的类型,比如MEM,IO,IRQ,DMA类型
23
structresource*parent,*sibling,*child;
24};这个结构表示设备所拥有的资源,即I/O端口、I/O映射内存、中断及DMA等。这里的地址指的是物理地址。另外还需要注意platform_device中的device结构,它详细描述了设备的情况,其为所有设备的基类,定义如下:
http://lxr.linux.no/#linux+v2.6.25/include/linux/device.h#L422
422structdevice{
423
structklist
klist_children;
424
structklist_node
knode_parent;
/*nodeinsiblinglist*/
425
structklist_node
knode_driver;
426
structklist_node
knode_bus;
427
structdevice
*parent;
428
429
structkobjectkobj;
430
char
bus_id[BUS_ID_SIZE];
/*positiononparentbus*/
431
structdevice_type
*type;
432
unsigned
is_registered:1;
433
unsigned
uevent_suppress:1;
434
435
structsemaphore
sem;
/*semaphoretosynchronizecallsto
436
*itsdriver.
437
*/
438
439
structbus_type*bus;
/*typeofbusdeviceison*/
440
structdevice_driver*driver;
/*whichdriverhasallocatedthis
441
device*/
442
void
*driver_data;
/*dataprivatetothedriver*/
443
void
*platform_data;/*Platformspecificdata,device
444
coredoesn''ttouchit*/
445
structdev_pm_info
power;
446
447#ifdefCONFIG_NUMA
448
int
numa_node;
/*NUMAnodethisdeviceiscloseto*/
449#endif
450
u64
*dma_mask;
/*dmamask(ifdma''abledevice)*/
451
u64
coherent_dma_mask;/*Likedma_mask,butfor
452
alloc_coherentmappingsas
453
notallhardwaresupports
454
64bitaddressesforconsistent
455
allocationssuchdescriptors.*/
456
457
structdevice_dma_parameters*dma_parms;
458
459
structlist_head
dma_pools;
/*dmapools(ifdma''ble)*/
460
461
structdma_coherent_mem*dma_mem;/*internalforcoherentmem
462
override*/
463
/*archspecificadditions*/
464
structdev_archdata
archdata;
465
466
spinlock_t
devres_lock;
467
structlist_head
devres_head;
468
469
/*class_devicemigrationpath*/
470
structlist_head
node;
471
structclass
*class;
472
dev_t
devt;
/*dev_t,createsthesysfs"dev"*/
473
structattribute_group
**groups;
/*optionalgroups*/
474
475
void
(*release)(structdevice*dev);
476};
4773
device_register和platform_device_registerhttp://lxr.linux.no/#linux+v2.6.25/drivers/base/core.c#L881
870/**
871*device_register-registeradevicewiththesystem.
872*@dev:pointertothedevicestructure
873*
874*Thishappensintwocleansteps-initializethedevice
875*andaddittothesystem.Thetwostepscanbecalled
876*separately,butthisistheeasiestandmostcommon.
877*I.e.youshouldonlycallthetwohelpersseparatelyif
878*haveaclearlydefinedneedtouseandrefcountthedevice
879*beforeitisaddedtothehierarchy.
880*/
881intdevice_register(structdevice*dev)
882{
883
device_initialize(dev);
884
returndevice_add(dev);
885}
初始化一个设备,然后加入到系统中。http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L325
316/**
317*platform_device_register-addaplatform-leveldevice
318*@pdev:platformdevicewe''readding
319*/
320intplatform_device_register(structplatform_device*pdev)
321{
322
device_initialize(&pdev->dev);
323
returnplatform_device_add(pdev);
324}
325EXPORT_SYMBOL_GPL(platform_device_register);我们看到注册一个platformdevice分为了两部分,初始化这个platform_device,然后将此platform_device添加到platform总线中。输入参数platform_device可以是静态的全局设备。另外一种机制就是动态申请platform_device_alloc一个platform_device设备,然后通过platform_device_add_resources及platform_device_add_data等添加相关资源和属性。无论哪一种platform_device,最终都将通过platform_device_add这册到platform总线上。229/**
230*platform_device_add-addaplatformdevicetodevicehierarchy
231*@pdev:platformdevicewe''readding
232*
233*Thisispart2ofplatform_device_register(),thoughmaybecalled
234*separately_iff_pdevwasallocatedbyplatform_device_alloc().
235*/
236intplatform_device_add(structplatform_device*pdev)
237{
238
inti,ret=0;
239
240
if(!pdev)
241
return-EINVAL;
242
初始化设备的parent为platform_bus,初始化驱备的总线为platform_bus_type。
243
if(!pdev->dev.parent)
244
pdev->dev.parent=&platform_bus;
245
246
pdev->dev.bus=&platform_bus_type;
247
/*++++++++++++++
Theplatform_device.dev.bus_idisthecanonicalnameforthedevices.
It''sbuiltfromtwocomponents:*platform_...whichisalsousedtofordrivermatching.
*platform_device.id...thedeviceinstancenumber,orelse"-1"
toindicatethere''sonlyone.Theseareconcatenated,soname/id"serial"/0indicatesbus_id"serial.0",and
"serial/3"indicatesbus_id"serial.3";bothwouldusetheplatform_driver
named"serial".While"my_rtc"/-1wouldbebus_id"my_rtc"(noinstanceid)
andusetheplatform_drivercalled"my_rtc".
++++++++++++++*/
248
if(pdev->id!=-1)
249
snprintf(pdev->dev.bus_id,BUS_ID_SIZE,"%s.%d",pdev->name,
250
pdev->id);
251
else
252
strlcpy(pdev->dev.bus_id,pdev->name,BUS_ID_SIZE);
253
设置设备structdevice的bus_id成员,留心这个地方,在以后还需要用到这个的。
254
for(i=0;i<pdev->num_resources;i++){
255
structresource*p,*r=&pdev->resource[i];
256
257
if(r->name==NULL)
258
r->name=pdev->dev.bus_id;
259
260
p=r->parent;
261
if(!p){
262
if(r->flags&IORESOURCE_MEM)
263
p=&iomem_resource;
264
elseif(r->flags&IORESOURCE_IO)
265
p=&ioport_resource;
266
}
//resources分为两种IORESOURCE_MEM和IORESOURCE_IO
//CPU对外设IO端口物理地址的编址方式有两种:I/O映射方式和内存映射方式
267
268
if(p&&insert_resource(p,r)){
269
printk(KERN_ERR
270
"%s:failedtoclaimresource%d\n",
271
pdev->dev.bus_id,i);
272
ret=-EBUSY;
273
gotofailed;
274
}
275
}
276
277
pr_debug("Registeringplatformdevice''%s''.Parentat%s\n",
278
pdev->dev.bus_id,pdev->dev.parent->bus_id);
279
280
ret=device_add(&pdev->dev);
281
if(ret==0)
282
returnret;
283
284failed:
285
while(--i>=0)
286
if(pdev->resource[i].flags&(IORESOURCE_MEM|IORESOURCE_IO))
287
release_resource(&pdev->resource[i]);
288
returnret;
289}
290EXPORT_SYMBOL_GPL(platform_device_add);由platform_device_register和platform_device_add的实现可知,device_register()和platform_device_register()都会首先初始化设备
区别在于第二步:其实platform_device_add()包括device_add(),不过要先注册resources,然后将设备挂接到特定的platform总线。4
device_driver和platformdriver
Platformdevice是一种device自己是不会做事情的,要有人为它做事情,那就是platformdriver。platformdriver遵循linux系统的drivermodel。对于device的discovery/enumerate都不是driver自己完成的而是由由系统的driver注册机制完成。driver编写人员只要将注册必须的数据结构初始化并调用注册driver的kernelAPI就可以了。接下来来看platform_driver结构体的原型定义,在http://lxr.linux.no/#linux+v2.6.25/include/linux/platform_device.h#L48中,代码如下:
48structplatform_driver{
49
int(*probe)(structplatform_device*);
50
int(*remove)(structplatform_device*);
51
void(*shutdown)(structplatform_device*);
52
int(*suspend)(structplatform_device*,pm_message_tstate);
53
int(*suspend_late)(structplatform_device*,pm_message_tstate);
54
int(*resume_early)(structplatform_device*);
55
int(*resume)(structplatform_device*);
56
structdevice_driverdriver;
57};可见,它包含了设备操作的几个功能函数,同时包含了一个device_driver结构,说明device_driver是platform_driver的基类。驱动程序中需要初始化这个变量。下面看一下这个变量的定义,位于http://lxr.linux.no/#linux+v2.6.25/include/linux/device.h#L121中:
121structdevice_driver{
122
constchar
*name;
123
structbus_type
*bus;
124
125
structmodule
*owner;
126
constchar
*mod_name;
/*usedforbuilt-inmodules*/
127
128
int(*probe)(structdevice*dev);
129
int(*remove)(structdevice*dev);
130
void(*shutdown)(structdevice*dev);
131
int(*suspend)(structdevice*dev,pm_message_tstate);
132
int(*resume)(structdevice*dev);
133
structattribute_group**groups;
134
135
structdriver_private*p;
136};device_driver提供了一些操作接口,但其并没有实现,相当于一些虚函数,由派生类platform_driver进行重载,无论何种类型的driver都是基于device_driver派生而来的,具体的各种操作都是基于统一的基类接口的,这样就实现了面向对象的设计。需要注意这两个变量:name和owner。其作用主要是为了和相关的platform_device关联起来,owner的作用是说明模块的所有者,驱动程序中一般初始化为THIS_MODULE。device_driver结构中也有一个name变量。platform_driver从字面上来看就知道是设备驱动。设备驱动是为谁服务的呢?当然是设备了。内核正是通过这个一致性来为驱动程序找到资源,即platform_device中的resource。5
driver_register和platform_driver_register内核提供的platform_driver结构体的注册函数为platform_driver_register(),其原型定义在http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L458文件中,具体实现代码如下:
439/**
440*platform_driver_register
441*@drv:platformdriverstructure
442*/
443intplatform_driver_register(structplatform_driver*drv)
444{
445
drv->driver.bus=&platform_bus_type;
/*设置成platform_bus_type这个很重要,因为driver和device是通过bus联系在一起的,具体在本例中是通过
platform_bus_type中注册的回调例程和属性来是实现的,driver与device的匹配就是通过platform_bus_type注册的回调例程platform_match()来完成的。*/
446
if(drv->probe)
447
drv->be=platform_drv_probe;
//在really_probe函数中,回调了platform_drv_probe函数448
if(drv->remove)
449
drv->driver.remove=platform_drv_remove;
450
if(drv->shutdown)
451
drv->driver.shutdown=platform_drv_shutdown;
452
if(drv->suspend)
453
drv->driver.suspend=platform_drv_suspend;
454
if(drv->resume)
455
drv->driver.resume=platform_drv_resume;
456
returndriver_register(&drv->driver);
457}
458EXPORT_SYMBOL_GPL(platform_driver_register);不要被上面的platform_drv_XXX吓倒了,它们其实很简单,就是将structdevice转换为structplatform_device和structplatform_driver,然后调用platform_driver中的相应接口函数。那为什么不直接调用platform_drv_XXX等接口呢?这就是Linux内核中面向对象的设计思想。device_driver提供了一些操作接口,但其并没有实现,相当于一些虚函数,由派生类platform_driver进行重载,无论何种类型的driver都是基于device_driver派生而来的,device_driver中具体的各种操作都是基于统一的基类接口的,这样就实现了面向对象的设计。在文件http://lxr.linux.no/#linux+v2.6.25/drivers/base/driver.c#L234中,实现了driver_register()函数。209/**
210*driver_register-registerdriverwithbus
211*@drv:drivertoregister
212*
213*Wepassoffmostoftheworktothebus_add_driver()call,
214*sincemostofthethingswehavetododealwiththebus
215*structures.
216*/
217intdriver_register(structdevice_driver*drv)
218{
219
intret;
220
//如果总线的方法和设备自己的方法同时存在,将打印告警信息,对于platformbus,其没有probe等接口
221
if((drv->bus->probe&&drv->probe)||
222
(drv->bus->remove&&drv->remove)||
223
(drv->bus->shutdown&&drv->shutdown))
224
printk(KERN_WARNING"Driver''%s''needsupdating-pleaseuse"
225
"bus_typemethods\n",drv->name);
226
ret=bus_add_driver(drv);
227
if(ret)
228
returnret;
229
ret=driver_add_groups(drv,drv->groups);
230
if(ret)
231
bus_remove_driver(drv);
232
returnret;
233}
234EXPORT_SYMBOL_GPL(driver_register);226
其主要将驱动挂接到总线上,通过总线来驱动设备。644/**
645*bus_add_driver-Addadrivertothebus.
646*@drv:driver.
647*/
648intbus_add_driver(structdevice_driver*drv)
649{
650
structbus_type*bus;
651
structdriver_private*priv;
652
interror=0;
653
654
bus=bus_get(drv->bus);
655
if(!bus)
656
return-EINVAL;
657
658
pr_debug("bus:''%s'':adddriver%s\n",bus->name,drv->name);
659
660
priv=kzalloc(sizeof(*priv),GFP_KERNEL);
661
if(!priv){
662
error=-ENOMEM;
663
gotoout_put_bus;
664
}
665
klist_init(&priv->klist_devices,NULL,NULL);
666
priv->driver=drv;
667
drv->p=priv;
668
priv->kobj.kset=bus->p->drivers_kset;
669
error=kobject_init_and_add(&priv->kobj,&driver_ktype,NULL,
670
"%s",drv->name);
671
if(error)
672
gotoout_unregister;
673
674
if(drv->bus->p->drivers_autoprobe){
675
error=driver_attach(drv);
676
if(error)
677
gotoout_unregister;
678
}
679
klist_add_tail(&priv->knode_bus,&bus->p->klist_drivers);
680
module_add_driver(drv->owner,drv);
681
682
error=driver_create_file(drv,&driver_attr_uevent);
683
if(error){
684
printk(KERN_ERR"%s:ueventattr(%s)failed\n",
685
__FUNCTION__,drv->name);
686
}
687
error=driver_add_attrs(bus,drv);
688
if(error){
689
/*Howthehelldowegetoutofthispickle?Giveup*/
690
printk(KERN_ERR"%s:driver_add_attrs(%s)failed\n",
691
__FUNCTION__,drv->name);
692
}
693
error=add_bind_files(drv);
694
if(error){
695
/*Ditto*/
696
printk(KERN_ERR"%s:add_bind_files(%s)failed\n",
697
__FUNCTION__,drv->name);
698
}
699
700
kobject_uevent(&priv->kobj,KOBJ_ADD);
701
returnerror;
702out_unregister:
703
kobject_put(&priv->kobj);
704out_put_bus:
705
bus_put(bus);
706
returnerror;
707}如果总线上的driver是自动probe的话,则将该总线上的driver和device绑定起来。http://lxr.linux.no/#linux+v2.6.25/drivers/base/dd.c#L285
272/**
273*driver_attach-trytobinddrivertodevices.
274*@drv:driver.
275*
276*Walkthelistofdevicesthatthebushasonitandtryto
277*matchthedriverwitheachone.
Ifdriver_probe_device()
278*returns0andthe@dev->driverisset,we''vefounda
279*compatiblepair.
280*/
281intdriver_attach(structdevice_driver*drv)
282{
283
returnbus_for_each_dev(drv->bus,NULL,drv,__driver_attach);
284}
285EXPORT_SYMBOL_GPL(driver_attach);扫描该总线上的每一个设备,将当前driver和总线上的设备进行match,如果匹配成功,则将设备和driver绑定起来。246staticint__driver_attach(structdevice*dev,void*data)
247{
248
structdevice_driver*drv=data;
249
250
/*
251
*Lockdeviceandtrytobindtoit.Wedroptheerror
252
*hereandalwaysreturn0,becauseweneedtokeeptrying
253
*tobindtodevicesandsomedriverswillreturnanerror
254
*simplyifitdidn''tsupportthedevice.
255
*
256
*driver_probe_device()willspitawarningifthere
257
*isanerror.
258
*/
259
260
if(dev->parent)
/*NeededforUSB*/
261
down(&dev->parent->sem);
262
down(&dev->sem);
263
if(!dev->driver)
264
driver_probe_device(drv,dev);
265
up(&dev->sem);
266
if(dev->parent)
267
up(&dev->parent->sem);
268
269
return0;
270}263,如果该设备尚没有匹配的driver,则尝试匹配。http://lxr.linux.no/#linux+v2.6.25/drivers/base/dd.c#L187
170/**
171*driver_probe_device-attempttobinddevice&drivertogether
172*@drv:drivertobindadeviceto
173*@dev:devicetotrytobindtothedriver
174*
175*First,wecallthebus''smatchfunction,ifonepresent,whichshould
176*comparethedeviceIDsthedriversupportswiththedeviceIDsofthe
177*device.Notewedon''tdothisourselvesbecausewedon''tknowthe
178*formatoftheIDstructures,norwhatistobeconsideredamatchand
179*whatisnot.
180*
181*Thisfunctionreturns1ifamatchisfound,-ENODEVifthedeviceis
182*notregistered,and0otherwise.
183*
184*Thisfunctionmustbecalledwith@dev->semheld.
Whencalledfora
185*USBinterface,@dev->parent->semmustbeheldaswell.
186*/
187intdriver_probe_device(structdevice_driver*drv,structdevice*dev)
188{
189
intret=0;
190
191
if(!device_is_registered(dev))
192
return-ENODEV;
193
if(drv->bus->match&&!drv->bus->match(dev,drv))
194
gotodone;
195
196
pr_debug("bus:''%s'':%s:matcheddevice%swithdriver%s\n",
197
drv->bus->name,__FUNCTION__,dev->bus_id,drv->name);
198
199
ret=really_probe(dev,drv);
200
201done:
202
returnret;
203}193,如果该总线上的设备需要进行匹配,则验证是否匹配。对于platform总线,其匹配过程如下:
http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L555
542/**
543*platform_match-bindplatformdevicetoplatformdriver.
544*@dev:device.
545*@drv:driver.
546*
547*PlatformdeviceIDsareassumedtobeencodedlikethis:
548*"",whereisashortdescriptionofthetypeof
549*device,like"pci"or"floppy",andistheenumerated
550*instanceofthedevice,like''0''or''42''.
DriverIDsaresimply
551*"".
So,extractthefromtheplatform_devicestructure,
552*andcompareitagainstthenameofthedriver.Returnwhethertheymatch
553*ornot.
554*/
555staticintplatform_match(structdevice*dev,structdevice_driver*drv)
556{
557
structplatform_device*pdev;
558
559
pdev=container_of(dev,structplatform_device,dev);
560
return(strncmp(pdev->name,drv->name,BUS_ID_SIZE)==0);
561}560,简单的进行字符串匹配,这也是我们强调platform_device和platform_driver中的name属性需要一致的原因。匹配成功后,则调用probe接口。
http://lxr.linux.no/#linux+v2.6.25/drivers/base/dd.c#L101
98staticatomic_tprobe_count=ATOMIC_INIT(0);
99staticDECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
100
101staticintreally_probe(structdevice*dev,structdevice_driver*drv)
102{
103
intret=0;
104
105
atomic_inc(&probe_count);
106
pr_debug("bus:''%s'':%s:probingdriver%swithdevice%s\n",
107
drv->bus->name,__FUNCTION__,drv->name,dev->bus_id);
108
WARN_ON(!list_empty(&dev->devres_head));
109
110
dev->driver=drv;
111
if(driver_sysfs_add(dev)){
112
printk(KERN_ERR"%s:driver_sysfs_add(%s)failed\n",
113
__FUNCTION__,dev->bus_id);
114
gotoprobe_failed;
115
}
116
117
if(dev->bus->probe){
118
ret=dev->bus->probe(dev);
119
if(ret)
120
gotoprobe_failed;
121
}elseif(drv->probe){
122
ret=drv->probe(dev);
123
if(ret)
124
gotoprobe_failed;
125
}
126
127
driver_bound(dev);
128
ret=1;
129
pr_debug("bus:''%s'':%s:bounddevice%stodriver%s\n",
130
drv->bus->name,__FUNCTION__,dev->bus_id,drv->name);
131
gotodone;
132
133probe_failed:
134
devres_release_all(dev);
135
driver_sysfs_remove(dev);
136
dev->driver=NULL;
137
138
if(ret!=-ENODEV&&ret!=-ENXIO){
139
/*drivermatchedbuttheprobefailed*/
140
printk(KERN_WARNING
141
"%s:probeof%sfailedwitherror%d\n",
142
drv->name,dev->bus_id,ret);
143
}
144
/*
145
*Ignoreerrorsreturnedby->probesothatthenextdrivercantry
146
*itsluck.
147
*/
148
ret=0;
149done:
150
atomic_dec(&probe_count);
151
wake_up(&probe_waitqueue);
152
returnret;
153}
154如果bus和driver同时具备probe方法,则优先调用总线的probe函数。否则调用device_driver的probe函数,此probe函数是经过各种类型的driver重载的函数,这就实现了利用基类的统一方法来实现不同的功能。对于platform_driver来说,其就是:
http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L394
394staticintplatform_drv_probe(structdevice*_dev)
395{
396
structplatform_driver*drv=to_platform_driver(_dev->driver);
397
structplatform_device*dev=to_platform_device(_dev);
398
399
returndrv->probe(dev);
400}然后调用特定platform_driver所定义的操作方法,这个是在定义某个platform_driver时静态指定的操作接口。至此,platform_driver成功挂接到platformbus上了,并与特定的设备实现了绑定,并对设备进行了probe处理。6
bus、device及driver三者之间的关系
在数据结构设计上,总线、设备及驱动三者相互关联。platformdevice包含device,根据device可以获得相应的bus及driver。设备添加到总线上后形成一个双向循环链表,根据总线可以获得其上挂接的所有device,进而获得了platformdevice。根据device也可以获得驱动该总线上所有设备的相关driver。platformdriver包含driver,根据driver可以获得相应的bus,进而获得bus上所有的device,进一步获得platformdevice,根据name对driver与platformdevice进行匹配,匹配成功后将device与相应的driver关联起来,即实现了platformdevice和platformdriver的关联。匹配成功后调用driver的probe进而调用platformdriver的probe,在probe里实现驱动特定的功能。
7
哪些适用于plarform驱动?
platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platformdevice提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,这样拥有更好的可移植性。platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。Platformdriver通过platformbus获取platform_device。通常情况下只要和内核本身运行依赖性不大的外围设备,相对独立的,拥有各自独立的资源(地址总线和IRQs),都可以用platform_driver来管理,而timer,irq等小系统之内的设备则最好不用platfrom_driver机制。platform_device最大的特定是CPU直接寻址设备的寄存器空间,即使对于其他总线设备,设备本身的寄存器无法通过CPU总线访问,但总线的controller仍然需要通过platformbus来管理。总之,platfrom_driver的根本目的是为了统一管理系统的外设资源,为驱动程序提供统一的接口来访问系统资源,将驱动和资源分离,提高程序的可移植性。8
基于platform总线的驱动开发流程
基于Platform总线的驱动开发流程如下:
?
定义初始化platformbus
?
定义各种platformdevices
?
注册各种platformdevices
?
定义相关platformdriver
?
注册相关platformdriver
?
操作相关设备
图platform机制开发驱动流程以S3C24xx平台为例,来简单讲述下platform驱动的实现流程。
8.1
初始化platform_bus
Platform总线的初始化是在platform_bus_init()完成的,代码如下:
http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L621
26structdeviceplatform_bus={
27
.bus_id
="platform",
28};
29EXPORT_SYMBOL_GPL(platform_bus);621int__initplatform_bus_init(void)
622{
623
interror;
624
625
error=device_register(&platform_bus);
626
if(error)
627
returnerror;
628
error=
bus_register(&platform_bus_type);
629
if(error)
630
device_unregister(&platform_bus);
631
returnerror;
632}该函数创建了一个名为“platform”的设备,后续platform的设备都会以此为parent。在sysfs中表示为:所有platform类型的设备都会添加在platform_bus所代表的目录下,即/sys/devices/platform下面。
-sh-3.1#ls/sys/devices/platform/
FixedMDIObus.0
fsl-i2c.0
serial8250
fsl-ehci.0
fsl-i2c.1
serial8250.0
fsl-gianfar.0
mpc83xx_spi.0
uevent
fsl-gianfar.1
mpc83xx_wdt.0
fsl-gianfar_mdio.-5
power-sh-3.1#ls/sys/
block/
class/
firmware/kernel/
power/
bus/
devices/
fs/
module/
-sh-3.
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 矿山资源勘查与评价方法考核试卷
- 硅冶炼与深海资源开发考核试卷
- 绝缘制品在海洋工程设备中的应用考核试卷
- 电气设备在智能电网实时监控系统中的应用考核试卷
- 水产品冷冻加工中的食品安全监管考核试卷
- 社会看护服务中的非言语沟通技巧考核试卷
- 有线电视传输网络文化产品与内容创新考核试卷
- 税务合规与风险防范培训考核试卷
- 礼仪情境模拟对话课件
- 生态农业建设生物技术实践考核试卷
- 抖音房产直播敏感词汇表
- (高清版)JTGT 3383-01-2020 公路通信及电力管道设计规范
- 国际公法学马工程全套教学课件
- 微专题地质地貌的形成过程(解析)
- YY/T 0655-2024干式化学分析仪
- 中华民族共同体概论课件专家版2第二讲 树立正确的中华民族历史观
- 四年级四年级下册阅读理解100篇及答案经典
- 中职对口升学复习资料:《汽车机械基础》试题库+答案
- 部编版语文五年级下册第六单元整体教学设计教案
- 平面变压器设计与仿真
- 合作取得更大的成功辩论稿范文六篇
评论
0/150
提交评论