目录
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来暂停传输、恢复传输、终止传输等操作。文章来源:https://www.toymoban.com/news/detail-436610.html
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模板网!