2. Linux DMA 子系统架构:dmaengine 框架概述、dma_device 与 dma_chan 结构体、DMA 控制器驱动模型
2.1 dmaengine 框架概述
Linux 内核的 dmaengine 子系统为 DMA 控制器驱动和 DMA 用户(如音频、网络、存储、加速器驱动)提供了一套统一的抽象层。它解决了传统 DMA 驱动实现方式各异、接口不统一的问题,使上层驱动可以跨平台复用,而无需关心底层 DMA 控制器的具体寄存器细节。
核心设计目标:
- 统一 API:为 DMA 操作提供标准化的提交、控制、回调接口。
- 硬件抽象:将 DMA 控制器的物理能力(通道数、传输类型、地址宽度)封装为通用属性。
- 通道管理:支持虚拟通道(Virtual Channel)和物理通道的映射,实现多请求复用。
- 异步操作:基于事务描述符(Descriptor)的异步提交模式,支持 DMA 完成回调。
dmaengine 框架位于 drivers/dma/ 目录,核心头文件为 include/linux/dmaengine.h。其分层架构如下图所示:
2.2 dma_device 结构体详解
struct dma_device 是 DMA 控制器驱动的核心抽象,代表一个 DMA 控制器硬件实例。每个 DMA 控制器驱动在 probe 阶段分配并初始化一个 dma_device,然后通过 dma_async_device_register() 注册到 dmaengine 核心。
struct dma_device {
struct kref refcount;
unsigned int chancnt; /* 物理通道数量 */
unsigned int privatecnt; /* 私有通道计数 */
struct list_head channels; /* 通道链表头 */
struct list_head global_node; /* 全局设备链表节点 */
enum dma_capability cap_mask; /* 设备能力位图 */
bool fill_align; /* 填充对齐要求 */
/* 传输能力描述 */
enum dma_transaction_type cap_mask_bits[0];
int dev_id;
struct device *dev;
/* 设备级回调函数 */
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);
enum dma_status (*device_tx_status)(struct dma_chan *chan,
dma_cookie_t cookie, struct dma_tx_state *txstate);
};
关键字段解读:
cap_mask:使用dma_cap_set()宏设置,声明该控制器支持的传输类型(如 MEMCPY、SLAVE、CYCLIC)。device_prep_*系列函数:准备 DMA 事务描述符,不启动传输,返回dma_async_tx_descriptor。device_issue_pending:将准备好的描述符推送到硬件执行队列。device_tx_status:查询 DMA 传输的当前状态和已传输字节数。
2.3 dma_chan 结构体详解
struct dma_chan 代表一个 DMA 通道,是上层驱动与 DMA 控制器交互的直接句柄。通道可以是物理通道(直接对应硬件通道),也可以是虚拟通道(通过 virt-dma 层复用物理通道)。
struct dma_chan {
struct dma_device *device; /* 所属 DMA 控制器 */
dma_cookie_t cookie; /* 当前事务 cookie */
dma_cookie_t completed_cookie; /* 已完成事务 cookie */
/* 通道属性 */
enum dma_transfer_direction dir;
struct dma_slave_config slave_config;
/* 客户端信息 */
void *private;
const char *name;
/* 通道链表节点 */
struct list_head device_node;
struct list_head global_node;
struct list_head free_node;
/* 回调 */
void (*client_terminate)(struct dma_chan *chan);
};
| 字段 | 类型 | 说明 |
|---|---|---|
device |
struct dma_device * |
指向所属 DMA 控制器,用于调用设备级操作 |
cookie |
dma_cookie_t |
当前提交事务的标识符,用于状态查询 |
completed_cookie |
dma_cookie_t |
最后一个已完成事务的 cookie |
slave_config |
struct dma_slave_config |
从设备配置(方向、地址、宽度、突发大小) |
private |
void * |
通道私有数据,通常指向 virt-dma 的 vchan |
2.4 DMA 控制器驱动模型
DMA 控制器驱动的实现遵循 Linux 设备驱动模型的标准流程,结合 dmaengine 框架的特定注册机制。整体驱动模型包含以下关键步骤:
- 设备树匹配与平台驱动绑定:通过
of_match_table匹配设备树节点,触发 probe。 - 分配并初始化 dma_device:设置能力掩码、通道数、回调函数指针。
- 创建并初始化 dma_chan:为每个物理通道分配
dma_chan,设置device_node链表。 - 注册到 dmaengine 核心:调用
dma_async_device_register()。 - 实现传输回调:
device_prep_slave_sg、device_issue_pending、device_tx_status等。 - 中断处理:在硬件中断中调用
dma_cookie_complete()和dmaengine_desc_get_callback_invoke()。
2.5 关键数据结构关系图
以下 SVG 展示了 dma_device、dma_chan、dma_async_tx_descriptor 之间的核心关联:
2.6 虚拟通道(virt-dma)扩展
对于多通道复用场景,dmaengine 提供了 virt-dma 层(drivers/dma/virt-dma.h)。它在 dma_chan 之上封装了 virt_dma_chan,支持软件队列管理和描述符重用:
struct virt_dma_chan {
struct dma_chan chan; /* 基类 */
struct tasklet_struct task; /* 软中断处理 */
spinlock_t lock;
struct list_head desc_allocated; /* 已分配描述符链表 */
struct list_head desc_submitted; /* 已提交描述符链表 */
struct list_head desc_issued; /* 已下发硬件链表 */
struct list_head desc_completed; /* 已完成链表 */
void (*desc_free)(struct virt_dma_desc *);
};
开发注意事项:
- virt-dma 的
vchan_tx_submit()只将描述符放入 submitted 队列,真正的硬件下发由vchan_issue_pending()触发。 - 中断处理中应调用
vchan_cookie_complete()将描述符移至 completed 队列,并在 tasklet 中调用用户回调。 - 使用
vchan_find_desc()可以在 completed 队列中查找特定 cookie 的描述符。
2.7 本章小结
| 概念 | 数据结构 | 核心作用 |
|---|---|---|
| DMA 控制器 | dma_device |
硬件控制器抽象,注册到核心框架 |
| DMA 通道 | dma_chan |
传输通道句柄,上层驱动直接操作 |
| 事务描述符 | dma_async_tx_descriptor |
单次传输的完整描述,含回调 |
| 虚拟通道 | virt_dma_chan |
软件队列管理,支持描述符复用 |
| 能力掩码 | dma_cap_mask_t |
声明控制器支持的传输类型 |