linux当中大多数的设备都是以paltform虚拟总线挂载上去的,这里以kernel/drivers/net/dm9000.c为例子分析一下,platform设备挂在过程;
(1)device
通常在kernel/arch/arm/mach-xx/mach-xx.c 里面存放着device设备的一些信息;
static struct resource smdkv210_dm9000_resources[] = {
[0] = {
.start = S5PV210_PA_SROM_BANK5,
.end = S5PV210_PA_SROM_BANK5,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = S5PV210_PA_SROM_BANK5 + 2,
.end = S5PV210_PA_SROM_BANK5 + 2,
.flags = IORESOURCE_MEM,
},
[2] = {
.start = IRQ_EINT(9),
.end = IRQ_EINT(9),
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
},
};
static struct dm9000_plat_data smdkv210_dm9000_platdata = {
.flags = DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM,
.dev_addr = { 0x00, 0x09, 0xc0, 0xff, 0xec, 0x48 },
};
struct platform_device smdkv210_dm9000 = {
.name = "dm9000",
.id = -1,
.num_resources = ARRAY_SIZE(smdkv210_dm9000_resources),
.resource = smdkv210_dm9000_resources,
.dev = {
.platform_data = &smdkv210_dm9000_platdata,
},
};
设置为platform_device设备并分配地址和中断的信息
看看device是如何注册到paltform bus上的
static struct platform_device *smdkv210_devices[] __initdata = {
....
&smdkv210_dm9000, //放在这个列表里面
....
}
static void __init smdkv210_machine_init(void)
{
....
platform_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices));
....
}
这样通过platform_add_devices函数添加device到platform的总线上了:
drivers/base/platform.c
int platform_add_devices(struct platform_device **devs, int num)
{
int i, ret = 0;
for (i = 0; i < num; i++) {
ret = platform_device_register(devs[i]);
if (ret) {
while (--i >= 0)
platform_device_unregister(devs[i]);
break;
}
}
return ret;
}
EXPORT_SYMBOL_GPL(platform_add_devices);
platform_device_register(devs[i]) -->platform_device_add() -->device_add() 最后通过driver/base/core.c的device_add()添加
device_add()函数重要的两个函数bus_add_device(dev),bus_probe_device(dev);
(2)driver
现在看是从driver角度分析:
platform_driver_register(&dm9000_driver);
static struct platform_driver dm9000_driver = {
.driver = {
.name = "dm9000",
.owner = THIS_MODULE,
.pm = &dm9000_drv_pm_ops,
},
.probe = dm9000_probe,
.remove = __devexit_p(dm9000_drv_remove),
};
在dm9000的init函数里面都会调用platform_driver_register
一步步深入:
platform_driver_register()
--> driver_register()
--> bus_add_driver()
-->driver_attach()
-->bus_for_each_dev(..(*fn)()) //drivers/base/bus.c
-->while遍历device列表 执行__driver_attach() //drivers/base/dd.c
__driver_attach:
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
.....
if (!driver_match_device(drv, dev))
return 0;
if (dev->parent) /* Needed for USB */
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev);
device_unlock(dev);
if (dev->parent)
device_unlock(dev->parent);
return 0;
}
(1) driver_match_device()-->调用paform bus的 mach函数 platform_match 判断名字是否匹配:
kernel/drivers/base/base.h
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
应为palform也是总线在platform当中也有注册:
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
所以会调用这个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);
}
到这里就实现了device和driver的匹配,对于一个驱动 可以匹配多个设备,drvier里面就需要各一个id_table;
(2)driver_probe_device,当device和driver匹配完之后就开始 driver_probe_device--> really_probe() --> drv->probe即调用了驱动的probe 函数;
这就是platform上发生的故事,其实linux上的其他总线也遵循这样一个过程,构成了linux设备基本构架;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)