2. Linux DMA子系统架构:dmaengine框架层次、核心数据结构与驱动/API分层
Linux内核的DMA子系统(dmaengine)是一个通用、抽象的框架,旨在统一管理不同硬件平台的DMA控制器。它通过清晰的层次划分和标准化的API,使得DMA驱动开发者与外围设备驱动开发者能够解耦工作。本章将深入剖析dmaengine框架的层次结构、核心数据结构以及驱动与API的分层模型。
2.1 dmaengine框架层次结构
dmaengine框架采用经典的分层设计,从上到下依次为:用户层/驱动层API、核心管理层、硬件抽象层。这种分层确保了上层使用者无需关心底层硬件细节。
设计思想: 核心管理层是框架的“大脑”,它不直接操作硬件,而是通过一组标准化的回调函数(封装在
struct dma_device 中)来驱动底层硬件。这种设计使得上层驱动(如音频、网络、存储)可以完全独立于具体的DMA控制器型号。
2.2 核心数据结构详解
dmaengine框架围绕三个核心数据结构构建:struct dma_device、struct dma_chan 和 struct dma_async_tx_descriptor。理解它们是掌握DMA子系统的关键。
2.2.1 struct dma_device:DMA控制器的抽象
该结构体代表一个DMA控制器硬件设备,由底层驱动填充并注册到核心层。它包含了控制器的能力描述、操作函数指针集以及通道列表。
| 成员 | 类型/说明 | 作用 |
|---|---|---|
dev |
struct device * |
关联的Linux内核设备模型节点 |
channels |
struct list_head |
该控制器下所有DMA通道的链表头 |
cap_mask |
dma_cap_mask_t |
位掩码,声明控制器支持的操作类型(如DMA_SLAVE、DMA_CYCLIC) |
device_alloc_chan_resources |
回调函数 | 分配并初始化通道资源 |
device_free_chan_resources |
回调函数 | 释放通道资源 |
device_prep_slave_sg |
回调函数 | 准备一个从设备到内存(或反向)的SG列表传输描述符 |
device_issue_pending |
回调函数 | 将提交的待处理事务推送到硬件队列 |
device_tx_status |
回调函数 | 查询传输状态(已完成/进行中/错误) |
2.2.2 struct dma_chan:DMA通道的抽象
每个 dma_chan 实例代表一个逻辑上的DMA通道。它是上层驱动与底层硬件交互的“句柄”。
struct dma_chan {
struct dma_device *device; // 所属的DMA控制器
dma_cookie_t cookie; // 当前事务的cookie(用于跟踪)
struct list_head global_node; // 全局通道链表节点
struct dma_chan_dev *dev; // 对应的字符设备(用于sysfs)
const struct dma_chan_ops *ops; // 通道操作函数(如terminate_all)
// ... 其他成员(如私有数据、状态标志等)
};
2.2.3 struct dma_async_tx_descriptor:传输事务的描述符
这是描述一次DMA传输事务的核心结构体。它包含了源地址、目的地址、传输长度、回调函数以及描述符链管理所需的指针。
| 成员 | 说明 |
|---|---|
cookie |
事务的唯一标识符,用于状态查询 |
callback / callback_param |
传输完成后的回调函数及其参数 |
tx_submit |
将描述符提交到通道的待处理队列 |
desc_free |
释放描述符资源的回调 |
tx_list |
用于构建描述符链的链表头(支持DMA链式传输) |
phys |
描述符的物理地址(用于硬件访问) |
2.3 驱动与API分层
dmaengine框架的驱动模型分为两个层次:DMA控制器驱动 和 DMA引擎使用者驱动。它们通过核心层提供的标准API进行交互。
2.3.1 DMA控制器驱动(底层)
该层负责实现 struct dma_device 中定义的所有回调函数,并调用 dma_async_device_register() 将控制器注册到核心层。开发者需要处理硬件寄存器编程、中断服务、描述符链表的硬件管理等。
// 伪代码:DMA控制器驱动注册流程
static struct dma_device my_dma_device = {
.dev = &pdev->dev,
.cap_mask = ...,
.device_alloc_chan_resources = my_dma_alloc_chan,
.device_free_chan_resources = my_dma_free_chan,
.device_prep_slave_sg = my_dma_prep_slave_sg,
.device_issue_pending = my_dma_issue_pending,
.device_tx_status = my_dma_tx_status,
// ... 其他回调
};
static int my_dma_probe(struct platform_device *pdev) {
// 初始化硬件
// 填充 my_dma_device
dma_async_device_register(&my_dma_device);
return 0;
}
2.3.2 DMA引擎使用者驱动(上层)
该层通常是其他外设驱动(如SPI、I2S、MMC),它们通过dmaengine核心API来请求通道、配置传输、提交事务并等待完成。
// 伪代码:使用者驱动发起DMA传输
struct dma_chan *chan;
struct dma_async_tx_descriptor *tx;
dma_cookie_t cookie;
// 1. 请求DMA通道
chan = dma_request_chan(dev, "rx");
// 2. 配置从设备参数
struct dma_slave_config config = {
.direction = DMA_DEV_TO_MEM,
.src_addr = ...,
.dst_addr = ...,
.src_maxburst = 16,
};
dmaengine_slave_config(chan, &config);
// 3. 准备传输描述符
tx = dmaengine_prep_slave_sg(chan, sgl, sg_len, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
tx->callback = my_dma_callback;
tx->callback_param = my_data;
// 4. 提交事务
cookie = dmaengine_submit(tx);
// 5. 启动传输
dma_async_issue_pending(chan);
// 6. 等待完成(可选)
dma_wait_for_async_tx_or_timeout(tx, ...);
关键API分层总结:
- 通道管理:
dma_request_chan()/dma_release_channel()— 由核心层实现,底层驱动通过device_alloc_chan_resources回调响应。 - 传输控制:
dmaengine_prep_*()系列函数 — 由核心层封装,最终调用底层驱动的device_prep_*()回调。 - 事务提交与执行:
dmaengine_submit()+dma_async_issue_pending()— 核心层管理队列,底层驱动负责硬件触发。 - 状态查询:
dmaengine_tx_status()— 核心层封装,底层驱动通过device_tx_status回调返回硬件状态。
注意事项: 在编写DMA控制器驱动时,必须正确处理描述符的生命周期管理。描述符在
tx_submit 后所有权转移给核心层,底层驱动不应再直接访问或释放它,直到核心层通过回调通知完成。错误的描述符管理是导致内核崩溃的常见原因。