Linux 4.0的dmaengine编程

在Linux 4.0下进行dmaengine的编程主要分为两部分,DMA Engine控制器编程和DMA Engine API编程。

DMA Engine API编程

slave DMA用法包括以下的步骤: 
1. 分配一个DMA slave通道; 
2. 设置slave和controller特定的参数; 
3. 获取一个传输描述符; 
4. 提交传输描述符; 
5. 发起等待的请求并等待回调通知。

下面是以上每一步的详细说明。

1. 分配一个DMA slave通道 
在slave DMA上下文通道的分配略有不同,客户端驱动通常需要一个通道,这个通道源自特定的DMA控制器,在某些情况甚至需要一个特定的通道。请求通道的API是channel dma_request_channel()。 
其接口如下:

struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
            dma_filter_fn filter_fn,
            void *filter_param);
1
2
3
其中dma_filter_fn接口定义如下:

typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
1
filter_fn是可选的,但是对于slave和cyclic通道我们强烈推荐使用,因为它们需要获取一个特定的DMA通道。

当filter_fn参数为空,dma_request_channel()函数简单地返回第一个满足mask参数的通道。 
否则,filter_fn函数将会对每个空闲的通道调用一次,同样这些通道也是要满足mask参数。当filter_fn返回true的时候说明期望的通道已经找到。

通过这个API分配的通道在dma_release_channel()函数调用前对于其他调用者是互斥的。

2. 设置slave和controller特定的参数 
这一步通常是传递一些特定的信息到DMA驱动。大多数slave DMA使用到的通用信息都在结构体dma_slave_config中。它允许客户端对外设指定DMA的方向、DMA地址、总线宽度、DMA突发长度等等。

如果一些DMA控制器有更多要发送的参数,那它们应该试图把结构体dma_slave_config内嵌到控制器特定的结构体中。对于客户端来说将有更多的灵活性来传递更多需要的参数。 
接口如下:

int dmaengine_slave_config(struct dma_chan *chan,
                  struct dma_slave_config *config)
1
2
对于dma_slave_config结构体成员的详解可在dmaengine.h中查看。

3. 获取一个传输描述符 
DMA-engine支持多种slave传输模式: 
- slave_sg:DMA一列聚散buffers from/to外设; 
- dma_cyclic:实现一个循环的DMA操作 from/to外设,直到操作被停止; 
- interleaved_dma:对于Slave客户端和M2M客户端都很常见。这种情况下驱动已知Slave设备FIFO的地址。可对dma_interleaved_template结构体的成员设置适当的值来表示多种类型的操作;

这个传输API的返回值就是一个传输描述符。 
其接口如下:

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);

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);

struct dma_async_tx_descriptor *dmaengine_prep_interleaved_dma(
        struct dma_chan *chan, struct dma_interleaved_template *xt,
        unsigned long flags);
1
2
3
4
5
6
7
8
9
10
11
12
在调用dmaengine_prep_slave_sg()函数前外设驱动必须已经映射scatterlist,该映射必须保持直到DMA操作完成。 
一般的步骤如下:

    nr_sg = dma_map_sg(chan->device->dev, sgl, sg_len);
    if (nr_sg == 0)
        /* error */

    desc = dmaengine_prep_slave_sg(chan, sgl, nr_sg, direction, flags);
1
2
3
4
5
一旦传输描述符获取成功,回调信息加入后描述符被提交。

注意:

对于Slave DMA,在回调函数被调用前接下来的传输描述符可能不能提交,因此slave DMA回调函数允许准备并提交一个新的传输描述符;
对于cyclic 
DMA,回调函数可被用来停止DMA操作,通过在回调函数内部调用dmaengine_terminate_all()函数完成;
因此,在调用回调函数前DMA engine驱动解锁是非常重要的,因为它会导致死锁。
回调函数通常通过DMA engines的底半部tasklet方式调用,而不是直接通过中断顶半部调用。 
4. 提交传输描述符 
一旦传输描述符准备好并且回调函数也加入后,下一步就是把传输描述符加入到DMA engine驱动的等待队列。 
其接口如下:
dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc)
1
返回值是一个cookie,主要用来检查DMA engine活动的状态过程,可通过其他的DMA engine API来实现。

dmaengine_submit()函数仅仅提交描述符到DMA engine的等待队列,它不会启动DMA操作。

5. 发起等待的请求并等待回调通知 
等待队列里面的传输描述符可通过调用issue_pending API激活。此时如果通道是空闲的,等待队列中的第一个传输描述符将会启动DMA操作。 
每次DMA操作完成后,等待队列中的下一个描述符将会启动DMA操作并且一个tasklet将会被处罚。如果我们在前面设定了客户端驱动的回调通知函数,那么tasklet将会调用这个函数。 
其接口如下:

void dma_async_issue_pending(struct dma_chan *chan);
1
6. 其他API接口

dma engine API    说明
int dmaengine_terminate_all(struct dma_chan *chan)    指定DMA通道的所有活动都会被停止,DMA FIFO里面尚未传输完成的数据可能会丢失。未完成的DMA传输不会调用任何回调函数
int dmaengine_pause(struct dma_chan *chan)    指定DMA通道的活动将会被暂停,不会造成数据的丢失
int dmaengine_resume(struct dma_chan *chan)    指定DMA通道的活动将会被恢复
enum dma_status dma_async_is_tx_complete(struct dma_chan *chan,dma_cookie_t cookie, dma_cookie_t *last, dma_cookie_t *used)    这个函数用来检查指定DMA通道的状态。可在linux/dmaengine.h查看这个API更详细的用法,这个函数可与dma_async_is_complete()函数一并使用,从dmaengine_submit()函数返回的cookie可用来检查指定DMA传输是否完成。注意:不是所有的DMA engine驱动能在一个正在运行的DMA通道返回准确的信息。因此我们推荐用户在使用这个API前先暂停或者停止指定DMA通道。
DMA Engine controller编程

和其他的内核框架类似,dmaengine的注册依赖于填充一个结构并把它注册到框架中。对于dmaengine,这个结构是dma_device。

1. 分配dma_device结构

2. 初始化dma_device结构 
dma_device结构的成员说明如下:

dma_device结构体成员    说明
channel    使用INIT_LIST_HEAD宏初始化
src_addr_widths    包含支持源传输宽度的bitmask
dst_addr_widths    包含支持目的传输宽度的bitmask
directions    包含一个支持从设备方向的bitmask
residue_granularity    使用dma_set_residue设置
Descriptor    你的设备不支持任何种类的residue报告。框架仅仅知道特定传输描述符完成
Segment    你的设备能报告哪个数据块完成传输
Burst    你的设备能报告哪一次突发传输已经完成
dev    保存指向device的指针,这个指针和你当前的驱动实例相关
3. 设置设备支持的传输类型 
dma_device结构有一个域cap_mask,这个域保存支持的传输类型,可使用dma_cap_set()函数改变,支持的传输类型定义在dma_transaction_type中,它位于include/linux/dmaengine.h头文件中:

DMA传输类型    说明
DMA_MEMCPY    内存到内存的拷贝
DMA_SG    设备支持内存到内存的分散/聚合传输
DMA_INTERLEAVE    内存到内存的交错传输;交错传输的定义:传输数据从一个非连续的buffer到一个非连续的buffer,和DMA_SLAVE相反。通常用在2D内容的传输,在那种场景下你想直接传输一部分未经压缩的数据到显示部分
DMA_XOR    设备能在内存区域执行XOR操作,用来加速对XOR敏感的任务,例如RAID5
DMA_XOR_VAL    使用XOR进行内存Buffer的奇偶校验
DMA_PQ    内存到内存的P+Q 计算
DMA_PQ_VAL    设备能在内存buffer执行奇偶校验使用RAID6 P+Q算法
DMA_ INTERRUPT    设备能触发一个虚拟的传输,它将会产生一个中断
DMA_SLAVE    设备能处理设备到内存的传输,包括分散/聚合传输。在mem2mem情况下我们有两种传输类型:一种是单数据块拷贝,一种是数据块的集合拷贝。这里我们仅仅使用单传输类型来处理以上两种情况。如果你想传输一个单独的连续内存buffer,只需要简单建立一个scatter链表,这个链表仅有一个项
DMA_CYCLIC    设备能处理循环传输;循环传输的定义:数据块集合能循环遍历它自己,并且最后的项指向第一项。通常用在音频传输,操作一个环形buffer,你需要做的仅仅是往这个Buffer填充音频数据
DMA_PRIVATE    不通过dma_request_channel函数请求的通道进行异步发送,使用随机的信道进行传输
DMA_ASYNC_TX    不必由设备设置,如何需要将会由框架设置
以上的类型都将会影响源地址和目的地址如何随着时间如何改变。

4. 设备的操作 
为了完成实际的逻辑,dma_device结构需要有一些函数指针,现在我们开始讨论有哪些操作我们可以实现的。我们必须填充的一些函数基于我们选择的传输类型。

DMA设备操作函数    说明
device_alloc_chan_resources    当client驱动调用dma_request_channel的时候将会调用device_alloc_chan_resources,负责分配通道需要的资源
device_free_chan_resources    当client驱动调用dma_release_channel的时候将会调用device_free_chan_resources,负责释放通道需要的资源
device_prep_dma_*    为DMA传输准备传输描述符
device_issue_pending    从pending queue中取走第一个传输描述符并启动传输。当传输完成后将会移到列表中的下一个传输描述符。这个函数可以在中断上下文中使用
device_tx_status    报告一个通道还有多少字节数据要传输
device_config    使用给定的参数重新配置通道
device_pause    暂停通道的传输
device_resume    恢复通道的传输
device_terminate_all    停止通道中所有的(包括pending的和正在进行的传输)传输
5. 注册DMA设备 
int dma_async_device_register(struct dma_device *device) 
利用这个函数把填充好的dma_device结构实体注册到内核中。

注册DMA控制器到DT DMA helpers
int of_dma_controller_register(struct device_node *np,
                struct dma_chan *(*of_dma_xlate)
                (struct of_phandle_args *, struct of_dma *),
                void *data);
 - @np:         device node of DMA controller
 - @of_dma_xlate:   translation function which converts a phandle
 -      arguments list into a dma_chan structure
 - @data        pointer to controller specific data to be used by
 -      translation function
 ———————————————— 
版权声明:本文为CSDN博主「were0415」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/were0415/article/details/54095899

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值