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——实现从“硬件能力描述”到“传输事务管理”的完整抽象。
dmaengine 子系统三层架构 客户端驱动(Client Driver) 音频、网络、存储、SPI等 dmaengine API 核心层 dma_request_channel / dmaengine_prep_slave_sg / submit / issue_pending DMA引擎驱动(DMA Engine Driver) 实现 dma_device 回调,管理 dma_chan 和描述符 DMA控制器硬件 PL330 / EDMA / XDMA / Intel IOAT 客户端驱动通过标准API调用,无需关心底层DMA控制器的寄存器细节

2.1 dma_device 结构体:DMA控制器的能力描述

dma_device 是DMA引擎驱动的核心注册结构体,每个DMA控制器实例对应一个 dma_device。它向dmaengine核心层声明该控制器支持哪些传输类型、通道数量、对齐要求以及硬件能力。

字段类型说明
devstruct device *指向DMA控制器的设备结构体,用于电源管理和设备模型关联
channelsstruct list_head该控制器下所有可用DMA通道的链表头
cap_maskdma_cap_mask_t位掩码,声明支持的传输类型(如DMA_SLAVE、DMA_CYCLIC、DMA_INTERLEAVE)
directionsu32支持的DMA方向位图(DEV_TO_MEM、MEM_TO_DEV、MEM_TO_MEM)
src_addr_widthsu32支持的源端数据宽度位图(1/2/4/8字节)
dst_addr_widthsu32支持的目的端数据宽度位图
copy_alignintMEM_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 */
};

通道的生命周期管理遵循以下流程:

  1. 请求通道: 客户端调用 dma_request_channel(mask, filter_fn, filter_param),核心层遍历所有注册的 dma_device,调用过滤函数匹配通道。
  2. 配置通道: 通过 dmaengine_slave_config() 设置源/目的地址、数据宽度、突发长度等参数。
  3. 使用通道: 准备描述符、提交、触发传输。
  4. 释放通道: 传输完成后调用 dma_release_channel() 归还通道。
dma_chan 生命周期与状态转换 空闲(Idle) 已分配(Allocated) 已配置(Configured) 传输中(Active) dma_request_channel slave_config issue_pending 传输完成 → 回到已配置状态 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 协作流程 客户端驱动 dma_device dma_chan dma_async_tx_descriptor dma_request_channel device_alloc_chan_resources device_prep_slave_sg dmaengine_submit device_issue_pending callback 通知完成
架构总结: dma_device 描述“有什么硬件能力”,dma_chan 代表“一个可用的传输通道”,dma_async_tx_descriptor 封装“一次具体的传输任务”。三者通过dmaengine核心层的标准API串联,构成了Linux内核中灵活、可扩展的DMA传输框架。