2. Linux DMA子系统架构:dmaengine框架层次、核心数据结构与驱动/API分层

Linux内核的DMA子系统(dmaengine)是一个通用、抽象的框架,旨在统一管理不同硬件平台的DMA控制器。它通过清晰的层次划分和标准化的API,使得DMA驱动开发者与外围设备驱动开发者能够解耦工作。本章将深入剖析dmaengine框架的层次结构、核心数据结构以及驱动与API的分层模型。

2.1 dmaengine框架层次结构

dmaengine框架采用经典的分层设计,从上到下依次为:用户层/驱动层API、核心管理层、硬件抽象层。这种分层确保了上层使用者无需关心底层硬件细节。

dmaengine 框架层次结构 用户层 / 驱动层 API dmaengine_slave_config() / dmaengine_prep_slave_sg() / dmaengine_submit() / dma_async_issue_pending() 核心管理层 (drivers/dma/dmaengine.c) 通道分配/释放 (dma_request_channel / dma_release_channel) 事务提交队列管理 (tx_submit / issue_pending) 回调通知机制 (dma_async_tx_descriptor 回调) 硬件抽象层 (DMA Controller Driver) struct dma_device / struct dma_chan / 中断处理 / 硬件寄存器操作 DMA 控制器硬件 (PL330 / EDMA / SDMA 等)
设计思想: 核心管理层是框架的“大脑”,它不直接操作硬件,而是通过一组标准化的回调函数(封装在 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 描述符的物理地址(用于硬件访问)
核心数据结构关系图 struct dma_device - channels 链表头 - cap_mask - device_prep_*() 回调 - device_issue_pending() struct dma_chan - device (所属控制器) - cookie - 描述符队列 - ops (terminate_all) dma_async_tx_descriptor - cookie - callback / param - tx_submit() - tx_list (链指针) 关系说明 1. 一个 dma_device 包含多个 dma_chan(通过 channels 链表管理)。 2. 一个 dma_chan 对应一个 dma_device(通过 device 指针反向关联)。 3. 一个 dma_chan 可以处理多个 dma_async_tx_descriptor(通过提交队列和描述符链)。

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 后所有权转移给核心层,底层驱动不应再直接访问或释放它,直到核心层通过回调通知完成。错误的描述符管理是导致内核崩溃的常见原因。

2.4 本章小结