武汉科技大学本科毕业设计
在DMA engine遍历通道时会执行这个过滤函数,判断遍历到的通道是否满足用户的需要,该过滤函数只有在满足特定条件下才返回TRUE。
fn_param:过滤函数参数。 2、
释放DMA通道
当任务完成,我们在卸载相关的驱动程序时,我们需要将前面申请的通道释放,释放函数如下:
void dma_release_channel(struct dma_chan *chan) 3、
DMA 驱动操作
在DMA数据准备完成后就可以进入操作DMA设备阶段,调用DMA设备向DMA engine注册的各种类型的操作接口来操作DMA设备。这个阶段DMA设备驱动会将操作的数据填充到DMA硬件能够识别的操作描述符中。在填充的过程中,根据DMA硬件的能力可能需要多个描述符来完成一个DMA操作,这些操作都由DMA设备驱动来完成。操作完成返回一个异步传输描述符tx。如果操作失败则返回NULL。
dev->device_prep_dma_xxx(chan, …);
struct dma_async_tx_descriptor *(*device_prep_dma_xxx)( struct dma_chan *chan, …)
如果返回一个正确的异步传输描述符,就可以将DMA用户的回调函数(如果需要)设置到这个异步传输描述符上,则在这个描述符被完成后,将会调用这个回调函数。
4、
DMA操作回调
在获得了异步传输描述符后,就可以设置回调函数,设置到异步传输描述符(dma_async_tx_descriptor结构)的callback字段。回调函数由DMA使用者提供,其原型如下:
typedef void (*dma_async_tx_callback)(void *dma_async_param) dma_async_param也是由DMA使用者提供,设置到异步传输描述符的callback_param字段。
该回调函数在tasklet环境中被调用。因为DMA异步操作完成并不一定会产生中断,所以也并不是每一个异步传输操作都需要回调函数。反过来说,如果需要回调操作,就必须在DMA驱动操作中设置DMA_PREP_INTERRUPT宏,指示DMA驱动在本次操作后产生一个中断。
5、
DMA操作提交
DMA操作描述符准备后,需要提交给DMA硬件,提交的请求DMA硬件并不知道,只是将通道的pending计数增加,直到后面的发送pending完成。这个过程是调用DMA驱动提供的submit方法来完成的。Submit在DMA驱动初始化时填充到异步描述符的submit函数指针。其原型如下:
dma_cookie_t (*tx_submit)(struct dma_async_tx_descriptor *tx)
22
武汉科技大学本科毕业设计
提交返回一个cookie值,这个值用户并不需要关心其内容和含义,只需要了解如果cookie为负值表示操作错误码。如果大于零则是一个DMA请求的cookie。
使用dma_submit_error(cookie)宏来判断cookie是否是失败。 6、
DMA通道发送pending的请求
将一个DMA通道上的pending的请求都提交到DMA硬件来操作。在IOAT驱动中,在提交DMA请求操作阶段,如果pending的请求过多,也会执行发送pending的操作。发送pending的操作DMA engine的函数接口来完成,实际是调用的DMA驱动注册的issue_pending接口。
void dma_async_issue_pending(struct dma_chan *chan) 7、 举类型值。
enum dma_status dma_async_is_tx_complete(struct dma_chan *chan,
dma_cookie_t cookie, dma_cookie_t *last, dma_cookie_t *used)
cookie是提交DMA请求操作返回的DMA请求cookie;last和used,正在执行的DMA请求,可以设置为NULL。可用dma_async_is_tx_complete检查DMA驱动内部状态,而不用返回驱动的内部状态值。
8、
DMA操作链
1、IOAT驱动硬件描述符链
IOAT驱动中,硬件描述符在驱动初始化阶段就已经分配,并将DMA描述符组成了描述符环,如下图所示。各种DMA操作循环利用这些描述符。在DMA硬件中,逐个处理DMA描述符,遇到需要触发中断的描述符,DMA硬件产生DMA中断用于提示操作完成。
DMA操作状态查询
一个DMA请求是否完成可以通过下面的接口查询,该接口返回DMA操作状态枚
RRRC/PPCovered by DMACOUNTPZZDescriptor being programedOwned by SW,must not be touched by HWOwned by HW,must not be touched by SWR = retiredC = Current descriptor being processedP = PendingZ = descriptor being modified (not yet pending)
图3.7 DMA硬件描述符环
23
武汉科技大学本科毕业设计
2、异步传输描述符依赖链
与硬件描述符环对应的,异步传输描述符也可以组织成链式操作。如下图所示,在异步传输中支持多个异步传输描述符之间形成一个依赖链。描述符链中的各个请求之间有严格的先后顺序依赖,只有在前一个请求完成后才会进行下一个请求。
Channel mChannel nnextparentnextparentnext
图3.8 异步描述符依赖链
在异步描述符结构中,parent字段指向本请求依赖的请求的异步传输描述符,next字段指向依赖本描述符的下一个异步传输描述符。从图中可以看出,异步描述符链可以跨越多个DMA通道,这样就可以把不同的DMA操作放到一个描述符链中进行。但如果同一个通道具有描述符链所要求的所有功能,最好是将描述符链放到同一个DMA通道中,(这样就不会阻塞通道中的与本描述符链不相关其他请求)。 描述符链需要考虑的问题:
1)同一个通道可以保证依赖关系,不用设置依赖链直接submit。如果在同一通道又存在上一级的依赖链时,为什么不能submit?而只能建立依赖关系放在依赖者后面?(这个问题是通过pending设置位来保证后面的请求可以下发,只要issue_pending就可以将chan上的所有请求下发)。issue_pending只会刷有pending标识的chan,参见ioat2_issue_pending函数,如果有一个依赖关系存在,在依赖没有完成前,pending不会被设置。
2)如果不在同一个通道,又要保证依赖关系,使用switch_channel。Switch channel的操作是在被依赖的chan上插入一个interrupt请求,将新的请求作为interrupt请求的后续依赖请求,只有在interrupt请求完成后才会提交新的请求到自己的chan上。
3.3.3 DMA engine与I/OAT DMA的接口
为了屏蔽各种不同DMA硬件差异,DMA engine将DMA的功能进行了抽象,提取了DMA需要实现的公共功能,而将具体硬件的差异隐藏在DMA硬件驱动中实现。DMA引擎提供给各种不同的DMA硬件驱动的注册和功能接口,DMA引擎通过调用DMA硬件驱动注册的功能函数来操作DMA硬件。
24
武汉科技大学本科毕业设计
1、 IOAT_DMA通道资源分配:
使能channel的中断标志,分配至多达256 个desc并做初始化, 返回分配的desc数:
ioat_dma_alloc_chan_resources(struct dma_chan *chan, struct dma_client *client) 具体完成:
1) 使能channel的错误中断、异常中止和错误发送completion信号(写硬件) 2) 清Channel Error Register
3) 分配至多256个desc,并添加到tmp_list链表 4) 把tmp_list链表添加到ioat_chan->free_desc链表中 5) 把ioat_chan通道中的desc连成环;
6) 分配并映射存放最后一次completion的desc的缓冲区(大小64bit),该地址为用于存放返回completion status
7) 设置返回用于存储writes the completion status的地址(把6得到的地址写硬件) 8) 使能清通道已完成desc的小任务
9) 启动channel,传输一个空的desc(没有数据与地址=0),用于设置通道的desc链表的首地址发给硬件(把描述符环首地址写到硬件中)
10) 返回分配的desc数 2、 释放通道中所有的描述符
static void ioat_dma_free_chan_resources(struct dma_chan *chan) 主要功能如下:
1) 禁止小任务,清除所有已待处理的descriptor 2) RESET 通道(写寄存器)
3) 遍历通道的free descriptor链表,删除表项,释放DMA 缓冲池缓冲区,重 新初始化descriptor链表
4) 释放完成completion status的 DMA 池 5) 清0
3、 IOAT DMA操作状态查询: 判断DMA请求cookie是否已经完成
static enum dma_status ioat_dma_is_complete(
struct dma_chan *chan,dma_cookie_t cookie, dma_cookie_t *done,dma_cookie_t *used)
4、 IOAT DMA 驱动操作:
从chan中获取一个空闲的desc,使用传输的参数做初始化后返回desc->tx。 static struct dma_async_tx_descriptor *ioat3_dma_prep_memcpy(
25
武汉科技大学本科毕业设计
struct dma_chan *chan, dma_addr_t dma_dest, dma_addr_t dma_src, size_t len,
unsigned long flags)
5、 IOAT DMA操作提交:
构建满足IOAT DMA硬件传送大小的desc描述符,若之前的desc量过大,需要拆分,则需要改写回调函数(只最后一个有),若通道上待处理的desc>=4个,马上启动第6步;
static dma_cookie_t ioat3_tx_submit(struct dma_async_tx_descriptor *tx) 1) 根据要传送的数据长度计算需要的desc数量,并做初始化; 2) 设置回调函数
3) new->tx_cnt = desc_count;//完成这次传输使用的desc数量,一个被分成多少个 4) 增加cookie
5) 增加ioat_chan->dmacount和pending数量
6) 通道上待处理的desc>=4个,马上启动第6步issue pending。 6、 IOAT DMA通道发送pending的请求:
写DMACOUNT寄存器: Number of Descriptors to Process,启动DMA channel处理描述符,清channel->pending=0;
static void ioat3_dma_memcpy_issue_pending(struct dma_chan *chan) 7、 IOAT DMA 定时器功能
个人认为定时器的应用是整个贯穿DMA驱动的亮点。它主要实现四个方面的作用: 1) 对used descriptors 资源的回收; 2) 错误的检查,并打印err info; 3) 重启dma channel,完成相关任务; 4) 对环的重塑,合理的利用资源; 函数如下:
static void ioat3_timer_event(unsigned long data) 8、 IOAT DMA tasklet事件
之所以引进这个概念,主要是为了利用软中断延时的功能,对于那些不是那么紧急的中断任务,我们可以将它们提出来,然后放在tasklet队列中,这样有助于提高 CPU的利用率,而在IOAT DMA 代码中主要应用于资源的回收和维护工作。
26