4. 平台设备与驱动匹配机制
在 Linux 设备树驱动模型中,设备树节点描述“硬件是什么”,platform_driver 描述“驱动能处理什么”,而匹配机制负责把两者对应起来。理解这条链路,是编写任何设备树驱动的前提。本节先剖析匹配流程,再用一个最小工业 GPIO 驱动验证匹配是否成功。
一、匹配的三个关键角色
设备树中的每个设备节点通常包含一个 compatible 属性,它是一个字符串列表,按“从具体到通用”的顺序排列。例如:
gpio_led: led@0 {
compatible = "demo,industrial-gpio", "demo,gpio-generic";
reg = <0x00 0x10>;
gpios = <&gpio1 12 GPIO_ACTIVE_HIGH>;
status = "okay";
};
内核在启动时会把这些节点转换为 platform_device,并把 compatible 列表保存到设备的 of_node 中。驱动侧则通过 of_match_table 声明自己支持的 compatible 字符串:
static const struct of_device_id demo_gpio_of_match[] = {
{ .compatible = "demo,industrial-gpio" },
{ .compatible = "demo,gpio-generic" },
{ }
};
MODULE_DEVICE_TABLE(of, demo_gpio_of_match);
当驱动注册到 platform 总线时,总线核心会遍历设备链表,对每个设备调用匹配函数。匹配的核心逻辑是:取设备的 of_node,用 of_match_node() 在驱动的 of_match_table 中查找 compatible 是否命中。命中后,platform_driver 的 probe() 被调用,驱动开始接管硬件。
二、匹配优先级与常见误区
platform 总线的匹配顺序是:先尝试 of_match_table,再尝试 acpi_match_table,最后回退到 id_table 或驱动名与设备名直接比较。在设备树场景下,of_match_table 是主路径。需要注意几个常见问题:
compatible字符串必须与驱动中的完全一致,包括厂商前缀和逗号,大小写敏感。- 设备节点的
status必须为"okay",否则内核不会创建对应的 platform_device。 - 驱动模块必须已加载或已编译进内核,否则匹配表根本不存在。
- 如果多个驱动声明了相同的 compatible,先注册的驱动会先匹配,后注册的无法接管。
三、最小工业 GPIO 驱动实现
下面实现一个最小 platform 驱动,它匹配 "demo,industrial-gpio",在 probe 中获取 GPIO 并输出当前电平,用于验证匹配链路是否打通。
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h>
struct demo_gpio_priv {
struct gpio_desc *gpio;
};
static int demo_gpio_probe(struct platform_device *pdev)
{
struct demo_gpio_priv *priv;
int val;
dev_info(&pdev->dev, "probe: matched node %s\n",
pdev->dev.of_node->name);
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_IN);
if (IS_ERR(priv->gpio)) {
dev_err(&pdev->dev, "failed to get gpio\n");
return PTR_ERR(priv->gpio);
}
val = gpiod_get_value(priv->gpio);
dev_info(&pdev->dev, "gpio value = %d\n", val);
platform_set_drvdata(pdev, priv);
return 0;
}
static int demo_gpio_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "removed\n");
return 0;
}
static const struct of_device_id demo_gpio_of_match[] = {
{ .compatible = "demo,industrial-gpio" },
{ }
};
MODULE_DEVICE_TABLE(of, demo_gpio_of_match);
static struct platform_driver demo_gpio_driver = {
.probe = demo_gpio_probe,
.remove = demo_gpio_remove,
.driver = {
.name = "demo-industrial-gpio",
.of_match_table = demo_gpio_of_match,
},
};
module_platform_driver(demo_gpio_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Minimal industrial GPIO platform driver");
四、验证匹配是否成功
将上述驱动编译为模块并加载后,可通过以下方式验证:
- 查看
/sys/bus/platform/drivers/demo-industrial-gpio/目录下是否出现设备节点链接。 - 执行
dmesg | grep demo,应看到probe: matched node led和gpio value = 1等日志。 - 若没有 probe 日志,先检查
/proc/device-tree/下对应节点是否存在,再确认compatible是否与驱动一致。
of_match_node() 的返回值判断,或在 probe 入口打印 pdev->dev.of_node->name,确认驱动确实拿到了设备树节点。若节点存在但 probe 未执行,多半是 compatible 不匹配或驱动未注册。
掌握这条匹配链路后,后续任何工业外设驱动都可以沿用同一套骨架:设备树声明 compatible,驱动声明 of_match_table,probe 中获取资源并初始化。匹配是入口,probe 是落地,两者缺一不可。