I2C设备与驱动的关联作者:leeoo 联系方式:neu_linuxer@在Linux操作系统中,驱动程序的加载分为两种:内核启动时自动加载和用户手动加载;硬件设备也可以采用两种方式添加到系统中:在系统启动前及系统运行时的热插拨。
下面,我们以arm体系结构下的at91处理器中的I2C控制器为例,介绍一下硬件设备及相关的驱动程序是如何绑定及松绑的。
1.平台驱动注册过程1.1 at91_i2c_init()函数在文件drivers/i2c/busses/i2c-at91.c中,定义了结构体struct platform_driver并进行了初始化,通过使用module_init()宏进行声明,当模块被加载到内核时会调用 at91_i2c_init()函数。
在此函数中,调用了platform_driver_register()函数来完成注册。
static struct platform_driver at91_i2c_driver = {.probe = at91_i2c_probe,.remove = __devexit_p(at91_i2c_remove),.suspend = at91_i2c_suspend,.resume = at91_i2c_resume,.driver = {.name = "at91_i2c",.owner = THIS_MODULE,},};static int __init at91_i2c_init(void){return platform_driver_register(&at91_i2c_driver);}1.2 platform_driver_register()函数在文件drivers/base/platform.c中,实现并导出了platform_driver_register()函数,以便使其他模块中的函数可以调用此函数。
它在完成简单的包装后,调用了driver_register()函数,完成了从平台实现到Linux内核实现的过渡。
在此,我们需要关注一下platform_match()和platform_drv_probe()函数。
platform_match() 函数确定驱动与设备的关联,而platform_drv_probe()函数会在随后介绍的函数中被调用。
//比较驱动信息中的name与设备信息中的name两者是否一致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);}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,};EXPORT_SYMBOL_GPL(platform_bus_type);/*** platform_driver_register* @drv: platform driver structure*/int platform_driver_register(struct platform_driver *drv){drv->driver.bus = &platform_bus_type;//在really_probe函数中,回调了platform_drv_probe函数if (drv->probe)drv->driver.probe = platform_drv_probe;if (drv->remove)drv->driver.remove = platform_drv_remove;if (drv->shutdown)drv->driver.shutdown = platform_drv_shutdown;if (drv->suspend)drv->driver.suspend = platform_drv_suspend;if (drv->resume)drv->driver.resume = platform_drv_resume;return driver_register(&drv->driver);}EXPORT_SYMBOL_GPL(platform_driver_register);1.3 driver_register()函数在文件drivers/base/driver.c中,实现了driver_register()函数。
在此函数中,初始化结构体struct device_driver中的klist_device和unloaded字段,通过klist_device字段,可以保存此驱动支持的设备链表,通过“完成”接口机制,完成线程间的同步。
链表和“完成”接口的详细信息可以参考文献[1]。
返回bus_add_driver()函数的运行结果。
/*** driver_register - register driver with bus* @drv: driver to register** We pass off most of the work to the bus_add_driver() call,* since most of the things we have to do deal with the bus* structures.** The one interesting aspect is that we setup @drv->unloaded* as a completion that gets complete when the driver reference* count reaches 0.*/int driver_register(struct device_driver * drv){if ((drv->bus->probe && drv->probe) ||(drv->bus->remove && drv->remove) ||(drv->bus->shutdown && drv->shutdown)) {printk(KERN_WARNING "Driver '%s' needs updating - please usebus_type methods\n", drv->name);}klist_init(&drv->klist_devices, NULL, NULL);init_completion(&drv->unloaded);return bus_add_driver(drv);}1.4 bus_add_driver()函数在文件drivers/base/bus.c中实现了bus_add_driver()函数,它通过语句klist_add_tail(&drv->knode_bus, &bus->klist_drivers); 将驱动信息保存到总线结构中,在设备注册过程中,我们就可以明白此语句的作用了。
在此语句之前,调用了driver_attach()函数。
/*** bus_add_driver - Add a driver to the bus.* @drv: driver.**/int bus_add_driver(struct device_driver *drv){struct bus_type * bus = get_bus(drv->bus);int error = 0;if (!bus)return 0;pr_debug("bus %s: add driver %s\n", bus->name, drv->name);error = kobject_set_name(&drv->kobj, "%s", drv->name);if (error)goto out_put_bus;drv->kobj.kset = &bus->drivers;if ((error = kobject_register(&drv->kobj)))goto out_put_bus;error = driver_attach(drv);if (error)goto out_unregister;klist_add_tail(&drv->knode_bus, &bus->klist_drivers);module_add_driver(drv->owner, drv);error = driver_add_attrs(bus, drv);if (error) {/* How the hell do we get out of this pickle? Give up */printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",__FUNCTION__, drv->name);}error = a dd_bind_files(drv);if (error) {/* Ditto */printk(KERN_ERR "%s: add_bind_files(%s) failed\n",__FUNCTION__, drv->name);}return error;out_unregister:kobject_unregister(&drv->kobj);out_put_bus:put_bus(bus);return error;}1.5 dd.c文件在文件drivers/base/dd.c中,实现了设备与驱动交互的核心函数。