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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值