在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 use bus_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);