在设备驱动程序中经常会见到和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 进行注册。
(1)平台设备在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; -
-
-
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 等。
- 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(); - }
(2)平台驱动
- 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);