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);
释放操作的核心流程:
- 终止所有进行中的传输:调用
dmaengine_terminate_sync()确保硬件停止,等待所有回调完成。 - 解除中断绑定:释放通道对应的中断线,清除回调上下文。
- 归还通道至空闲池: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传输中,通道管理必须与中断处理严格协同。下图展示了从通道申请到释放的完整生命周期,以及中断回调的介入点。
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控制器的状态查询架构,体现中断与轮询的协同关系。
工程建议:在中断回调中避免调用
dmaengine_tx_status() 查询当前通道状态,因为回调本身已隐含传输完成语义。状态查询应留给非中断路径(如应用层ioctl、定时轮询)使用,以减少中断延迟。