织梦CMS - 轻松建站从此开始!

罗索

Linux平台设备驱动 platform_device

落鹤生 发布于 2012-12-11 09:18 点击:次 
从 Linux2.6内核起,引入一套新的驱动管理和注册机制:platform_device 和 platform_driver 。Linux 中大部分的设备驱动,都可以使用这套机制,设备用 platform_device 表示;驱动用platform_driver 进行注册。
TAG:

Linux平台设备驱动

在 设备驱动程序中经常会见到和platform相关的字段,分布在驱动程序的多个角落,这也是2.6内核中比较重要的一种机制,把它原理弄懂,对以后分析驱 动程序很有帮助:在linux2.6设备模型中,关心总线,设备,驱动这三个实体,总线将设备和驱动绑定,在系统每注册一个设备的时候,会寻找与之匹配的 驱动。相反,在系统每注册一个驱动的时候,寻找与之匹配的设备,匹配是由总线来完成的。

一 个现实的Linux 设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2C、SPI 等的设备而言,这自然不是问题,但是在嵌入式系统里面,SoC 系统中集成的独立的外设控制器、挂接在SoC 内存空间的外设等确不依附于此类总线。基于这一背景,Linux 发明了一种虚拟的总线,称为platform 总线。SOC系统中集成的独立外设单元(LCD,RTC,WDT等)都被当作平台设备来处理,而它们本身是字符型设备。

从 Linux2.6内核起,引入一套新的驱动管理和注册机制:platform_device 和 platform_driver 。Linux 中大部分的设备驱动,都可以使用这套机制,设备用 platform_device 表示;驱动用platform_driver 进行注册。

一.平台设备

在 Linux设备驱动中,有一类设备被称为“平台设备”,通常把SoC系统中集成的独立外设单元都当作平台设备来处理。平台设备用 platform_device结构体来描述,在2.6.32.2内核中定义在include/linux/platform_devide.h中,其结 构体如下:

struct platform_device {

const char * name;

int id;

struct device dev;

u32 num_resources;

struct resource * resource; //设备使用的资源



struct platform_device_id *id_entry;



/* 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;

};

我们通常关心start、end 和flags 这3 个字段,分别标明资源的开始值、结束值和类型,flags可以为IORESOURCE_IO、IORESOURCE_MEM、IORESOURCE_IRQ、IORESOURCE_DMA 等。

在Linux中定义了许多平台设备,比如在:arch/arm/plat-s3c24xx/devs.c中,下面贴出WatchDog的平台设备定义:

static struct resource s3c_wdt_resource[] = {

[0] = { //IO端口资源

.start = S3C24XX_PA_WATCHDOG,

.end = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,

.flags = IORESOURCE_MEM,

},

[1] = { //中断资源

.start = IRQ_WDT,

.end = IRQ_WDT,

.flags = IORESOURCE_IRQ,

}

};

struct platform_device s3c_device_wdt = {

.name = "s3c2410-wdt",

.id = -1,

.num_resources = ARRAY_SIZE(s3c_wdt_resource),

.resource = s3c_wdt_resource,

};

EXPORT_SYMBOL(s3c_device_wdt);

定 义好平台设备后,在系统中怎么使用?在arch/arm/mach-s3c2440/mach-smdk2440.c中,这个ARM2440平台的系统入 口文件,系统初始化函数smdk2440_machine_init中使用platform_add_devices函数将一系列的平台设备添加到系统 中。

static struct platform_device *smdk2440_devices[] __initdata = {

&s3c_device_usb,

&s3c_device_lcd,

&s3c_device_wdt,

&s3c_device_i2c0,

&s3c_device_iis,

};

static void __init smdk2440_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();

}



二.平台驱动

在Linux系统中为平台设备定义了平台驱动platform_driver,平台驱动结构体中定义了probe、remove、suspend、resume等接口函数来实现驱动。定义在include/linux/platform_devide.h中。

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;

struct platform_device_id *id_table;

};

下面是WatchDog的platform_driverde初始化,其中driver结构体中初始化了owner、name变量,这里的name要和平台设备的name一致,这样平台设备和平台驱动就关联起来。

static struct platform_driver s3c2410wdt_driver = {

.probe = s3c2410wdt_probe,

.remove = __devexit_p(s3c2410wdt_remove),

.shutdown = s3c2410wdt_shutdown,

.suspend = s3c2410wdt_suspend,

.resume = s3c2410wdt_resume,

.driver = {

.owner = THIS_MODULE,

.name = "s3c2410-wdt",

},

};

驱动又是如何实现呢,在驱动初始化module_init()这个宏实现平台驱动注册,module_exit()中实现平台驱动注销。

static int __init watchdog_init(void)

{

printk(banner);

return platform_driver_register(&s3c2410wdt_driver);

}

static void __exit watchdog_exit(void)

{

platform_driver_unregister(&s3c2410wdt_driver);

}

module_init(watchdog_init);

module_exit(watchdog_exit);

 

Platform Device and Drivers
从<linux/platform_device.h>我们可以了解Platform bus上面的驱动模型接口:platform_device,platform_driver。和PCI和USB这些大结构的总线不同,虚拟总线 Platform bus使用最小结构来集成SOC processer上的各种外设,或者各种“legacy”之间的互联。

Platform device
典型的Platform device是系统中的各种自主设备,包括各种桥接在外围总线上的port-based device和host,以及各种集成在SOC platform上的控制器。他们都有一个特点,那就是CPU BUS可以直接寻址,或者特殊的情况platform_device连接在其他总线上,但是它的寄存器是可以被直接寻址的。
Platform device有一个名字,用来进行driver的绑定;还有诸如中断,地址之类的一些资源列表

struct platform_device {
const char *name;
u32 id;
struct device dev;
u32 num_resources;
struct resource *resource;
};


Platform drivers
Platform driver满足标准driver model,对driver的discovery/enumeration是在driver外部进行的,driver提供了probe()和 remove()方法.Platfomr dirvers通过标准模型提供power management和shutdown通知。

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 (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};

Probe()函数必须验证指定设备的硬件是否真的存在,probe()可以使用设备的资源,包括时钟,platform_data等,Platform driver可以通过下面的函数完成对驱动的注册:
int platform_driver_register(struct platform_driver *drv);
一般来说设备是不能被热插拔的,所以可以将probe()函数放在init段里面来节省driver运行时候的内存开销:
int platform_driver_probe(struct platform_driver *drv,
     int (*probe)(struct platform_device *))

Device Enumeration
作为一个规则,平台(一般来说是板级)启动代码会注册所有的Platform device:
int platform_device_register(struct platform_device *pdev);
int platform_add_devices(struct platform_device **pdevs, int ndev);
一般来说只会注册那些实际存在的设备,不过也存在特殊的情况,比如kernel可能需要与一个不在板子上的外部网络适配器工作,或者那些不挂在任何总线上的控制器。
一般情况下,固件启动的过程会输出一个描述板子上所有存在设备的表。如果没有这个表,系统启动代码建立正确的设备的唯一方法就是为一个特定的板子编译一个kernel。这种board-specific kernel广泛用在嵌入式和一般系统的开发上。
在大部分情况下,设备的memory和IRQ资源不足够让驱动正常工作。board setup code会用device的platform_data域来为设备提供一些额外的资源。
嵌入式系统上的设备会频繁地使用一个或者多个时钟,这些时钟因为节电的原因只有在真正使用的时候才会被打开,系统在启动过程中会为设备分配时钟,可以通过clk_get(&pdev->dev, clock_name)来获得需要的时钟。

Legacy Drivers : Device Probing
一些driver并不会完全遵守标准driver model,这些driver会去注册自己的platform device,而不是让系统来完成注册。这是不值得推
荐的,主要用来兼容以前的一些旧设备。可以通过下面的API来支持这些legacy driver,一般这些API使用在不支持热插拔的driver上面:
struct platform_device *platform_device_alloc(
   const char *name, int id);
可以使用platform_device_alloc动态地创建一个设备,一个更好的方法是,通过下面的函数动态创建一个设备,并把这个设备注册到系统中:
struct platform_device *platform_device_register_simple(
   const char *name, int id,
   struct resource *res, unsigned int nres);

Device Naming and Driver Binding
platform_device.dev.bus_id是一个设备在总线上的名字,它包含两部分:
* platform_device.name   设备名字,用来进行driver的匹配
* platform_device.id     设备实例的标号,如果是-1,表示同样名字的设备只有一个
举个简单的例子,name/id是“serial/1”则它的bus_id就是serial.1 如果name/id是“serial/0”则它的bus_id就是serial.0 ,如果它的name/id是“serial/-1”则它的bus_id就是serial。
driver的绑定是通过driver core自动完成的,完成driver和device的匹配后以后会自动执行probe()函数,如果函数执行成功,则driver和device就绑定在一起了,drvier和device匹配的方法有3种:
* 当一个设备注册的时候,他会在总线上寻找匹配的driver,platform device一般在系统启动很早的时候就注册了
* 当一个驱动注册[platform_driver_register()]的时候,他会遍历所有总线上的设备来寻找匹配,在启动的过程驱动的注册一般比较晚,或者在模块载入的时候
* 当一个驱动注册[platform_driver_probe()]的时候, 功能上和使用platform_driver_register()是一样的,唯一的区别是它不能被以后其他的device probe了,也就是说这个driver只能和一个device绑定。

      Platform device 和 Platform driver实际上是cpu总线可以直接寻址的设备和驱动,他们挂载在一个虚拟的总线platform_bus_type上,是一种bus- specific设备和驱动。与其他bus-specific驱动比如pci是一样的。他们都是将device和device_driver加了一个 warpper产生,仔细看看platform_device就可以看到它必然包含一个device dev,而platform_driver也一样,它必然包含一个device_driver driver。
      所有的设备通过bus_id挂在总线上,多个device可以共用一个driver,但是一个device不可以对应多个driver。驱动去注册时候会 根据设备名寻找设备,没有设备会注册失败,注册的过程会通过probe来进行相应资源的申请,以及硬件的初始化,如果probe执行成功,则device 和driver的绑定就成功了。设备注册的时候同样会在总线上寻找相应的驱动,如果找到他也会试图绑定,绑定的过程同样是执行probe。

(yuyin86)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201212/16421.html]
本文出处:CSDN博客 作者:yuyin86 原文
顶一下
(1)
100%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容