03、DMA 控制器驱动结构体抽象
首先要了解DMA controller driver涉及到的数据结构,知道每个数据结构以及成员的含义之后,再去看源码就容易了。
一、struct dma_device
linux内核内使用struct dma_device数据结构来描述一个DMA设备(对应的物理设备就是一个DMA控制器),dma_device包含了字符设备,dma设备操作函数(这些操作函数会被client driver调用)等信息,定义在include/linux/dmaengine.h:
/**
* struct dma_device - info on the entity supplying DMA services
* @ref: reference is taken and put every time a channel is allocated or freed
* @chancnt: how many DMA channels are supported
* @privatecnt: how many DMA channels are requested by dma_request_channel
* @channels: the list of struct dma_chan
* @global_node: list_head for global dma_device_list
* @filter: information for device/slave to filter function/param mapping
* @cap_mask: one or more dma_capability flags
* @desc_metadata_modes: supported metadata modes by the DMA device
* @max_xor: maximum number of xor sources, 0 if no capability
* @max_pq: maximum number of PQ sources and PQ-continue capability
* @copy_align: alignment shift for memcpy operations
* @xor_align: alignment shift for xor operations
* @pq_align: alignment shift for pq operations
* @fill_align: alignment shift for memset operations
* @dev_id: unique device ID
* @dev: struct device reference for dma mapping api
* @owner: owner module (automatically set based on the provided dev)
* @chan_ida: unique channel ID
* @src_addr_widths: bit mask of src addr widths the device supports
* Width is specified in bytes, e.g. for a device supporting
* a width of 4 the mask should have BIT(4) set.
* @dst_addr_widths: bit mask of dst addr widths the device supports
* @directions: bit mask of slave directions the device supports.
* Since the enum dma_transfer_direction is not defined as bit flag for
* each type, the dma controller should set BIT(<TYPE>) and same
* should be checked by controller as well
* @min_burst: min burst capability per-transfer
* @max_burst: max burst capability per-transfer
* @max_sg_burst: max number of SG list entries executed in a single burst
* DMA tansaction with no software intervention for reinitialization.
* Zero value means unlimited number of entries.
* @descriptor_reuse: a submitted transfer can be resubmitted after completion
* @residue_granularity: granularity of the transfer residue reported
* by tx_status
* @device_alloc_chan_resources: allocate resources and return the
* number of allocated descriptors
* @device_router_config: optional callback for DMA router configuration
* @device_free_chan_resources: release DMA channel's resources
* @device_prep_dma_memcpy: prepares a memcpy operation
* @device_prep_dma_xor: prepares a xor operation
* @device_prep_dma_xor_val: prepares a xor validation operation
* @device_prep_dma_pq: prepares a pq operation
* @device_prep_dma_pq_val: prepares a pqzero_sum operation
* @device_prep_dma_memset: prepares a memset operation
* @device_prep_dma_memset_sg: prepares a memset operation over a scatter list
* @device_prep_dma_interrupt: prepares an end of chain interrupt operation
* @device_prep_slave_sg: prepares a slave dma operation
* @device_prep_dma_cyclic: prepare a cyclic dma operation suitable for audio.
* The function takes a buffer of size buf_len. The callback function will
* be called after period_len bytes have been transferred.
* @device_prep_interleaved_dma: Transfer expression in a generic way.
* @device_prep_dma_imm_data: DMA's 8 byte immediate data to the dst address
* @device_caps: May be used to override the generic DMA slave capabilities
* with per-channel specific ones
* @device_config: Pushes a new configuration to a channel, return 0 or an error
* code
* @device_pause: Pauses any transfer happening on a channel. Returns
* 0 or an error code
* @device_resume: Resumes any transfer on a channel previously
* paused. Returns 0 or an error code
* @device_terminate_all: Aborts all transfers on a channel. Returns 0
* or an error code
* @device_synchronize: Synchronizes the termination of a transfers to the
* current context.
* @device_tx_status: poll for transaction completion, the optional
* txstate parameter can be supplied with a pointer to get a
* struct with auxiliary transfer status information, otherwise the call
* will just return a simple status code
* @device_issue_pending: push pending transactions to hardware
* @device_release: called sometime atfer dma_async_device_unregister() is
* called and there are no further references to this structure. This
* must be implemented to free resources however many existing drivers
* do not and are therefore not safe to unbind while in use.
* @dbg_summary_show: optional routine to show contents in debugfs; default code
* will be used when this is omitted, but custom code can show extra,
* controller specific information.
* @dbg_dev_root: the root folder in debugfs for this device
*/
struct dma_device {
struct kref ref;
unsigned int chancnt;
unsigned int privatecnt;
struct list_head channels;
struct list_head global_node;
struct dma_filter filter;
dma_cap_mask_t cap_mask;
enum dma_desc_metadata_mode desc_metadata_modes;
unsigned short max_xor;
unsigned short max_pq;
enum dmaengine_alignment copy_align;
enum dmaengine_alignment xor_align;
enum dmaengine_alignment pq_align;
enum dmaengine_alignment fill_align;
#define DMA_HAS_PQ_CONTINUE (1 << 15)
int dev_id;
struct device *dev;
struct module *owner;
struct ida chan_ida;
u32 src_addr_widths;
u32 dst_addr_widths;
u32 directions;
u32 min_burst;
u32 max_burst;
u32 max_sg_burst;
bool descriptor_reuse;
enum dma_residue_granularity residue_granularity;
int (*device_alloc_chan_resources)(struct dma_chan *chan);
int (*device_router_config)(struct dma_chan *chan);
void (*device_free_chan_resources)(struct dma_chan *chan);
struct dma_async_tx_descriptor *(*device_prep_dma_memcpy)(
struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
size_t len, unsigned long flags);
struct dma_async_tx_descriptor *(*device_prep_dma_xor)(
struct dma_chan *chan, dma_addr_t dst, dma_addr_t *src,
unsigned int src_cnt, size_t len, unsigned long flags);
struct dma_async_tx_descriptor *(*device_prep_dma_xor_val)(
struct dma_chan *chan, dma_addr_t *src, unsigned int src_cnt,
size_t len, enum sum_check_flags *result, unsigned long flags);
struct dma_async_tx_descriptor *(*device_prep_dma_pq)(
struct dma_chan *chan, dma_addr_t *dst, dma_addr_t *src,
unsigned int src_cnt, const unsigned char *scf,
size_t len, unsigned long flags);
struct dma_async_tx_descriptor *(*device_prep_dma_pq_val)(
struct dma_chan *chan, dma_addr_t *pq, dma_addr_t *src,
unsigned int src_cnt, const unsigned char *scf, size_t len,
enum sum_check_flags *pqres, unsigned long flags);
struct dma_async_tx_descriptor *(*device_prep_dma_memset)(
struct dma_chan *chan, dma_addr_t dest, int value, size_t len,
unsigned long flags);
struct dma_async_tx_descriptor *(*device_prep_dma_memset_sg)(
struct dma_chan *chan, struct scatterlist *sg,
unsigned int nents, int value, unsigned long flags);
struct dma_async_tx_descriptor *(*device_prep_dma_interrupt)(
struct dma_chan *chan, unsigned long flags);
struct dma_async_tx_descriptor *(*device_prep_slave_sg)(
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
unsigned long flags, void *context);
struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)(
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
size_t period_len, enum dma_transfer_direction direction,
unsigned long flags);
struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)(
struct dma_chan *chan, struct dma_interleaved_template *xt,
unsigned long flags);
struct dma_async_tx_descriptor *(*device_prep_dma_imm_data)(
struct dma_chan *chan, dma_addr_t dst, u64 data,
unsigned long flags);
void (*device_caps)(struct dma_chan *chan, struct dma_slave_caps *caps);
int (*device_config)(struct dma_chan *chan, struct dma_slave_config *config);
int (*device_pause)(struct dma_chan *chan);
int (*device_resume)(struct dma_chan *chan);
int (*device_terminate_all)(struct dma_chan *chan);
void (*device_synchronize)(struct dma_chan *chan);
enum dma_status (*device_tx_status)(struct dma_chan *chan,
dma_cookie_t cookie,
struct dma_tx_state *txstate);
void (*device_issue_pending)(struct dma_chan *chan);
void (*device_release)(struct dma_device *dev);
/* debugfs support */
void (*dbg_summary_show)(struct seq_file *s, struct dma_device *dev);
struct dentry *dbg_dev_root;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
以下是DMA控制器结构的说明:
成员说明
引用计数(ref)
- 记录通道被分配或释放的次数,每次操作增减计数。
支持的DMA通道数量(chancnt)
- 表示该DMA控制器能同时使用的通道总数。
已分配通道数(privatecnt)
- 通过
dma_request_channel
函数申请的DMA通道数量。
- 通过
通道链表(channels)
- 存储所有可用DMA通道的列表。初始化时用
INIT_LIST_HEAD
创建空列表,随后用list_add_tail
将通道逐个添加到列表末尾。
- 存储所有可用DMA通道的列表。初始化时用
全局设备节点(global_node)
- 该DMA设备通过此节点添加到系统全局DMA设备列表
dma_device_list
中。
- 该DMA设备通过此节点添加到系统全局DMA设备列表
核心能力与参数
支持的传输类型(cap_mask)
用位图标记控制器支持的传输类型:
- 内存复制(DMA_MEMCPY):内存间数据复制。
- 异或操作(DMA_XOR):内存区域的异或计算。
- 分散/聚合传输(DMA_SG):分段数据的合并或拆分。
- 设备传输(DMA_SLAVE):设备与内存之间的数据传输。
- 循环传输(DMA_CYCLIC):循环数据传输(如音频流)。
操作对齐要求
- memcpy对齐(copy_align):内存复制操作的最小数据对齐要求。
- xor对齐(xor_align):异或操作的最小对齐要求。
- pq对齐(pq_align):PQ操作(校验和计算)的最小对齐要求。
- memset对齐(fill_align):内存填充操作的最小对齐要求。
硬件限制
最大XOR源数(max_xor):支持同时参与异或运算的数据源数量。
最大PQ源数(max_pq):支持同时参与PQ运算的数据源数量及连续处理能力。
地址宽度支持
- 源地址宽度(src_addr_widths):支持的源设备地址位宽(如8位、16位等)。
- 目标地址宽度(dst_addr_widths):支持的目标设备地址位宽。
传输方向(directions):支持的数据传输方向(如设备到内存、内存到设备)。
Burst传输限制
- 最小Burst(min_burst):最小单次连续传输量。
- 最大Burst(max_burst):最大单次连续传输量。
- SG列表Burst(max_sg_burst):单次Burst可处理的分段列表条目数。
资源与标识
设备标识
- 设备ID(dev_id):全局唯一的设备标识符。
- 设备结构(dev):指向Linux内核设备对象的指针,用于DMA内存映射。
- 模块所有者(owner):驱动模块的所有权信息(自动关联到注册驱动)。
通道管理
- 通道ID(chan_ida):为每个通道分配全局唯一的ID。
回调函数(关键操作接口)
资源管理
- 分配通道资源(device_alloc_chan_resources):当客户端请求通道时调用,分配硬件资源并返回描述符数量。
- 释放通道资源(device_free_chan_resources):当通道被释放时调用,回收资源。
传输配置
- 通道配置(device_config):设置DMA通道参数(如传输方向、地址等)。
- 准备传输(device_prep_*):准备不同类型的传输操作(如memcpy、XOR、循环传输等),返回描述符。
状态与控制
- 暂停传输(device_pause):暂停通道当前传输。
- 恢复传输(device_resume):重新启动已暂停的传输。
- 终止传输(device_terminate_all):强制停止所有正在进行的传输。
- 传输状态(device_tx_status):查询传输进度和状态。
- 启动传输(device_issue_pending):开始执行待处理的传输队列。
调试与清理
- 调试信息(dbg_summary_show):在调试文件系统(debugfs)显示设备状态。
- 设备释放(device_release):当设备被卸载时调用,释放所有资源。
其他特性
- 元数据模式(desc_metadata_modes):支持的描述符元数据类型(如传输参数附加信息)。
- 残留粒度(residue_granularity):传输进度查询的最小精度。
- 描述符复用(descriptor_reuse):允许重复使用已完成的传输描述符,提升效率。
初始化与注册
- 通道链表需在初始化时用
INIT_LIST_HEAD
创建,并通过list_add_tail
逐个添加通道。 - 设备通过
global_node
加入全局DMA设备列表,供系统识别和管理。
1.1、transfer width
DMA传输宽度使用struct dma_slave_buswidth表示,定义在include/linux/dmaengine.h,内容如下:
/**
* enum dma_slave_buswidth - defines bus width of the DMA slave
* device, source or target buses
*/
enum dma_slave_buswidth {
DMA_SLAVE_BUSWIDTH_UNDEFINED = 0,
DMA_SLAVE_BUSWIDTH_1_BYTE = 1,
DMA_SLAVE_BUSWIDTH_2_BYTES = 2,
DMA_SLAVE_BUSWIDTH_3_BYTES = 3,
DMA_SLAVE_BUSWIDTH_4_BYTES = 4,
DMA_SLAVE_BUSWIDTH_8_BYTES = 8,
DMA_SLAVE_BUSWIDTH_16_BYTES = 16,
DMA_SLAVE_BUSWIDTH_32_BYTES = 32,
DMA_SLAVE_BUSWIDTH_64_BYTES = 64,
DMA_SLAVE_BUSWIDTH_128_BYTES = 128,
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在数据结构struct dma_device中src_addr_widths/sdst_addr_widths分别用于指定源地址/目标支持支持的传输宽度。
1.2、传输方向
DMA传输方向使用struct dma_transfer_direction表示,定义在include/linux/dmaengine.h,内容如下:
/**
* enum dma_transfer_direction - dma transfer mode and direction indicator
* @DMA_MEM_TO_MEM: Async/Memcpy mode
* @DMA_MEM_TO_DEV: Slave mode & From Memory to Device
* @DMA_DEV_TO_MEM: Slave mode & From Device to Memory
* @DMA_DEV_TO_DEV: Slave mode & From Device to Device
*/
enum dma_transfer_direction {
DMA_MEM_TO_MEM,
DMA_MEM_TO_DEV,
DMA_DEV_TO_MEM,
DMA_DEV_TO_DEV,
DMA_TRANS_NONE,
};
2
3
4
5
6
7
8
9
10
11
12
13
14
1.3、传输状态
DMA传输状态使用enum dma_status表示,定义在include/linux/dmaengine.h,内容如下:
/**
* enum dma_status - DMA transaction status
* @DMA_COMPLETE: transaction completed
* @DMA_IN_PROGRESS: transaction not yet processed
* @DMA_PAUSED: transaction is paused
* @DMA_ERROR: transaction failed
*/
enum dma_status {
DMA_COMPLETE,
DMA_IN_PROGRESS,
DMA_PAUSED,
DMA_ERROR,
DMA_OUT_OF_ORDER,
};
2
3
4
5
6
7
8
9
10
11
12
13
14
1.4、传输类型
我们的dma_device结构体有一个字段叫做cap_mask,用来保存支持的各种传输类型,并且你需要使用dma_cap_set函数修改这个掩码,将支持的传输类型作为参数传入。
DMA传输类型使用enum dma_transaction_type表示,定义在include/linux/dmaengine.h,内容如下:
/**
* enum dma_transaction_type - DMA transaction types/indexes
*
* Note: The DMA_ASYNC_TX capability is not to be set by drivers. It is
* automatically set as dma devices are registered.
*/
enum dma_transaction_type {
DMA_MEMCPY,
DMA_XOR,
DMA_PQ,
DMA_XOR_VAL,
DMA_PQ_VAL,
DMA_MEMSET,
DMA_MEMSET_SG,
DMA_INTERRUPT,
DMA_PRIVATE,
DMA_ASYNC_TX,
DMA_SLAVE,
DMA_CYCLIC,
DMA_INTERLEAVE,
DMA_COMPLETION_NO_ORDER,
DMA_REPEAT,
DMA_LOAD_EOT,
/* last transaction type for creation of the capabilities mask */
DMA_TX_TYPE_END,
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
以下是DMA类型及其功能的说明:
1. DMA_MEMCPY(内存到内存复制)
- 设备可以复制内存中的数据。
- 源和目标的内存块总大小不同的话,只复制较小的那个总大小。例如:源有100KB,目标有80KB,最终只复制80KB。
- 常用于显卡与主机内存之间的数据传输(比如把显存中的图像数据复制到主机内存)。
2. DMA_XOR(异或运算)
- 设备能对内存区域执行异或(XOR)操作。
- 常用于需要大量异或运算的任务,例如RAID5磁盘阵列的奇偶校验计算。
3. DMA_XOR_VAL(异或校验)
- 用异或算法检查内存缓冲区的奇偶校验是否正确。
4. DMA_PQ(RAID6计算)
- 支持RAID6的P+Q算法(P是异或,Q是Reed-Solomon编码)。
- 用于更复杂的磁盘阵列数据保护。
5. DMA_PQ_VAL(RAID6校验)
- 用RAID6的P+Q算法检查内存缓冲区的奇偶校验。
6. DMA_MEMSET(内存填充)
- 设备能用指定的单字节模式填充内存。
- 例如:把一块内存区域全部填成0xFF。
7. DMA_INTERRUPT(周期性中断)
- 设备能触发周期性中断,让驱动程序定时执行回调函数。
8. DMA_PRIVATE(私有传输)
- 只支持从设备到特定位置的传输,不支持异步操作(即不能后台执行)。
9. DMA_ASYNC_TX(异步传输标志)
- 此标志由系统框架自动设置,设备无需手动配置。
10. DMA_SLAVE(设备到内存传输)
- 设备能从外设(如传感器、网卡)传输数据到内存,支持分散-聚集操作(多个不连续内存块)。
- 如果要传输连续内存,只需用单元素的内存列表。
11. DMA_CYCLIC(循环传输)
- 数据在一组缓冲区中循环传输,最后一个缓冲区指向第一个。
- 常用于音频处理(如环形缓冲区)。
12. DMA_INTERLEAVE(交错传输)
- 支持从非连续内存块传输到另一个非连续内存块。
- 常用于2D图像或显示数据的直接传输(例如直接显示未压缩的视频帧)。
13. DMA_COMPLETION_NO_ORDER(无序完成)
- 设备无法保证传输完成的顺序。
- 需要驱动程序返回“无序完成”状态,且无法追踪任务完成的先后顺序。
14. DMA_REPEAT(重复传输)
- 支持自动重复传输任务,类似循环但可被新任务替换。
- 仅适用于交错传输类型(需同时支持DMA_INTERLEAVE)。
15. DMA_LOAD_EOT(结束时加载新任务)
- 在传输结束时,可用新任务替换当前重复的任务。
- 未来可能支持在传输中途替换任务。
地址变化说明
- 内存地址:通常每次传输后递增(或递减),类似指针移动。
- 环形缓冲区(如DMA_CYCLIC):地址循环使用,末尾接回开头。
- 设备寄存器(如FIFO):地址固定不变,数据直接写入或读出。
总结
不同DMA类型对应不同场景:
- 数据复制:MEMCPY、SLAVE。
- 计算任务:XOR、PQ(用于RAID)。
- 特殊需求:MEMSET(填充)、CYCLIC(循环)、INTERLEAVE(交错)。
- 中断与控制:INTERRUPT、REPEAT(重复)、LOAD_EOT(替换任务)。
二、dma_chan 通道
linux内核使用数据结构struct dam_chan来表示DMA通道,定义在include/linux/dmaengine.h,内容如下:
/**
* struct dma_chan - devices supply DMA channels, clients use them
* @device: ptr to the dma device who supplies this channel, always !%NULL
* @slave: ptr to the device using this channel
* @cookie: last cookie value returned to client
* @completed_cookie: last completed cookie for this channel
* @chan_id: channel ID for sysfs
* @dev: class device for sysfs
* @name: backlink name for sysfs
* @dbg_client_name: slave name for debugfs in format:
* dev_name(requester's dev):channel name, for example: "2b00000.mcasp:tx"
* @device_node: used to add this to the device chan list
* @local: per-cpu pointer to a struct dma_chan_percpu
* @client_count: how many clients are using this channel
* @table_count: number of appearances in the mem-to-mem allocation table
* @router: pointer to the DMA router structure
* @route_data: channel specific data for the router
* @private: private data for certain client-channel associations
*/
struct dma_chan {
struct dma_device *device;
struct device *slave;
dma_cookie_t cookie;
dma_cookie_t completed_cookie;
/* sysfs */
int chan_id;
struct dma_chan_dev *dev;
const char *name;
#ifdef CONFIG_DEBUG_FS
char *dbg_client_name;
#endif
struct list_head device_node;
struct dma_chan_percpu __percpu *local;
int client_count;
int table_count;
/* DMA router */
struct dma_router *router;
void *route_data;
void *private;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
该结构包含以下主要成员:
设备指针(device):指向提供该DMA通道的DMA控制器设备。
从设备指针(slave):指向使用该通道进行数据传输的外部设备。
最后cookie值(cookie):记录返回给应用程序的最后一个任务标识号。
完成cookie值(completed_cookie):记录该通道已完成的最后一个任务标识号。
通道ID(chan_id):为该DMA通道分配的唯一编号标识。
通道设备结构(dev):存储DMA通道设备相关信息的专用数据结构。
系统名称(name):在系统文件系统(sysfs)中显示的通道名称。
调试名称(dbg_client_name):用于调试的设备名称,例如"2b00000.mcasp:tx"。
设备节点(device_node):将通道链接到DMA设备通道列表中的链表节点。
每CPU数据(local):指向每个CPU对应的通道专用数据结构的指针。
客户端数量(client_count):当前使用该通道的应用程序数量。
表使用计数(table_count):在内存到内存传输表中被引用的次数。
路由器指针(router):指向DMA数据路由管理器的指针。
路由数据(route_data):用于路由配置的通道特定参数。
私有数据(private):供应用程序和通道之间存储专用信息的空间
每个成员都直接对应DMA通道的核心功能,包括设备管理、任务跟踪、系统集成、性能统计和扩展功能支持。
其中dma_cookie_t本质上就是s32类型,它是一个随时间递增的DMA传输ID。
include/linux/dmaengine.h:22:typedef s32 dma_cookie_t;
2.1、struct dma_chan_dev
struct dma_chan_dev用于将sysfs设备节点与通道支持的设备相关联,定义如下:
/**
* struct dma_chan_dev - relate sysfs device node to backing channel device
* @chan: driver channel device
* @device: sysfs device
* @dev_id: parent dma_device dev_id
* @chan_dma_dev: The channel is using custom/different dma-mapping
* compared to the parent dma_device
*/
struct dma_chan_dev {
struct dma_chan *chan;
struct device device;
int dev_id;
bool chan_dma_dev;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
它包含以下成员:
- chan:指向DMA通道的指针;
- device:sysfs设备节点;
- dev_id:所属的DMA设备的id,即父dma_device ->dev_id;
- chan_dma_dev:该通道使用与父dma_device不同的定制/不同的 dma-mapping;
2.2、struct virt_dma_desc
每个DMA控制器包含多个通道,每个DMA通道可以连接到多个外设的请求,比如UART、SPI、I2C等。DMA控制器抽象出来struct dam_chan对应DMA控制器的物理channel,又抽象出来虚拟的channel,软件上可以实现多个虚拟channel对应一个物理channel。
在linux内核中,使用数据结构struct virt_dma_desc表示虚拟channel,定义在drivers/dma/virt-dma.h:
struct virt_dma_chan {
struct dma_chan chan;
struct tasklet_struct task;
void (*desc_free)(struct virt_dma_desc *);
spinlock_t lock;
/* protected by vc.lock */
struct list_head desc_allocated;
struct list_head desc_submitted;
struct list_head desc_issued;
struct list_head desc_completed;
struct list_head desc_terminated;
struct virt_dma_desc *cyclic;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
其中:
- chan:指向DMA channel的指针,用于和client driver打交道;
- task:一个tasklet,用于等待该虚拟channel上传输的完成(由于是虚拟channel,传输完成与否只能由软件判断);
- desc_allocated & desc_submitted & desc_issued & desc_completed:四个链表头,用于保存不同状态的虚拟channel描述符;
- cyclic:描述了一个struct dma_async_tx_descriptor的链表;