4. Platform 驱动模型:Platform 总线、Platform 设备、Platform 驱动、匹配机制
Platform 驱动模型是 Linux 内核中用于管理那些不依赖传统物理总线(如 PCI、USB、I2C)的设备的软件架构。它通过虚拟的 Platform 总线,将硬件设备信息与驱动程序解耦,使得驱动代码可以专注于功能逻辑,而设备描述则通过设备树或板级文件提供。本章将深入剖析 Platform 总线的核心组件与匹配机制。
核心概念:Platform 模型本质上是一种软件总线,它模拟了物理总线的设备发现与驱动绑定流程,是 Linux 设备驱动模型(Device Driver Model)在嵌入式系统中的关键实现。
4.1 Platform 模型的三要素
Platform 驱动模型由三个核心部分组成,它们协同工作,完成设备的注册、匹配与驱动绑定。
| 要素 | 内核数据结构 | 主要职责 | 注册/注销 API |
|---|---|---|---|
| Platform 总线 | struct bus_type platform_bus_type |
维护设备与驱动链表,执行匹配算法,协调 Probe 流程 | 内核自动初始化,无需手动注册 |
| Platform 设备 | struct platform_device |
描述硬件资源(地址、中断、DMA、时钟等),代表一个物理或虚拟设备 | platform_device_register() / platform_device_unregister() |
| Platform 驱动 | struct platform_driver |
实现设备的具体操作逻辑(Probe、Remove、PM 回调),提供 id_table 用于匹配 |
platform_driver_register() / platform_driver_unregister() |
关键理解:Platform 总线是内核中一个全局唯一的
bus_type 实例。当一个新的 Platform 设备或 Platform 驱动注册到内核时,总线会触发一次匹配扫描,寻找彼此合适的搭档。
4.2 Platform 总线:虚拟的硬件连接器
Platform 总线并不对应任何物理硬件连线,它只是一个软件抽象层。其核心作用体现在 bus_type 结构体的 match 函数指针上。当设备或驱动注册时,内核会遍历总线上的设备链表和驱动链表,调用 match 函数进行配对。
// 内核源码中的 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,
};
Platform 总线的工作流程可以概括为:
- 注册触发:调用
platform_device_register()或platform_driver_register()。 - 总线扫描:内核将新对象加入对应链表,并调用
bus_for_each_dev()或bus_for_each_drv()进行遍历。 - 匹配执行:对每一对 (device, driver),调用
platform_match()函数。 - 绑定 Probe:匹配成功,则调用驱动的
.probe()方法,将设备资源传递给驱动。
4.3 Platform 设备:硬件资源的载体
struct platform_device 是内核描述一个 Platform 设备的核心结构。它包含了设备的名称、ID、以及最重要的——资源(Resource)。资源通过 struct resource 数组描述,涵盖了内存地址范围、中断号、DMA 通道等硬件关键信息。
// struct platform_device 关键成员
struct platform_device {
const char *name; // 设备名称,用于名称匹配
int id; // 设备实例 ID,-1 表示单实例
struct device dev; // 内嵌的通用设备结构
u32 num_resources; // 资源数量
struct resource *resource; // 指向资源数组的指针
const struct platform_device_id *id_entry; // 匹配到的 ID 表项
/* ... 其他成员 */
};
// struct resource 定义
struct resource {
resource_size_t start; // 起始地址(物理地址或中断号)
resource_size_t end; // 结束地址
const char *name; // 资源名称
unsigned long flags; // 资源类型标志(IORESOURCE_MEM, IORESOURCE_IRQ 等)
struct resource *parent, *sibling, *child;
};
注意:在设备树环境下,Platform 设备通常由内核在解析设备树时动态创建。驱动开发者不需要手动调用
platform_device_register(),而是通过设备树节点中的 compatible 属性与驱动匹配。资源(reg、interrupts 等)也会被内核自动解析并填充到 platform_device 的 resource 数组中。
4.4 Platform 驱动:功能逻辑的实现者
struct platform_driver 定义了驱动需要实现的核心回调函数。其中 .probe() 是驱动被匹配成功后的入口,驱动在此函数中获取资源、初始化硬件、注册字符设备或框架接口。
// struct platform_driver 定义
struct platform_driver {
int (*probe)(struct platform_device *); // 匹配成功后的初始化
int (*remove)(struct platform_device *); // 设备移除时的清理
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver; // 内嵌的通用驱动结构
const struct platform_device_id *id_table; // 非设备树匹配时的 ID 表
/* ... */
};
一个典型的 Platform 驱动注册模板如下:
// 驱动 Probe 函数
static int my_driver_probe(struct platform_device *pdev)
{
struct resource *res;
void __iomem *base;
int irq;
// 1. 获取内存资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENXIO;
// 2. 请求并映射 IO 内存
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
// 3. 获取中断资源
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
// 4. 注册中断处理函数
devm_request_irq(&pdev->dev, irq, my_isr, 0, "my_device", dev);
dev_info(&pdev->dev, "Driver probed successfully\n");
return 0;
}
// 驱动移除函数
static int my_driver_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "Driver removed\n");
return 0;
}
// 设备树匹配表
static const struct of_device_id my_of_match[] = {
{ .compatible = "vendor,my-device", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_of_match);
// Platform 驱动结构体
static struct platform_driver my_driver = {
.probe = my_driver_probe,
.remove = my_driver_remove,
.driver = {
.name = "my_device",
.of_match_table = my_of_match, // 绑定设备树匹配表
},
};
// 模块入口/出口
module_platform_driver(my_driver);
4.5 匹配机制:四种匹配策略
Platform 总线的 platform_match() 函数按照优先级顺序尝试四种匹配方式。一旦某一种匹配成功,即停止后续匹配。
| 优先级 | 匹配方式 | 匹配依据 | 典型场景 |
|---|---|---|---|
| 1 (最高) | OF (Device Tree) 匹配 | 驱动 of_match_table 中的 compatible 值与设备树节点的 compatible 属性 |
所有使用设备树的 ARM/ARM64/RISC-V 平台 |
| 2 | ACPI 匹配 | 驱动 acpi_match_table 与 ACPI 表项 |
x86/ARM64 上的 ACPI 系统 |
| 3 | ID 表匹配 | 驱动 id_table 中的 name 字段与设备 name 字段 |
非设备树平台(如旧版 x86)或板级文件 |
| 4 (最低) | 名称匹配 | 驱动 driver.name 与设备 name 字段直接比较 |
最简单的后备匹配,不推荐用于生产代码 |
4.6 匹配流程的完整时序
当驱动调用 platform_driver_register() 时,内核内部会执行以下步骤:
- 注册驱动:将
platform_driver添加到 Platform 总线的驱动链表中。 - 遍历设备:调用
bus_for_each_dev(),遍历当前已注册的所有 Platform 设备。 - 调用 match:对每个设备,调用
platform_match()函数,按优先级尝试四种匹配。 - 绑定 Probe:如果匹配成功,调用驱动的
.probe()方法,并将匹配到的设备指针作为参数传入。 - 设备引用:Probe 成功后,设备和驱动建立双向引用关系,设备状态变为“已绑定”。
最佳实践:在设备树普及的今天,始终优先使用 OF 匹配。在驱动中定义
of_match_table,并在设备树节点中设置正确的 compatible 属性。这不仅能实现最清晰的硬件描述,还能让驱动自动适配不同硬件版本(通过不同的 compatible 值)。