Linux驱动子系统:DMA子系统 —— Consumer和Provider(006)

深入理解 Linux DMA 子系统:dmaengine 设计思想、UML 类图与设备树映射

本文按《深入理解 Linux pinctrl 子系统》的同一框架重构 dmaengine:先讲设计思想,再按 Consumer → Core → Provider → DT 映射自上而下展开;关键类统一用 UML 类图描述,并说明类与成员职责;最后给出逻辑漏洞与修正清单。


1. DMA 子系统设计思想

1.1 它要解决的核心问题

DMA(Direct Memory Access)让外设与内存之间可以不经 CPU 搬运数据。它看起来只是"把数据从 A 搬到 B",但进入 Linux 内核后,会立刻遇到七类复杂问题:

  1. 硬件差异:ARM PL330、DesignWare AXI DMAC、Intel ioatdma、TI EDMA、RZN1 DMAC,寄存器模型与描述符格式完全不同。
  2. 通道管理:一个 DMA 控制器有多个物理/逻辑通道,谁用哪条、独占还是共享、怎么匹配外设请求线。
  3. 异步生命周期prep → submit → issue_pending → complete 四段式,每一步都有状态与 cookie。
  4. 描述符管理:SG 链表、循环缓冲、内存拷贝、XOR/PQ、memset、interleaved,描述符类型多样。
  5. 完成通知:硬件中断 → tasklet/线程 → callback,回调上下文与锁必须精确。
  6. 终止与同步terminate_all / terminate_sync / synchronize,语义差异很大。
  7. DT 描述dmas / dma-names / #dma-cells 必须与内核结构一一对应。

dmaengine 的使命,就是用一套统一的 dma_device + dma_chan + dma_async_tx_descriptor 抽象,把这些全部收口到 drivers/dma/

1.2 三个平面:Consumer、Core、Provider

dmaengine 的顶层设计是三层:

text 复制代码
Consumer 层:外设驱动,拿 dma_chan,prep/submit/issue_pending,等 callback
      │
      ▼
Core 层:dmaengine,维护 dma_device_list、of_dma_list、通道分配、cookie、能力匹配
      │
      ▼
Provider 层:DMA 控制器驱动,实现 dma_device 回调,注册 dma_chan
  • Consumerdrivers/mmcspii2cuartnetaudiodisplay 等。
  • Coredrivers/dma/dmaengine.cof-dma.cvirt-dma.c
  • Provider :如 drivers/dma/pl330.c(RK3568 使用 ARM PL330),真正生成微码、读写寄存器、处理 IRQ。

1.3 核心抽象:dma_device / dma_chan / dma_async_tx_descriptor

dmaengine 最有价值的抽象是三个层次:

抽象 归属 语义 谁在用
dma_device Provider DMA 控制器,能力掩码 + 回调表 Provider
dma_chan Core 一条通道,Consumer 的句柄 Consumer
dma_async_tx_descriptor Core 一次异步传输,含 cookie 与回调 Consumer

三者关系:

text 复制代码
dma_device ──1:N──► dma_chan ──1:N──► dma_async_tx_descriptor

设计原则

Provider 实现 dma_device,Core 管理 dma_chan,Consumer 拿 dma_chantx_descriptor。三者解耦。

1.4 异步传输模型:四段式生命周期

dmaengine 最重要的设计是 异步四段式

text 复制代码
prep_xxx()                准备描述符(填充 sg/地址/长度,返回 tx descriptor,未启动)
   │
dmaengine_submit()        提交(分配 cookie,进入 pending 队列)
   │
dmaengine_issue_pending() 触发硬件真正开始
   │
完成中断 → callback(callback_param) → dmaengine_tx_status() 查状态

设计动机

  • 准备阶段可以在任意上下文完成;
  • 提交阶段只分配 cookie,不写硬件;
  • 触发阶段由 Core 统一调用 device_issue_pending()
  • 完成阶段通过 callback 通知 Consumer。

1.5 cookie:异步事务的序号

dma_cookie_t 是每次传输的序号:

  • chan->cookie:最后发出的 cookie;
  • chan->completed_cookie:最后完成的 cookie;
  • dma_cookie_status() 通过比较判断完成/乱序/回绕;
  • 回绕到 0 时自动跳过 DMA_MIN_COOKIEDMA_MAX_COOKIE 之间的无效值。

1.6 能力 + 过滤器分配通道

通道分配有两条路径:

DT 路径

text 复制代码
dma_request_chan(dev, "tx")
   │
   ▼
of_dma_request_slave_channel(np, "tx")
   ├ 解析 dmas/dma-names 找 index
   ├ of_parse_phandle_with_args → of_phandle_args
   ├ of_dma_find_controller() → of_dma
   └ ofdma->of_dma_xlate() → dma_chan

能力 + 过滤器路径

text 复制代码
__dma_request_channel(mask, filter, fn_param)
   ├ 遍历 dma_device_list
   ├ 检查 cap_mask
   ├ 调 filter() 过滤
   └ 返回匹配的 dma_chan

能力掩码dma_cap_mask_t 用位图声明控制器能力:

  • DMA_MEMCPY / DMA_SLAVE / DMA_CYCLIC / DMA_XOR / DMA_PQ / DMA_INTERRUPT / DMA_PRIVATE 等。

引用计数

  • dma_chan.client_count:被多少 client 使用;
  • dma_chan.table_count:mem2mem 分发表引用;
  • dma_device.ref:kref;
  • dmaengine_ref_count:模块引用。

1.7 virt-dma 模板框架

很多 DMA 控制器遵循"环形/链式描述符 + tasklet 完成处理"的模式。virt-dma 为这类驱动提供模板方法:

  • virt_dma_chan:内嵌 dma_chan + 5 个描述符链表 + tasklet;
  • 5 个链表:desc_allocated / desc_submitted / desc_issued / desc_completed / desc_terminated
  • 驱动只需实现 desc_free 和硬件相关部分。

注意 :PL330 没有 使用 virt-dma,而是自建 submitted_list / work_list / completed_list 链表。这是一个重要的设计选择差异。

1.8 与其它子系统的关系

子系统 交互
pinctrl / clk DMA 控制器自身的 pinmux 与时钟
IRQ DMA 完成中断,走 devm_request_irq()
reset reset_control 复位 DMA 控制器
regmap 部分 DMA 控制器用 regmap 访问寄存器
dma-mapping DMA 地址映射与 cache 一致性
async_tx 依赖链与 XOR/PQ 组合

1.9 设计原则总结

  1. 三层抽象dma_device / dma_chan / tx_descriptor 分离。
  2. 异步四段式:prep → submit → issue_pending → callback。
  3. cookie 序号:每个传输一个 cookie,支持完成查询。
  4. 能力匹配cap_mask + filter 决定通道分配。
  5. 回调通知:完成通过 callback 异步通知。
  6. DT 优先dmas / dma-names 描述 Consumer 与 Provider 的绑定。
  7. 模板复用virt-dma 为常见模式提供骨架。

2. 总体架构:自上而下看 dmaengine

2.1 框架图

text 复制代码
┌───────────────────────────────────────────────────────────────────────────────┐
│                     消费者 (Client / Slave 驱动)                                │
│  drivers/mmc|spi|i2c|uart|net|audio|display ...                                │
│  chan = dma_request_chan(dev,"tx")                                             │
│  dmaengine_slave_config() → prep_slave_sg()/prep_dma_cyclic() → submit()       │
│  → issue_pending() → (完成回调) → dmaengine_tx_status()                        │
└───────────────────────────────┬───────────────────────────────────────────────┘
                                │  Consumer API  include/linux/dmaengine.h
                                ▼
┌───────────────────────────────────────────────────────────────────────────────┐
│                  DMA ENGINE CORE  (drivers/dma/)                               │
│                                                                                │
│  dmaengine.c : dma_device_list / dma_chan 分配与能力匹配 / cookie / 模块引用    │
│  of-dma.c    : DT 提供者注册(of_dma_list) + of_phandle_args→dma_chan 翻译      │
│  virt-dma.c  : 虚拟通道框架(virt_dma_chan 的 5 个描述符链表 + tasklet)          │
│  dmaengine.h : 内核内部接口(dma_cookie_*)                                      │
│                                                                                │
│  核心数据模型: dma_device ─1:N─► dma_chan ─1:N─► dma_async_tx_descriptor       │
│                dma_chan(device_node 挂入 device->channels)                     │
│  能力匹配: cap_mask(dma_cap_mask_t) + dma_filter_fn + dma_slave_map            │
│  全局: dma_device_list / of_dma_list / dma_chan_ida / per-cpu 通道表           │
└───────────▲───────────────────────────────────────────────┬──────────────────┘
            │ Provider API (dmaengine.h / of_dma.h / virt-dma.h)
            │ dma_async_device_register() / of_dma_controller_register()
            ▼
┌───────────────────────────────────────┐      ┌───────────────────────────────┐
│  Provider (DMA 控制器驱动)              │      │  DT: dma-controller 节点        │
│  drivers/dma/pl330.c  (ARM PL330)      │◄────►│  dmac0@fe530000 / dmac1@fe550000│
│  struct dma_device + device_* ops      │      │  #dma-cells=<1> ; dmas/dma-names│
│  struct pl330_dmac / dma_pl330_chan    │      │  arm,pl330-periph-burst        │
└───────────────────┬───────────────────┘      └───────────────────────────────┘
                    │ MMIO (pl330->base) + 微码(mcode)
                    ▼
        RK3568 PL330 寄存器 / 通道线程(thread) / 微码执行

2.2 关键源文件

文件 功能
drivers/dma/dmaengine.c 核心dma_device_list 注册/注销、通道分配、能力匹配、cookie 管理
drivers/dma/dmaengine.h 内核内部接口:dma_cookie_init/assign/completedma_chan_get/put
drivers/dma/of-dma.c DT 层of_dma_listof_dma_controller_register()of_dma_request_slave_channel()
drivers/dma/virt-dma.c / virt-dma.h 虚通道框架virt_dma_chan / virt_dma_descvchan_*
drivers/dma/pl330.c RK3568 提供者(ARM PL330)pl330_probe()of_dma_pl330_xlate()、微码生成
include/linux/dmaengine.h 公共 API + 类dma_chandma_devicedma_async_tx_descriptor
include/linux/of_dma.h struct of_dmaof_dma_controller_register

2.3 初始化顺序与依赖

text 复制代码
DMA 控制器 probe
   │
   ├─ devm_ioremap_resource() 映射寄存器
   ├─ devm_request_irq() 注册完成中断
   ├─ 初始化 dma_device
   │     ├─ cap_mask 设置能力
   │     ├─ device_* 回调填充
   │     └─ channels 初始化
   ├─ dma_async_device_register(&pl330->ddma)
   │     └─ 挂入 dma_device_list,创建 sysfs 设备
   └─ of_dma_controller_register(np, of_dma_pl330_xlate, pl330)
         └─ 挂入 of_dma_list

Consumer probe
   │
   ├─ dma_request_chan(dev, "tx")
   │     ├─ of_dma_request_slave_channel()
   │     └─ of_dma_pl330_xlate() → dma_get_slave_channel()
   ├─ dmaengine_slave_config()
   ├─ dmaengine_prep_slave_sg()
   ├─ dmaengine_submit()
   └─ dmaengine_issue_pending()

关键依赖 :Consumer probe 时,Provider 必须已经注册;否则 dma_request_chan() 返回 -EPROBE_DEFER


3. Consumer API:驱动如何使用 DMA

3.1 典型流程

text 复制代码
设备驱动 consumer
   │ chan = dma_request_chan(dev, "tx")           → struct dma_chan *
   │ dmaengine_slave_config(chan, &cfg)           → 配置方向/地址/位宽/突发
   │ tx = dmaengine_prep_slave_sg(chan, sgl, ...) → tx_descriptor
   │ tx->callback = my_callback
   │ tx->callback_param = dev
   │ dmaengine_submit(tx)                         → cookie
   │ dmaengine_issue_pending(chan)                → 触发硬件
   │ ... 等待 callback ...
   │ dmaengine_tx_status(chan, cookie, &state)    → 查状态
   │ dma_release_channel(chan)                    → 释放
   ▼
Consumer 只持有 dma_chan 与 tx_descriptor。

3.2 Consumer 常用 API

c 复制代码
/* 请求通道 */
struct dma_chan *dma_request_chan(struct device *dev, const char *name);
struct dma_chan *dma_request_chan_by_mask(const dma_cap_mask_t *mask);
struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
                                       dma_filter_fn fn, void *fn_param,
                                       struct device_node *np);

/* 释放通道 */
void dma_release_channel(struct dma_chan *chan);

/* 配置通道 */
int dmaengine_slave_config(struct dma_chan *chan, struct dma_slave_config *config);
int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps);

/* 准备描述符 */
struct dma_async_tx_descriptor *
dmaengine_prep_slave_single(struct dma_chan *chan, dma_addr_t buf, size_t len,
                            enum dma_transfer_direction dir, unsigned long flags);
struct dma_async_tx_descriptor *
dmaengine_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                        unsigned int sg_len, enum dma_transfer_direction dir,
                        unsigned long flags);
struct dma_async_tx_descriptor *
dmaengine_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
                          size_t period_len, enum dma_transfer_direction dir,
                          unsigned long flags);
struct dma_async_tx_descriptor *
dmaengine_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
                          size_t len, unsigned long flags);

3.3 提交与触发

c 复制代码
static inline dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc);
static inline void dmaengine_issue_pending(struct dma_chan *chan);
static inline enum dma_status
dmaengine_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
                    struct dma_tx_state *state);

3.4 暂停 / 恢复 / 终止 / 同步

c 复制代码
static inline int dmaengine_pause(struct dma_chan *chan);
static inline int dmaengine_resume(struct dma_chan *chan);

int dmaengine_terminate_all(struct dma_chan *chan);       /* 立即终止(原子上下文) */
int dmaengine_terminate_sync(struct dma_chan *chan);      /* 终止并等待回调(可睡眠) */
int dmaengine_terminate_async(struct dma_chan *chan);     /* 异步终止 */

void dmaengine_synchronize(struct dma_chan *chan);        /* 等待所有回调结束 */

关键区分

API 上下文 语义
dmaengine_terminate_all 原子 立即终止,不等待回调
dmaengine_terminate_sync 可睡眠 终止并等待回调结束
dmaengine_terminate_async 原子 异步终止,不等待
dmaengine_synchronize 可睡眠 仅等待回调,不终止

3.5 元数据与 async_tx

c 复制代码
static inline void async_tx_ack(struct dma_async_tx_descriptor *tx);
static inline bool async_tx_test_ack(struct dma_async_tx_descriptor *tx);

static inline int dmaengine_desc_attach_metadata(struct dma_async_tx_descriptor *desc,
                                                 const void *data, size_t len);
static inline void *dmaengine_desc_get_metadata_ptr(struct dma_async_tx_descriptor *desc,
                                                    size_t *payload_len, size_t *max_len);
static inline int dmaengine_desc_set_metadata_len(struct dma_async_tx_descriptor *desc,
                                                  size_t payload_len);

static inline int dma_submit_error(dma_cookie_t cookie);

3.6 典型使用示例

c 复制代码
static void my_dma_callback(void *param)
{
        struct my_dev *dev = param;
        complete(&dev->dma_done);
}

static int my_start_dma(struct my_dev *dev, struct scatterlist *sgl, int nents)
{
        struct dma_async_tx_descriptor *tx;
        dma_cookie_t cookie;

        tx = dmaengine_prep_slave_sg(dev->chan, sgl, nents,
                                     DMA_MEM_TO_DEV,
                                     DMA_PREP_INTERRUPT);
        if (!tx)
                return -ENOMEM;

        tx->callback = my_dma_callback;
        tx->callback_param = dev;

        cookie = dmaengine_submit(tx);
        if (dma_submit_error(cookie))
                return -EIO;

        dmaengine_issue_pending(dev->chan);
        return 0;
}

4. Core 核心层:dmaengine 的全局容器与状态机

4.1 Core 的职责

  1. 维护全局容器:dma_device_listof_dma_list
  2. 分配与回收 dma_chan
  3. 实现通道分配与能力匹配。
  4. 管理 cookie。
  5. 提供 dma_async_device_register()
  6. 提供 sysfs 与 debugfs。
  7. 模块引用计数。

4.2 通道分配流程

text 复制代码
Consumer: dma_request_chan(dev, "tx")
    │
    ├─ 路径 A(DT):of_dma_request_slave_channel(np, "tx")
    │     ├─ of_property_count_strings(np, "dma-names") 定位索引
    │     ├─ of_parse_phandle_with_args(np, "dmas", "#dma-cells", i, &dma_spec)
    │     ├─ of_dma_find_controller(&dma_spec) 在 of_dma_list 中匹配
    │     └─ ofdma->of_dma_xlate(&dma_spec, ofdma) → dma_chan
    │
    └─ 路径 B(mask + filter):__dma_request_channel(mask, filter, fn_param)
          ├─ 遍历 dma_device_list
          ├─ 检查 cap_mask
          ├─ 调 filter() 过滤
          └─ 返回匹配的 dma_chan
text 复制代码
dmaengine_submit(desc)
    │
    ▼
desc->tx_submit(desc)
    │
    ├─ 分配 cookie(dma_cookie_assign)
    │     ├─ cookie = chan->cookie + 1
    │     ├─ 回绕时跳过 DMA_MIN_COOKIE / DMA_MAX_COOKIE
    │     └─ chan->cookie = cookie
    │
    └─ 描述符进入 pending 队列

完成时:
    dma_cookie_complete(desc)
        ├─ chan->completed_cookie = desc->cookie
        └─ 调度 callback

4.4 完成通知

text 复制代码
硬件完成中断
    │
    ▼
Provider IRQ handler
    │
    ├─ 读取中断状态
    ├─ 确定完成的描述符
    ├─ 调 dma_cookie_complete(desc)
    └─ 调度 tasklet / 线程

tasklet / 线程
    │
    ├─ 取 desc->callback
    ├─ 调 callback(desc->callback_param)
    └─ 清理描述符

4.5 能力匹配

dma_cap_mask_t 是位图:

能力 含义
DMA_MEMCPY 内存到内存拷贝
DMA_SLAVE 从设备传输
DMA_CYCLIC 循环传输
DMA_XOR XOR 计算
DMA_PQ PQ 计算
DMA_INTERRUPT 仅触发中断
DMA_PRIVATE 私有通道(从设备专用)

4.6 全局容器

c 复制代码
static LIST_HEAD(dma_device_list);      /* 所有 dma_device */
static DEFINE_MUTEX(dma_list_mutex);
static struct dma_chan_tbl_ent __percpu *channel_table[DMA_TX_TYPE_END];
static DEFINE_IDA(dma_ida);             /* 控制器 id */
static int dmaengine_ref_count;         /* 模块引用 */

/* of-dma.c */
static LIST_HEAD(of_dma_list);          /* 所有 of_dma 提供者 */
static DEFINE_MUTEX(of_dma_lock);

5. Provider API:DMA 控制器驱动如何注册

5.1 Provider 的核心类

作用
dma_device DMA 控制器抽象,能力掩码 + 回调表
dma_chan 通道,内嵌于 Provider 私有结构
of_dma DT 提供者注册节点
virt_dma_chan 虚通道模板

5.2 注册流程

注册控制器

c 复制代码
int dma_async_device_register(struct dma_device *device);
int dmaenginem_async_device_register(struct dma_device *device);   /* devm 版 */
void dma_async_device_unregister(struct dma_device *device);

int dma_async_device_channel_register(struct dma_device *device, struct dma_chan *chan);
void dma_async_device_channel_unregister(struct dma_device *device, struct dma_chan *chan);

注册 DT 提供者

c 复制代码
int of_dma_controller_register(struct device_node *np,
                               struct dma_chan *(*of_dma_xlate)
                                   (struct of_phandle_args *, struct of_dma *),
                               void *data);
void of_dma_controller_free(struct device_node *np);

int of_dma_router_register(struct device_node *np,
                           void *(*of_dma_route_allocate)
                               (struct of_phandle_args *, struct of_dma *),
                           struct dma_router *dma_router);

内置 xlate

c 复制代码
struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma);
struct dma_chan *of_dma_xlate_by_chan_id(struct of_phandle_args *dma_spec, struct of_dma *ofdma);

获取通道

c 复制代码
struct dma_chan *dma_get_slave_channel(struct dma_chan *chan);
struct dma_chan *dma_get_any_slave_channel(struct dma_device *device);

5.3 virt-dma 模板

c 复制代码
void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev);
dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx);
int vchan_tx_desc_free(struct dma_async_tx_descriptor *tx);
struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *vc, dma_cookie_t cookie);
void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head);
void vchan_get_all_descriptors(struct virt_dma_chan *vc, struct list_head *head);
bool vchan_issue_pending(struct virt_dma_chan *vc);
void vchan_cookie_complete(struct virt_dma_desc *vd);
struct virt_dma_desc *vchan_next_desc(struct virt_dma_chan *vc);
void vchan_cyclic_callback(struct virt_dma_desc *vd);
void vchan_terminate_vdesc(struct virt_dma_desc *vd);
void vchan_vdesc_fini(struct virt_dma_desc *vd);
void vchan_free_chan_resources(struct virt_dma_chan *vc);
void vchan_synchronize(struct virt_dma_chan *vc);

5.4 PL330 专有类

c 复制代码
struct pl330_dmac {                  /* 控制器 */
        struct dma_device ddma;      /* 内嵌 dma_device */
        struct list_head desc_pool;
        spinlock_t pool_lock;
        unsigned mcbufsz;
        void __iomem *base;
        struct pl330_config pcfg;
        int events[32];
        dma_addr_t mcode_bus;
        void *mcode_cpu;
        struct pl330_thread *channels, *manager;
        unsigned int num_peripherals;
        struct dma_pl330_chan *peripherals;
        int quirks;
        struct reset_control *rstc, *rstc_ocp;
};

struct dma_pl330_chan {              /* 逻辑通道 */
        struct tasklet_struct task;
        struct dma_chan chan;        /* 内嵌 dma_chan */
        struct list_head submitted_list, work_list, completed_list;
        struct pl330_dmac *dmac;
        spinlock_t lock;
        struct pl330_thread *thread; /* 硬件线程 */
        int burst_sz, burst_len;
        phys_addr_t fifo_addr;
        enum dma_data_direction dir;
        struct dma_slave_config slave_config;
};

struct dma_pl330_desc {              /* 描述符 */
        struct list_head node;
        struct dma_async_tx_descriptor txd;  /* 内嵌 */
        struct pl330_xfer px;
        struct pl330_reqcfg rqcfg;
        enum desc_status status;             /* FREE/PREP/BUSY/DONE */
        int bytes_requested;
        bool last;
        struct dma_pl330_chan *pchan;
        enum dma_transfer_direction rqtype;
        unsigned peri:5;
        struct list_head rqd;
        bool cyclic;
        size_t num_periods;
};

注意 :PL330 没有 使用 virt-dma,而是自建 submitted_list / work_list / completed_list 三个链表。这是一个重要的设计选择。


6. UML 类图:从 Consumer 到 Provider

说明:Mermaid 类图中的 note 描述类的作用;类图后的表格补充关键成员职责。

6.1 Consumer 句柄类图

#mermaid-svg-h2MKYxTiBzrGz0Ry{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-h2MKYxTiBzrGz0Ry .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-h2MKYxTiBzrGz0Ry .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-h2MKYxTiBzrGz0Ry .error-icon{fill:#552222;}#mermaid-svg-h2MKYxTiBzrGz0Ry .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-h2MKYxTiBzrGz0Ry .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-h2MKYxTiBzrGz0Ry .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-h2MKYxTiBzrGz0Ry .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-h2MKYxTiBzrGz0Ry .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-h2MKYxTiBzrGz0Ry .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-h2MKYxTiBzrGz0Ry .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-h2MKYxTiBzrGz0Ry .marker{fill:#333333;stroke:#333333;}#mermaid-svg-h2MKYxTiBzrGz0Ry .marker.cross{stroke:#333333;}#mermaid-svg-h2MKYxTiBzrGz0Ry svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-h2MKYxTiBzrGz0Ry p{margin:0;}#mermaid-svg-h2MKYxTiBzrGz0Ry g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-h2MKYxTiBzrGz0Ry g.classGroup text .title{font-weight:bolder;}#mermaid-svg-h2MKYxTiBzrGz0Ry .cluster-label text{fill:#333;}#mermaid-svg-h2MKYxTiBzrGz0Ry .cluster-label span{color:#333;}#mermaid-svg-h2MKYxTiBzrGz0Ry .cluster-label span p{background-color:transparent;}#mermaid-svg-h2MKYxTiBzrGz0Ry .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-h2MKYxTiBzrGz0Ry .cluster text{fill:#333;}#mermaid-svg-h2MKYxTiBzrGz0Ry .cluster span{color:#333;}#mermaid-svg-h2MKYxTiBzrGz0Ry .nodeLabel,#mermaid-svg-h2MKYxTiBzrGz0Ry .edgeLabel{color:#131300;}#mermaid-svg-h2MKYxTiBzrGz0Ry .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-h2MKYxTiBzrGz0Ry .label text{fill:#131300;}#mermaid-svg-h2MKYxTiBzrGz0Ry .labelBkg{background:#ECECFF;}#mermaid-svg-h2MKYxTiBzrGz0Ry .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-h2MKYxTiBzrGz0Ry .classTitle{font-weight:bolder;}#mermaid-svg-h2MKYxTiBzrGz0Ry .node rect,#mermaid-svg-h2MKYxTiBzrGz0Ry .node circle,#mermaid-svg-h2MKYxTiBzrGz0Ry .node ellipse,#mermaid-svg-h2MKYxTiBzrGz0Ry .node polygon,#mermaid-svg-h2MKYxTiBzrGz0Ry .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-h2MKYxTiBzrGz0Ry .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-h2MKYxTiBzrGz0Ry g.clickable{cursor:pointer;}#mermaid-svg-h2MKYxTiBzrGz0Ry g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-h2MKYxTiBzrGz0Ry g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-h2MKYxTiBzrGz0Ry .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-h2MKYxTiBzrGz0Ry .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-h2MKYxTiBzrGz0Ry .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-h2MKYxTiBzrGz0Ry .dashed-line{stroke-dasharray:3;}#mermaid-svg-h2MKYxTiBzrGz0Ry .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-h2MKYxTiBzrGz0Ry #compositionStart,#mermaid-svg-h2MKYxTiBzrGz0Ry .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-h2MKYxTiBzrGz0Ry #compositionEnd,#mermaid-svg-h2MKYxTiBzrGz0Ry .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-h2MKYxTiBzrGz0Ry #dependencyStart,#mermaid-svg-h2MKYxTiBzrGz0Ry .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-h2MKYxTiBzrGz0Ry #dependencyStart,#mermaid-svg-h2MKYxTiBzrGz0Ry .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-h2MKYxTiBzrGz0Ry #extensionStart,#mermaid-svg-h2MKYxTiBzrGz0Ry .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-h2MKYxTiBzrGz0Ry #extensionEnd,#mermaid-svg-h2MKYxTiBzrGz0Ry .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-h2MKYxTiBzrGz0Ry #aggregationStart,#mermaid-svg-h2MKYxTiBzrGz0Ry .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-h2MKYxTiBzrGz0Ry #aggregationEnd,#mermaid-svg-h2MKYxTiBzrGz0Ry .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-h2MKYxTiBzrGz0Ry #lollipopStart,#mermaid-svg-h2MKYxTiBzrGz0Ry .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-h2MKYxTiBzrGz0Ry #lollipopEnd,#mermaid-svg-h2MKYxTiBzrGz0Ry .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-h2MKYxTiBzrGz0Ry .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-h2MKYxTiBzrGz0Ry .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-h2MKYxTiBzrGz0Ry .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-h2MKYxTiBzrGz0Ry .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-h2MKYxTiBzrGz0Ry :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} device
chan
提交
1
1
DmaChan
+device : DmaDevice
+slave : device
+cookie : dma_cookie_t
+completed_cookie : dma_cookie_t
+chan_id : int
+dev : DmaChanDev
+name : const char
+device_node : list_head
+local : DmaChanPercpu
+client_count : int
+table_count : int
+router : DmaRouter
+route_data : void
+private : void
DmaSlaveConfig
+direction : enum dma_transfer_direction
+src_addr : phys_addr_t
+dst_addr : phys_addr_t
+src_addr_width : enum dma_slave_buswidth
+dst_addr_width : enum dma_slave_buswidth
+src_maxburst : u32
+dst_maxburst : u32
+device_fc : bool
+slave_id : unsigned int
+peripheral_config : void
+peripheral_size : size_t
DmaSlaveCaps
+src_addr_widths : u32
+dst_addr_widths : u32
+directions : u32
+min_burst : u32
+max_burst : u32
+cmd_pause : bool
+cmd_resume : bool
+cmd_terminate : bool
+residue_granularity : enum
+descriptor_reuse : bool
DmaAsyncTxDescriptor
+cookie : dma_cookie_t
+flags : enum dma_ctrl_flags
+phys : dma_addr_t
+chan : DmaChan
+callback : dma_async_tx_callback
+callback_result : dma_async_tx_callback_result
+callback_param : void
+unmap : DmaengineUnmapData
+tx_submit()
+desc_free()
DmaTxState
+residue : unsigned int
+in_flight : unsigned int
+last : dma_cookie_t
DmaDevice
通道句柄:Consumer 持有的核心对象。
从设备通道运行时配置:方向/地址/位宽/突发。
通道能力:Consumer 可查询。
异步事务描述符:cookie + tx_submit + callback。
传输状态:残留字节与 in_flight。

作用 关键成员说明
DmaChan 通道句柄 device 所属控制器;cookie/completed_cookie 状态;client_count/table_count 引用计数;private 驱动私有
DmaSlaveConfig 从设备配置 direction 方向;src_addr/dst_addr 外设 FIFO;*_maxburst 突发;slave_id 请求线
DmaSlaveCaps 通道能力 *_addr_widths 位宽;directions 支持方向;residue_granularity 残留粒度
DmaAsyncTxDescriptor 异步描述符 cookie 序号;tx_submit 提交回调;callback/callback_result 完成通知
DmaTxState 传输状态 residue 残留;in_flight 已传;last 最后 cookie

6.2 Provider 类图

#mermaid-svg-2buBO0heEvbMorz5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2buBO0heEvbMorz5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2buBO0heEvbMorz5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2buBO0heEvbMorz5 .error-icon{fill:#552222;}#mermaid-svg-2buBO0heEvbMorz5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2buBO0heEvbMorz5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2buBO0heEvbMorz5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2buBO0heEvbMorz5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2buBO0heEvbMorz5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2buBO0heEvbMorz5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2buBO0heEvbMorz5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2buBO0heEvbMorz5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2buBO0heEvbMorz5 .marker.cross{stroke:#333333;}#mermaid-svg-2buBO0heEvbMorz5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2buBO0heEvbMorz5 p{margin:0;}#mermaid-svg-2buBO0heEvbMorz5 g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-2buBO0heEvbMorz5 g.classGroup text .title{font-weight:bolder;}#mermaid-svg-2buBO0heEvbMorz5 .cluster-label text{fill:#333;}#mermaid-svg-2buBO0heEvbMorz5 .cluster-label span{color:#333;}#mermaid-svg-2buBO0heEvbMorz5 .cluster-label span p{background-color:transparent;}#mermaid-svg-2buBO0heEvbMorz5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2buBO0heEvbMorz5 .cluster text{fill:#333;}#mermaid-svg-2buBO0heEvbMorz5 .cluster span{color:#333;}#mermaid-svg-2buBO0heEvbMorz5 .nodeLabel,#mermaid-svg-2buBO0heEvbMorz5 .edgeLabel{color:#131300;}#mermaid-svg-2buBO0heEvbMorz5 .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-2buBO0heEvbMorz5 .label text{fill:#131300;}#mermaid-svg-2buBO0heEvbMorz5 .labelBkg{background:#ECECFF;}#mermaid-svg-2buBO0heEvbMorz5 .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-2buBO0heEvbMorz5 .classTitle{font-weight:bolder;}#mermaid-svg-2buBO0heEvbMorz5 .node rect,#mermaid-svg-2buBO0heEvbMorz5 .node circle,#mermaid-svg-2buBO0heEvbMorz5 .node ellipse,#mermaid-svg-2buBO0heEvbMorz5 .node polygon,#mermaid-svg-2buBO0heEvbMorz5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2buBO0heEvbMorz5 .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-2buBO0heEvbMorz5 g.clickable{cursor:pointer;}#mermaid-svg-2buBO0heEvbMorz5 g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-2buBO0heEvbMorz5 g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-2buBO0heEvbMorz5 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-2buBO0heEvbMorz5 .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-2buBO0heEvbMorz5 .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-2buBO0heEvbMorz5 .dashed-line{stroke-dasharray:3;}#mermaid-svg-2buBO0heEvbMorz5 .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-2buBO0heEvbMorz5 #compositionStart,#mermaid-svg-2buBO0heEvbMorz5 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2buBO0heEvbMorz5 #compositionEnd,#mermaid-svg-2buBO0heEvbMorz5 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2buBO0heEvbMorz5 #dependencyStart,#mermaid-svg-2buBO0heEvbMorz5 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2buBO0heEvbMorz5 #dependencyStart,#mermaid-svg-2buBO0heEvbMorz5 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2buBO0heEvbMorz5 #extensionStart,#mermaid-svg-2buBO0heEvbMorz5 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2buBO0heEvbMorz5 #extensionEnd,#mermaid-svg-2buBO0heEvbMorz5 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2buBO0heEvbMorz5 #aggregationStart,#mermaid-svg-2buBO0heEvbMorz5 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2buBO0heEvbMorz5 #aggregationEnd,#mermaid-svg-2buBO0heEvbMorz5 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2buBO0heEvbMorz5 #lollipopStart,#mermaid-svg-2buBO0heEvbMorz5 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2buBO0heEvbMorz5 #lollipopEnd,#mermaid-svg-2buBO0heEvbMorz5 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2buBO0heEvbMorz5 .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-2buBO0heEvbMorz5 .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2buBO0heEvbMorz5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2buBO0heEvbMorz5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2buBO0heEvbMorz5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} filter
map
dma_router
DmaDevice
+ref : kref
+chancnt : unsigned int
+privatecnt : unsigned int
+channels : list_head
+global_node : list_head
+filter : DmaFilter
+cap_mask : dma_cap_mask_t
+dev_id : int
+dev : device
+owner : module
+device_alloc_chan_resources()
+device_free_chan_resources()
+device_prep_dma_memcpy()
+device_prep_slave_sg()
+device_prep_dma_cyclic()
+device_prep_interleaved_dma()
+device_caps()
+device_config()
+device_pause()
+device_resume()
+device_terminate_all()
+device_synchronize()
+device_tx_status()
+device_issue_pending()
+device_release()
DmaFilter
+fn : dma_filter_fn
+map : DmaSlaveMap
+mapcnt : int
DmaSlaveMap
+devname : const char
+slave : const char
+param : void
OfDma
+of_dma_controllers : list_head
+of_node : device_node
+dma_router : DmaRouter
+of_dma_data : void
+of_dma_xlate()
+of_dma_route_allocate()
DmaRouter
DMA 控制器抽象:Provider 实现全部 device_* 回调。
过滤器:fn 回调 + dma_slave_map 静态映射。
DT 提供者注册节点:xlate 把 phandle 参数转成 dma_chan。

作用 关键成员说明
DmaDevice 控制器 channels 通道链表;cap_mask 能力;device_prep_* 描述符准备;device_issue_pending 触发
DmaFilter 过滤器 fn 过滤回调;map 静态映射
DmaSlaveMap 静态映射 devname/slave 匹配键;param 参数
OfDma DT 提供者 of_dma_xlate 翻译回调;of_dma_data 私有数据

6.3 虚通道框架类图

#mermaid-svg-S0rgqT2Wmf3ZMWCm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-S0rgqT2Wmf3ZMWCm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .error-icon{fill:#552222;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .marker.cross{stroke:#333333;}#mermaid-svg-S0rgqT2Wmf3ZMWCm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-S0rgqT2Wmf3ZMWCm p{margin:0;}#mermaid-svg-S0rgqT2Wmf3ZMWCm g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-S0rgqT2Wmf3ZMWCm g.classGroup text .title{font-weight:bolder;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .cluster-label text{fill:#333;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .cluster-label span{color:#333;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .cluster-label span p{background-color:transparent;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .cluster text{fill:#333;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .cluster span{color:#333;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .nodeLabel,#mermaid-svg-S0rgqT2Wmf3ZMWCm .edgeLabel{color:#131300;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .label text{fill:#131300;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .labelBkg{background:#ECECFF;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .classTitle{font-weight:bolder;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .node rect,#mermaid-svg-S0rgqT2Wmf3ZMWCm .node circle,#mermaid-svg-S0rgqT2Wmf3ZMWCm .node ellipse,#mermaid-svg-S0rgqT2Wmf3ZMWCm .node polygon,#mermaid-svg-S0rgqT2Wmf3ZMWCm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-S0rgqT2Wmf3ZMWCm g.clickable{cursor:pointer;}#mermaid-svg-S0rgqT2Wmf3ZMWCm g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-S0rgqT2Wmf3ZMWCm g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .dashed-line{stroke-dasharray:3;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-S0rgqT2Wmf3ZMWCm #compositionStart,#mermaid-svg-S0rgqT2Wmf3ZMWCm .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-S0rgqT2Wmf3ZMWCm #compositionEnd,#mermaid-svg-S0rgqT2Wmf3ZMWCm .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-S0rgqT2Wmf3ZMWCm #dependencyStart,#mermaid-svg-S0rgqT2Wmf3ZMWCm .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-S0rgqT2Wmf3ZMWCm #dependencyStart,#mermaid-svg-S0rgqT2Wmf3ZMWCm .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-S0rgqT2Wmf3ZMWCm #extensionStart,#mermaid-svg-S0rgqT2Wmf3ZMWCm .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-S0rgqT2Wmf3ZMWCm #extensionEnd,#mermaid-svg-S0rgqT2Wmf3ZMWCm .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-S0rgqT2Wmf3ZMWCm #aggregationStart,#mermaid-svg-S0rgqT2Wmf3ZMWCm .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-S0rgqT2Wmf3ZMWCm #aggregationEnd,#mermaid-svg-S0rgqT2Wmf3ZMWCm .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-S0rgqT2Wmf3ZMWCm #lollipopStart,#mermaid-svg-S0rgqT2Wmf3ZMWCm .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-S0rgqT2Wmf3ZMWCm #lollipopEnd,#mermaid-svg-S0rgqT2Wmf3ZMWCm .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-S0rgqT2Wmf3ZMWCm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-S0rgqT2Wmf3ZMWCm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 5 个链表
tx_result
1
*
VirtDmaDesc
+tx : DmaAsyncTxDescriptor
+tx_result : DmaengineResult
+node : list_head
VirtDmaChan
+chan : DmaChan
+task : tasklet_struct
+lock : spinlock_t
+desc_allocated : list_head
+desc_submitted : list_head
+desc_issued : list_head
+desc_completed : list_head
+desc_terminated : list_head
+cyclic : VirtDmaDesc
+desc_free()
DmaengineResult
+result : enum dmaengine_tx_result
+residue : u32
虚描述符:内嵌 tx + 结果 + 链表节点。
虚通道:内嵌 dma_chan + tasklet + 5 个描述符链表。
传输结果:NOERROR/READ_FAILED/WRITE_FAILED/ABORTED。

作用 关键成员说明
VirtDmaDesc 虚描述符 tx 内嵌异步描述符;tx_result 结果;node 链表节点
VirtDmaChan 虚通道 task 完成处理;desc_* 5 个链表;desc_free 驱动实现
DmaengineResult 传输结果 result 错误类型;residue 残留

5 个链表的状态流转

text 复制代码
desc_allocated ──vchan_tx_submit──► desc_submitted
                                          │
                                     vchan_issue_pending
                                          ▼
                                    desc_issued
                                          │
                                    vchan_cookie_complete
                                          ▼
                                    desc_completed

desc_terminated ◄── vchan_terminate_vdesc(终止时)

6.4 PL330 Provider 类图

#mermaid-svg-bWztjWcAWbAIgCm2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bWztjWcAWbAIgCm2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bWztjWcAWbAIgCm2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bWztjWcAWbAIgCm2 .error-icon{fill:#552222;}#mermaid-svg-bWztjWcAWbAIgCm2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bWztjWcAWbAIgCm2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bWztjWcAWbAIgCm2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bWztjWcAWbAIgCm2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bWztjWcAWbAIgCm2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bWztjWcAWbAIgCm2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bWztjWcAWbAIgCm2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bWztjWcAWbAIgCm2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bWztjWcAWbAIgCm2 .marker.cross{stroke:#333333;}#mermaid-svg-bWztjWcAWbAIgCm2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bWztjWcAWbAIgCm2 p{margin:0;}#mermaid-svg-bWztjWcAWbAIgCm2 g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-bWztjWcAWbAIgCm2 g.classGroup text .title{font-weight:bolder;}#mermaid-svg-bWztjWcAWbAIgCm2 .cluster-label text{fill:#333;}#mermaid-svg-bWztjWcAWbAIgCm2 .cluster-label span{color:#333;}#mermaid-svg-bWztjWcAWbAIgCm2 .cluster-label span p{background-color:transparent;}#mermaid-svg-bWztjWcAWbAIgCm2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bWztjWcAWbAIgCm2 .cluster text{fill:#333;}#mermaid-svg-bWztjWcAWbAIgCm2 .cluster span{color:#333;}#mermaid-svg-bWztjWcAWbAIgCm2 .nodeLabel,#mermaid-svg-bWztjWcAWbAIgCm2 .edgeLabel{color:#131300;}#mermaid-svg-bWztjWcAWbAIgCm2 .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-bWztjWcAWbAIgCm2 .label text{fill:#131300;}#mermaid-svg-bWztjWcAWbAIgCm2 .labelBkg{background:#ECECFF;}#mermaid-svg-bWztjWcAWbAIgCm2 .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-bWztjWcAWbAIgCm2 .classTitle{font-weight:bolder;}#mermaid-svg-bWztjWcAWbAIgCm2 .node rect,#mermaid-svg-bWztjWcAWbAIgCm2 .node circle,#mermaid-svg-bWztjWcAWbAIgCm2 .node ellipse,#mermaid-svg-bWztjWcAWbAIgCm2 .node polygon,#mermaid-svg-bWztjWcAWbAIgCm2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bWztjWcAWbAIgCm2 .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-bWztjWcAWbAIgCm2 g.clickable{cursor:pointer;}#mermaid-svg-bWztjWcAWbAIgCm2 g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-bWztjWcAWbAIgCm2 g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-bWztjWcAWbAIgCm2 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-bWztjWcAWbAIgCm2 .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-bWztjWcAWbAIgCm2 .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-bWztjWcAWbAIgCm2 .dashed-line{stroke-dasharray:3;}#mermaid-svg-bWztjWcAWbAIgCm2 .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-bWztjWcAWbAIgCm2 #compositionStart,#mermaid-svg-bWztjWcAWbAIgCm2 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-bWztjWcAWbAIgCm2 #compositionEnd,#mermaid-svg-bWztjWcAWbAIgCm2 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-bWztjWcAWbAIgCm2 #dependencyStart,#mermaid-svg-bWztjWcAWbAIgCm2 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-bWztjWcAWbAIgCm2 #dependencyStart,#mermaid-svg-bWztjWcAWbAIgCm2 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-bWztjWcAWbAIgCm2 #extensionStart,#mermaid-svg-bWztjWcAWbAIgCm2 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-bWztjWcAWbAIgCm2 #extensionEnd,#mermaid-svg-bWztjWcAWbAIgCm2 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-bWztjWcAWbAIgCm2 #aggregationStart,#mermaid-svg-bWztjWcAWbAIgCm2 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-bWztjWcAWbAIgCm2 #aggregationEnd,#mermaid-svg-bWztjWcAWbAIgCm2 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-bWztjWcAWbAIgCm2 #lollipopStart,#mermaid-svg-bWztjWcAWbAIgCm2 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-bWztjWcAWbAIgCm2 #lollipopEnd,#mermaid-svg-bWztjWcAWbAIgCm2 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-bWztjWcAWbAIgCm2 .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-bWztjWcAWbAIgCm2 .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bWztjWcAWbAIgCm2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bWztjWcAWbAIgCm2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bWztjWcAWbAIgCm2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} peripherals
channels
3 个链表
thread
desc_pool
1
1
*
*
Pl330Dmac
+ddma : DmaDevice
+desc_pool : list_head
+pool_lock : spinlock_t
+mcbufsz : unsigned
+base : void __iomem
+pcfg : pl330_config
+events : int32
+mcode_bus : dma_addr_t
+mcode_cpu : void
+channels : Pl330Thread
+manager : Pl330Thread
+num_peripherals : unsigned int
+peripherals : DmaPl330Chan
+quirks : int
+rstc : reset_control
+rstc_ocp : reset_control
DmaPl330Chan
+task : tasklet_struct
+chan : DmaChan
+submitted_list : list_head
+work_list : list_head
+completed_list : list_head
+dmac : Pl330Dmac
+lock : spinlock_t
+thread : Pl330Thread
+burst_sz : int
+burst_len : int
+fifo_addr : phys_addr_t
+dir : enum dma_data_direction
+slave_config : DmaSlaveConfig
DmaPl330Desc
+node : list_head
+txd : DmaAsyncTxDescriptor
+px : pl330_xfer
+rqcfg : pl330_reqcfg
+status : enum desc_status
+bytes_requested : int
+last : bool
+pchan : DmaPl330Chan
+rqtype : enum dma_transfer_direction
+peri : unsigned
+rqd : list_head
+cyclic : bool
+num_periods : size_t
Pl330Thread
+id : u8
+ev : int
+free : bool
+dmac : Pl330Dmac
+req : _pl330_req2
+lstenq : unsigned
+req_running : int
PL330 控制器:内嵌 dma_device + 微码 + 线程池。
PL330 逻辑通道:内嵌 dma_chan + 3 个链表。
PL330 描述符:内嵌 txd + 微码参数 + 状态。
PL330 硬件线程:执行微码。

作用 关键成员说明
Pl330Dmac 控制器 ddma 内嵌 dma_devicedesc_pool 描述符池;channels 线程池;peripherals 逻辑通道
DmaPl330Chan 逻辑通道 chan 内嵌 dma_chan;3 个链表;thread 硬件线程
DmaPl330Desc 描述符 txd 内嵌 tx_descriptorpx/rqcfg 微码参数;status 状态
Pl330Thread 硬件线程 ev 事件号;req[2] 双缓冲请求

勘误:PL330 没有 使用 virt-dma,而是自建 submitted_list / work_list / completed_list。这是与 virt_dma_chan 的 5 个链表不同的一种实现风格。


6.5 Core 全局容器类图

#mermaid-svg-Sva7dDzw1KeBiWp8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Sva7dDzw1KeBiWp8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Sva7dDzw1KeBiWp8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Sva7dDzw1KeBiWp8 .error-icon{fill:#552222;}#mermaid-svg-Sva7dDzw1KeBiWp8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Sva7dDzw1KeBiWp8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Sva7dDzw1KeBiWp8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Sva7dDzw1KeBiWp8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Sva7dDzw1KeBiWp8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Sva7dDzw1KeBiWp8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Sva7dDzw1KeBiWp8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Sva7dDzw1KeBiWp8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Sva7dDzw1KeBiWp8 .marker.cross{stroke:#333333;}#mermaid-svg-Sva7dDzw1KeBiWp8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Sva7dDzw1KeBiWp8 p{margin:0;}#mermaid-svg-Sva7dDzw1KeBiWp8 g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-Sva7dDzw1KeBiWp8 g.classGroup text .title{font-weight:bolder;}#mermaid-svg-Sva7dDzw1KeBiWp8 .cluster-label text{fill:#333;}#mermaid-svg-Sva7dDzw1KeBiWp8 .cluster-label span{color:#333;}#mermaid-svg-Sva7dDzw1KeBiWp8 .cluster-label span p{background-color:transparent;}#mermaid-svg-Sva7dDzw1KeBiWp8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Sva7dDzw1KeBiWp8 .cluster text{fill:#333;}#mermaid-svg-Sva7dDzw1KeBiWp8 .cluster span{color:#333;}#mermaid-svg-Sva7dDzw1KeBiWp8 .nodeLabel,#mermaid-svg-Sva7dDzw1KeBiWp8 .edgeLabel{color:#131300;}#mermaid-svg-Sva7dDzw1KeBiWp8 .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-Sva7dDzw1KeBiWp8 .label text{fill:#131300;}#mermaid-svg-Sva7dDzw1KeBiWp8 .labelBkg{background:#ECECFF;}#mermaid-svg-Sva7dDzw1KeBiWp8 .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-Sva7dDzw1KeBiWp8 .classTitle{font-weight:bolder;}#mermaid-svg-Sva7dDzw1KeBiWp8 .node rect,#mermaid-svg-Sva7dDzw1KeBiWp8 .node circle,#mermaid-svg-Sva7dDzw1KeBiWp8 .node ellipse,#mermaid-svg-Sva7dDzw1KeBiWp8 .node polygon,#mermaid-svg-Sva7dDzw1KeBiWp8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Sva7dDzw1KeBiWp8 .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-Sva7dDzw1KeBiWp8 g.clickable{cursor:pointer;}#mermaid-svg-Sva7dDzw1KeBiWp8 g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-Sva7dDzw1KeBiWp8 g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-Sva7dDzw1KeBiWp8 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-Sva7dDzw1KeBiWp8 .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-Sva7dDzw1KeBiWp8 .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-Sva7dDzw1KeBiWp8 .dashed-line{stroke-dasharray:3;}#mermaid-svg-Sva7dDzw1KeBiWp8 .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-Sva7dDzw1KeBiWp8 #compositionStart,#mermaid-svg-Sva7dDzw1KeBiWp8 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Sva7dDzw1KeBiWp8 #compositionEnd,#mermaid-svg-Sva7dDzw1KeBiWp8 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Sva7dDzw1KeBiWp8 #dependencyStart,#mermaid-svg-Sva7dDzw1KeBiWp8 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Sva7dDzw1KeBiWp8 #dependencyStart,#mermaid-svg-Sva7dDzw1KeBiWp8 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Sva7dDzw1KeBiWp8 #extensionStart,#mermaid-svg-Sva7dDzw1KeBiWp8 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Sva7dDzw1KeBiWp8 #extensionEnd,#mermaid-svg-Sva7dDzw1KeBiWp8 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Sva7dDzw1KeBiWp8 #aggregationStart,#mermaid-svg-Sva7dDzw1KeBiWp8 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Sva7dDzw1KeBiWp8 #aggregationEnd,#mermaid-svg-Sva7dDzw1KeBiWp8 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Sva7dDzw1KeBiWp8 #lollipopStart,#mermaid-svg-Sva7dDzw1KeBiWp8 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Sva7dDzw1KeBiWp8 #lollipopEnd,#mermaid-svg-Sva7dDzw1KeBiWp8 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-Sva7dDzw1KeBiWp8 .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-Sva7dDzw1KeBiWp8 .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Sva7dDzw1KeBiWp8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Sva7dDzw1KeBiWp8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Sva7dDzw1KeBiWp8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} chan
local
router
DmaChanPercpu
+memcpy_count : unsigned long
+bytes_transferred : unsigned long
DmaChanDev
+chan : DmaChan
+device : struct device
+dev_id : int
DmaRouter
+dev : device
+route_free()
DmaengineUnmapData
+map_cnt : u8
+to_cnt : u8
+from_cnt : u8
+bidi_cnt : u8
+dev : device
+kref : kref
+len : size_t
+addr : dma_addr_t\[\]
DmaChan
per-CPU 统计:memcpy 次数与字节数。
sysfs 设备:/sys/class/dma/dmaNchanM。
DMA 路由:interconnect/DMA router。
异步描述的地址映射管理。


7. 设备树到内核结构体的映射

7.1 Provider 侧 DT

dts 复制代码
dmac0: dmac@fe530000 {
    compatible = "arm,pl330", "arm,primecell";
    reg = <0x0 0xfe530000 0x0 0x4000>;
    interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>,
                 <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&cru ACLK_BUS>;
    clock-names = "apb_pclk";
    #dma-cells = <1>;
    arm,pl330-periph-burst;
};

dmac1: dmac@fe550000 {
    compatible = "arm,pl330", "arm,primecell";
    #dma-cells = <1>;
    ...
};

7.2 Provider 侧映射表

DT 元素/属性 内核类/处理
compatible="arm,pl330", "arm,primecell" amba_driver pl330_driver 匹配 → pl330_probe()
reg = <...> devm_ioremap_resourcepl330_dmac.base
interrupts = <...>(2 条) devm_request_irq(pl330_irq_handler)
clocks / clock-names="apb_pclk" AMBA 时钟
#dma-cells = <1> of_dma_xlate 参数个数
arm,pl330-periph-burst `pl330->quirks
(驱动创建) struct dma_device + dma_pl330_chan[]
dma_async_device_register() 挂入 dma_device_list
of_dma_controller_register() 挂入 of_dma_list

7.3 Consumer 侧 DT

dts 复制代码
uart2: serial@fe660000 {
    dmas = <&dmac0 0>, <&dmac0 1>;   /* <&控制器 请求线号> */
    dma-names = "tx", "rx";
};

i2s1_8ch: i2s@fe410000 {
    dmas = <&dmac1 0>, <&dmac1 1>;
    dma-names = "tx", "rx";
};

7.4 Consumer 映射流程

text 复制代码
DT: dmas = <&dmac0 0>, <&dmac0 1>;  dma-names = "tx", "rx";
        │
        ▼
dma_request_chan(dev, "tx")
   │
   ├─ of_dma_request_slave_channel(np, "tx")
   │    ├─ of_property_count_strings(np, "dma-names") 定位索引
   │    ├─ of_parse_phandle_with_args(np, "dmas", "#dma-cells", i, &dma_spec)
   │    │      → dma_spec.np = &dmac0, dma_spec.args[0] = 请求线号(0)
   │    ├─ of_dma_find_controller(&dma_spec) 在 of_dma_list 中按 of_node 匹配
   │    └─ ofdma->of_dma_xlate(&dma_spec, ofdma) → of_dma_pl330_xlate()
   │           ├─ chan_id = dma_spec->args[0]
   │           └─ return dma_get_slave_channel(&pl330->peripherals[chan_id].chan)
   │
   └─ 消费者持有 struct dma_chan *

后续:
   dmaengine_slave_config(chan, &cfg)   → device_config(pl330_config)
   dmaengine_prep_slave_sg(...)         → device_prep_slave_sg(pl330_prep_slave_sg)
   dmaengine_submit(desc)               → desc->tx_submit()
   dmaengine_issue_pending(chan)        → device_issue_pending(pl330_issue_pending)
   ...PL330 完成中断 → pl330_irq_handler → tasklet → callback()...
   dmaengine_tx_status(chan, cookie, &st) → device_tx_status(pl330_tx_status)
   dma_release_channel(chan)            → device_free_chan_resources / client_count--

7.5 DT 与内核类映射总表

DT 元素/属性 内核类/字段 源码
compatible="arm,pl330" amba_driver pl330_driverpl330_probe() pl330.c:3448/3185
reg pl330_dmac.base pl330.c:3222
interrupts devm_request_irq(pl330_irq_handler) pl330.c:3254
#dma-cells=<1> of_dma_xlate 参数个数 of-dma.c / pl330.c:2354
arm,pl330-periph-burst pl330->quirksPL330_QUIRK_PERIPH_BURST pl330.c:501/3217
(驱动注册) struct dma_device + dma_pl330_chan[] pl330.c:457/414
dmas = <&dmacN arg> dma_chan(经 of_dma_pl330_xlate of-dma.c:247pl330.c:2344
dma-names dma_request_chan(dev, name) 的键 of-dma.c:260
dma_spec.args[0] PL330 外设请求线(→ pl330->peripherals[id] pl330.c:2357

8. 调试与实操

8.1 debugfs / sysfs

  • /sys/class/dma/:所有 DMA 通道;
  • /sys/kernel/debug/dmaengine/:debugfs 通道与描述符信息;
  • /proc/interrupts:DMA 完成中断计数。

8.2 常用命令

bash 复制代码
# 查看 DMA 通道
ls /sys/class/dma/

# 查看通道详情
cat /sys/class/dma/dma0chan0/name
cat /sys/class/dma/dma0chan0/bytes_transferred

# 查看 DMA 中断
grep -i dma /proc/interrupts

# 查看 debugfs
ls /sys/kernel/debug/dmaengine/

8.3 常见错误码

错误码 含义 处理
-EPROBE_DEFER Provider 未就绪 返回 -EPROBE_DEFER
-ENODEV DT 中找不到 dmas 检查 dmas / dma-names
-EBUSY 通道已被占用 检查 client_count
-EINVAL 配置非法 检查 dma_slave_config
-ENOMEM 描述符分配失败 检查内存
-EIO 提交失败 检查 dma_submit_error

8.4 验证步骤

  1. dmesg | grep -i dma 确认 Provider 注册;
  2. ls /sys/class/dma/ 确认通道存在;
  3. cat /sys/class/dma/dmaNchanM/name 确认通道名;
  4. 触发 DMA 传输,确认 bytes_transferred 增长;
  5. 检查 /proc/interrupts,确认完成中断;
  6. dmaengine_tx_status 验证残留字节。

9. 总结

DMA 子系统的本质是:

dma_device + dma_chan + dma_async_tx_descriptor 三层抽象,把"通道分配、异步生命周期、cookie、完成通知、DT 描述"全部收口到 drivers/dma/

理解 DMA 的关键是抓住四条线:

  1. 句柄线dma_request_chan()dma_chandma_release_channel()
  2. 生命周期线prep_*submitissue_pendingcallbacktx_status
  3. Provider 线dma_devicedma_async_device_register()device_* 回调;
  4. DT 线dmas / dma-namesof_dma_request_slave_channel()of_dma_xlate()

掌握这四条线,再看 pl330.cdmaengine.cof-dma.cvirt-dma.c 和 RK3568 的 PL330 寄存器,就能把 DMA 子系统串起来。


附:关键源码位置速查

主题 位置
Consumer/Provider API + 类 include/linux/dmaengine.hdma_chan:330、dma_slave_config:438、dma_async_tx_descriptor:608、dma_device:854)
核心 drivers/dma/dmaengine.cdma_get_slave_channel:696、__dma_request_channel:755、dma_request_chan:813、dma_async_device_register:1140)
DT 层 drivers/dma/of-dma.cof_dma_controller_register:108、of_dma_request_slave_channel:247、of_dma_simple_xlate:311、of_dma_xlate_by_chan_id:341)
虚通道 drivers/dma/virt-dma.hvirt_dma_desc:15、virt_dma_chan:22)+ virt-dma.c
PL330 提供者 drivers/dma/pl330.cdma_pl330_chan:414、pl330_dmac:457、dma_pl330_desc:515、of_dma_pl330_xlate:2344、pl330_probe:3185、注册:3332/3339pl330_driver:3448)
of_dma 结构 include/linux/of_dma.h:18
RK3568 DTS rk3568.dtsidmac0:3027、dmac1:3038;消费者 dmas/dma-names 如 :983/:2871/:3189...)
相关推荐
byte轻骑兵1 小时前
【BlueZ】 log 模块:日志系统的实现与自定义日志输出配置
linux·人工智能·bluez·电脑蓝牙·嵌入式蓝牙
探索云原生1 小时前
KubeClipper 1.7.0 发布:Operation 优化与 Kubernetes 1.37 支持
linux·docker·云原生·kubernetes·go
一技安身2 小时前
【信创】docker-26.0.2.tgz 原理 + 完整安装
linux·运维·服务器
程曦曦2 小时前
MySQL 生产库误删 98 张表后的时间点恢复实战:从 binlog 解析到资金对账
linux·数据结构·其他·算法·ubuntu·运维开发
赵民勇3 小时前
systemd-mount命令详解
linux
H_oRIZoN_3 小时前
Linux入门DAY42【51 单片机】DS18B20 数字温度传感器完整解析(单总线原理 + 时序 + 读取流程)
linux·单片机·linux应用编程
j7~3 小时前
【Linux网络加餐】(篇六)网络版计算器(上):模板方法模式、序列化与 JSON
linux·c++·json·序列化·反序列化·网络版计算机
流浪0013 小时前
Linux系统篇34——信号(六):信号处理期间又来了怎么办?——sigaction、sa_mask、可重入函数与 volatile
linux·运维·面试·操作系统·信号处理·信号
学烹饪的小胡桃4 小时前
资产设备管理系统 WGFIX 怎么设置https访问
linux·运维·服务器·网络·安全