首先来解释下平台设备驱动所涉及的一些结构:
struct resource {
resource_size_t start; //该平台设备所占用的资源起始地址
resource_size_t end; //结束地址
const char *name; //名字
unsigned long flags; //资源类型
struct resource *parent, *sibling, *child;
};
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;
};
平台设备驱动用platform_device表示平台设备,用platform_add_devices注册平台设备;用platform_driver表示平台驱动,用platform_add_devices注册平台驱动。在Linux设备驱动模型中有三个关键元素:总线-设备-驱动。将设备和驱动挂载在platform这个虚拟的总线上,当挂载在总线的设备和驱动匹配时就会调用probe方法,那设备和驱动根据什么进行匹配呢?回答这个问题前先看看内核中的看门狗设备驱动的代码。
static struct resource s3c_wdt_resource[] = {
[0] = {
.start = S3C24XX_PA_WATCHDOG, //看门狗的启示地址为0x53000000;
.end = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,//结束地址0x53100000-1;
.flags = IORESOURCE_MEM, //资源类型为IO内存
},
[1] = {
.start = IRQ_WDT, //这段没看懂
.end = IRQ_WDT,
.flags = IORESOURCE_IRQ,
}
};
struct platform_device s3c_device_wdt = {
.name = "s3c2410-wdt", //这是设备和驱动匹配的依据
.id = -1, //id为-1表示只有一个这样的设备
.num_resources = ARRAY_SIZE(s3c_wdt_resource),
.resource = s3c_wdt_resource,
};
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt, //这个结构数组将各种platform设备的地址放在数组中;
&s3c_device_i2c0, //可以看出lcd,iis,iic等设备都挂载在platform总线上的;
&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)); //从数组中取出各个平台设备结构的地址,
// 将平台设备注册进platform总线;
smdk_machine_init();
}
以上代码注册了看门狗的platform设备,接下来注册驱动;
static struct platform_driver s3c2410wdt_driver = {
.probe = s3c2410wdt_probe,//设备和驱动匹配后会调用probe方法;
.remove = __devexit_p(s3c2410wdt_remove),
.shutdown = s3c2410wdt_shutdown,
.suspend = s3c2410wdt_suspend,
.resume = s3c2410wdt_resume,
.driver = {
.owner = THIS_MODULE,
.name = "s3c2410-wdt", //这个name和platform_device的name一致即可将设备和驱动匹配起来
},
};
static int __init watchdog_init(void)
{
printk(banner);
return platform_driver_register(&s3c2410wdt_driver); //注册platform_driver;
}
static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
{
//在probe方法里进行对看门狗的操作。
//在内核中这段代码很长啊,没看懂, 没贴出来。
}
总结:
在平台设备驱动中,总线将设备和驱动绑在一起,系统每注册一个设备的时候,会寻找和它匹配的驱动,相反的,系统每注册一个驱动的时候会寻找和它匹配的设备,匹配的工作是由总线来完成的,匹配的依据多种多样,这里是匹配设备和驱动的name,在其它总线中(usb总线、i2c总线、pci总线等)有其它匹配方法。platform总线是Linux中的一种虚拟的总线,从2.6的内核版本开始引入,这种设备驱动模型思想遍布内核的每个角落。片上系统的资源如lcd、watchdog、rtc等都被当作平台设备来处理,platform机制的优点是将资源注册进内核,由内核统一管理,驱动程序中要使用这些资源时通过platform_device中提供的统一接口进行访问,这样驱动和资源相对独立了,提高了可移植性和安全性。对platform驱动的理解还不是很透彻,我想将led的驱动注册进platform总线中试试,哈哈。
----征途开始
2013.8.5晚