LV015-总线的匹配
平台总线怎么完成平台设备和平台驱动的匹配的?
一、平台总线匹配
在前面学习总线的时候,我们知道,在编写总线驱动的时候都会实现一个 xxx_match()函数来完成驱动和设备的匹配,平台总线也属于总线,内核已经帮我们实现好了这个函数:platform_match()。接下来就来了解一下。
1. platform_match()
platform_match() 函数定义如下:
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);
}这里调用了 to_platform_device() 和 to_platform_driver() 宏。这两个宏定义的原型如下:
#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_device、struct platform_driver 的成员变量, 通过 container_of() 宏可以获取到正在进行匹配的 platform_driver 和 platform_device 。

platform 总线提供了 四种匹配方式,并且这四种方式存在着优先级:设备树机制 > ACPI 匹配模式 > id_table 方式 > 字符串比较。
虽然匹配方式有这么多种,但是并没有涉及到任何复杂的算法,都只是在匹配的过程中,比较一下设备和驱动提供的某个成员的字符串是否相同。 设备树是一种描述硬件的数据结构,它用一个非 C 语言的脚本来描述这些硬件设备的信息,后面会详细学习。驱动和设备之间的匹配时通过比较 compatible 的值。 acpi 主要是用于电源管理,基本上用不到,这里就暂时不去了解了。
2. 四种匹配方式
其实算起来算是 5 种匹配方式,并且有有优先级:
- 方式一:最先比较是否强制选择某个 driver
- 方式二:设备树匹配
- 方式三:ACPI 匹配
- 方式四:id 匹配
- 方式五:name 匹配
2.1 方式一:是否强制选择某个 driver

这种方式比较的是 platform_device.driver_override 和 platform_driver.driver. name。可以设置 platform_device 的 driver_override,强制选择某个 platform_driver。这里就暂时不深入了解了。
2.2 方式二:设备树匹配
这个后面学习设备树的时候再学习,这里先简单了解一下。

设备树匹配比较的是 platform_device.dev. of_node 和 platform_driver.driver. of_match_table。由设备树节点转换得来的 platform_device 中,含有一个结构: struct device_node。它的类型如下:
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:
/*
* 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 匹配

ACPI(高级配置和电源接口),这里就暂时不去了解了。
2.4 方式四:id 匹配

比较 platform_device.name 和 platform_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_device 和 platform_driver 中均含有“platform_device_id”指针,名字分别为 platform_device.id_entry 和 platform_driver.id_table 。platform_driver.id_table 将会提供一个数组,里面包含这个驱动支持的所有设备的名称,以及私有数据,当设备和驱动匹配之后,会将驱动中匹配的这个设备的名字以及驱动将要提供的私有信息 返回 给设备中的 platform_device.id_entry 成员:

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

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

通过这种方式完成匹配的话, platform_driver.id_table 需要为空或者里面没有 platform_device.name 。
3. 匹配方式总结
画张图来帮助理解,不考虑 ACPI 匹配方式:

二、平台总线匹配实现 demo
1. 名称匹配 demo
1.1 demo 源码
1.2 开发板验证
将编译完的 sdriver_demo.ko、sdevice_demo.ko 拷贝到开发板,然后执行以下命令加载:
insmod sdriver_demo.ko
insmod sdevice_demo.ko然后就会看到以下打印信息:

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

发现两个设备和驱动已经绑定在一起了。需要知道的是,这种匹配方式,驱动和设备的名称都是一样的,都是 sumu-sdev。
2. id 匹配 demo
2.1 demo 源码
2.2 开发板验证
将编译完的 sdriver_demo.ko、sdevice_demo.ko 拷贝到开发板,然后执行以下命令加载:
insmod sdevice_demo.ko
insmod sdriver_demo.ko然后就会看到以下打印信息:

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

可以看到驱动和设备的名字已经不同了,但是两者还是成功进行了匹配。
3. 设备树匹配
这个后面学习设备树了再说。
参考资料: