4. platform_device 注册与资源获取:设备树节点解析、IORESOURCE_MEM/IRQ 资源获取、devm_* API

在 Linux 平台驱动模型中,设备与驱动的分离是核心设计思想。本章聚焦于设备端——即 platform_device 的注册机制,以及驱动如何从设备树(Device Tree)或静态平台数据中获取内存地址、中断号等关键资源。掌握这些内容是后续实现 DMA 传输与 mmap 内存映射的基础。

4.1 设备树节点与 platform_device 的生成

现代 ARM/嵌入式 Linux 系统普遍使用设备树(Device Tree)来描述硬件。内核在启动时解析 .dts 文件,为每个带有 compatible 属性的节点创建 platform_device。

关键概念:设备树中的 compatible 属性是驱动匹配的“身份证”。驱动通过 of_match_table 声明支持的设备,内核据此将设备与驱动绑定。

一个典型的 DMA 控制器设备树节点示例如下:

dma_controller: dma@1c00000 {
    compatible = "vendor,dma-controller";
    reg = <0x01c00000 0x1000>;
    interrupts = <0 32 4>;
    dma-channels = <8>;
    #dma-cells = <2>;
};

内核在 of_platform_populate() 或 of_platform_default_populate() 中遍历设备树,为每个匹配的节点分配并初始化一个 platform_device 结构体。该结构体包含了从设备树解析出的所有资源信息。

核心流程:设备树节点 → of_platform_device_create() → platform_device 结构体(含 resource 数组) → 添加到 platform bus。

4.2 IORESOURCE_MEM 与 IRQ 资源获取

platform_device 中的 resource 数组是驱动访问硬件资源的桥梁。最常见的两种资源类型是 IORESOURCE_MEM(内存地址范围)和 IORESOURCE_IRQ(中断号)。

4.2.1 资源类型与标志

资源类型宏 含义 设备树对应属性
IORESOURCE_MEM 物理内存地址区间(寄存器基址) reg
IORESOURCE_IRQ 中断号 interrupts
IORESOURCE_DMA DMA 通道号 dmas
IORESOURCE_IO I/O 端口地址(x86 常用) reg(配合 ranges)

4.2.2 资源获取 API

驱动中获取资源的标准方法是通过 platform_get_resource() 系列函数:

// 获取第 0 个 MEM 类型资源
struct resource *res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);

// 获取第 0 个 IRQ 类型资源
struct resource *res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

// 直接获取中断号(推荐)
int irq_num = platform_get_irq(pdev, 0);
if (irq_num < 0) {
    dev_err(&pdev->dev, "failed to get IRQ\n");
    return irq_num;
}
注意:platform_get_irq() 返回负数表示错误,正数或 0 表示有效中断号。不要使用 platform_get_resource(pdev, IORESOURCE_IRQ, 0) 后直接取 start 字段,因为该方式无法正确处理中断映射失败的情况。

4.3 devm_* API:自动资源管理

传统驱动中,开发者需要手动管理资源的申请与释放,容易导致内存泄漏或资源未正确释放。devm_*(device-managed)系列 API 将资源生命周期与 struct device 绑定,当设备被移除或驱动卸载时,内核自动释放所有已注册的 devm 资源。

4.3.1 常用 devm_* API 对照表

传统 API devm_* 版本 释放时机
kmalloc() devm_kmalloc() 设备分离时自动 kfree
request_mem_region() devm_request_mem_region() 设备分离时自动 release
ioremap() devm_ioremap_resource() 设备分离时自动 iounmap
request_irq() devm_request_irq() 设备分离时自动 free_irq
dma_alloc_coherent() dmam_alloc_coherent() 设备分离时自动 dma_free_coherent

4.3.2 实战:使用 devm_ioremap_resource 获取 MEM 资源

devm_ioremap_resource() 是推荐的内存资源获取方式,它整合了 request_mem_region() 和 ioremap() 两个步骤,并自动处理错误:

static int my_dma_probe(struct platform_device *pdev)
{
    struct resource *res;
    void __iomem *base;

    // 获取 MEM 资源并自动映射(包含内存区域申请)
    base = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(base))
        return PTR_ERR(base);

    // 获取中断号
    int irq = platform_get_irq(pdev, 0);
    if (irq < 0)
        return irq;

    // 注册中断(自动管理)
    ret = devm_request_irq(&pdev->dev, irq, my_dma_isr, 0,
                           dev_name(&pdev->dev), dev);
    if (ret) {
        dev_err(&pdev->dev, "failed to request IRQ %d\n", irq);
        return ret;
    }

    // ... 后续初始化 ...

    return 0;
}

static int my_dma_remove(struct platform_device *pdev)
{
    // 无需手动释放资源!devm_* 自动处理
    return 0;
}
最佳实践:在 probe 函数中优先使用 devm_* API,可以大幅简化 remove 函数,并避免资源泄漏。对于 DMA 缓冲区,使用 dmam_alloc_coherent() 同样能实现自动释放。

4.4 设备树解析与资源获取流程

下图展示了从设备树节点到驱动获取资源的完整数据流:

设备树解析与资源获取流程 设备树 → platform_device → 驱动资源获取 流程 设备树 .dts 文件 of_platform_populate() 解析 platform_device resource[0..n] 数组 IORESOURCE_MEM IORESOURCE_IRQ platform_get_resource() devm_ioremap_resource() platform_get_irq() devm_request_irq() 驱动 probe 成功,设备就绪

4.5 资源获取的常见陷阱与避坑指南

  • 资源索引错误:设备树中 reg 属性可能有多个地址区间,使用 platform_get_resource(pdev, IORESOURCE_MEM, 1) 获取第二个区间。索引从 0 开始。
  • 中断号检查:始终使用 platform_get_irq() 并检查返回值是否小于 0,不要假设中断号总是有效。
  • 内存映射对齐:devm_ioremap_resource() 会自动检查资源对齐和有效性,比手动 ioremap() 更安全。
  • DMA 资源获取:对于 DMA 通道,使用 dma_request_slave_channel() 或 dma_request_chan(),而不是直接解析 IORESOURCE_DMA。
重要提醒:在 probe 函数中,如果某个 devm_* 资源申请失败,不需要手动释放之前已申请的 devm 资源。内核会在 probe 失败时自动回滚所有 devm 分配。但要注意,非 devm 资源(如手动 kmalloc)仍需手动释放。

4.6 本章小结

通过本章学习,你应掌握:

  1. 设备树节点如何转化为 platform_device 及其 resource 数组。
  2. 使用 platform_get_resource() 和 platform_get_irq() 获取 MEM 和 IRQ 资源。
  3. 利用 devm_* API 实现资源的自动管理,简化驱动代码。
  4. 理解资源获取流程中的常见错误与最佳实践。