2. Linux DMA子系统架构:dmaengine框架、dma_device结构体、dma_chan通道模型
在Linux内核中,DMA(Direct Memory Access)子系统通过一套统一的框架——dmaengine,为设备驱动开发者提供了标准化的DMA操作接口。本章将深入剖析dmaengine框架的层次结构、核心数据结构dma_device以及通道模型dma_chan,帮助您理解DMA子系统如何实现从“硬件控制器”到“软件抽象”的完整映射。
2.1 dmaengine框架:三层架构与设计哲学
dmaengine框架的设计遵循“分层抽象、统一接口”的原则,将DMA控制器硬件差异封装在底层,向上层驱动提供一致的API。其整体架构可分为三层:
- DMA引擎核心层(Core Layer):提供框架初始化、通道管理、事务调度等通用逻辑。
- DMA控制器驱动层(Controller Driver):由芯片厂商实现,负责填充
dma_device结构体,注册到核心层。 - DMA用户层(Consumer/User):包括网络、存储、音频等子系统驱动,通过dmaengine API发起DMA传输请求。
设计要点: dmaengine框架通过“回调函数”和“描述符”机制,将硬件DMA通道抽象为软件可管理的资源,支持同步、异步、循环等多种传输模式。
2.2 dma_device结构体:DMA控制器的软件抽象
dma_device是DMA控制器驱动向核心层注册的核心数据结构,它描述了一个DMA控制器的全部能力、资源与操作函数。该结构体定义在include/linux/dmaengine.h中,主要字段如下:
| 字段 | 类型 | 描述 |
|---|---|---|
dev |
struct device * |
指向DMA控制器的设备结构体,用于驱动模型绑定 |
channels |
struct list_head |
该控制器管理的所有DMA通道链表头 |
cap_mask |
dma_cap_mask_t |
位掩码,标识控制器支持的DMA传输类型(如MEMCPY、SG、CYCLIC等) |
directions |
unsigned long |
支持的DMA方向位图(DEV_TO_MEM、MEM_TO_DEV、MEM_TO_MEM) |
src_addr_widths |
unsigned long |
支持的源地址宽度位图(8位、16位、32位、64位) |
dst_addr_widths |
unsigned long |
支持的目的地址宽度位图 |
device_alloc_chan_resources |
函数指针 |
分配并初始化通道硬件资源 |
device_free_chan_resources |
函数指针 |
释放通道硬件资源 |
device_prep_dma_memcpy |
函数指针 |
准备内存到内存的DMA传输描述符 |
device_prep_slave_sg |
函数指针 |
准备外设与内存间的SG(Scatter-Gather)传输描述符 |
device_issue_pending |
函数指针 |
将已提交的描述符推送到硬件执行 |
device_tx_status |
函数指针 |
查询传输状态(已完成字节数、错误码等) |
实战提示: 在编写DMA控制器驱动时,
cap_mask和directions的配置直接影响上层驱动能否成功请求通道。例如,若控制器不支持DMA_SLAVE能力,则外设驱动无法通过dma_request_channel()获取通道。
2.3 dma_chan通道模型:从硬件通道到软件句柄
dma_chan是DMA通道在软件层面的抽象,代表一个可独立调度和使用的DMA传输资源。每个dma_chan实例对应一个硬件DMA通道,并包含以下关键信息:
- 设备归属:
device指针指向所属的dma_device,建立通道与控制器的关联。 - 客户端信息:
client指针指向当前使用该通道的消费者驱动,用于权限管理和回调通知。 - 描述符队列:
desc链表用于管理已提交但尚未完成的传输描述符。 - 状态与配置:包括通道是否空闲、是否循环模式、中断配置等。
通道生命周期: 请求通道(
dma_request_channel)→ 配置通道(dmaengine_slave_config)→ 提交描述符(dmaengine_submit)→ 启动传输(dma_async_issue_pending)→ 传输完成回调 → 释放通道(dma_release_channel)。
2.3.1 通道分配策略
dmaengine框架支持两种通道分配策略:
| 策略 | API | 适用场景 |
|---|---|---|
| 基于能力匹配 | dma_request_channel() |
根据cap_mask和过滤函数自动匹配可用通道 |
| 基于设备树/平台数据 | dma_request_slave_channel() |
通过设备树节点或平台数据指定通道名称,适用于外设与DMA通道固定绑定的场景 |
2.4 核心数据结构关系总览
为了帮助您建立全局认知,下表总结了dmaengine框架中三个核心数据结构的分工与协作关系:
| 数据结构 | 职责 | 关键关联 |
|---|---|---|
dma_device |
描述DMA控制器的能力、资源和操作函数 | 通过channels链表管理多个dma_chan |
dma_chan |
抽象单个DMA通道,维护传输状态和描述符队列 | 通过device指针关联dma_device |
dma_async_tx_descriptor |
描述一次DMA传输任务,包含源地址、目的地址、长度、回调等 | 由dma_device的device_prep_*函数创建,提交到dma_chan的队列 |
常见误区: 初学者容易混淆
dma_device和dma_chan的关系。请记住:dma_device是“控制器”,dma_chan是“通道”。一个控制器可以拥有多个通道,但每个通道只能属于一个控制器。在驱动开发中,您通常操作的是dma_chan句柄,而不是直接操作dma_device。
2.5 本章小结
本章详细介绍了Linux DMA子系统的核心架构:
- dmaengine框架采用三层分层设计,实现了硬件无关的DMA编程接口。
- dma_device结构体是DMA控制器的软件抽象,通过能力位图、方向位图和函数指针暴露硬件能力。
- dma_chan通道模型将硬件通道封装为可分配、配置、提交和释放的软件资源,具有清晰的生命周期状态机。