Linux DMA子系统(3):DMA设备驱动(consumer)

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

目录

1. 前言

2. 重要的结构体

2.1 struct dma_slave_config

2.2 struct dma_async_tx_descriptor

3. 设备驱动使用DMA Engine的方法

3.1 分配一个DMA从通道

3.2 设置DMA通道的具体参数

3.3 获取描述符

3.4 提交传输并启动传输

3.5 等待传输完成

4. 参考文章


1. 前言

上文从DMA控制器驱动的角度去分析了DMA Engine,即从provider的角度,本文将从需要使用DMA传输的设备驱动的角度,即consumer的角度去分析DMA Engine,看看DMA Engine为client driver提供了哪些功能和API。

2. 重要的结构体

2.1 struct dma_slave_config

struct dma_slave_config结构体包含了完成一次DMA传输所需要的所有可能的参数。

struct dma_slave_config {
	enum dma_transfer_direction direction;
	phys_addr_t src_addr;
	phys_addr_t dst_addr;
	enum dma_slave_buswidth src_addr_width;
	enum dma_slave_buswidth dst_addr_width;
	u32 src_maxburst;
	u32 dst_maxburst;
	u32 src_port_window_size;
	u32 dst_port_window_size;
	bool device_fc;
	unsigned int slave_id;
};

1. direction:DMA传输的方向。

2. src_addr:当src是dev时,即从dev2dev或者从dev2mem时,读取DMA从数据的物理地址,反之,当src是mem时,即从mem2dev时(mem2mem的传输,一般不会直接使用dma engine提供的API),则忽略此参数。

3. dst_addr:当dst是dev时,即从mem2dev或者从dev2dev时,写入DMA从数据的物理地址,反之,当dst是mem时,即从dev2mem时(mem2mem的传输,一般不会直接使用dma engine提供的API),则忽略此参数。

4. src_addr_width:src地址的宽度,其中包括包括1、2、3、4、8、16、32、64,单位为byte。

5. dst_addr_width:dst地址的宽度,其中包括包括1、2、3、4、8、16、32、64,单位为byte。

6. src_maxburst:src最大可传输的burst的大小。

7. dst_maxburst:dst最大可传输的burst的大小。

8. device_fc:流控制器设置,仅对从通道有效。如果外围设备是流控制器,则填写“true”。 方向将在运行时选择。

9. slave_id:从属请求者id,仅对从通道有效。DMA从外设将具有唯一的id作为DMA请求者,需要作为从配置传递。

2.2 struct dma_async_tx_descriptor

struct dma_async_tx_descriptor用于描述一次DMA传输。

struct dma_async_tx_descriptor {
	dma_cookie_t cookie;
	enum dma_ctrl_flags flags; /* not a 'long' to pack with cookie */
	dma_addr_t phys;
	struct dma_chan *chan;
	dma_cookie_t (*tx_submit)(struct dma_async_tx_descriptor *tx);
	int (*desc_free)(struct dma_async_tx_descriptor *tx);
	dma_async_tx_callback callback;
	dma_async_tx_callback_result callback_result;
	void *callback_param;
	struct dmaengine_unmap_data *unmap;
#ifdef CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH
	struct dma_async_tx_descriptor *next;
	struct dma_async_tx_descriptor *parent;
	spinlock_t lock;
#endif
};

enum dma_ctrl_flags {
	DMA_PREP_INTERRUPT = (1 << 0),
	DMA_CTRL_ACK = (1 << 1),
	DMA_PREP_PQ_DISABLE_P = (1 << 2),
	DMA_PREP_PQ_DISABLE_Q = (1 << 3),
	DMA_PREP_CONTINUE = (1 << 4),
	DMA_PREP_FENCE = (1 << 5),
	DMA_CTRL_REUSE = (1 << 6),
	DMA_PREP_CMD = (1 << 7),
};

1. cookie:跟踪此transaction的cookie。

2. flags:用于增强操作准备、控制完成和通信状态的标志。

3. phys:描述符的物理地址。

4. chan:此操作的目标通道。

5. tx_submit:将此描述符提交到待传输列表的回调函数。

6. desc_free:将此描述符释放的回调函数。

7. callback:此操作完成后(传输完成后)调用的回调函数。

8. callback_param:callback回调函数的参数。

3. 设备驱动使用DMA Engine的方法

enum dma_transfer_direction {
	DMA_MEM_TO_MEM,
	DMA_MEM_TO_DEV,
	DMA_DEV_TO_MEM,
	DMA_DEV_TO_DEV,
	DMA_TRANS_NONE,
};

首先介绍个概念,上面的enum表明,DMA传输从方向上来说分为mem2mem、mem2dev、dev2mem和dev2dev。

其中,mem2dev、dev2mem和dev2dev属于Slave-DMA传输,mem2mem属于Async TX传输。

因为Linux为mem2mem的DMA传输在DMA Engine之上封装了更为简洁的API接口,即Async TX API,想要了解Async TX API,请参考内核文档:/crypto/async-tx-api.txt,因此,DMA Engine提供的API就是Slave-DMA API。

下面说说设备驱动使用DMA Engine的步骤:

1. 分配一个DMA从通道。

2. 设置从机和控制器(DMA channel)具体参数。

3. 获取一个用于识别本次传输的描述符。

4.提交传输并启动传输。

5. 发出待处理的请求并等待传输结束后的回调通知。

3.1 分配一个DMA从通道

使用 dma_request_chan() API 请求通道。

它将查找并返回与dev设备关联的name的DMA通道。此关联可以通过设备树、ACPI或基于板文件的dma_slave_map匹配表来完成。

通过此接口分配的通道对调用者来说是专有的,直到dma_release_channel()被调用。

struct dma_chan *dma_request_chan(struct device *dev, const char *name);

void dma_release_channel(struct dma_chan *chan);

3.2 设置DMA通道的具体参数

下一步是将一些特定信息传递给DMA控制器驱动(provider)。 consumer可以使用的大多数通用信息都在struct dma_slave_config中,如DMA direction,DMA addresses,bus widths,DMA burst lengths等。

使用dmaengine_slave_config()来设置DMA通道的具体参数。

static inline int dmaengine_slave_config(struct dma_chan *chan,
					  struct dma_slave_config *config)
{
	if (chan->device->device_config)
		return chan->device->device_config(chan, config);

	return -ENOSYS;
}

3.3 获取描述符

对于consumer使用DMA,DMA Engine支持的各种slave传输模式有:

slave_sg:用于在“scatter gather buffers”列表和总线设备之间进行DMA传输。

dma_cyclic:用于执行循环DMA操作,直到操作明确停止,常应用与音频等场景中。

interleaved_dma:用于不连续的、交叉的DMA传输,常应用与图像处理等场景中。

使用以下API来获取不同传输模式的描述符:

static inline 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)
{
	if (!chan || !chan->device || !chan->device->device_prep_slave_sg)
		return NULL;

	return chan->device->device_prep_slave_sg(chan, sgl, sg_len,
						  dir, flags, NULL);
}

static inline 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)
{
	if (!chan || !chan->device || !chan->device->device_prep_dma_cyclic)
		return NULL;

	return chan->device->device_prep_dma_cyclic(chan, buf_addr, buf_len,
						period_len, dir, flags);
}


static inline struct dma_async_tx_descriptor *dmaengine_prep_interleaved_dma(
		struct dma_chan *chan, struct dma_interleaved_template *xt,
		unsigned long flags)
{
	if (!chan || !chan->device || !chan->device->device_prep_interleaved_dma)
		return NULL;

	return chan->device->device_prep_interleaved_dma(chan, xt, flags);
}

3.4 提交传输并启动传输

准备好描述符并添加回调信息后,必须将其放置在DMA Engine驱动程序传输队列中,通过调用dmaengine_submit()API来将准备好的描述符放到传输队列上,之后调用dma_async_issue_pending()API来启动传输。

static inline dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc)
{
	return desc->tx_submit(desc);
}

static inline void dma_async_issue_pending(struct dma_chan *chan)
{
	chan->device->device_issue_pending(chan);
}

3.5 等待传输完成

在每个DMA传输请求提交并启动后,队列中的下一个启动并触发一个小任务,tasklet然后将调用设备驱动(consumer)完成回调函数来通知传输完成。

还可以调用dmaengine_pause、dmaengine_resume、dmaengine_terminate_*()等API来暂停传输、恢复传输、终止传输等操作。

static inline int dmaengine_pause(struct dma_chan *chan)
{
	if (chan->device->device_pause)
		return chan->device->device_pause(chan);

	return -ENOSYS;
}

static inline int dmaengine_resume(struct dma_chan *chan)
{
	if (chan->device->device_resume)
		return chan->device->device_resume(chan);

	return -ENOSYS;
}

static inline int dmaengine_terminate_all(struct dma_chan *chan)
{
	if (chan->device->device_terminate_all)
		return chan->device->device_terminate_all(chan);

	return -ENOSYS;
}

4. 参考文章

Documentation/dmaengine/client.txt文章来源地址https://www.toymoban.com/news/detail-436610.html

到了这里,关于Linux DMA子系统(3):DMA设备驱动(consumer)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 字符设备驱动之输入子系统分析(一)

    作者: Bright-Ho 联系方式: 836665637@qq.com 前言背景描述: 虽然在网上看了很多有关输入子系统的资料和视频,但是真正的,系统的,全面的,来弄清输入子系统,还是要花些时间和精力的!现在我以一个初学者的角度来分析 input 输入子系统; 那么分析 input 输入子系统之前,

    2024年02月15日
    浏览(39)
  • 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. 参考文章 本文将从DMA控制器驱动(provider)的角度来介绍DMA Engine,包括重要的结构体和API接口。 DMA控制器驱动主要作用

    2023年04月09日
    浏览(38)
  • 浅析Linux SCSI子系统:设备管理

    Linux SCSI子系统通过SCSI主机适配器(HBA)接入所有SCSI存储设备,在Linux系统中,可以安装多种主机适配器,SCSI中层会提供主机适配器的统一抽象,这些主机适配器的厂商提供具体的低层驱动实现;主机适配器接入到SCSI子系统后,SCSI会通过扫描或者低层驱动主动上报的方式,

    2024年02月11日
    浏览(36)
  • Linux驱动开发:SPI子系统

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

    2024年02月06日
    浏览(53)
  • Linux MMC 驱动子系统详解

    SD/SDIO/MMC 驱动是一种基于 SDMMC 和 SD SPI 主机驱动的协议级驱动程序,目前已支持 SD 存储器、SDIO 卡和 eMMC 芯片。 因为linux内核mmc子系统里面已经实现了这些协议,我们以后并不需要重新实现这些,只需要对协议有个简单的了解。 mmc是比较老的存储卡了,sd是mmc的替代者,sdi

    2024年02月06日
    浏览(41)
  • Linux驱动开发:gpio子系统

    目录 1、GPIO配置流程 2、GPIO子系统API 2.1 of_find_node_by_path 2.2 of_get_named_gpio 2.3 gpio_request 与 gpiod_get 与 gpiod_get_index 2.4 gpio_direction_input 与 gpiod_direction_input 2.5 gpio_direction_output 与 gpiod_direction_output 2.6 gpio_get_value 与 gpiod_get_value 2.7 gpio_set_value 与 gpiod_set_value 2.8  gpiod_get_from

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

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

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

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

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

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

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

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

    2024年02月02日
    浏览(34)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包