目录
1. 前言
2. 重要的结构体
2.1 struct dma_device
2.2 struct dma_chan
2.3 struct virt_dma_chan
3. 重要的API
3.1 注册及注销API
3.2 cookie相关API
4. DMA控制器驱动的编写步骤
5. 参考文章
1. 前言
本文将从DMA控制器驱动(provider)的角度来介绍DMA Engine,包括重要的结构体和API接口。
DMA控制器驱动主要作用是管理channel并响应client driver(consumer)的传输请求并控制DMA 控制器执行传输。
2. 重要的结构体
2.1 struct dma_device
就像任何其他内核框架一样,整个DMA Engine注册依赖于驱动程序填充结构体并针对框架进行注册,该结构体就是dma_device。
在控制器驱动中做的第一件事就是分配此结构体,此外还需要在其中初始化一些必要的字段,因为struct dma_device结构体很庞大,所以选了重要的成员介绍下。
struct dma_device {
...
struct list_head channels;
dma_cap_mask_t cap_mask;
u32 src_addr_widths;
u32 dst_addr_widths;
u32 directions;
u32 max_burst;
int (*device_alloc_chan_resources)(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);
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);
};
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,
/* last transaction type for creation of the capabilities mask */
DMA_TX_TYPE_END,
};
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,
};
enum dma_transfer_direction {
DMA_MEM_TO_MEM,
DMA_MEM_TO_DEV,
DMA_DEV_TO_MEM,
DMA_DEV_TO_DEV,
DMA_TRANS_NONE,
};
1. channels:保存DMA控制器支持的所有channel的链表的链表头,在初始化时应该使用 INIT_LIST_HEAD宏将其初始化为链表,随后然后调用list_add_tail将所有的channel添加到该链表头中。
2. cap_mask:一个bitmap,表示DMA控制器支持的传输类型,支持的传输类型见enum dma_transaction_type中的定义:
DMA_MEMCPY:该设备能够进行内存到内存的复制。
DMA_XOR:该设备能够对内存区域执行异或操作。
DMA_SG:该设备支持内存到内存的分散/聚合传输。
DMA_SLAVE:该设备可以处理设备到内存的传输,包括分散-聚集转移。
DMA_CYCLIC:该设备可以处理循环传输。
3. src_addr_widths:一个bitmap,表示该控制器支持哪些宽度的src类型,宽度类型见enum dma_slave_buswidth中的定义。
4. dst_addr_widths:一个bitmap,表示该控制器支持哪些宽度的dst类型,宽度类型见enum dma_slave_buswidth中的定义。
5. directions:一个bitmap,表示该控制器支持哪些传输方向,支持的传输方向见enum dma_transfer_direction中的定义。
6. max_burst:该设备支持的最大的burst传输的大小。
7. device_alloc_chan_resources & device_free_chan_resources:分配DMA channel资源并返回分配的描述符数量 & 释放资源DMA channels资源。
8. device_prep_dma_*:准备各种各样的操作,client driver通过dmaengine_prep_* API获取传输描述符的时候,damengine则会直接回调DMA控制器驱动相应的device_prep_dma_*接口。
9. device_config:将新配置推送到通道。client driver调用dmaengine_slave_config配置dma channel的时候,dmaengine会调用该回调函数,交给DMA控制器驱动处理。
10. device_pause & device_resume & device_terminate_all:暂停通道上的传输 & 恢复通道上的传输 & 中止通道上所有未决和正在进行的传输。client driver调用dmaengine_pause、dmaengine_resume、dmaengine_terminate_*等API的时候,dmaengine会调用相应的回调函数。
11. device_issue_pending:获取待处理队列中的第一个事务描述符,并开始传输。每当传输完成时,它应该移动到列表中的下一个事务。client driver调用dma_async_issue_pending启动传输的时候,会调用该回调函数。
dmaengine对DMA控制器仅封装出来一些回调函数,由DMA控制器驱动实现,被client driver调用。
2.2 struct dma_chan
struct dma_chan {
struct dma_device *device;
dma_cookie_t cookie;
dma_cookie_t completed_cookie;
/* sysfs */
int chan_id;
struct dma_chan_dev *dev;
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;
};
1. device:指向提供此通道的dma设备的指针。
2. cookie:返回给客户端的最后一个 cookie 值。
3. completed_cookie:此通道最后完成的cookie。
4. device_node:用于将其添加到设备(dma_device)通道列表中。
2.3 struct virt_dma_chan
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 virt_dma_desc *cyclic;
};
chan:一个struct dma_chan类型的变量,用于和client driver打交道。
task:一个tasklet,用于等待该虚拟channel上传输的完成(由于是虚拟channel,传输完成与否只能由软件判断)。
desc_allocated & desc_submitted & desc_issued & desc_completed:四个链表头,用于保存不同状态的虚拟channel描述符。
cyclic:描述了一个struct dma_async_tx_descriptor的链表。
3. 重要的API
3.1 注册及注销API
int dma_async_device_register(struct dma_device *device);
void dma_async_device_unregister(struct dma_device *device);
参数:struct dma_device *device
作用:将DMA控制器驱动注册进内核、从内核中注销。
3.2 cookie相关API
/* drivers/dma/dmaengine.h */
static inline void dma_cookie_init(struct dma_chan *chan)
static inline dma_cookie_t dma_cookie_assign(struct dma_async_tx_descriptor *tx)
static inline void dma_cookie_complete(struct dma_async_tx_descriptor *tx)
static inline enum dma_status dma_cookie_status(struct dma_chan *chan,
dma_cookie_t cookie, struct dma_tx_state *state)
dma_cookie_init:初始化DMA通道的cookie。
dma_cookie_assign:将DMA engine cookie分配给描述符。
dma_cookie_complete:完成一个描述符。通过更新通道完成cookie标记来标记此描述符完成。将描述符cookie清零以防止意外重复完成(调用者应持有锁以防止并发)。
dma_cookie_status:报告cookie状态。
4. DMA控制器驱动的编写步骤
编写一个dma controller driver的基本步骤包括(不考虑虚拟channel的情况):
(1)定义一个struct dma_device变量,并根据实际的硬件情况,填充其中的关键字段。
(2)根据controller支持的channel个数,为每个channel定义一个struct dma_chan变量,进行必要的初始化后,将每个channel都添加到struct dma_device变量的channels链表中。
(3)根据硬件特性,实现struct dma_device变量中必要的回调函数(device_alloc_chan_resources/device_free_chan_resources、device_prep_dma_xxx、device_config、device_issue_pending等等)。、
(4)调用dma_async_device_register将struct dma_device变量注册到kernel中。
(5)当client driver申请dma channel时(例如通过device tree中的dma节点获取),dmaengine core会调用dma controller driver的device_alloc_chan_resources函数,controller driver需要在这个接口中将该channel的资源准备好。
(6)当client driver配置某个dma channel时,dmaengine core会调用dma controller driver的device_config函数,controller driver需要在这个函数中将client想配置的内容准备好,以便进行后续的传输。
(7)client driver开始一个传输之前,会把传输的信息通过dmaengine_prep_slave_xxx接口交给controller driver,controller driver需要在对应的device_prep_dma_xxx回调中,将这些要传输的内容准备好,并返回给client driver一个传输描述符。
(8)然后,client driver会调用dmaengine_submit将该传输提交给controller driver,此时dmaengine会调用controller driver为每个传输描述符所提供的tx_submit回调函数,controller driver需要在这个函数中将描述符挂到该channel对应的传输队列中。
(9)client driver开始传输时,会调用dma_async_issue_pending,controller driver需要在对应的回调函数(device_issue_pending)中,依次将队列上所有的传输请求提交给硬件。
(10)其他需要的步骤。
5. 参考文章
\Documentation\dmaengine\provider.txt文章来源:https://www.toymoban.com/news/detail-405646.html
Linux内核4.14版本——DMA Engine框架分析(3)_dma controller驱动_风雨兼程8023的博客-CSDN博客文章来源地址https://www.toymoban.com/news/detail-405646.html
到了这里,关于Linux DMA子系统(2):DMA控制器驱动(provider)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!