4. Linux DMA子系统概述
Linux内核提供了一套统一的DMA引擎框架——dmaengine,用于抽象不同硬件平台的DMA控制器差异,为驱动开发者提供标准化的API接口。本章深入剖析dmaengine的分层架构、核心数据结构及其协作关系。
4.1 Linux内核DMA引擎框架
dmaengine框架位于内核源码 drivers/dma/ 目录下,采用三层次架构:
- 硬件适配层:各厂商DMA控制器驱动(如Intel IDMA、TI EDMA、PL330等)实现底层寄存器操作。
- 核心管理层:提供通道分配、描述符管理、传输调度等通用逻辑。
- 用户接口层:向其他内核子系统(如音频、网络、存储)暴露统一的dmaengine API。
设计目标: 解耦DMA控制器硬件差异,使驱动开发者无需关心具体寄存器细节,仅通过标准API即可完成DMA传输配置与启动。
4.2 dmaengine API分层结构
dmaengine API按照使用场景分为设备注册层、通道管理层和传输控制层三层:
| 层次 | 核心函数 | 功能描述 |
|---|---|---|
| 设备注册层 | dma_async_device_register() |
DMA控制器驱动向内核注册自身能力 |
| 通道管理层 | dma_request_channel() |
申请一个满足特定能力的DMA通道 |
| 通道管理层 | dma_release_channel() |
释放不再使用的DMA通道 |
| 传输控制层 | dmaengine_prep_slave_sg() |
准备分散/聚集传输描述符 |
| 传输控制层 | dmaengine_prep_dma_memcpy() |
准备内存拷贝传输描述符 |
| 传输控制层 | dmaengine_submit() |
将描述符提交到硬件执行队列 |
| 传输控制层 | dma_async_issue_pending() |
触发硬件开始执行已提交的传输 |
典型调用序列:
dma_request_channel() → dmaengine_prep_slave_sg() → dmaengine_submit() → dma_async_issue_pending()
4.3 核心数据结构
dmaengine框架围绕三个核心数据结构构建:dma_device、dma_chan 和 dma_async_tx_descriptor。它们分别代表DMA控制器、DMA通道和一次传输事务。
4.3.1 dma_device — DMA控制器抽象
dma_device 描述一个DMA控制器的能力和操作函数集,是驱动注册时的核心对象:
struct dma_device {
unsigned int chancnt; /* 通道数量 */
unsigned int copy_align; /* 内存拷贝对齐要求 */
enum dma_device_state state; /* 设备状态 */
/* 能力位图 */
u32 src_addr_widths; /* 支持的源地址宽度 */
u32 dst_addr_widths; /* 支持的目的地址宽度 */
u32 directions; /* 支持的传输方向 */
u32 max_burst; /* 最大突发传输长度 */
/* 操作函数集 */
int (*device_alloc_chan_resources)(struct dma_chan *chan);
void (*device_free_chan_resources)(struct dma_chan *chan);
struct dma_async_tx_descriptor *(*device_prep_dma_memcpy)(
struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
size_t len, unsigned long flags);
struct dma_async_tx_descriptor *(*device_prep_slave_sg)(
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
unsigned long flags, void *context);
int (*device_issue_pending)(struct dma_chan *chan);
int (*device_tx_status)(struct dma_chan *chan, dma_cookie_t cookie,
struct dma_tx_state *txstate);
};
关键约束:
copy_align 字段定义了内存拷贝时地址和长度的对齐要求(以2的幂次表示)。例如 copy_align = 3 表示必须8字节对齐。违反对齐会导致硬件异常或数据损坏。
4.3.2 dma_chan — DMA通道抽象
dma_chan 代表一个已分配的DMA通道,是驱动与DMA引擎交互的句柄:
struct dma_chan {
struct dma_device *device; /* 所属DMA设备 */
dma_cookie_t cookie; /* 当前传输cookie */
dma_cookie_t completed_cookie; /* 已完成传输cookie */
/* 通道属性 */
enum dma_transfer_direction direction;
dma_addr_t slave_addr; /* 外设地址 */
enum dma_slave_buswidth addr_widths;
u32 burst_size;
/* 回调机制 */
dma_async_tx_callback callback;
void *callback_param;
/* 内部状态 */
struct list_head free_nodes; /* 空闲描述符链表 */
struct list_head submitted; /* 已提交队列 */
struct list_head issued; /* 已下发队列 */
struct list_head completed; /* 已完成队列 */
};
4.3.3 dma_async_tx_descriptor — 传输描述符
每次DMA传输操作被封装为一个 dma_async_tx_descriptor,它描述了传输的所有参数和状态:
struct dma_async_tx_descriptor {
dma_cookie_t cookie; /* 传输标识符 */
enum dma_ctrl_flags flags; /* 控制标志 */
dma_addr_t src; /* 源地址 */
dma_addr_t dst; /* 目的地址 */
size_t len; /* 传输长度 */
/* 链表支持(用于分散收集) */
struct list_head tx_list; /* 描述符链表节点 */
unsigned int sg_count; /* 散列表项数量 */
/* 回调 */
dma_async_tx_callback callback; /* 传输完成回调 */
void *callback_param;
/* 依赖管理 */
struct dma_async_tx_descriptor *next;
struct dma_async_tx_descriptor *prev;
/* 硬件操作 */
enum dma_status (*tx_status)(struct dma_chan *chan,
dma_cookie_t cookie,
struct dma_tx_state *txstate);
void (*tx_submit)(struct dma_async_tx_descriptor *tx);
};
4.4 数据结构协作流程
三个核心数据结构在DMA传输生命周期中的协作关系如下:
- 注册阶段:DMA控制器驱动填充
dma_device结构,调用dma_async_device_register()注册到内核。 - 通道申请:客户端驱动通过
dma_request_channel()获取一个dma_chan实例,该实例指向所属的dma_device。 - 描述符创建:调用
dmaengine_prep_slave_sg()或dmaengine_prep_dma_memcpy()创建dma_async_tx_descriptor,填充源地址、目的地址、长度等参数。 - 提交与执行:
dmaengine_submit()将描述符加入通道的提交队列,dma_async_issue_pending()触发硬件开始传输。 - 完成通知:硬件传输完成后,通过描述符中注册的
callback函数通知上层驱动。
性能关键点: 描述符的
tx_list 链表支持将多个描述符串联成一次硬件操作,这是实现分散收集(Scatter-Gather)传输的基础。合理利用链表可以减少硬件中断次数,提升批量数据传输效率。