深入理解 Linux DMA 子系统:dmaengine 设计思想、UML 类图与设备树映射
本文按《深入理解 Linux pinctrl 子系统》的同一框架重构 dmaengine:先讲设计思想,再按 Consumer → Core → Provider → DT 映射自上而下展开;关键类统一用 UML 类图描述,并说明类与成员职责;最后给出逻辑漏洞与修正清单。
1. DMA 子系统设计思想
1.1 它要解决的核心问题
DMA(Direct Memory Access)让外设与内存之间可以不经 CPU 搬运数据。它看起来只是"把数据从 A 搬到 B",但进入 Linux 内核后,会立刻遇到七类复杂问题:
- 硬件差异:ARM PL330、DesignWare AXI DMAC、Intel ioatdma、TI EDMA、RZN1 DMAC,寄存器模型与描述符格式完全不同。
- 通道管理:一个 DMA 控制器有多个物理/逻辑通道,谁用哪条、独占还是共享、怎么匹配外设请求线。
- 异步生命周期 :
prep → submit → issue_pending → complete四段式,每一步都有状态与 cookie。 - 描述符管理:SG 链表、循环缓冲、内存拷贝、XOR/PQ、memset、interleaved,描述符类型多样。
- 完成通知:硬件中断 → tasklet/线程 → callback,回调上下文与锁必须精确。
- 终止与同步 :
terminate_all/terminate_sync/synchronize,语义差异很大。 - 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
- Consumer :
drivers/mmc、spi、i2c、uart、net、audio、display等。 - Core :
drivers/dma/dmaengine.c、of-dma.c、virt-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_chan与tx_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_COOKIE与DMA_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 设计原则总结
- 三层抽象 :
dma_device/dma_chan/tx_descriptor分离。 - 异步四段式:prep → submit → issue_pending → callback。
- cookie 序号:每个传输一个 cookie,支持完成查询。
- 能力匹配 :
cap_mask+filter决定通道分配。 - 回调通知:完成通过 callback 异步通知。
- DT 优先 :
dmas/dma-names描述 Consumer 与 Provider 的绑定。 - 模板复用 :
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/complete、dma_chan_get/put |
drivers/dma/of-dma.c |
DT 层 :of_dma_list、of_dma_controller_register()、of_dma_request_slave_channel() |
drivers/dma/virt-dma.c / virt-dma.h |
虚通道框架 :virt_dma_chan / virt_dma_desc、vchan_* |
drivers/dma/pl330.c |
RK3568 提供者(ARM PL330) :pl330_probe()、of_dma_pl330_xlate()、微码生成 |
include/linux/dmaengine.h |
公共 API + 类 :dma_chan、dma_device、dma_async_tx_descriptor |
include/linux/of_dma.h |
struct of_dma、of_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 的职责
- 维护全局容器:
dma_device_list、of_dma_list。 - 分配与回收
dma_chan。 - 实现通道分配与能力匹配。
- 管理 cookie。
- 提供
dma_async_device_register()。 - 提供 sysfs 与 debugfs。
- 模块引用计数。
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
4.3 cookie 管理
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_device;desc_pool 描述符池;channels 线程池;peripherals 逻辑通道 |
DmaPl330Chan |
逻辑通道 | chan 内嵌 dma_chan;3 个链表;thread 硬件线程 |
DmaPl330Desc |
描述符 | txd 内嵌 tx_descriptor;px/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_resource → pl330_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_driver → pl330_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->quirks(PL330_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:247、pl330.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 验证步骤
dmesg | grep -i dma确认 Provider 注册;ls /sys/class/dma/确认通道存在;cat /sys/class/dma/dmaNchanM/name确认通道名;- 触发 DMA 传输,确认
bytes_transferred增长; - 检查
/proc/interrupts,确认完成中断; - 用
dmaengine_tx_status验证残留字节。
9. 总结
DMA 子系统的本质是:
用
dma_device+dma_chan+dma_async_tx_descriptor三层抽象,把"通道分配、异步生命周期、cookie、完成通知、DT 描述"全部收口到drivers/dma/。
理解 DMA 的关键是抓住四条线:
- 句柄线 :
dma_request_chan()→dma_chan→dma_release_channel(); - 生命周期线 :
prep_*→submit→issue_pending→callback→tx_status; - Provider 线 :
dma_device→dma_async_device_register()→device_*回调; - DT 线 :
dmas/dma-names→of_dma_request_slave_channel()→of_dma_xlate()。
掌握这四条线,再看 pl330.c、dmaengine.c、of-dma.c、virt-dma.c 和 RK3568 的 PL330 寄存器,就能把 DMA 子系统串起来。
附:关键源码位置速查
| 主题 | 位置 |
|---|---|
| Consumer/Provider API + 类 | include/linux/dmaengine.h(dma_chan:330、dma_slave_config:438、dma_async_tx_descriptor:608、dma_device:854) |
| 核心 | drivers/dma/dmaengine.c(dma_get_slave_channel:696、__dma_request_channel:755、dma_request_chan:813、dma_async_device_register:1140) |
| DT 层 | drivers/dma/of-dma.c(of_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.h(virt_dma_desc:15、virt_dma_chan:22)+ virt-dma.c |
| PL330 提供者 | drivers/dma/pl330.c(dma_pl330_chan:414、pl330_dmac:457、dma_pl330_desc:515、of_dma_pl330_xlate:2344、pl330_probe:3185、注册:3332/3339、pl330_driver:3448) |
of_dma 结构 |
include/linux/of_dma.h:18 |
| RK3568 DTS | rk3568.dtsi(dmac0:3027、dmac1:3038;消费者 dmas/dma-names 如 :983/:2871/:3189...) |