0 前言
关于平台设备驱动的资料网上非常多,本文并不打算重复这些描述,而是以看门狗平台设备和驱动为例子进行阐述。本文的源码均来自Linux内核(linux-2.6.39.4已经支持mini2440,无需作任何移植)。
1 平台设备
1.1 定义
(1)设备
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);
// @file: arch/arm/plat-samsung/dev-wdt.c
(2)资源
static struct resource s3c_wdt_resource[] = {
[0] = {
.start = S3C_PA_WDT,
.end = S3C_PA_WDT + SZ_1K,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_WDT,
.end = IRQ_WDT,
.flags = IORESOURCE_IRQ,
}
};
// @file: arch/arm/plat-samsung/dev-wdt.c
1.2 引用
定义一个平台设备数组mini2440_devices,该数组包含了mini2440开发板的所有平台设备,其中当然包括上述s3c_device_wdt:
static struct platform_device *mini2440_devices[] __initdata = {
...
&s3c_device_wdt,
...
};
// @file: arch/arm/mach-s3c2440/mach-mini2440.c
1.3 注册
(1)注册函数
mini2440_init()调用platform_add_devices()将上述mini2440_devices注册到系统,即可完成平台设备的注册:
static void __init mini2440_init(void)
{
...
platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
...
}
// @file: arch/arm/mach-s3c2440/mach-mini2440.c
(2)注册时机
上述mini2440_init()函数是何时被调用的呢?答案是在内核初始化设备的时候:
MACHINE_START(MINI2440, "MINI2440")
/* Maintainer: Michel Pollet <buserror@gmail.com> */
.atag_offset = 0x100,
.map_io = mini2440_map_io,
.init_machine = mini2440_init, // 瞧这里!
.init_irq = s3c2440_init_irq,
.init_time = mini2440_init_time,
MACHINE_END
// @file: arch/arm/mach-s3c2440/mach-mini2440.c
2 平台驱动
2.1 定义
为了和平台设备匹配,平台驱动的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",
},
};
// @file: drivers/watchdog/s3c2410_wdt.c
上述s3c2410wdt_probe、s3c2410wdt_remove、s3c2410wdt_shutdown、s3c2410wdt_suspend以及s3c2410wdt_resume都在同一个源文件中定义,详见源文件drivers/watchdog/s3c2410_wdt.c。
2.2 注册
在模块加载函数watchdog_init()中调用platform_driver_register()函数注册上述平台驱动s3c2410wdt_driver:
static int __init watchdog_init(void)
{
printk(banner);
return platform_driver_register(&s3c2410wdt_driver);
}
// @file: drivers/watchdog/s3c2410_wdt.c
3 设备与驱动匹配
3.1 匹配函数
系统自动调用platform_match()函数实现平台设备和平台驱动的匹配:
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))
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
// @file: drivers/base/platform.c
3.2 匹配时机
内核中调用上述匹配函数platform_match()函数的地方不多,只有两个,一个是early_platform_match()函数,另外一个是early_platform_left()函数:
(1)early_platform_match()
static __init struct platform_device *
early_platform_match(struct early_platform_driver *epdrv, int id)
{
struct platform_device *pd;
list_for_each_entry(pd, &early_platform_device_list, dev.devres_head)
if (platform_match(&pd->dev, &epdrv->pdrv->driver))
if (pd->id == id)
return pd;
return NULL;
}
// @file: drivers/base/platform.c
(2)early_platform_left()
static __init int early_platform_left(struct early_platform_driver *epdrv,
int id)
{
struct platform_device *pd;
list_for_each_entry(pd, &early_platform_device_list, dev.devres_head)
if (platform_match(&pd->dev, &epdrv->pdrv->driver))
if (pd->id >= id)
return 1;
return 0;
}
// @file: drivers/base/platform.c
分析上述两个函数可知道,内核通过遍历所有平台设备和平台驱动来进行匹配。
4 总结
平台设备台通常是集体注册的,而平台驱动则各自独立注册。
参考资料
[1]平台设备驱动总结
[2]linux平台设备驱动架构详解 Linux Platform Device and Driver
[3]Linux驱动开发入门与实战(2)