赞
踩
在上一篇文章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函数的整体匹配流程基本上可以由上图表示,分析过程中有错误的地方欢迎评论指出。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。