Linux驱动(四)platform总线匹配过程

发布时间:2024年01月12日

1、前言

在上一篇文章Linux驱动(三)platform总线驱动中讲解了platform的驱动框架,设备与驱动相匹配后会调用驱动的probe函数,设备与驱动的匹配则是platform总线中的match函数实现的,本文将详细讲解match函数的匹配过程。

2、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,
};

3、匹配过程

3.1 匹配优先级

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,用于根据结构体成员的地址来获取整个结构体本身的地址。

3.2?driver_override匹配

如果设置了driver_override,即driver_override不为NULL,那么只会进行driver_override的匹配,其优先级是最高的:

3.3?设备树匹配

设备树的匹配从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信息是通过解析设备树的节点得到的:

3.4?ACPI匹配

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);
}

跟踪代码,其流程大致如下,后面的联合体内容较多未全部展开:

3.5?id_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;
}

3.6?名称匹配

名称匹配代码如下,仅通过名称判定:

return (strcmp(pdev->name, drv->name) == 0);

汇总所有优先级的图示如下:

4、总结

match函数的整体匹配流程基本上可以由上图表示,分析过程中有错误的地方欢迎评论指出。

文章来源:https://blog.csdn.net/cesheng3410/article/details/135532665
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。