Platform设备驱动

来源:互联网 发布:iphone手机倒计时软件 编辑:程序博客网 时间:2024/06/05 05:27

Platform设备驱动

没有分析platform_device端的注册过程

1、概述:

Linux 2.6内核的设备驱动模型中,有总线(bus)、设备(device)和驱动(driver)3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;同样的,在系统每注册一个驱动时,会寻找与之匹配的设备,而匹配由总线完成。

一个现实的Linux设备和驱动通常都需要挂接在一种总线上,比如:PCIUSBI²CSPI等的设备。但是SOC的片上资源:比如片上集成的外设控制器(如LCD控制器)、WatchdogRTC、挂接在内存空间的外设等却不依赖与此类总线。基于这一背景,从2.6内核起,引入了一套虚拟总线Platform总线,相应的设备称为platform_device,驱动称为platform_driver

该虚拟机制与传统的总线机制相比,优先在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时,通过platform_device提供的标准接口进行申请并使用。

1、平台设备platform_device

1)platform_device涉及的主要结构体


平台设备注册时,需要实现platform_device结构体。

struct platform_device {    //include\linux\Platform_device.h

const char * name;

int id;

struct device dev; //设备结构体

u32 num_resources; //资源数目

struct resource * resource; //资源结构体

const struct platform_device_id *id_entry;

/* MFD cell pointer */

struct mfd_cell *mfd_cell;

/* arch specific additions */

struct pdev_archdata archdata;

};

该结构体里面需要定义具体设备占用的硬件资源(如:地址空间、中断号等)。

struct resource { //\include\linux\Ioport.h

resource_size_t start; //资源的开始地址

resource_size_t end; //资源的结束地址

const char *name; //资源的名字

unsigned long flags; //资源类型

struct resource *parent, *sibling, *child;

};

Resource结构体中的flags表示资源类型,可以为IORESOURCE_IOIO类资源)、IORESOURCE_MEM(内存资源)、IORESOURCE_IRQ(中断资源)、IORESOURCE_DMADMA资源)。

2)主要开发步骤

1)初始化platform_device结构体。

2)注册该platform_device

3)典型例子分析

platform_device设备是板文件相关的。以\arch\arm\mach-s3c24xx\Mach-mini2440.c文件为例进行说明。

 //dm9000以太网设备platform_device结构体初始化。

static struct platform_device mini2440_device_eth = {

.name = "dm9000",

.id = -1,

.num_resources = ARRAY_SIZE(mini2440_dm9k_resource), //资源数目=3

.resource = mini2440_dm9k_resource, //资源结构体

.dev = { //设备结构体

.platform_data = &mini2440_dm9k_pdata, },//附加的信息

};

设备除了定义资源外,还附加了一些数据信息,因为对设备的硬件描述除了中断、内存、DMA通道以外,可能还会有一些配置信息,而这些信息也依赖于板。因此,platform也提供了platform_data的支持,它的形式是自定义的。如对于DM9000网卡来说,platform_data为一个dm9000_plat_data结构体,我们可以将MAC地址、总线宽度、板上有无EEPROM信息放入platform_data

1)资源结构体结构体中定义了3个资源

/* DM9000AEP 10/100 ethernet controller */

static struct resource mini2440_dm9k_resource[] = {

[0] = { //第一个资源

.start = MACH_MINI2440_DM9K_BASE,   //内存的开始地址

.end   = MACH_MINI2440_DM9K_BASE + 3, //内的结束地址

.flags = IORESOURCE_MEM //内存类资源

},

[1] = { //第二个资源

.start = MACH_MINI2440_DM9K_BASE + 4, //内存的开始地址

.end   = MACH_MINI2440_DM9K_BASE + 7, //内的结束地址

.flags = IORESOURCE_MEM //内存类资源

},

[2] = {

.start = IRQ_EINT7, //中断的开始值

.end   = IRQ_EINT7, //中断的结束值

.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, //中断类资源

}

};

2)设置DM9000附加描述信息:16位、没有EEPROM

static struct dm9000_plat_data mini2440_dm9k_pdata = {

.flags = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),

};    

 

2、platform_driver

1)结构体定义

struct platform_driver {

int (*probe)(struct platform_device *);  

int (*remove)(struct platform_device *);

void (*shutdown)(struct platform_device *);

int (*suspend)(struct platform_device *, pm_message_t state);

int (*resume)(struct platform_device *);

struct device_driver driver;

const struct platform_device_id *id_table;

};

该结构体需要驱动编写者自己完成。

2)典型例子分析

static struct platform_driver dm9000_driver = {

.driver = {

.name    = "dm9000", //名字

.owner  = THIS_MODULE, 

.pm  = &dm9000_drv_pm_ops,  

},

.probe   = dm9000_probe,        //probe函数

.remove  = __devexit_p(dm9000_drv_remove),   //remove函数

};

此外还有一个id_table结构体,用于更宽泛的匹配(稍后涉及)。

3platform总线

struct bus_type platform_bus_type = { //drivers\base\Platform.c

 

.name = "platform",

.dev_attrs = platform_dev_attrs,

.match = platform_match,

.uevent = platform_uevent,

.pm = &platform_dev_pm_ops,

};

重点关注match()成员函数,它确定了platform_deviceplatform_driver之间是如何匹配的。该函数会顺序匹配of_match_tableid_tablename字段,其中之一匹配成功就返回。DM9000网卡中name字段会匹配成功。

static int platform_match(struct device *dev, struct device_driver *drv)

{

struct platform_device *pdev = to_platform_device(dev);

struct platform_driver *pdrv = to_platform_driver(drv);

 

/* Attempt an OF style match first */

if (of_driver_match_device(dev, drv)) //匹配of_match_table

return 1;

 

/* Then try to match against the id table */

if (pdrv->id_table)

return platform_match_id(pdrv->id_table, pdev) != NULL;  //匹配id_table

 

/* fall-back to driver name match */

return (strcmp(pdev->name, drv->name) == 0);   //匹配名字

}

4、注册驱动顺序

驱动注册时的函数调用过程:

Platform_driver_register()-->driver_register()-->bus_add_driver()-->driver_attach()-->bus_for_each_dev()。将每个挂在虚拟platform bus的设备driver匹配,如果匹配成功则调用platform_driver->probe(platform_device)

 

5、总结

由以上分析可知,设备驱动中引入platform的概念的优势有:

(1)使得设备被挂接在一个总线上。其结果是配套的sysfs节点、设备电源管理都成为可能。

(2)隔离BSP和驱动。在BSP中定义platform_device设备和设备使用的资源、具体配置信息。而在驱动中,只需通过通用的API去获取资源和数据,做到开发板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台型。

可参考《Linux设备驱动开发详解》(第二版)。

0 0