🔍
B站相应的视屏教程:
📌 内核:博文+视频 - 从静态绑定驱动模型到现代设备模型
主题:深入解析设备模型的数据流与匹配机制 —— 以 i.MX8M 与树莓派为例的实战对比
在上一节中,我们从驱动框架的历史演进出发,分析了早期的静态绑定驱动模型及其局限性,并逐步过渡到现代 Linux 设备模型架构。本节将聚焦于设备模型运行时的数据结构与匹配流程,结合实际平台(NXP i.MX8M 与 Raspberry Pi),从设备树的编写、设备注册、驱动匹配、probe 调用等多个角度展开,理论与实战融合讲解设备模型的本质运作方式。
📘 第一部分:设备模型的本质问题 —— 驱动如何找到设备?
在设备模型中,驱动程序不再"直接控制硬件",而是等待系统提供设备信息,再由总线驱动匹配机制完成“驱动与设备的配对”,最终执行 probe()
。
因此,理解设备模型的核心本质,就是要搞清楚:
“驱动是怎么和设备匹配的?设备又是怎么被注册到系统中的?”
我们将通过 i.MX8M 和 Raspberry Pi 两个平台,来回答这个问题。
📘 第二部分:设备节点的来源 —— 设备树(Device Tree)
✅ 什么是设备树?
设备树(DTS)是一种用于描述硬件信息的数据结构,编译为 DTB 后在内核启动初期被解析,生成内核中的 struct device_node
树形结构。内核随后根据设备树中的节点内容,注册相应的 platform_device
。
📎 i.MX8M 示例(LCDIF3 控制器)
lcdif3: lcd-controller@32fc6000 {
compatible = "fsl,imx8mp-lcdif1";
reg = <0x32fc6000 0x10000>;
interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk IMX8MP_CLK_MEDIA_APB_ROOT>, ...;
status = "okay";
};
📌 内核会根据 compatible
字符串创建一个 platform_device
,名字类似 lcdif3.32fc6000
。
📎 Raspberry Pi 示例(I2C 控制器)
i2c1: i2c@7e804000 {
compatible = "brcm,bcm2835-i2c";
reg = <0x7e804000 0x1000>;
interrupts = <2 21>;
clock-frequency = <100000>;
status = "okay";
};
设备树被内核解析后,注册为 platform_device
,后续等待匹配合适的驱动。
📘 第三部分:驱动如何声明匹配信息?
驱动需要提供一个 of_match_table
,用于告诉设备模型:“我支持哪些设备”。
static const struct of_device_id lcdifv3_dt_ids[] = {
{ .compatible = "fsl,imx8mp-lcdif1" },
{ }
};
MODULE_DEVICE_TABLE(of, lcdifv3_dt_ids);
static struct platform_driver lcdifv3_driver = {
.probe = lcdifv3_probe,
.remove = lcdifv3_remove,
.driver = {
.name = "imx-lcdifv3",
.of_match_table = lcdifv3_dt_ids,
},
};
🔍 注意:只有匹配成功,probe 才会被调用。
📘 第四部分:匹配过程是如何完成的?
✅ 匹配的参与者:
组件 | 数据结构 |
---|---|
设备 | struct platform_device |
驱动 | struct platform_driver |
匹配规则 | struct of_device_id[] |
总线中转调度器 | struct bus_type |
✅ 匹配流程
- 设备树解析阶段,生成
platform_device
(如lcdif3
) - 驱动注册时,添加到
platform_bus_type
的driver_list
- 内核自动遍历设备与驱动,调用
bus_type->match()
- 匹配成功后:
- 设置
pdev->dev.driver = &driver
- 调用
driver->probe(pdev)
完成初始化
- 设置
📘 第五部分:数据结构流动分析(从 DTS 到 probe)
📌 流程图:
DTS → of_node (设备树节点)
↓
of_platform_populate()
↓
platform_device_register()
↓
/sys/devices/platform/xxx ←→ /sys/bus/platform/devices/xxx
↓
platform_bus_type.match()
↓
platform_driver.probe()
📎 代码对应点(以 LCDIF3 为例):
关键节点 | 对应代码 |
---|---|
compatible = “fsl,imx8mp-lcdif1” | of_device_id 中匹配 |
reg/clocks 等资源 | of_address_to_resource() 等函数读取 |
probe 中访问资源 | platform_get_resource() / devm_ioremap_resource() |
📘 第六部分:i.MX8M vs 树莓派平台对比
对比维度 | NXP i.MX8M | Raspberry Pi |
---|---|---|
SoC 架构 | 多个 LCDIF 控制器 + VPU | Broadcom BCM283x |
DTS 中定义 | fsl,imx8mp-lcdif1 | brcm,bcm2835-i2c |
驱动模块名 | imx-lcdifv3 | i2c-bcm2835 |
驱动结构 | 完整 platform_driver + match | 同样采用 of_match_table 匹配 |
热插拔支持 | 支持 runtime pm / suspend / resume | 同样支持 PM、sysfs、modprobe 热加载 |
📌 虽然 SoC 不同,但设备模型使用方式完全统一。
📘 第七部分:常见问题与调试技巧
❓ Q1: 为什么 probe 没被调用?
- 没有写
of_match_table
compatible
写错,无法匹配- 驱动未被编译进内核或未加载
status = "disabled"
导致设备未注册
❓ Q2: 如何确认设备已注册?
- 查看
/sys/bus/platform/devices/
- 使用
dmesg
检查设备是否出现 - 加
dev_info()
等日志确认 probe 是否执行
❓ Q3: 如何查看匹配关系?
modinfo xxx.ko # 查看 compatible alias
ls /sys/bus/platform/drivers/xxx
📘 第八部分:实战建议
- 永远在驱动中写上正确的
of_match_table
- 使用
devm_*
系列管理资源,避免内存泄漏 - 善用
dev_dbg()
、dev_err()
等接口打印调试信息 - 多观察
/sys/
目录,理解设备与驱动的 sysfs 映射关系 - 多平台共享一个驱动时,合理利用
of_device_id.data
携带平台定制参数
✅ 总结与回顾
本篇深入分析了设备模型中从 DTS → 设备注册 → 驱动匹配 → probe 执行的完整过程。通过对比 i.MX8M 与树莓派平台,我们看到了设备模型在不同平台间的通用性与强大抽象能力。
📌 核心关键词:
- 设备树注册 → platform_device 创建
- 驱动注册 → platform_driver with of_match_table
- 内核总线 → bus_type 匹配 → 调用 probe
这正是现代 Linux 驱动开发的标准范式。
下一篇我们将从 资源管理角度(时钟、中断、寄存器、GPIO)展开,讲解 platform_get_resource()
、devm_*()
、of_property_read_*()
等函数在实际项目中的最佳使用方式。