Skip to content

LV015-总线的匹配

平台总线怎么完成平台设备和平台驱动的匹配的?

一、平台总线匹配

在前面学习总线的时候,我们知道,在编写总线驱动的时候都会实现一个 xxx_match()函数来完成驱动和设备的匹配,平台总线也属于总线,内核已经帮我们实现好了这个函数:platform_match()。接下来就来了解一下。

1. platform_match()

platform_match() 函数定义如下:

c
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);
}
image-20250119100853784

这里调用了 to_platform_device()to_platform_driver() 宏。这两个宏定义的原型如下:

c
#define to_platform_device(x)     (container_of((x), struct platform_device, dev)
#define to_platform_driver(drv)   (container_of((drv), struct platform_driver, driver))

其中, to_platform_device()to_platform_driver() 实现了对 container_of() 的封装, dev、driver 分别作为 struct platform_devicestruct platform_driver 的成员变量, 通过 container_of() 宏可以获取到正在进行匹配的 platform_driver 和 platform_device 。

image-20250119101404739

platform 总线提供了 四种匹配方式,并且这四种方式存在着优先级:设备树机制 > ACPI 匹配模式 > id_table 方式 > 字符串比较。

虽然匹配方式有这么多种,但是并没有涉及到任何复杂的算法,都只是在匹配的过程中,比较一下设备和驱动提供的某个成员的字符串是否相同。 设备树是一种描述硬件的数据结构,它用一个非 C 语言的脚本来描述这些硬件设备的信息,后面会详细学习。驱动和设备之间的匹配时通过比较 compatible 的值。 acpi 主要是用于电源管理,基本上用不到,这里就暂时不去了解了。

2. 四种匹配方式

其实算起来算是 5 种匹配方式,并且有有优先级:

  • 方式一:最先比较是否强制选择某个 driver
  • 方式二:设备树匹配
  • 方式三:ACPI 匹配
  • 方式四:id 匹配
  • 方式五:name 匹配

2.1 方式一:是否强制选择某个 driver

image-20250119105029722

这种方式比较的是 platform_device.driver_overrideplatform_driver.driver. name。可以设置 platform_device 的 driver_override,强制选择某个 platform_driver。这里就暂时不深入了解了。

2.2 方式二:设备树匹配

这个后面学习设备树的时候再学习,这里先简单了解一下。

image-20250119104932229

设备树匹配比较的是 platform_device.dev. of_nodeplatform_driver.driver. of_match_table。由设备树节点转换得来的 platform_device 中,含有一个结构: struct device_node。它的类型如下:

c
struct device_node {
	const char *name; // 来自节点的 name 属性
	const char *type; // 来自节点的 device_type 属性
	//......
	struct	property *properties;// 含有 compatible 属性
	//......
};

如果一个 platform_driver 支持设备树,它的 platform_driver.driver. of_match_table 是一个数组,类型为 struct of_device_id

c
/*
 * Struct used for matching a device
 */
struct of_device_id {
	char	name[32];
	char	type[32];
	char	compatible[128];
	const void *data;
};

使用设备树信息来判断 dev 和 drv 是否配对时 :

(1)首先,如果 of_match_table 中含有 compatible 值,就跟 dev 的 compatile 属性比较,若一致则成功,否则返回失败;

(2)其次,如果 of_match_table 中含有 type 值,就跟 dev 的 device_type 属性比较,若一致则成功,否则返回失败;

(3)最后,如果 of_match_table 中含有 name 值,就跟 dev 的 name 属性比较,若一致则成功,否则返回失败。而设备树中建议不再使用 devcie_type 和 name 属性,所以基本上只使用设备节点的 compatible 属性来寻找匹配的 platform_driver。

2.3 方式三:ACPI 匹配

image-20250119105102343

ACPI(高级配置和电源接口),这里就暂时不去了解了。

2.4 方式四:id 匹配

image-20250119105133421

比较 platform_device.nameplatform_driver.id_table[i].name, id_table 中可能有多项。

platform_driver.id_table 是“platform_device_id”指针,表示该 drv 支持若干个 device,它里面列出了各个 device 的{.name, .driver_data},其中的“ name”表示该 drv 支持的设备的名字, driver_data 是些提供给该 device 的私有数据。

platform_deviceplatform_driver 中均含有“platform_device_id”指针,名字分别为 platform_device.id_entryplatform_driver.id_tableplatform_driver.id_table 将会提供一个数组,里面包含这个驱动支持的所有设备的名称,以及私有数据,当设备和驱动匹配之后,会将驱动中匹配的这个设备的名字以及驱动将要提供的私有信息 返回 给设备中的 platform_device.id_entry 成员:

驱动和设备匹配过程

所以,通过这种方式匹配的时候,platform_device.name 一定要存在于 platform_driver.id_table 指向的数组中,这样才可以完成匹配,并且匹配后,还可以获取到驱动中的一些信息。

2.5 方式五:name 匹配

image-20250119110516247

当前面的都没有匹配上时,就会使用名称匹配,就是比较 platform_device.nameplatform_driver.driver. name

名称匹配方式

通过这种方式完成匹配的话, platform_driver.id_table 需要为空或者里面没有 platform_device.name

3. 匹配方式总结

画张图来帮助理解,不考虑 ACPI 匹配方式:

image-20260120194017189

二、平台总线匹配实现 demo

1. 名称匹配 demo

1.1 demo 源码

06_platform_bus/03_name_match

1.2 开发板验证

将编译完的 sdriver_demo.ko、sdevice_demo.ko 拷贝到开发板,然后执行以下命令加载:

shell
insmod sdriver_demo.ko
insmod sdevice_demo.ko

然后就会看到以下打印信息:

image-20250120104158719

可以看到设备和驱动名称相同时,驱动中的 xxx_probe()函数执行,然后会有提示驱动和设备匹配成功。我们再看一下 /sys/bus/platform/devices 和 /sys/bus/platform/drivers:

image-20250120104604003

发现两个设备和驱动已经绑定在一起了。需要知道的是,这种匹配方式,驱动和设备的名称都是一样的,都是 sumu-sdev。

2. id 匹配 demo

2.1 demo 源码

06_platform_bus/04_id_match

2.2 开发板验证

将编译完的 sdriver_demo.ko、sdevice_demo.ko 拷贝到开发板,然后执行以下命令加载:

shell
insmod sdevice_demo.ko
insmod sdriver_demo.ko

然后就会看到以下打印信息:

image-20250120110322289

驱动中的 xxx_probe()函数执行,然后会有提示驱动和设备匹配成功。我们再看一下 /sys/bus/platform/devices 和 /sys/bus/platform/drivers:

image-20250120110453176

可以看到驱动和设备的名字已经不同了,但是两者还是成功进行了匹配。

3. 设备树匹配

这个后面学习设备树了再说。

参考资料:

一张图掌握 Linux platform 平台设备驱动框架!【建议收藏】-CSDN 博客