DMAengine 框架详解

2025-04-16 336 0

DMAengine (Direct Memory Access Engine) 是 Linux 内核中的一个重要框架,用于提供统一的 DMA 子系统接口。DMA 技术允许外设直接与系统内存进行数据传输,无需 CPU 直接参与,从而显著提高系统性能和效率。

DMAengine 框架基础

DMAengine 框架提供了一个抽象层,使各种 DMA 控制器驱动能够以统一的方式与内核交互。主要特点包括:

  1. 设备驱动模型:基于 Linux 设备驱动模型,提供统一注册和管理机制
  2. 异步操作支持:支持异步 DMA 传输,包括完成通知机制
  3. 通道抽象:提供 DMA 通道抽象,简化资源管理
  4. 传输描述符:使用传输描述符来描述 DMA 操作

核心组件

DMAengine 框架中,DMA 操作通常分为以下几个主要组成部分:DMA 设备 (dma_device),DMA 通道 (dma_chan),DMA 描述符 (dma_async_tx_descriptor),DMA 操作 (dmaengine_prep_* 系列函数)。

  1. DMA 控制器结构体 (struct dma_device) 这是 DMA 控制器的抽象表示,包含控制器的基本信息和支持的功能:

    struct dma_device {
     struct device dev;
     enum dma_transaction_type cap_mask;
     unsigned int max_xor;
     unsigned int max_pq;
     int chancnt;
     int privatecnt;
     struct list_head channels;
     struct list_head global_node;
     int (*device_alloc_chan_resources)(struct dma_chan *chan);
     void (*device_free_chan_resources)(struct dma_chan *chan);
     /* ... 其他回调函数 ... */
    };

    dma_device 是 DMA 控制器的主要接口,它管理硬件 DMA 引擎的能力,提供了支持的功能(如 DMA_MEMCPY、DMA_SLAVE)以及用于操作 DMA 通道的回调函数。每个设备(如 DMA 控制器)通常会有一个或多个 DMA 通道。 dma_device 中的一些重要成员:

    1. dev: 关联的设备(如 struct device)。
    2. cap_mask: 支持的 DMA 能力,如 DMA_MEMCPY、DMA_SLAVE。
    3. device_alloc_chan_resources: 为 DMA 通道分配资源的函数。
    4. device_free_chan_resources: 释放 DMA 通道资源的函数。
  2. DMA 通道结构体 (struct dma_chan) 代表 DMA 控制器中的一个通道:

    struct dma_chan {
     struct dma_device *device;
     dma_cookie_t cookie;
     /* ... 其他成员 ... */
     struct dma_chan_dev *dev;
     struct list_head device_node;
     struct kobject kobj;
    };

    每个 dma_device 可以有一个或多个 DMA 通道。dma_chan 是每个物理 DMA 通道的表示。一个通道通常与设备的硬件 DMA 引擎直接相关联。 dma_chan 的一些重要成员:

    1. device: 关联的 dma_device
    2. cookie: 用于标识 DMA 事务的唯一 ID。
    3. desc: 该通道的当前 DMA 描述符(任务)。
    4. device_node: 将 dma_chan 链接到 dma_device 的链表中。
  3. DMA 传输描述符 (struct dma_async_tx_descriptor) 描述一个 DMA 传输操作:

    struct dma_async_tx_descriptor {
     dma_cookie_t cookie;
     enum dma_ctrl_flags flags;
     dma_addr_t phys;
     struct dma_chan *chan;
     dma_cookie_t (*tx_submit)(struct dma_async_tx_descriptor *tx);
     /* ... 其他成员和回调函数 ... */
    };

    每个 DMA 传输操作都由一个描述符(dma_async_tx_descriptor)表示。该描述符包含了 DMA 传输的详细信息,如源地址、目标地址、传输的大小等。 dma_async_tx_descriptor 的一些重要成员:

    1. callback: 完成时执行的回调函数。
    2. callback_param: 回调函数的参数。
    3. flags: DMA 操作的标志,通常用于控制操作的方式(如是否等待完成等)。
  4. DMA 操作函数 (dmaengine_prep_*)

    struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(
     struct dma_chan *chan,
     struct scatterlist *sgl, // 分散/聚集列表
     unsigned int sg_len,     // 列表长度
     enum dma_transfer_direction direction,
     unsigned long flags);
    /* ... 其他成员函数 ... */

    DMAEngine 提供了一些预备函数,这些函数用于准备 DMA 描述符,并提交到相应的通道进行处理。常见的操作函数有:

    1. dmaengine_prep_memcpy: 用于内存到内存的数据复制操作。
    2. dmaengine_prep_slave_sg: 用于设置从设备到内存或从内存到设备的传输。
    3. dmaengine_prep_dma_cyclic: 用于周期性传输(例如音频流)。

DMAengine 工作流程

  1. 申请 DMA 通道

    首先,驱动会使用 dma_request_chan()dma_request_channel() 来申请一个可用的 DMA 通道:

    struct dma_chan *chan;
    chan = dma_request_chan(dev, "tx");
    if (IS_ERR(chan)) {
     pr_err("Failed to request DMA channel\n");
    }
  2. 准备 DMA 描述符

    接下来,为 DMA 操作准备一个描述符,指定数据源、目标、大小等信息:

    struct dma_async_tx_descriptor *desc;
    desc = dmaengine_prep_memcpy(chan, dest, src, len, DMA_PREP_INTERRUPT);
    if (!desc) {
     pr_err("Failed to prepare DMA descriptor\n");
    }
  3. 提交 DMA 描述符

    将描述符提交到 DMA 引擎:

    dmaengine_submit(desc);
  4. 启动 DMA 传输 使用 dma_async_issue_pending() 启动 DMA 传输:

    
    dma_async_issue_pending(chan);
  5. 等待完成 通过 DMA 引擎的回调机制或等待通知来检查传输是否完成。例如,通过 dma_cookie_status() 来检查描述符的状态。

    if (dma_cookie_status(chan, cookie, NULL) == DMA_COMPLETE) {
     pr_info("DMA transfer completed successfully\n");
    }
  6. 清理 完成操作后,释放 DMA 资源:

    dma_release_channel(chan);

主要 API 函数

作用 函数 功能
控制器驱动 API dma_async_device_register() 注册 DMA 控制器
dma_async_device_unregister() 注销 DMA 控制器
客户端 API dma_request_channel() 请求分配 DMA 通道
dma_release_channel() 释放 DMA 通道
dmaengine_prep_dma_memcpy() 准备内存到内存的 DMA 传输
dmaengine_prep_slave_sg() 准备从设备到内存或内存到设备的 DMA 传输
maengine_submit() 提交 DMA 传输描述符
dma_async_issue_pending() 启动挂起的 DMA 传输
dma_async_is_tx_complete() 检查传输是否完成

高级特性

  1. 散列表传输(Scatter-Gather):支持不连续内存区域之间的传输
  2. 循环缓冲区:支持连续的循环 DMA 传输,适用于音频等应用
  3. 中断阈值:可配置中断触发阈值以平衡延迟和中断开销
  4. XOR/PQ 操作:支持 XOR 和 RAID6 PQ 计算
  5. 同步 API:提供阻塞式 API 简化使用
  6. 通道共享:一个物理 DMA 通道可以由多个虚拟通道共享,vchan(虚拟通道)是为了更好地管理多个 DMA 任务而引入的抽象层。
  7. DMA 传输完成回调:使用回调函数来通知 DMA 操作是否成功完成,通常用于异步传输。

常见的 DMA 类型

  1. 内存到内存(Memory-to-Memory, DMA_MEMCPY):将数据从一个内存位置复制到另一个内存位置。
  2. 内存到设备(Memory-to-Device, DMA_MEM_TO_DEV):将数据从内存传输到设备,常见于发送数据到外设(如网络、音频、GPU)。
  3. 设备到内存(Device-to-Memory, DMA_DEV_TO_MEM):从设备接收数据到内存,常见于接收设备数据(如从网卡接收数据到内存)。
  4. 从设备到设备(Device-to-Device: 直接在两个设备之间进行 DMA 传输,这种方式较为少见,通常会通过内存作为中介。

使用场景

DMAengine 框架广泛应用于:

  1. 音频/视频处理:高速数据流传输
  2. 网络设备:网络数据包传输
  3. 存储控制器:磁盘数据传输
  4. SPI/I2C 控制器:外设数据传输
  5. 加密引擎:数据安全处理

开发示例

以下是一个简单的 DMA 内存复制操作示例:

#include <linux/dmaengine.h>

static void dma_complete_callback(void *completion)
{
    complete(completion);
}

int sample_dma_memcpy(dma_addr_t dst, dma_addr_t src, size_t len)
{
    struct dma_chan *chan;
    struct dma_async_tx_descriptor *tx;
    dma_cookie_t cookie;
    enum dma_status status;
    DECLARE_COMPLETION_ONSTACK(completion);

    /* 1. 获取 DMA 通道 */
    chan = dma_request_chan(NULL, "memcpy");
    if (IS_ERR(chan))
        return PTR_ERR(chan);

    /* 2. 准备传输描述符 */
    tx = dmaengine_prep_dma_memcpy(chan, dst, src, len, 0);
    if (!tx) {
        dma_release_channel(chan);
        return -EINVAL;
    }

    /* 3. 设置完成回调 */
    tx->callback = dma_complete_callback;
    tx->callback_param = &completion;

    /* 4. 提交并开始传输 */
    cookie = dmaengine_submit(tx);
    dma_async_issue_pending(chan);

    /* 5. 等待完成 */
    wait_for_completion(&completion);

    /* 6. 检查状态 */
    status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);

    /* 7. 释放通道 */
    dma_release_channel(chan);

    return (status == DMA_COMPLETE) ? 0 : -EIO;
}

总结

DMAengine 框架是 Linux 内核中的一个强大子系统,它提供了统一的接口来管理和使用各种 DMA 控制器,使得驱动开发变得更加简单和标准化。通过减少 CPU 参与数据传输的负担,DMA 技术显著提高了系统性能,特别是在需要大量数据传输的应用场景中。

相关文章

发布评论