如何设计平台设备和驱动.docx

上传人:b****7 文档编号:15924865 上传时间:2023-07-09 格式:DOCX 页数:21 大小:82.97KB
下载 相关 举报
如何设计平台设备和驱动.docx_第1页
第1页 / 共21页
如何设计平台设备和驱动.docx_第2页
第2页 / 共21页
如何设计平台设备和驱动.docx_第3页
第3页 / 共21页
如何设计平台设备和驱动.docx_第4页
第4页 / 共21页
如何设计平台设备和驱动.docx_第5页
第5页 / 共21页
如何设计平台设备和驱动.docx_第6页
第6页 / 共21页
如何设计平台设备和驱动.docx_第7页
第7页 / 共21页
如何设计平台设备和驱动.docx_第8页
第8页 / 共21页
如何设计平台设备和驱动.docx_第9页
第9页 / 共21页
如何设计平台设备和驱动.docx_第10页
第10页 / 共21页
如何设计平台设备和驱动.docx_第11页
第11页 / 共21页
如何设计平台设备和驱动.docx_第12页
第12页 / 共21页
如何设计平台设备和驱动.docx_第13页
第13页 / 共21页
如何设计平台设备和驱动.docx_第14页
第14页 / 共21页
如何设计平台设备和驱动.docx_第15页
第15页 / 共21页
如何设计平台设备和驱动.docx_第16页
第16页 / 共21页
如何设计平台设备和驱动.docx_第17页
第17页 / 共21页
如何设计平台设备和驱动.docx_第18页
第18页 / 共21页
如何设计平台设备和驱动.docx_第19页
第19页 / 共21页
如何设计平台设备和驱动.docx_第20页
第20页 / 共21页
亲,该文档总共21页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

如何设计平台设备和驱动.docx

《如何设计平台设备和驱动.docx》由会员分享,可在线阅读,更多相关《如何设计平台设备和驱动.docx(21页珍藏版)》请在冰点文库上搜索。

如何设计平台设备和驱动.docx

如何设计平台设备和驱动

如何设计平台设备和驱动

2.6内核引入了platform机制,能够实现对设备所占用的资源进行统一管理。

Platform机制抽象出了platform_device和platform_driver两个核心概念,与此相关的还有一个重要概念就是资源resource。

1.1.1资源

1.描述和类型

资源resource是对设备所占用的硬件信息的抽象,目前包括I/O、内存、IRQ、DMA、BUS这5类。

在内核中,用resource结构来对资源进行描述。

resource结构在文件中定义,如程序清单2.35所示。

程序清单2.35resource数据结构

structresource{

resource_size_tstart;/*资源在CPU上的物理起始地址*/

resource_size_tend;/*资源在CPU上的物理结束地址*/

constchar*name;/*资源名称*/

unsignedlongflags;/*资源的标志*/

structresource*parent,*sibling,*child;/*资源的父亲、兄弟和子资源*/

};

flags通常被用来表示资源的类型,可用的资源类型有IO、MEM、IRQ等,在中定义,各资源类型和定义如下:

#defineIORESOURCE_TYPE_BITS0x00001f00/*资源类型*/

#defineIORESOURCE_IO0x00000100

#defineIORESOURCE_MEM0x00000200

#defineIORESOURCE_IRQ0x00000400

#defineIORESOURCE_DMA0x00000800

#defineIORESOURCE_BUS0x00001000

2.资源定义

一个设备的资源定义可以同时包含所占用的多种资源。

例如,对于一个既占用内存资源,又占用IRQ中断资源的设备,其资源定义可以如程序清单2.36所示。

程序清单2.36资源定义实例

#defineEMC_CS2_BASE0x11000000/*总线片选地址*/

staticstructresourceecm_ax88796b_resource[]={

[0]={/*内存资源*/

.start=EMC_CS2_BASE,/*起始地址*/

.end=EMC_CS2_BASE+0xFFF,/*结束地址*/

.flags=IORESOURCE_MEM,/*资源类型:

IORESOURCE_MEM*/

},

[1]={/*IRQ资源*/

.start=IRQ_GPIO_04,

.end=IRQ_GPIO_04,

.flags=IORESOURCE_IRQ,/*资源类型:

IORESOURCE_IRQ*/

}

};

3.资源获取

定义了一个设备的资源后,需通过特定函数获取才能使用,这些函数在文件中定义,一共有3个函数,分别是:

platform_get_resource()、platform_get_resource_byname()、platform_get_irq()和platform_get_irq_byname()。

platform_get_resource()函数用于获取指定类型的资源,函数原型如下:

externstructresource*platform_get_resource(structplatform_device*,unsignedint,unsignedint);

dev指向包含资源定义的platform_device结构;type表示将要获取的资源类型;num表示获取资源的数量。

返回值为0表示获取失败,成功返回申请的资源地址。

platform_get_resource_byname()则是根据平台设备的设备名称获取指定类型的资源,函数原型如下:

externstructresource*platform_get_resource_byname(structplatform_device*,unsignedint,constchar*);

另外,内核还单独提供了获取IRQ的接口函数platform_get_irq(),实际上就是platform_get_resource()获取IORESOURCE_IRQ的封装,方便用户使用。

原型如下:

intplatform_get_irq(structplatform_device*dev,unsignedintnum);

获取设备的私有数据,可通过宏platform_get_drvdata实现:

#defineplatform_get_drvdata(_dev)dev_get_drvdata(&(_dev)->dev)

实际上是获取_dev->dev->p->driver_data,可参考程序清单2.29device结构的定义。

platform_get_irq_byname()则可根据平台设备名称获取设备的IRQ资源,函数原型如下:

externintplatform_get_irq_byname(structplatform_device*,constchar*);

在驱动编写中如何实际使用这些函数,下面给出一个代码片段,如程序清单2.37所示。

程序清单2.37平台资源获取和使用范例

if(!

mem){

res=platform_get_resource(pdev,IORESOURCE_MEM,0);/*获取内存资源*/

if(!

res){

printk("%s:

getnoresource!

\n",DRV_NAME);

return-ENODEV;

}

mem=res->start;

}

if(!

irq)

irq=platform_get_irq(pdev,0);/*获取IRQ资源*/

if(!

request_mem_region(mem,AX88796B_IO_EXTENT,"ax88796b")){/*申请IO内存*/

PRINTK(ERROR_MSG,PFX"request_mem_regionfail!

");

return-EBUSY;

}

addr=ioremap_nocache(mem,AX88796B_IO_EXTENT);/*内存映射ioremap*/

if(!

addr){

ret=-EBUSY;

gotorelease_region;

}

该范例演示了内存资源和IRQ资源的获取和使用。

特别说明一下内存资源,在定义内存资源的时候,通常使用内存的物理地址,而在驱动中须转换为虚拟地址使用,所以需要进行ioremap操作,而在ioremap之前又需要先申请IO内存,所以在代码中看到的是先使用request_mem_region()函数申请IO内存,然后再通过ioremap_nocache()函数完成内存映射。

1.1.2平台设备

并不是任何设备都可以抽象成为platform_device。

platform_device是在系统中以独立实体出现的设备,包括传统的基于端口的设备、主机到外设的总线以及大部分片内集成的控制器等。

这些设备的一个共同点是CPU都可以通过总线直接对它们进行访问。

在极少数情况下,一个platform_device可能会经过一小段其它总线,但是它的寄存器依然可以被CPU直接访问。

1.platform_device

用于描述平台设备的数据结构是platform_device,在文件中定义,如程序清单2.38所示。

程序清单2.38platform_device数据结构

structplatform_device{

constchar*name;/*设备名称*/

intid;/*设备ID*/

structdevicedev;/*设备的device数据结构*/

u32num_resources;/*资源的个数*/

structresource*resource;/*设备的资源*/

conststructplatform_device_id*id_entry;/*设备ID入口*/

/*体系结构相关的附加项*/

structpdev_archdataarchdata;/*体系结构相关的数据*/

};

name是设备的名称,用于与platform_driver进行匹配绑定,resourse用于描述设备的资源如地址、IRQ等。

2.分配platform_device结构

注册一个platform_device之前,必须先定义或者通过platform_device_alloc()函数为设备分配一个platform_device结构,platform_device_alloc()函数原型如下:

structplatform_device*platform_device_alloc(constchar*name,intid);

3.添加资源

通过platform_device_alloc()申请得到的platform_device结构,必须添加相关资源和私有数据才能进行注册。

添加资源的函数是platform_device_add_resources:

intplatform_device_add_resources(structplatform_device*pdev,conststructresource*res,unsignedintnum);

添加私有数据的函数是platform_device_add_data:

intplatform_device_add_data(structplatform_device*pdev,constvoid*data,size_tsize);

4.注册和注销platform_device

申请到platform_device结构后,可以通过platform_device_register()往系统注册,platform_device_register()函数原型如下:

intplatform_device_register(structplatform_device*pdev);

platform_device_register()只能往系统注册一个platform_device,如果有多个platform_device,可以用platform_add_devices()一次性完成注册,platform_add_devices()函数原型如下:

intplatform_add_devices(structplatform_device**devs,intnum);

通过platform_device_unregister()可以注销系统的platform_device,platform_device_unregister()函数原型如下:

voidplatform_device_unregister(structplatform_device*pdev);

如果已经定义了设备的资源和私有数据,可以用platform_device_register_resndata()一次性完成数据结构申请、资源和私有数据添加以及设备注册:

structplatform_device*__init_or_moduleplatform_device_register_resndata(

structdevice*parent,

constchar*name,intid,

conststructresource*res,unsignedintnum,

constvoid*data,size_tsize);

platform_device_register_simple()函数是platform_device_register_resndata()函数的简化版,可以一步实现分配和注册设备操作,platform_device_register_simple()函数原型如下:

staticinlinestructplatform_device*platform_device_register_simple(

constchar*name,intid,

conststructresource*res,unsignedintnum);

实际上就是:

platform_device_register_resndata(NULL,name,id,res,num,NULL,0)。

文件还提供了更多的platform_device相关的操作接口函数,在有必要的时候可以查看并使用。

5.向系统添加平台设备的流程

向系统添加一个平台设备,可以通过两种方式完成:

●方式1:

定义资源,然后定义platform_device结构并初始化;最后注册;

●方式2:

定义资源,然后动态分配一个platform_device结构,接着往结构添加资源信息,最后注册。

两种方式归纳如图2.7所示。

图2.7添加平台设备的方式

1.1.3平台驱动

1.platform_driver

platform_driver是device_driver的封装,提供了驱动的probe和remove方法,也提供了与电源管理相关的shutdown和suspend等方法,如程序清单2.39所示。

程序清单2.39platform_driver数据结构

structplatform_driver{

int(*probe)(structplatform_device*);/*probe方法*/

int(*remove)(structplatform_device*);/*remove方法*/

void(*shutdown)(structplatform_device*);/*shutdown方法*/

int(*suspend)(structplatform_device*,pm_message_tstate);/*suspend方法*/

int(*resume)(structplatform_device*);/*resume方法*/

structdevice_driverdriver;/*设备驱动*/

conststructplatform_device_id*id_table;/*设备的ID表*/

};

Platform_driver有5个方法:

●probe成员指向驱动的探测代码,在probe方法中获取设备的资源信息并进行处理,如进行物理地址到虚拟地址的remap,或者申请中断等操作,与模块的初始化代码不同;

●remove成员指向驱动的移除代码,进行一些资源释放和清理工作,如取消物理地址与虚拟地址的映射关系,或者释放中断号等,与模块的退出代码不同;

●shutdown成员指向设备被关闭时的实现代码;

●suspend成员执行设备挂起时候的处理代码;

●resume成员执行设备从挂起中恢复的处理代码。

2.注册和注销platform_driver

注册和注销platform_driver的函数分别是platform_driver_register()和platform_driver_unregister(),函数原型分别如下:

intplatform_driver_register(structplatform_driver*drv);

voidplatform_driver_unregister(structplatform_driver*drv);

另外,platform_driver_probe()函数也能完成设备注册,原型如下:

intplatform_driver_probe(structplatform_driver*driver,int(*probe)(structplatform_device*));

如果已经明确知道一个设备不支持热插拔,可以在__init断代码中调用platform_driver_probe()函数,以减少运行时对内存的消耗。

如程序清单2.40所示代码是中的范例,可以参考。

程序清单2.40使用platform_driver_probe的范例

int__initinit_module(void)

{

intretval;

ne_add_devices();

retval=platform_driver_probe(&ne_driver,ne_drv_probe);

if(retval){

if(io[0]==0)

printk(KERN_NOTICE"ne.c:

Youmustsupply\"io=0xNNN\""

"value(s)forISAcards.\n");

ne_loop_rm_unreg

(1);

returnretval;

}

/*Unregisterunusedplatform_devices.*/

ne_loop_rm_unreg(0);

returnretval;

}

注意:

在设备驱动模型中已经提到,bus根据驱动和设备的名称寻找匹配的设备和驱动,因此注册驱动必须保证platform_driver的driver.name字段必须和platform_device的name相同,否则无法将驱动和设备进行绑定而注册失败。

1.1.4平台驱动与普通驱动的差异

基于platform机制编写的驱动与普通字符驱动,只是在框架上有差别,驱动的实际内容是差不多相同的,如果有必要的话,一个普通驱动很容易就可被改写为platform驱动。

图2.8是普通字符驱动与平台驱动的框架对照。

图2.8普通驱动与平台驱动对比

可以看到,将一个普通字符驱动改写为平台驱动,驱动各方法方法的实现以及fops定义都是一样的,不同之处是框架结构发生了变化,资源的申请和释放等代码的位置发生了变化:

●资源申请、设备注册等从普通字符驱动的模块初始化部分移到了平台驱动的probe方法,对于特殊情况,也可以继续放在模块初始化代码中;

●设备注销、资源释放等从普通字符驱动的模块退出代码移到了平台驱动的remove方法。

平台驱动还增加了资源定义和初始化、平台设备和驱动的定义和初始化,以及驱动必要方法的实现等。

平台驱动的模块初始化代码可以很简单,几乎只需简单的调用平台设备注册和注销的接口函数。

1.1.5平台驱动范例

前面已经提到过,采用platform方式编程,能够很好的将资源与驱动分开,便于程序移植和驱动复用。

本节继续以LED为例,用platform方式重新实现LED驱动,实现与第2.5.4小节驱动相同的功能。

为了演示资源和驱动分离,本例将驱动分为如下两个模块:

⏹led_platform模块:

实现资源定义和platform设备注册;

⏹led_drv模块:

通过platform方式实现LED驱动。

在使用的时候,须依次插入led_platform和led_drv,才能生成设备节点。

1.led_platform模块

led_platform模块只有led_platform.c一个文件。

该文件实现了LED资源定义,并向系统注册了一个ledplatform设备,代码如程序清单2.41所示。

程序清单2.41led_platform.c参考代码

1#include

2#include

3#include

4#include

5

6#defineGPIO_LED_PIN_NUM55/*gpio1_23*/

7

8/*定义LED资源*/

9staticstructresourceled_resources[]={

10[0]={

11.start=GPIO_LED_PIN_NUM,

12.end=GPIO_LED_PIN_NUM,

13.flags=IORESOURCE_IO,

14},

15};

16

17staticvoidled_platform_release(structdevice*dev)

18{

19return;

20}

21

22/*定义平台设备*/

23staticstructplatform_deviceled_platform_device={

24.name="led",/*platform_driver中,.name必须与该名字相同*/

25.id=-1,

26.num_resources=ARRAY_SIZE(led_resources),

27.resource=led_resources,

28.dev={

29/*Device'led'doesnothavearelease()function,itisbrokenandmustbefixed.*/

30.release=led_platform_release,

31.platform_data=NULL,

32},

33};

34

35staticint__initled_platform_init(void)

36{

37intret;

38

39ret=platform_device_register(&led_platform_device);

40if(ret<0){

41platform_device_put(&led_platform_device);

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 经管营销 > 经济市场

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2