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 结构体。该结构体包含了从设备树解析出的所有资源信息。
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;
}
devm_* API,可以大幅简化 remove 函数,并避免资源泄漏。对于 DMA 缓冲区,使用 dmam_alloc_coherent() 同样能实现自动释放。
4.4 设备树解析与资源获取流程
下图展示了从设备树节点到驱动获取资源的完整数据流:
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。
kmalloc)仍需手动释放。
4.6 本章小结
通过本章学习,你应掌握:
- 设备树节点如何转化为
platform_device及其resource数组。 - 使用
platform_get_resource()和platform_get_irq()获取 MEM 和 IRQ 资源。 - 利用
devm_*API 实现资源的自动管理,简化驱动代码。 - 理解资源获取流程中的常见错误与最佳实践。