2. 旧版 dmaengine API 回顾:核心数据结构详解
在开始从旧版 dmaengine 向新 API 升级之前,必须透彻理解旧版 API 的三大核心数据结构:dma_chan、dma_slave_config 和 dma_async_tx_descriptor。它们是整个 DMA 传输的基石,也是升级过程中需要重点替换或适配的对象。
升级提示: 新版 API 虽然重构了接口,但底层硬件抽象和传输描述的概念并未改变。理解旧版结构体,能帮助你更清晰地定位新 API 中对应的替代方案。
2.1 dma_chan:DMA 通道的抽象
dma_chan 是旧版 dmaengine 中代表一个 DMA 通道的核心结构体。它封装了通道的硬件能力、当前状态、以及操作函数集。驱动开发者通过它来发起和控制 DMA 传输。
关键字段解析:
struct dma_chan {
struct dma_device *device; // 所属 DMA 控制器设备
dma_cookie_t cookie; // 当前传输的 cookie(用于跟踪)
int chan_id; // 通道 ID(硬件索引)
struct list_head global_node; // 全局通道链表节点
struct list_head slave_node; // 从设备通道链表节点
void *private; // 通道私有数据(通常指向 dma_slave_chan)
int client_count; // 绑定到此通道的客户端数量
};
该结构体在旧版 API 中扮演着“句柄”的角色。当外设(如 SPI、I2C、UART)请求一个 DMA 通道时,dmaengine 会返回一个 dma_chan 指针。后续的配置、提交、回收操作都围绕这个指针进行。
升级注意点: 在新版 API 中,
dma_chan 结构体被保留但内部字段大幅简化,private 字段的使用方式发生变化,不再推荐直接访问内部链表节点。升级时需检查所有对 dma_chan 内部字段的直接访问。
2.2 dma_slave_config:从设备传输配置
dma_slave_config 是旧版 API 中用于配置 DMA 传输参数的桥梁结构体。它定义了外设(slave)与 DMA 控制器之间的硬件连接参数,包括地址、方向、数据宽度和突发大小等。
结构体定义:
struct dma_slave_config {
enum dma_transfer_direction direction; // 传输方向:DMA_MEM_TO_DEV / DMA_DEV_TO_MEM
dma_addr_t src_addr; // 源物理地址(外设侧)
dma_addr_t dst_addr; // 目的物理地址(外设侧)
enum dma_slave_buswidth src_addr_width; // 源数据宽度(字节)
enum dma_slave_buswidth dst_addr_width; // 目的数据宽度(字节)
u32 src_maxburst; // 源突发传输长度(单位:数据宽度)
u32 dst_maxburst; // 目的突发传输长度(单位:数据宽度)
u32 slave_id; // 从设备 ID(用于硬件握手信号路由)
};
该结构体通过 dmaengine_slave_config() 函数传递给 DMA 引擎。配置的正确性直接影响传输的稳定性和性能。常见错误包括:地址未对齐、突发长度超过硬件 FIFO 深度、数据宽度不匹配等。
| 字段 | 典型值 | 说明 |
|---|---|---|
| direction | DMA_MEM_TO_DEV | 内存到外设(如 SPI 发送) |
| src_addr | 0x4802A000 | 外设数据寄存器物理地址 |
| src_addr_width | DMA_SLAVE_BUSWIDTH_4_BYTES | 32 位数据宽度 |
| src_maxburst | 8 | 每次突发传输 8 个数据单元 |
| slave_id | 0x1A | 硬件握手信号 ID(平台相关) |
升级注意点: 新版 API 将
dma_slave_config 扩展为 dma_slave_config(名称相同但字段不同),新增了 peripheral_config 和 peripheral_size 字段用于传递平台特定配置。旧版中通过 slave_id 传递的硬件路由信息,在新版中可能被移入 peripheral_config 或设备树属性中。
2.3 dma_async_tx_descriptor:传输描述符
dma_async_tx_descriptor 是旧版 API 中描述一次 DMA 传输的完整元数据。它包含了传输的物理地址、长度、回调函数、以及用于链接多个传输的链表节点。该结构体是异步传输的核心,驱动通过它来提交、回收和跟踪传输状态。
结构体定义:
struct dma_async_tx_descriptor {
dma_cookie_t cookie; // 传输 cookie(用于状态查询)
enum dma_ctrl_flags flags; // 控制标志(如 DMA_PREP_INTERRUPT)
dma_addr_t src; // 源物理地址
dma_addr_t dst; // 目的物理地址
size_t len; // 传输长度(字节)
struct dma_async_tx_descriptor *next; // 链表下一个描述符(用于链式传输)
struct dma_chan *chan; // 所属通道
dma_async_tx_callback callback; // 传输完成回调函数
void *callback_param; // 回调函数参数
struct list_head tx_list; // 用于挂接到通道的待处理队列
};
描述符的生命周期如下:
- 创建: 通过
dmaengine_prep_slave_sg()或dmaengine_prep_dma_cyclic()等函数创建。 - 提交: 通过
dmaengine_submit()将描述符提交到通道的待处理队列。 - 启动: 通过
dma_async_issue_pending()触发 DMA 控制器开始处理。 - 完成: 传输完成后,回调函数被调用,描述符被回收或重新使用。
性能关键点: 旧版 API 中,描述符的
next 指针支持硬件链表(LLI),允许一次提交多个描述符实现连续传输。这是实现高吞吐量 DMA 的关键机制。在新版 API 中,这一机制被 dmaengine_prep_dma_chain() 或 struct dma_async_tx_descriptor 的 tx_submit 回调替代。
2.4 三大结构体的协作关系
在实际的 DMA 传输中,这三个结构体协同工作:
- dma_chan 提供通道上下文,是传输的“容器”。
- dma_slave_config 配置通道的硬件参数,是传输的“调谐器”。
- dma_async_tx_descriptor 描述单次传输的细节,是传输的“任务单”。
| 结构体 | 角色 | 创建方式 | 升级影响 |
|---|---|---|---|
| dma_chan | 通道句柄 | dma_request_channel() | 内部字段简化,私有数据访问方式改变 |
| dma_slave_config | 硬件配置 | 驱动手动填充 | 新增 peripheral_config 字段,slave_id 可能废弃 |
| dma_async_tx_descriptor | 传输描述 | dmaengine_prep_*() 系列函数 | 回调机制保留,链表管理方式更新 |
升级注意点: 旧版 API 中,这三个结构体的内部字段被大量直接访问(如
chan->private、desc->cookie)。在新版 API 中,推荐使用访问器函数(如 dma_cookie_status())替代直接字段访问,以提高代码的可移植性和兼容性。