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 总线的工作流程可以概括为:

  1. 注册触发:调用 platform_device_register() 或 platform_driver_register()。
  2. 总线扫描:内核将新对象加入对应链表,并调用 bus_for_each_dev() 或 bus_for_each_drv() 进行遍历。
  3. 匹配执行:对每一对 (device, driver),调用 platform_match() 函数。
  4. 绑定 Probe:匹配成功,则调用驱动的 .probe() 方法,将设备资源传递给驱动。
Platform 总线匹配与绑定流程 注册 Platform 设备 注册 Platform 驱动 platform_match() 执行 驱动 .probe() 被调用 匹配顺序 1. of_match_table (设备树) 2. id_table (ACPI/名称)

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 字段直接比较 最简单的后备匹配,不推荐用于生产代码
Platform 匹配机制决策树 platform_match() 开始 1. OF 匹配 (compatible) 2. ACPI 匹配 3. ID 表匹配 4. 名称匹配 (后备) 匹配成功 → 调用 .probe() 匹配终止条件 任一匹配成功即停止 全部失败则返回 -ENODEV

4.6 匹配流程的完整时序

当驱动调用 platform_driver_register() 时,内核内部会执行以下步骤:

  1. 注册驱动:将 platform_driver 添加到 Platform 总线的驱动链表中。
  2. 遍历设备:调用 bus_for_each_dev(),遍历当前已注册的所有 Platform 设备。
  3. 调用 match:对每个设备,调用 platform_match() 函数,按优先级尝试四种匹配。
  4. 绑定 Probe:如果匹配成功,调用驱动的 .probe() 方法,并将匹配到的设备指针作为参数传入。
  5. 设备引用:Probe 成功后,设备和驱动建立双向引用关系,设备状态变为“已绑定”。
最佳实践:在设备树普及的今天,始终优先使用 OF 匹配。在驱动中定义 of_match_table,并在设备树节点中设置正确的 compatible 属性。这不仅能实现最清晰的硬件描述,还能让驱动自动适配不同硬件版本(通过不同的 compatible 值)。

4.7 本章小结