4、DMA通道管理:通道申请、释放与状态查询

DMA通道是系统中有限的硬件资源,驱动开发者必须遵循“申请→使用→释放”的生命周期管理规范。本章聚焦三个核心操作:dma_request_channel(通道申请)、dma_release_channel(通道释放)以及通道状态查询,并深入分析中断协同与缓存一致性场景下的通道管理要点。

4.1 通道申请:dma_request_channel

函数原型定义在 <linux/dmaengine.h> 中,驱动通过该函数向DMA引擎框架申请一个可用通道。申请过程涉及设备树匹配、DMA控制器能力协商以及回调函数绑定。

struct dma_chan *dma_request_channel(const dma_cap_mask_t *mask,
                                     dma_filter_fn filter_fn,
                                     void *filter_param);
参数 类型 说明
mask dma_cap_mask_t * DMA能力掩码,指定所需传输类型(如DMA_SLAVE、DMA_MEMCPY)
filter_fn dma_filter_fn 过滤回调函数,用于从候选通道中选择匹配硬件连接的通道
filter_param void * 传递给过滤函数的私有参数,通常为设备指针或通道ID
最佳实践:在设备树中为DMA客户端节点指定 dmas 和 dma-names 属性,驱动通过 of_dma_request_slave_channel() 或 dma_request_chan() 自动完成过滤,避免手动编写filter_fn。

4.2 通道释放:dma_release_channel

当驱动模块卸载或DMA传输任务结束时,必须显式释放通道,否则会导致资源泄漏,影响其他驱动或后续传输。

void dma_release_channel(struct dma_chan *chan);

释放操作的核心流程:

  1. 终止所有进行中的传输:调用 dmaengine_terminate_sync() 确保硬件停止,等待所有回调完成。
  2. 解除中断绑定:释放通道对应的中断线,清除回调上下文。
  3. 归还通道至空闲池:DMA引擎框架将通道标记为可用,供其他请求者使用。
常见错误:在中断上下文中直接调用 dma_release_channel() 可能导致死锁。释放操作必须在进程上下文(如模块退出函数或文件操作release回调)中执行。

4.3 通道状态查询

DMA引擎框架提供多种状态查询接口,用于监控通道的实时状态、传输进度和错误标志。

接口函数 返回类型 用途
dmaengine_tx_status() enum dma_status 查询指定传输描述符的状态(DMA_COMPLETE / DMA_IN_PROGRESS / DMA_PAUSED / DMA_ERROR)
dmaengine_pause() int 暂停通道传输,配合状态查询实现流控
dmaengine_resume() int 恢复暂停的通道传输
dmaengine_terminate_sync() int 终止传输并等待硬件完全停止,返回最终状态

典型的状态轮询代码片段:

struct dma_chan *chan;
struct dma_tx_state state;
enum dma_status status;

status = dmaengine_tx_status(chan, cookie, &state);
if (status == DMA_COMPLETE) {
    /* 传输完成,处理数据 */
} else if (status == DMA_IN_PROGRESS) {
    /* 仍在传输,可查询已传输字节数 */
    pr_info("Residue: %u bytes\n", state.residue);
} else if (status == DMA_ERROR) {
    /* 硬件错误,需要复位通道 */
    dev_err(chan->device->dev, "DMA channel error\n");
}

4.4 通道生命周期与中断协同

在中断驱动的DMA传输中,通道管理必须与中断处理严格协同。下图展示了从通道申请到释放的完整生命周期,以及中断回调的介入点。

DMA通道生命周期与中断协同流程 dma_request_channel 配置描述符 & 提交 dmaengine_issue_pending 中断回调 (tx_callback) dmaengine_tx_status dma_release_channel 申请时绑定中断号 注册回调函数 硬件开始传输 中断待触发 查询剩余字节 判断完成/错误 先terminate_sync 再释放通道

4.5 缓存一致性对通道管理的影响

在使用DMA进行内存与外设数据传输时,CPU缓存与DMA直接访问的内存之间可能存在数据不一致。通道管理阶段必须考虑以下缓存操作:

  • 申请通道后:对DMA缓冲区执行 dma_map_single() 或 dma_alloc_coherent(),确保CPU和DMA看到一致的内存视图。
  • 传输完成中断中:调用 dma_unmap_single() 配合 DMA_FROM_DEVICE 方向,使CPU缓存失效,读取最新数据。
  • 释放通道前:确保所有DMA映射已解除,避免悬空映射导致缓存污染。
关键原则:通道的生命周期管理必须与DMA缓冲区映射/解映射操作严格配对。在 dma_release_channel() 之前,必须完成所有未完成的 dma_unmap_* 调用,否则可能导致缓存一致性问题在后续传输中暴露。

4.6 多通道场景下的状态查询策略

在复杂嵌入式系统中,一个DMA控制器可能管理多个通道。驱动需要高效查询所有通道的状态,避免阻塞关键路径。

场景 推荐策略 说明
单通道轮询 定时器 + dmaengine_tx_status 适用于低速率、确定性传输,轮询间隔需大于传输时间
多通道批量查询 中断聚合 + 状态位图 每个通道完成中断设置位图标志,主循环统一查询
实时性要求高 中断回调直接处理 在回调中完成数据搬运,避免二次状态查询
错误恢复 dmaengine_terminate_sync + 全状态转储 捕获DMA_ERROR后,读取所有通道寄存器用于调试

以下SVG展示了多通道DMA控制器的状态查询架构,体现中断与轮询的协同关系。

多通道DMA状态查询架构 DMA控制器 通道0 (音频) 通道1 (网络) 通道2 (存储) 通道3 (显示) 中断控制器 (GIC/INTC) 状态查询层 dmaengine_tx_status 状态位图轮询 中断聚合处理 错误恢复流程
工程建议:在中断回调中避免调用 dmaengine_tx_status() 查询当前通道状态,因为回调本身已隐含传输完成语义。状态查询应留给非中断路径(如应用层ioctl、定时轮询)使用,以减少中断延迟。