一般的外设驱动是通过module_init();module_exit();加载的,这种情况下是直接加载驱动,没有设备和驱动的匹配过程。
linux总线、设备、驱动模型:驱动只管驱动,设备只管设备,总线则负责匹配设备和驱动。
主机控制器驱动和外设驱动分离
分层设计
在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
虚拟总线:platform总线 ,相应的设备是platform_device,驱动是platform_driver
platform_device是linux系统提供的一种附加手段,例如,我们通常把SOC内部集成的I2C,RTC,LCD等控制器都归纳为platform_device,而他们本身就是字符设备。
/**
* platform_match - bind platform device to platform driver.
* @dev: device.
* @drv: driver.
*
* Platform device IDs are assumed to be encoded like this:
* "", where is a short description of the type of
* device, like "pci" or "floppy", and is the enumerated
* instance of the device, like '0' or '42'. Driver IDs are simply
* "". So, extract the from the platform_device structure,
* and compare it against the name of the driver. Return whether they match
* or not.
*/
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);
/* When driver_override is set, only bind to the matching driver */
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try ACPI style match */
if (acpi_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);
}
从以上信息可以知道, bind platform device to platform driver,先有平台设备,然后再去找平台驱动,然后返回匹配结果。
linux 3.x之后,ARM Linux不太喜欢以编码的形式去填写platform_device和注册,而倾向于根据设备树种的内容自动展开platform_device。
platform_driver 通过 module_platform_driver宏所定义的模块加载和卸载函数
#define module_platform_driver(__platform_driver) \
module_driver(__platform_driver, platform_driver_register, \
platform_driver_unregister)
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);
1. platform_bus_type--总线先被kenrel注册。
2. 系统初始化过程中调用platform_add_devices或者platform_device_register,将平台设备(platform devices)注册到平台总线中(platform_bus_type)
3. 平台驱动(platform driver)与平台设备(platform device)的关联是在platform_driver_register或者driver_register中实现,一般这个函数在驱动的初始化过程调用。
通过这三步,就将平台总线,设备,驱动关联起来。
1. platform bus先被kenrel注册。
------------------------------------------------------
do_basic_setup() -->-driver_init() -->-platform_bus_init()-->bus_register()
2. 系统初始化过程中调用platform_add_devices或者platform_device_register,将平台设备(platform devices)注册到平台总线中(platform_bus_type)
------------------------------------------------------
系统启动阶段,总线的驱动链表还是空的,所以启动阶段的platform_add_devices()只负责将设备添加到总线的设备链表上。
使用设备树指定硬件资源:
驱动程序也分为两部分(platform_driver, 设备树*.dts)
在设备树*.dts中指定硬件资源, dts被编译为dtb文件, 在启动单板时会将dtb文件传给内核,
内核根据dtb文件分配/设置/注册多个platform_device
platform_driver的编写方法跟"总线设备驱动模型"一样。
"来自dts的platform_device结构体" 与 "我们写的platform_driver" 的匹配过程:
"来自dts的platform_device结构体"里面有成员".dev.of_node", 它里面含有各种属性, 比如 compatible, reg, pin
"我们写的platform_driver"里面有成员".driver.of_match_table", 它表示能支持哪些来自于dts的platform_device
如果"of_node中的compatible" 跟 "of_match_table中的compatible" 一致, 就表示匹配成功, 则调用 platform_driver中的probe函数;
在probe函数中, 可以继续从of_node中获得各种属性来确定硬件资源
platform_device:
platform_driver: