2. Linux DMA子系统架构:dmaengine框架概述
Linux内核的DMA引擎(dmaengine)子系统为设备驱动开发者提供了一套统一的、与硬件无关的DMA传输控制接口。它屏蔽了不同SoC厂商(如TI、NXP、Xilinx、Intel)DMA控制器的寄存器级差异,使得上层驱动(如音频、网络、存储)可以以标准化的方式发起和完成DMA传输。
核心设计目标: 将DMA控制器驱动(称为“DMA引擎驱动”)与使用DMA的客户端驱动解耦,通过三个核心结构体——
dma_device、dma_chan、dma_async_tx_descriptor——实现从“硬件能力描述”到“传输事务管理”的完整抽象。
2.1 dma_device 结构体:DMA控制器的能力描述
dma_device 是DMA引擎驱动的核心注册结构体,每个DMA控制器实例对应一个 dma_device。它向dmaengine核心层声明该控制器支持哪些传输类型、通道数量、对齐要求以及硬件能力。
| 字段 | 类型 | 说明 |
|---|---|---|
dev | struct device * | 指向DMA控制器的设备结构体,用于电源管理和设备模型关联 |
channels | struct list_head | 该控制器下所有可用DMA通道的链表头 |
cap_mask | dma_cap_mask_t | 位掩码,声明支持的传输类型(如DMA_SLAVE、DMA_CYCLIC、DMA_INTERLEAVE) |
directions | u32 | 支持的DMA方向位图(DEV_TO_MEM、MEM_TO_DEV、MEM_TO_MEM) |
src_addr_widths | u32 | 支持的源端数据宽度位图(1/2/4/8字节) |
dst_addr_widths | u32 | 支持的目的端数据宽度位图 |
copy_align | int | MEM_TO_MEM传输的对齐要求(2^align) |
device_alloc_chan_resources | 函数指针 | 分配通道资源回调,在通道被请求时调用 |
device_prep_slave_sg | 函数指针 | 准备从设备到内存或内存到设备的SG列表传输 |
device_issue_pending | 函数指针 | 触发硬件开始执行已提交的传输描述符 |
关键理解:
dma_device 中的函数指针(如 device_prep_slave_sg)是DMA引擎驱动必须实现的核心回调。dmaengine核心层通过调用这些回调,将客户端的传输请求转化为硬件操作。
2.2 dma_chan 结构体:传输通道的抽象
dma_chan 代表一个具体的DMA通道。客户端驱动通过 dma_request_channel() 获取一个可用通道,之后所有传输操作都通过该通道进行。
struct dma_chan {
struct dma_device *device; /* 所属的DMA控制器 */
dma_cookie_t cookie; /* 当前传输的cookie标识 */
struct list_head global_node; /* 全局通道链表节点 */
struct list_head device_node; /* 所属dma_device的通道链表节点 */
void *private; /* 客户端驱动的私有数据指针 */
int chan_id; /* 通道ID,用于调试和标识 */
struct dma_chan_dev *dev; /* 通道对应的字符设备,用于sysfs */
};
通道的生命周期管理遵循以下流程:
- 请求通道: 客户端调用
dma_request_channel(mask, filter_fn, filter_param),核心层遍历所有注册的dma_device,调用过滤函数匹配通道。 - 配置通道: 通过
dmaengine_slave_config()设置源/目的地址、数据宽度、突发长度等参数。 - 使用通道: 准备描述符、提交、触发传输。
- 释放通道: 传输完成后调用
dma_release_channel()归还通道。
2.3 dma_async_tx_descriptor 结构体:传输事务的描述
每次DMA传输操作都被抽象为一个 dma_async_tx_descriptor 结构体。它描述了“从哪里传到哪里、传多少、传完后做什么”。客户端驱动通过 device_prep_* 系列回调获取描述符,然后提交并触发执行。
struct dma_async_tx_descriptor {
dma_cookie_t cookie; /* 传输的cookie,用于跟踪状态 */
enum dma_ctrl_flags flags; /* 控制标志:DMA_PREP_INTERRUPT等 */
struct dma_chan *chan; /* 所属通道 */
dma_async_tx_callback callback; /* 传输完成回调函数 */
void *callback_param; /* 回调参数 */
struct list_head tx_list; /* 用于链式传输的链表节点 */
struct dmaengine_unmap_data *unmap; /* 用于DMA一致性映射管理 */
/* 引擎驱动私有数据区域 */
};
重要注意事项: 描述符的生命周期由DMA引擎驱动管理。客户端驱动在回调中收到传输完成通知后,不应再访问描述符。描述符可能在回调返回后被引擎驱动回收或重用。
2.4 三个结构体的协作关系
在典型的DMA传输流程中,三个结构体协同工作:
| 步骤 | 涉及结构体 | 操作 |
|---|---|---|
| 1. 通道请求 | dma_device → dma_chan | 核心层遍历 dma_device.channels,匹配并分配通道 |
| 2. 传输准备 | dma_chan → dma_device | 调用 dma_device.device_prep_slave_sg() 创建描述符 |
| 3. 描述符提交 | dma_async_tx_descriptor | 调用 dmaengine_submit() 将描述符加入通道的提交队列 |
| 4. 触发执行 | dma_chan → dma_device | 调用 dma_chan.device->device_issue_pending() 启动硬件传输 |
| 5. 完成通知 | dma_async_tx_descriptor | 硬件中断触发后,引擎驱动调用描述符中的 callback |
架构总结:
dma_device 描述“有什么硬件能力”,dma_chan 代表“一个可用的传输通道”,dma_async_tx_descriptor 封装“一次具体的传输任务”。三者通过dmaengine核心层的标准API串联,构成了Linux内核中灵活、可扩展的DMA传输框架。