邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛
分类: LINUX
2015-11-16 16:40:35
平台总线设备驱动程序采用了分层分离的机制,我们先简述一下其大体上的原理,然后在从具体代码出发进行分析。
大体原理:在内核中存在平台设备总线,我们要把设备挂载到总线设备列表中,同时也要把驱动挂在到总线驱动列表中,但是相匹配的设备和驱动的名字要保持一致,因为总线会根据设备和驱动的名字是否一致,来决定他们是否匹配,一旦设备和驱动匹配起来,就会调用驱动程序里的probe函数进行处理。
代码分析,以/dirvers/mtd/nand/s3c2410.c为例来分析:
platform_driver_register(&s3c2410_nand_driver);//驱动名为s3c2410-nand
drv->driver.bus = &platform_bus_type;//platform_bus_type里存在match函数,待会会分析
driver_register(&drv->driver);
bus_add_driver(drv);//将驱动添加到平台总线驱动列表中
我们搜索名“s3c2410-nand”的设备,最终在/arch/arm/plat-s3c24xx/devs.c中发现了线索:
struct platform_device s3c_device_nand = {
.name = "s3c2410-nand",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource,
};
那么这个设备是如何添加到总线上的呢?
.init_machine = osiris_init,
platform_add_devices(osiris_devices, ARRAY_SIZE(osiris_devices));//关于osiris_devices的定义见注释1
platform_device_register(devs[i]);
platform_device_add(pdev);
device_add(&pdev->dev);//将设备添加到总线
这样设备和驱动都注册到了总线上面,那么总线要做什么呢?
我们还记得注册驱动的时候有这么一句吧:
drv->driver.bus = &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,
};
这里面有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);
}
我们看到了,确实是根据驱动和设备的名字是否一致,来判断是否匹配的。
一旦匹配就会调用probe函数。
注释1:
static struct platform_device *osiris_devices[] __initdata = {
&s3c_device_i2c,
&s3c_device_wdt,
&s3c_device_nand,
&osiris_pcmcia,
};
platform_driver_register驱动注册函数分析:
platform_driver_register(struct platform_driver *drv)
drv->driver.bus = &platform_bus_type;
drv->driver.probe = platform_drv_probe;
drv->driver.remove = platform_drv_remove;
drv->driver.shutdown = platform_drv_shutdown;
driver_register(&drv->driver);
bus_add_driver(drv);
//将drv加入平台总线下的驱动链表
kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name);
//下面的代码试图将drv绑定到dev
driver_attach(drv);
//对平台总线下设备链表进行遍历,调用__driver_attach以找到与drv匹配的设备
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
__driver_attach
driver_match_device(drv, dev)
//调用drv->bus->match来判断drv与设备是否匹配
drv->bus->match ? drv->bus->match(dev, drv) : 1;
platform_match
//根据dev和drv的名字是否一致来判断是否匹配
strcmp(pdev->name, drv->name)
//如果匹配继续执行
driver_probe_device(drv, dev);
really_probe(dev, drv);
//我们看到调用了drv里面注册的probe函数
drv->probe(dev);
platform_device_registe设备注册函数分析:
platform_device_register(struct platform_device *pdev)
platform_device_add(pdev);
device_add(&pdev->dev);
//将dev加入平台总线下的设备链表
bus_add_device(dev);
bus_probe_device(dev);
//对平台总线下驱动链表进行遍历,调用 __device_attach以找到与dev匹配的驱动
bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
__device_attach
driver_match_device(drv, dev)
//依旧是调用drv->bus->match来找到名字一样的设备与驱动
drv->bus->match ? drv->bus->match(dev, drv) : 1;
platform总线注册分析:
我们从入口函数head.S说起:
b start_kernel
rest_init();
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
kernel_init
do_basic_setup();
driver_init();
platform_bus_init();
bus_register(&platform_bus_type);
如此一来这个总线、设备、驱动模型就很清楚了,我们再来总结一下:
首先在系统启动的时候会注册平台总线,当我们注册设备的时候,就会将该设备加入到平台总线旗下的驱动列表中,并且遍历驱动看是否有跟设备名字相同的驱动,如果有的话,就会调用驱动的probe函数。同样在注册驱动的时候,会将该驱动加入到平台总线旗下的驱动列表中,并且遍历设备看是否有跟驱动名字相同的设备,如果有的话,就会调用驱动的probe函数。
所有编写平台总线、设备、驱动模型的驱动程序,最起始的工作要在probe函数里面完成!