在上一篇文章Linux驱动(三)platform总线驱动中讲解了platform的驱动框架,设备与驱动相匹配后会调用驱动的probe函数,设备与驱动的匹配则是platform总线中的match函数实现的,本文将详细讲解match函数的匹配过程。
match函数原型如下:
/**
* platform_match - 将平台设备与平台驱动程序绑定。
* @dev: 设备。
* @drv: 驱动程序。
*
* 假定平台设备的ID编码方式如下:
* "<name><instance>",其中 <name> 是设备类型的简短描述,如 "pci" 或 "floppy",
* <instance> 是设备的枚举实例,如 '0' 或 '42'。驱动程序的ID只是 "<name>"。
* 因此,从 platform_device 结构中提取 <name>,并将其与驱动程序的名称进行比较。
* 返回它们是否匹配。
*/
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);
/* 当 driver_override 被设置时,只绑定到匹配的驱动程序 */
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);
/* 首先尝试 OF 风格的匹配 */
if (of_driver_match_device(dev, drv))
return 1;
/* 然后尝试 ACPI 风格的匹配 */
if (acpi_driver_match_device(dev, drv))
return 1;
/* 然后尝试与 id 表进行匹配 */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* 回退到驱动程序名称匹配 */
return (strcmp(pdev->name, drv->name) == 0);
}
在设备与驱动匹配比较的时候调用:
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
该接口在__platform_driver_register中赋值:
int __platform_driver_register(struct platform_driver *drv,
struct module *owner)
{
……
drv->driver.bus = &platform_bus_type;
……
}
其中platform_bus_type :
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
match函数中代码执行顺序则代表了匹配的优先级:
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);
if (of_driver_match_device(dev, drv))
return 1;
if (acpi_driver_match_device(dev, drv))
return 1;
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
return (strcmp(pdev->name, drv->name) == 0);
其中to_platform_device和to_platform_driver是两个宏定义,在其内部使用了另一个宏定义container_of,用于根据结构体成员的地址来获取整个结构体本身的地址。
如果设置了driver_override,即driver_override不为NULL,那么只会进行driver_override的匹配,其优先级是最高的:
设备树的匹配从of_driver_match_device开始分析,其函数调用关系如下:
of_driver_match_device(struct device *dev,const struct device_driver *drv)
-->of_match_device(drv->of_match_table, dev)
---->of_match_node(matches, dev->of_node);
------>__of_match_node(matches, node);
-------->__of_device_is_compatible(node, matches->compatible,
matches->type, matches->name);
分析内核代码可以得到设备树匹配比较的具体对象,其中device的device_node信息是通过解析设备树的节点得到的:
acpi的驱动在开发中没有使用到过,在此仅做简单分析,其代码调用如下,当没有设置acpi_match_table的时候会调用acpi_of_match_device进行匹配,设置了acpi_match_table的话会调用__acpi_match_device进行匹配:
bool acpi_driver_match_device(struct device *dev,
const struct device_driver *drv)
{
if (!drv->acpi_match_table)
return acpi_of_match_device(ACPI_COMPANION(dev),
drv->of_match_table);
return !!__acpi_match_device(acpi_companion_match(dev),
drv->acpi_match_table, drv->of_match_table);
}
跟踪代码,其流程大致如下,后面的联合体内容较多未全部展开:
id_table的匹配代码如下,通过比较pdev和id的name变量是否相等进行判定:
static const struct platform_device_id *platform_match_id(
const struct platform_device_id *id,
struct platform_device *pdev)
{
while (id->name[0]) {
if (strcmp(pdev->name, id->name) == 0) {
pdev->id_entry = id;
return id;
}
id++;
}
return NULL;
}
名称匹配代码如下,仅通过名称判定:
return (strcmp(pdev->name, drv->name) == 0);
汇总所有优先级的图示如下:
match函数的整体匹配流程基本上可以由上图表示,分析过程中有错误的地方欢迎评论指出。