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。其分层架构如下图所示:

dmaengine 框架分层架构 DMA 用户层(Audio/Net/Storage/Accel) dmaengine 核心 API(dmaengine.h) dma_device (DMA 控制器抽象) dma_chan (DMA 通道抽象) 硬件 DMA 控制器(PL330 / EDMA / SDMA ...)

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);
};
dma_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 框架的特定注册机制。整体驱动模型包含以下关键步骤:

  1. 设备树匹配与平台驱动绑定:通过 of_match_table 匹配设备树节点,触发 probe。
  2. 分配并初始化 dma_device:设置能力掩码、通道数、回调函数指针。
  3. 创建并初始化 dma_chan:为每个物理通道分配 dma_chan,设置 device_node 链表。
  4. 注册到 dmaengine 核心:调用 dma_async_device_register()。
  5. 实现传输回调:device_prep_slave_sg、device_issue_pending、device_tx_status 等。
  6. 中断处理:在硬件中断中调用 dma_cookie_complete() 和 dmaengine_desc_get_callback_invoke()。
DMA 控制器驱动模型流程 1. 设备树匹配 → probe() 2. 分配 dma_device + 设置 cap_mask 3. 创建 dma_chan 链表 4. dma_async_device_register() 5. 实现传输回调 + 中断处理 6. 上层驱动通过 dma_request_channel() 使用

2.5 关键数据结构关系图

以下 SVG 展示了 dma_device、dma_chan、dma_async_tx_descriptor 之间的核心关联:

dma_device / dma_chan / descriptor 关系 dma_device channels: list_head cap_mask: MEMCPY|SLAVE device_issue_pending() dma_chan[0] cookie=3, dir=MEM_TO_DEV dma_chan[1] cookie=7, dir=DEV_TO_MEM dma_chan[2] cookie=0, free dma_async_tx_descriptor callback, callback_param tx_submit(), desc_free()

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 本章小结

dmaengine 核心概念速查
概念 数据结构 核心作用
DMA 控制器 dma_device 硬件控制器抽象,注册到核心框架
DMA 通道 dma_chan 传输通道句柄,上层驱动直接操作
事务描述符 dma_async_tx_descriptor 单次传输的完整描述,含回调
虚拟通道 virt_dma_chan 软件队列管理,支持描述符复用
能力掩码 dma_cap_mask_t 声明控制器支持的传输类型