Linux2.6的设备驱动模型中,关心总线、设备和驱动3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,在系统每次注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
Linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动称为platform_driver。
platform_device结构体:
-
struct platform_device {
-
const char * name;
-
u32 id;
-
struct device dev;
-
u32 num_resources;
-
struct resource * resource;
-
};
platform_driver结构体:
-
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;
-
};
系统中为platform总线定义了一个bus_type的实例platform_bus_type:
-
struct bus_type platform_bus_type = {
-
.name = "platform",
-
.dev_attrs = platform_dev_attrs,
-
.match = platform_match,
-
.uevent = platform_uevent,
-
.suspend = platform_suspend,
-
.suspend_late = platform_suspend_late,
-
.resume_early = platform_resume_early,
-
.resume = platform_resume,
-
};
下面就以2.6.22.6内核和DM9000为例,介绍下platform总线、设备与驱动在代码中的实现,DM9000驱动部分,依照《嵌入式Linux应用开发完全手册》进行配置,配置如下:
arch\arm\plat-s3c24xx\common-smdk.c文件中添加,对应的是platform_device这个结构体:
-
#include <linux/dm9000.h>
-
-
static struct resource s3c_dm9k_resource[] = {
-
[0] = {
-
.start = S3C2410_CS4, //ADDR2 = 0 ,发送地址时使用这个地址
-
.end = S3C2410_CS4 + 3,
-
.flags = IORESOURCE_MEM,
-
},
-
[1] = {
-
.start = S3C2410_CS4 + 4, //ADDR2 = 1 ,发送数据时使用这个地址
-
.end = S3C2410_CS4 + 4 + 3,
-
.flags = IORESOURCE_MEM,
-
},
-
[2] = {
-
.start = IRQ_EINT7, //IRQ_DM9000
-
.end = IRQ_EINT7, //IRQ_DM9000
-
.flags = IORESOURCE_IRQ,
-
}
-
-
};
-
-
static struct dm9000_plat_data s3c_dm9k_platdata = {
-
.flags = DM9000_PLATF_16BITONLY, //数据总线宽度为16
-
};
-
-
static struct platform_device s3c_device_dm9k = {
-
.name = "dm9000",
-
.id = 0,
-
.num_resources = ARRAY_SIZE(s3c_dm9k_resource),
-
.resource = s3c_dm9k_resource,
-
.dev = {
-
.platform_data = &s3c_dm9k_platdata,
-
}
-
};
-
-
-
static struct platform_device __initdata *smdk_devs[] = {
-
……
-
&s3c_device_dm9k,
-
};
那么很显然有platform_device就对应应该有个platform_driver,其在dm9000.c文件中:
-
static struct platform_driver dm9000_driver = {
-
.driver = {
-
.name = "dm9000",
-
.owner = THIS_MODULE,
-
},
-
.probe = dm9000_probe,
-
.remove = dm9000_drv_remove,
-
.suspend = dm9000_drv_suspend,
-
.resume = dm9000_drv_resume,
-
};
那么具体去加载platform_device和platform_driver到函数是什么呢?在内核代码中可以找到:
-
static int __init dm9000_init(void)
-
{
-
……
-
return platform_driver_register(&dm9000_driver); /* search board and register */
-
}
-
module_init(dm9000_init);
-
-
void __init smdk_machine_init(void)
-
{
-
……
-
platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));
-
……
-
}
有一点需要注明:
对platform_device的定义通常是在BSP的板文件中实现,在板文件中,platform_device归纳为一个数组,最终通过platform_add_devices(struct platform_device **devs, int num)函数统一注册。
接下来就分析下platform_device_register和platform_driver_register这两个函数的流程:
-
platform_device_register(devs[i])
-
platform_device_add(pdev);
-
device_add(&pdev->dev);
-
bus_add_device(dev) //将设备加载到总线
-
bus_attach_device(dev); //在总线上查找对应的驱动
-
device_attach(dev);
-
bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
-
while ((drv = next_driver(&i)) && !error)
-
error = fn(drv, data); //fn --> __device_attach
-
driver_probe_device(drv, dev);
-
if (drv->bus->match && !drv->bus->match(dev, drv))
-
goto done;
-
really_probe(dev, drv);
-
drv->probe(dev);
-
-
-
platform_driver_register(struct platform_driver *drv)
-
drv->driver.bus = &platform_bus_type;
-
driver_register(&drv->driver)
-
bus_add_driver(drv);
-
driver_attach(drv);
-
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
-
while ((dev = next_device(&i)) && !error)
-
error = fn(dev, data); //fn --> __driver_attach
-
driver_probe_device(drv, dev);
-
if (drv->bus->match && !drv->bus->match(dev, drv))
-
goto done;
-
really_probe(dev, drv);
-
drv->probe(dev);
drv->bus->match对应的就视platform_bus_type实例中的platform_match函数:
-
static int platform_match(struct device * dev, struct device_driver * drv)
-
{
-
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
-
-
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0); //也就是说匹配platform设备和驱动是根据两者的name字段是否相同。
-
}
当总线匹配到了相应的设备和驱动,则就会运行drv->probe(dev);
这个对应的就是开发者自行定义的probe函数,在此处就是指dm9000_driver实例中dm9000_probe函数,函数中会对dm9000进行相应的探测和初始化操作,并在最后运行register_netdev(ndev);在内核中注册一个网络设备,这样,系统运行后会调用到dm9000_open函数申请相应的中断和初始化操作,这样网络设备dm9000就完成了全部的初始化操作,并可以使用了。
platform总线的引入,方便了设备和驱动的管理与匹配,并能完成相应设备的自举和初始化操作,整个过程清晰明了。
resource这个结构体需要说明下:
-
struct resource {
-
resource_size_t start;
-
resource_size_t end;
-
const char *name;
-
unsigned long flags;
-
struct resource *parent, *sibling, *child;
-
};
resource 结构体主要是表明设备的物理资源是那些,通过flags来区别是内存地址还是中断资源,或是别的。这些资源会在probe中进行调用,dm9000_probe中使用如下:
-
db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-
db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
具体的代码就不去分析了,比较简单。
阅读(1173) | 评论(0) | 转发(1) |