Linux DMA 框架介绍(demengine.c)

1.介绍

从我们的直观感受来说,DMA并不是一个复杂的东西,要做的事情也很单纯直白。因此Linux kernel对它的抽象和实现,也应该简洁、易懂才是。不过现实却不甚乐观(个人感觉),Linux kernel dmaengine framework的实现,真有点晦涩的感觉。为什么会这样呢?

如果一个软件模块比较复杂、晦涩,要么是设计者的功力不够,要么是需求使然。当然,我们不敢对Linux kernel的那些大神们有丝毫怀疑和不敬,只能从需求上下功夫了:难道Linux kernel中的driver对DMA的使用上,有一些超出了我们日常的认知范围?

要回答这些问题并不难,将dmaengine framework为consumers提供的功能和API梳理一遍就可以了,这就是本文的目的。当然,也可以借助这个过程,加深对DMA的理解,以便在编写那些需要DMA传输的driver的时候,可以更游刃有余。

2. Slave-DMA API和Async TX API

从方向上来说,DMA传输可以分为4类:memory到memory、memory到device、device到memory以及device到device。Linux kernel作为CPU的代理人,从它的视角看,外设都是slave,因此称这些有device参与的传输(MEM2DEV、DEV2MEM、DEV2DEV)为Slave-DMA传输。而另一种memory到memory的传输,被称为Async TX。

为什么强调这种差别呢?因为Linux为了方便基于DMA的memcpy、memset等操作,在dma engine之上,封装了一层更为简洁的API(如下面图片1所示),这种API就是Async TX API(以async_开头,例如async_memcpy、async_memset、async_xor等)。


最后,因为memory到memory的DMA传输有了比较简洁的API,没必要直接使用dma engine提供的API,最后就导致dma engine所提供的API就特指为Slave-DMA API(把mem2mem剔除了)。
本文主要介绍dma engine为consumers提供的功能和API,因此就不再涉及Async TX API了

3.DMAENGINE 使用步骤

本文大部分内容翻译自kernel document[1],喜欢读英语的读者可以自行参考。
对设备驱动的编写者来说,要基于dma engine提供的Slave-DMA API进行DMA传输的话,需要如下的操作步骤:
1)申请一个DMA channel。
2)根据设备(slave)的特性,配置DMA channel的参数。
3)要进行DMA传输的时候,获取一个用于识别本次传输(transaction)的描述符(descriptor)。
4)将本次传输(transaction)提交给dma engine并启动传输。
5)等待传输(transaction)结束。

然后,重复3~5即可。

3.1 申请DMA channe

任何consumer(文档[1]中称作client,也可称作slave driver,意思都差不多,不再特意区分)在开始DMA传输之前,都要申请一个DMA channel(有关DMA channel的概念,请参考[2]中的介绍)。

DMA channel(在kernel中由“struct dma_chan”数据结构表示)由provider(或者是DMA controller)提供,被consumer(或者client)使用。对consumer来说,不需要关心该数据结构的具体内容(我们会在dmaengine provider的介绍中在详细介绍)。
consumer可以通过如下的API申请DMA channel:

/* 从dma_device_list上找到一个合适的dma控制器,并从控制器上获取一个dma channel */
struct dma_chan *dma_request_chan(struct device *dev, const char *name);

最后,申请得到的dma channel可以在不需要使用的时候通过下面的API释放掉:
void dma_release_channel(struct dma_chan *chan);

3.2 配置DMA channel的参数

driver申请到一个为自己使用的DMA channel之后,需要根据自身的实际情况,以及DMA controller的能力,对该channel进行一些配置。可配置的内容由struct dma_slave_config数据结构表示(具体可参考4.1小节的介绍)。driver将它们填充到一个struct dma_slave_config变量中后,可以调用如下API将这些信息告诉给DMA controller:

int dmaengine_slave_config(struct dma_chan *chan, struct dma_slave_config *config)

3.3 获取传输描述(tx descriptor)

DMA传输属于异步传输,在启动传输之前,slave driver需要将此次传输的一些信息(例如src/dst的buffer、传输的方向等)提交给dma engine(本质上是dma controller driver),dma engine确认okay后,返回一个描述符(由struct dma_async_tx_descriptor抽象)。此后,slave driver就可以以该描述符为单位,控制并跟踪此次传输。
struct dma_async_tx_descriptor数据结构可参考4.2小节的介绍。根据传输模式的不同,slave driver可以使用下面三个API获取传输描述符(具体可参考Documentation/dmaengine/client.txt[1]中的说明):

/*dmaengine_prep_slave_sg用于在“scatter gather buffers”列表和总线设备之间进行DMA传输*/
struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,unsigned int sg_len, enum dma_data_direction direction,unsigned long flags);
/* dmaengine_prep_dma_cyclic常用于音频等场景中,在进行一定长度的dma传输(buf_addr&buf_len)的过程中,每传输一定的byte(period_len),就会调用一次传输完成的回调函数*/
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_data_direction direction);
/*dmaengine_prep_interleaved_dma可进行不连续的、交叉的DMA传输,通常用在图像处理、显示等场景中。*/
struct dma_async_tx_descriptor *dmaengine_prep_interleaved_dma(struct dma_chan *chan, struct dma_interleaved_template *xt,unsigned long flags);

3.4 启动传输

通过3.3章节介绍的API获取传输描述符之后,client driver可以通过dmaengine_submit接口将该描述符放到传输队列上,然后调用dma_async_issue_pending接口,启动传输。

/*参数为传输描述符指针,返回一个唯一识别该描述符的cookie,用于后续的跟踪、监控。*/
dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc)
void dma_async_issue_pending(struct dma_chan *chan);

3.5 等待传输结束

传输请求被提交之后,client driver可以通过回调函数获取传输完成的消息,当然,也可以通过dma_async_is_tx_complete等API,测试传输是否完成。不再详细说明了。
最后,如果等不及了,也可以使用dmaengine_pause、dmaengine_resume、dmaengine_terminate_xxx等API,暂停、终止传输,具体请参考kernel document[1]以及source code。

4. 重要数据结构说明

4.1 struct dma_slave_config

中包含了完成一次DMA传输所需要的所有可能的参数,其定义如下:

/* include/linux/dmaengine.h */
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;bool device_fc;unsigned int slave_id;
};
/*
direction,指明传输的方向,包括(具体可参考enum dma_transfer_direction的定义和注释):DMA_MEM_TO_MEM,memory到memory的传输;DMA_MEM_TO_DEV,memory到设备的传输;DMA_DEV_TO_MEM,设备到memory的传输;DMA_DEV_TO_DEV,设备到设备的传输。注5:controller不一定支持所有的DMA传输方向,具体要看provider的实现。注6:参考第2章的介绍,MEM to MEM的传输,一般不会直接使用dma engine提供的API。src_addr,传输方向是dev2mem或者dev2dev时,读取数据的位置(通常是固定的FIFO地址)。对mem2dev类型的channel,不需配置该参数(每次传输的时候会指定);
dst_addr,传输方向是mem2dev或者dev2dev时,写入数据的位置(通常是固定的FIFO地址)。对dev2mem类型的channel,不需配置该参数(每次传输的时候会指定);
src_addr_width、dst_addr_width,src/dst地址的宽度,包括1、2、3、4、8、16、32、64(bytes)等(具体可参考enum dma_slave_buswidth 的定义)。src_maxburst、dst_maxburst,src/dst最大可传输的burst size(可参考[2]中有关burst size的介绍),单位是src_addr_width/dst_addr_width(注意,不是byte)。device_fc,当外设是Flow Controller(流控制器)的时候,需要将该字段设置为true。CPU中有关DMA和外部设备之间连接方式的设计中,决定DMA传输是否结束的模块,称作flow controller,DMA controller或者外部设备,都可以作为flow controller,具体要看外设和DMA controller的设计原理、信号连接方式等,不在详细说明(感兴趣的同学可参考[4]中的介绍)。slave_id,外部设备通过slave_id告诉dma controller自己是谁(一般和某个request line对应)。很多dma controller并不区分slave,只要给它src、dst、len等信息,它就可以进行传输,因此slave_id可以忽略。而有些controller,必须清晰地知道此次传输的对象是哪个外设,就必须要提供slave_id了(至于怎么提供,可dma controller的硬件以及驱动有关,要具体场景具体对待)。
*/

4.2 struct dma_async_tx_descriptor

传输描述符用于描述一次DMA传输(类似于一个文件句柄)。client driver将自己的传输请求通过3.3中介绍的API提交给dma controller driver后,controller driver会返回给client driver一个描述符。
client driver获取描述符后,可以以它为单位,进行后续的操作(启动传输、等待传输完成、等等)。也可以将自己的回调函数通过描述符提供给controller driver。
传输描述符的定义如下:

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;void *callback_param;struct dmaengine_unmap_data *unmap;
#ifdef CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCHstruct dma_async_tx_descriptor *next;struct dma_async_tx_descriptor *parent;spinlock_t lock;
#endif
}
/*
cookie,一个整型数,用于追踪本次传输。一般情况下,dma controller driver会在内部维护一个递增的number,每当client获取传输描述的时候(参考3.3中的介绍),都会将该number赋予cookie,然后加一。
注7:有关cookie的使用场景,我们会在后续的文章中再详细介绍。flags, DMA_CTRL_开头的标记,包括:
DMA_CTRL_REUSE,表明这个描述符可以被重复使用,直到它被清除或者释放;
DMA_CTRL_ACK,如果该flag为0,表明暂时不能被重复使用。phys,该描述符的物理地址??不太懂!chan,对应的dma channel。tx_submit,controller driver提供的回调函数,用于把改描述符提交到待传输列表。通常由dma engine调用,client driver不会直接和该接口打交道。desc_free,用于释放该描述符的回调函数,由controller driver提供,dma engine调用,client driver不会直接和该接口打交道。callback、callback_param,传输完成的回调函数(及其参数),由client driver提供。后面其它参数,client driver不需要关心,暂不描述了。
*/

Linux DMA 框架介绍,demengine.c文件相关推荐

  1. 全志 Tina Linux 图形系统 框架介绍 最全介绍 MiniGUI、QT5、EFL、GTK+(WebkitGtk、Midori)、DirectFB、Wayland

    1 概述 本文档将介绍 Allwinner Tina Linux 中已经移植好的窗口系统,以及怎么使用,包括 MiniGUI.QT5.EFL.GTK+(WebkitGtk.Midori).Direct ...

  2. linux查找目录下含有xx的文件,linux基础命令介绍三:文件搜索及其它

    1.linux中包含大量的文件,对于文件查找,linux提供了find命令. find是一个非常有效的工具,它可以遍历目标目录甚至整个文件系统来查找某些文件或目录: find [path...] [e ...

  3. linux常见文件打开,Linux常用操作有哪些? Linux常用操作介绍

    Linux系统,相信很多洞电脑的朋友都知道,也知道怎么使用,但是很多电脑新手可不一定知道的.今天我们就来看看电脑新手需要掌握哪些Linux系统的常用操作.下面,U大侠小编就给大家介绍Linux常用操作 ...

  4. linux常用删除空文件夹,Linux基础 linux系统中的批量删除文件与空文件删除的命令介绍...

    Linux基础教程linux系统中的批量删除文件与空文件删除的命令介绍 Linux资料下面删除文件或者目录命令rm(remove): Linux培训功能说明:删除文件或目录. 语 法:rm [-dfi ...

  5. Linux DMA Engine framework(2)_功能介绍及解接口分析

    转载.蜗窝科技,www.wowotech.net. Linux DMA Engine framework(2)_功能介绍及解接口分析 作者:wowo 发布于:2017-5-2 22:47 分类:Lin ...

  6. Linux驱动框架与杂项字符设备框架介绍

    1. Linux下驱动框架介绍 1.1 驱动框架分类 Linux下驱动框架分为3大类型: 字符设备 --------- 块设备 存储设备 SD 硬盘 网络设备 网卡 无线 有线 字符设备和块设备都会生 ...

  7. Linux常用命令(本篇包括,Linux目录结构介绍、Linux Shell介绍、9个常见命令介绍、文件的概念、文件的操作(20个)、目录的操作、文件和目录的权限、文件压缩及解压缩)

    Linux常用命令(本篇包括,Linux目录结构介绍.Linux Shell介绍.9个常见命令介绍.文件的概念.文件的操作(20个).目录的操作.文件和目录的权限.文件压缩及解压缩)         ...

  8. Linux基础知识--- 1、centos文件夹介绍

    /boot 引导程序,内核等存放的目录. 这个目录,包括了在引导过程中所必需的文件,引导程序的相关文件(例如grub,lilo以及相应的配置文件以及Linux操作系统内核相关文件(例如vmlinuz等 ...

  9. Linux DMA 驱动学习总结

    Linux DMA驱动构架分析 以linux2.6.32中的S3C2440驱动为例进行分析,DMA驱动所对应的源码为linux-2.6.32.2\arch \arm\mach-s3c2440\dma. ...

最新文章

  1. 使用 Python 和 OpenCV 进行数据增广
  2. python爬虫经典教程-python爬虫经典例子有哪些
  3. 第14章:信息文档与配置管理和知识与流程管理
  4. win7台式电脑怎么连wifi_修改WiFi密码后电脑连不上网如何解决 修改WiFi密码后电脑连不上网解决方法【详解】...
  5. OpenGL 各类库的解析gl glu glut freeglut glfw glew
  6. IntelliTrace 调试、定位异常
  7. springboot 多了8小时_日本人不明白:中国的奶茶有多好喝,值得排队8小时去买?...
  8. 软考信息安全工程师备考笔记5:第五章应用系统安全基础备考要点
  9. 【英语学习】【Daily English】U09 Fashion L03 You're my fashion icon
  10. 深入了解帆软报表系统的启动过程二
  11. 在Javascript中得到站点的根路径
  12. 与计算机专业相关的英语科普短文,英语科普文选-中英文对照(计算机.doc
  13. [在Windows上使用Unix工具]MKS
  14. R语言超星学习通习题
  15. 智能陈桥输入法软件测试,智能陈桥五笔输入法
  16. c语言编程入门教程+网易,人话讲编程·C语言入门:第一讲,Hello World
  17. 常用DOS命令(jAVA开发时大多数用不到)
  18. 专访丨兼容国内外市场的代码分析软件,鉴释科技帮助企业减少bug发生率
  19. C#精挑整理知识要点11 委托和事件(建议收藏)
  20. 超级好看的动态官网源码

热门文章

  1. 亚马逊防关联怎么做?软关联硬关联有什么?
  2. 如何给运行中的docker容器增加映射端口
  3. linux系统电视播放格式,OpenPCTV--支持电视的 Linux
  4. matlab仿真中pv,PV的matlab仿真
  5. K8S学习之污点容忍
  6. GCC自带的一些builtin内建函数
  7. Linux 管理面板云帮手、APPNODE与宝塔哪个好
  8. POJ 1370 Gossiping 笔记
  9. 首届 Rust China Hackathon Online 参赛队伍名单出炉
  10. 来自19位科技大亨的励志箴言(绝对值得收藏)