2. Linux 设备模型基础:总线、设备、驱动模型,platform 总线与设备驱动

Linux 内核从 2.6 版本开始引入了统一的设备驱动模型(Device Driver Model),旨在将系统中的硬件设备、驱动程序和通信路径抽象为标准的软件对象,从而简化驱动开发、电源管理和用户空间接口。本章将深入剖析该模型的核心三要素——总线(Bus)、设备(Device)和驱动(Driver),并重点讲解嵌入式开发中最常用的 platform 总线及其驱动框架。

2.1 设备模型三要素

Linux 设备模型将硬件拓扑抽象为三个核心对象,它们在内核中通过数据结构相互关联,形成一棵以 struct device 为节点的树形结构。

要素 内核数据结构 职责描述
总线(Bus) struct bus_type 定义设备与驱动的匹配规则、热插拔操作、电源管理回调;是设备与驱动之间的桥梁。
设备(Device) struct device 代表一个物理或虚拟硬件实体,包含资源信息(如 I/O 地址、中断号)、电源状态、父子关系。
驱动(Driver) struct device_driver 封装操作硬件的具体代码,提供 probe()、remove()、suspend()、resume() 等接口。
核心机制:当内核注册一个新设备或新驱动时,总线会遍历其维护的设备链表和驱动链表,调用匹配函数(match())进行配对。配对成功后,总线调用驱动的 probe() 方法,驱动开始初始化硬件并注册子设备。

下图展示了设备模型中三要素的协作关系:

Linux 设备模型三要素协作关系 总线 (bus_type) match() / probe() / remove() 设备 (device) 资源 / 电源 / 拓扑 驱动 (driver) probe / remove / suspend 匹配成功 → 调用 probe() 设备与驱动绑定

2.2 总线匹配与 probe 流程

设备与驱动的匹配是设备模型的核心动作。当系统启动或热插拔事件发生时,总线按照以下步骤完成绑定:

  1. 设备注册:总线将新设备加入设备链表,并遍历驱动链表调用 match()。
  2. 驱动注册:总线将新驱动加入驱动链表,并遍历设备链表调用 match()。
  3. 匹配判断:match() 函数根据总线类型返回布尔值。例如 PCI 总线匹配 VID/PID,USB 匹配 VID/PID/接口类,platform 总线匹配设备树 compatible 属性或设备名。
  4. 绑定与 probe:匹配成功后,总线调用驱动的 probe() 方法,驱动获得设备资源指针,开始初始化硬件。
  5. 解绑与 remove:设备移除或驱动卸载时,总线调用驱动的 remove() 方法释放资源。
关键点:一个驱动可以支持多个设备(通过 of_match_table 或 id_table),一个设备只能绑定一个驱动。匹配失败时,设备保持“孤儿”状态,不会产生任何操作。

2.3 platform 总线:嵌入式系统的虚拟总线

在嵌入式系统中,大量设备(如 GPIO 按键、LED、I2C 控制器、SPI 控制器)并不挂载在 PCI、USB 等可枚举总线上。Linux 为此引入了 platform 总线(platform_bus_type),它是一种虚拟总线,用于管理那些 直接由 CPU 内存映射或通过板级代码描述 的设备。

特性 platform 总线 PCI / USB 总线
硬件发现方式 静态描述(设备树 / ACPI / 板级文件) 自动枚举(配置空间 / 描述符)
匹配依据 compatible 属性、设备名 VID / PID / 类代码
资源传递 struct resource 数组(I/O、中断、DMA) PCI BAR、USB endpoint
典型应用 GPIO、UART、SPI、I2C 控制器、看门狗 网卡、显卡、USB 外设

2.4 platform 设备与驱动核心数据结构

platform 体系在通用设备模型基础上增加了两层封装:

  • struct platform_device:继承 struct device,增加 struct resource *resource(资源数组)和 int num_resources。
  • struct platform_driver:继承 struct device_driver,增加 probe()、remove()、shutdown() 等回调,以及 struct of_device_id *of_match_table(设备树匹配表)。

以下是一个典型的 platform 驱动框架代码:

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>

/* 设备树匹配表 */
static const struct of_device_id my_driver_of_match[] = {
    { .compatible = "vendor,my-gpio-key" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_driver_of_match);

/* probe 函数:驱动与设备匹配成功后调用 */
static int my_probe(struct platform_device *pdev)
{
    struct resource *res;
    int irq;

    pr_info("my_driver: probe called\n");

    /* 获取 I/O 内存资源 */
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res)
        return -ENXIO;

    /* 获取中断资源 */
    irq = platform_get_irq(pdev, 0);
    if (irq < 0)
        return irq;

    /* 硬件初始化代码 */
    return 0;
}

/* remove 函数:驱动卸载或设备移除时调用 */
static int my_remove(struct platform_device *pdev)
{
    pr_info("my_driver: remove called\n");
    return 0;
}

/* platform_driver 结构体 */
static struct platform_driver my_driver = {
    .probe  = my_probe,
    .remove = my_remove,
    .driver = {
        .name = "my_gpio_key",
        .of_match_table = my_driver_of_match,
    },
};

module_platform_driver(my_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Embedded Expert");
MODULE_DESCRIPTION("Platform driver example for GPIO key");
注意事项:
  • of_match_table 必须与设备树中节点的 compatible 属性完全一致,包括大小写和逗号。
  • module_platform_driver() 宏展开为 module_init() 和 module_exit(),自动处理注册与注销。
  • 资源获取应使用 platform_get_resource() 系列函数,避免直接访问 pdev->resource。

2.5 设备树与 platform 设备的绑定流程

在设备树使能的内核中,platform 设备的创建和注册由内核在启动阶段自动完成。下图展示了从设备树节点到 platform 设备实例的完整链路:

设备树节点 → platform 设备 → 驱动绑定流程 设备树 DTB 解析 of_platform_populate() 创建 platform_device 分配资源、填充 of_node 注册到 platform 总线 总线 match() 匹配 比较 compatible / name 驱动 probe() 执行 获取资源、注册 input 设备

2.6 关键 API 速查表

以下是在 platform 驱动开发中高频使用的内核 API:

API 函数 所属头文件 功能说明
platform_get_resource() linux/platform_device.h 获取指定类型和索引的资源(I/O、内存、DMA)
platform_get_irq() linux/platform_device.h 获取中断号,返回值为 int
devm_platform_ioremap_resource() linux/platform_device.h 获取 I/O 资源并建立内存映射,自动管理生命周期
of_match_node() linux/of.h 在 of_match_table 中查找匹配的 compatible 条目
module_platform_driver() linux/platform_device.h 宏,展开为 module_init / module_exit 注册 platform_driver
最佳实践:推荐使用 devm_ 系列资源管理函数(如 devm_platform_ioremap_resource、devm_request_irq),它们会在驱动卸载或 probe 失败时自动释放资源,避免内存泄漏和中断残留。

2.7 本章小结

  • Linux 设备模型通过 总线、设备、驱动 三个核心对象实现硬件与软件的松耦合。
  • platform 总线 是嵌入式 Linux 中最常用的虚拟总线,适用于非可枚举设备。
  • 设备树中的节点在系统启动时被解析为 platform_device,并通过 compatible 属性与 platform_driver 匹配。
  • 驱动开发者只需实现 probe() 和 remove() 回调,并在 of_match_table 中声明支持的设备。
  • 掌握 platform_get_resource() 和 devm_ 系列 API 是编写健壮驱动的基础。