Linux DMA子系统(2):DMA控制器驱动(provider)

这篇具有很好参考价值的文章主要介绍了Linux DMA子系统(2):DMA控制器驱动(provider)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

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

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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux驱动开发:SPI子系统

    MISO:主设备数据输入,从设备数据输出。 MOSI:主设备数据输出,从设备数据输入。 SCLK:时钟信号,由主设备产生。 CS:    从设备片选信号,由主设备控制。 CPOL(时钟极性) :   0:时钟起始位低电平      1:时钟起始为高电平   CPHA(时钟相位) :0:第一个时钟周期采样   1

    2024年02月06日
    浏览(48)
  • 【Linux驱动开发】010 pinctrl子系统

    上一章我们编写了基于设备树的 LED 驱动,但是驱动的本质还是没变,都是配置 LED 灯所使用的 GPIO 寄存器,驱动开发方式和裸机基本没啥区别。本章我们就来学习一下如何借助 pinctrl 和 gpio 子系统来简化 GPIO 驱动开发。   Linux 内核针对 PIN 的配置推出了 pinctrl 子系统,对于

    2024年02月14日
    浏览(42)
  • 【Linux驱动开发】011 gpio子系统

    前面我们编写了基于设备树的 LED 驱动,但是驱动的本质还是没变,都是配置 LED 灯所使用的 GPIO 寄存器,驱动开发方式和裸机基本没啥区别。本章我们就来学习一下如何借助 pinctrl 和 gpio 子系统来简化 GPIO 驱动开发。   Linux 内核针对 PIN 的配置推出了 pinctrl 子系统,对于

    2024年02月03日
    浏览(46)
  • Linux驱动之input输入子系统

    目录 前言: 介绍: input_dev结构体: 输入子系统的使用流程: 实例测试 : 前言: 输入子系统用于实现Linux系统输入设备(鼠标 键盘 触摸屏  游戏杆)驱动的一种框架。Linux内核将其中的固定部分放入内核,驱动开发时只需要实现其中的不固定部分(主要还是和硬件相关的部分

    2024年02月15日
    浏览(38)
  • Linux clock子系统及驱动实例

    晶振 :晶源振荡器 PLL :Phase lock loop,锁相环。用于提升频率 OSC :oscillator的简写,振荡器 Linux的时钟子系统由CCF(common clock framework)框架管理,CCF向上给用户提供了通用的时钟接口,向下给驱动开发者提供硬件操作的接口。各结构体关系如下: CCF框架比较简单,只有这几

    2024年02月02日
    浏览(32)
  • <Linux开发>驱动开发 -之-Linux INPUT 子系统

    <Linux开发>驱动开发 -之-Linux INPUT 子系统 交叉编译环境搭建: <Linux开发> linux开发工具-之-交叉编译环境搭建 uboot移植可参考以下: <Linux开发> -之-系统移植 uboot移植过程详细记录(第一部分) <Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分) <Linux开发

    2024年02月09日
    浏览(48)
  • 【Linux驱动开发】012 gpio子系统API函数

    设置好设备树以后, 在驱动程序中就可以使用 gpio 子系统提供的 API 函数来操作指定的 GPIO, gpio 子系统向驱动开发人员屏蔽了具体的读写寄存器过程。这就是驱动分层与分离的好处,大家各司其职,做好自己的本职工作即可。 gpio 子系统提供的常用的 API 函数有下面几个:

    2023年04月18日
    浏览(40)
  • Linux驱动开发之【pinctrl和gpio子系统】

    目录 一、 pinctrl和gpio子系统 1.pinctrl子系统 1.1 pinctrl子系统简介 1.2 pinctrl子系统驱动 1.3 设备树中添加pinctrl节点模版 2. gpio子系统 2.1 gpio子系统简介 2.2 gpio子系统驱动 2.3 gpio子系统API函数 2.4 设备树中添加gpio节点模板 2.5 与gpio相关的OF函数 3. 驱动程序编写 3.1 驱动入口函数 3

    2024年02月06日
    浏览(42)
  • <Linux开发>驱动开发 -之-基于pinctrl/gpio子系统的beep驱动

    <Linux开发>驱动开发 -之-基于pinctrl/gpio子系统的beep驱动 交叉编译环境搭建: <Linux开发> linux开发工具-之-交叉编译环境搭建 uboot移植可参考以下: <Linux开发> -之-系统移植 uboot移植过程详细记录(第一部分) <Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分

    2024年02月13日
    浏览(38)
  • 【LED子系统深度剖析】一、开篇词|Linux驱动开发必读

    我的圈子: 高级工程师聚集地 我是董哥,高级嵌入式软件开发工程师,从事嵌入式Linux驱动开发和系统开发,曾就职于世界500强公司! 创作理念:专注分享高质量嵌入式文章,让大家读有所得!

    2024年02月09日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包