4. 旧版 dmaengine API 回顾:回调机制与事务生命周期管理
在开始升级到新 API 之前,必须深入理解旧版 dmaengine 的核心机制。本章聚焦于 回调机制 与 事务生命周期,这是驱动开发者最容易出错、也是新旧 API 差异最大的两个领域。掌握这些概念,后续的移植工作才能有的放矢。
4.1 事务描述符与回调注册
旧版 API 中,一次 DMA 操作被抽象为一个 dma_async_tx_descriptor 结构体。该结构体不仅描述了传输的源、目的、长度等硬件参数,还内嵌了完成回调函数及其参数。
核心数据结构:
struct dma_async_tx_descriptor 中的回调相关字段。
struct dma_async_tx_descriptor {
dma_cookie_t cookie; /* 事务的唯一标识 */
enum dma_ctrl_flags flags; /* 控制标志,如 DMA_PREP_INTERRUPT */
dma_async_tx_callback callback; /* 完成回调函数指针 */
void *callback_param; /* 回调函数的用户参数 */
struct dma_chan *chan; /* 所属的 DMA 通道 */
/* ... 其他硬件描述字段 ... */
};
回调函数的类型定义如下:
typedef void (*dma_async_tx_callback)(void *callback_param);
驱动开发者通过 tx_submit 将描述符提交到硬件队列前,必须先设置好 callback 和 callback_param。这一设计将“传输描述”与“完成通知”紧密耦合在一起。
4.2 事务生命周期:从分配到完成
一个典型的旧版 DMA 事务经历以下五个阶段。理解每个阶段的状态转换,是正确使用 API 的前提。
关键认知: 旧版 API 中,事务的生命周期是线性的,且回调的触发时机严格绑定在硬件中断处理路径中。
4.3 tx_submit 与 cookie 机制
tx_submit 是连接软件准备与硬件执行的桥梁。调用该函数后,描述符被移交给 DMA 引擎,引擎返回一个 dma_cookie_t 类型的 cookie,用于后续查询事务状态。
| 函数/宏 | 作用 | 返回值 |
|---|---|---|
tx_submit() |
将描述符提交到通道的 pending 队列 | dma_cookie_t (成功) / 负值 (失败) |
dma_async_is_complete() |
检查指定 cookie 对应的事务是否完成 | DMA_COMPLETE / DMA_IN_PROGRESS / DMA_ERROR |
dma_async_tx_descriptor_init() |
初始化描述符,设置 cookie 为初始值 | void |
常见陷阱: 旧版 API 中,
tx_submit 成功后,描述符的所有权转移给 DMA 引擎。驱动不能再修改描述符中的回调函数指针或参数,否则会导致竞态条件或野指针。
4.4 tx_status 与轮询模式
除了依赖回调,旧版 API 也支持通过 tx_status 轮询事务状态。这在不需要中断通知或需要超时控制的场景中非常有用。
enum dma_status dma_async_tx_status(
struct dma_chan *chan,
dma_cookie_t cookie,
struct dma_tx_state *txstate
);
参数 txstate 是一个输出参数,用于返回更详细的状态信息,包括已传输的字节数(residue)和硬件 DMA 计数器的值。
| 状态码 | 含义 | 典型处理 |
|---|---|---|
DMA_COMPLETE |
事务已完成 | 释放资源,触发后续处理 |
DMA_IN_PROGRESS |
事务正在执行或等待执行 | 继续轮询或等待下一次中断 |
DMA_ERROR |
事务执行出错 | 调用错误处理,重置通道 |
4.5 回调机制的实现细节
旧版 API 的回调触发点位于 DMA 引擎的中断处理函数中。典型的实现流程如下:
- 硬件中断到达:DMA 控制器完成一次传输,触发中断。
- 引擎 ISR 处理:调用
device_tx_status确认完成状态,从完成队列中取出描述符。 - 回调执行:在 ISR 上下文中直接调用
desc->callback(desc->callback_param)。 - 描述符回收:将描述符归还给内存池或空闲链表。
性能影响: 旧版回调在中断上下文中执行,因此回调函数必须快速、不可睡眠。任何可能导致阻塞的操作(如
mutex_lock、kmalloc(GFP_KERNEL))都会导致系统崩溃。
4.6 旧版 API 的局限性总结
在进入新 API 之前,先总结旧版设计的主要痛点,这些正是新 API 要解决的核心问题。
| 方面 | 旧版 API 问题 | 后果 |
|---|---|---|
| 回调上下文 | 在 ISR 中直接执行 | 限制回调功能,容易导致死锁 |
| 生命周期管理 | 描述符所有权转移不明确 | 驱动容易误用已释放的描述符 |
| 状态查询 | 依赖 cookie 轮询,接口单一 | 无法高效支持批量完成通知 |
| 错误恢复 | 错误处理路径不统一 | 各驱动实现混乱,可移植性差 |