Linux DMA Engine架构与API
Linux提供了一个架构一种通用的方式支持大多数的DMA硬件,这个架构称为DMA
Engine,它提供了一套基础的机制,可以让不同的DMA驱动插入,其它的客户端驱动可以通过一套标准的API从内核空间访问它。
这个框架设计为兼容不同形式的DMA,包括内存到内存的DMA(如AXI
CDMA)和内存到设备/设备到内存的DMA(如AXI
DMA)。该框架同样支持分散集中的模式(scatter gather)。
DMAEngine development documentation(DMAEngine Controller Documentation)
参考文献:https://www.kernel.org/doc/html/latest/driver-api/dmaengine/provider.html
该文档提供了DMA Engine内部的API帮助,并提供了编写设备驱动的指导。
硬件概述
大多数的DMA从设备控制器具有相同的操作理论。
它们具有给定数量的DMA传输的通道数,和给定数量的请求线路。
请求和通道相当于正交的关系,通道可以服务于几个到任意个请求。
通道可以简单的认为是实例,可以复制例化,而请求可以认为是端口。
请求线路实际上可以对应于DMA设备到控制器的物理线路。每当其想要开始发起传输时,将给出一个DMA请求信号(DRQ)。
一个非常简单的DMA控制器通常只需要考虑一个参数,即传输的大小。
在每一个时钟周期,它将把1byte的数据从一个Buffer传输到另一个Buffer,直到传输完成了指定**传输大小(Transfer
Size)**的数据。
然而,在现实中,通常不会运行的这么好。这是因为从设备在一个周期中可以需要传输一些特殊数量的bits。
例如,在一个简单的内存复制的操作中,我们希望能够传输物理总线允许的最大的数据,然而我们的声卡设备的FIFO位宽很小,只能够允许每次写入16bit或者24bit的数据。这就是大多数的DMA控制器能够调整数据位宽的原因,由此引入了另一个参数,就是传输的位宽(Transfer
Width)。
在很多情况下,一些DMA控制器传输的源或者目标为RAM时,通常会采用Buffer,将分散的大量小片内存的访问集中在Buffer中,这样可以转变为几个大的传输,以提高系统的效率。这时使用的参数称为迸发大小(Burst
Size),其定义了单次读写时控制器不可打散的最小的子传输尺寸。
理论上DMA控制器只能够传输单个连续的数据块。但是有些时候我们通常需要将数据从不连续的Buffer中传输到一个连续空间的Buffer中,我们称这种模式为分散集中模式(Scatter-gather)。
DMAEngine,至少是对于内存到设备的传输,要求支持Scatter-gather模式。
所以,通常有两种情况:1. DMA控制器不支持SG模式,此时我们需要在软件中来实现它;2.
DMA控制器支持SG模式。
对于后者通常使用要传输的块集合进行编程,每当传输开始时,控制器就会检查该集合,执行我们在那里编写的任何操作。
该集合通常是一个表格或者链表,您可能需要将表格的地址和其成员,或者链表的首元素放入到DMA的通道控制,每当有DRQ请求时,控制器将调用集合获取从哪里进行数据搬运的位置信息。
然而该集合的形式与硬件密切相关,每一种DMA控制器要求的集合的结构各不相同,但是它们都要求有一个块信息,至少包含源和目标地址,地址是否需要递增,以及之前的三个参数:Burst
Size,Transfer Width, Transfer Size。
最后一件值得注意的事项是,从设备通常默认不会发起DRQ,当您使用DMA时,您必须在您的从设备中使能该功能。
这里只提到了mem2mem和mem2dev类型的DMA,大多数DMA设备通常支持DMAEngine支持的其它类型的数据传输或内存操作,将稍后进行介绍。
Linux对DMA的支持
历史上,DMA控制器驱动程序使用异步 async TX
API实现,以减轻CPU的负担,如内存复制,异或,密码学等,基本上任何内存到内存操作。
随着时间的推移,设备传输对内存的需求出现了,dmaengine也得到了扩展。现在,异步TX
API是作为dmaengine之上的一个层编写的,充当客户机的角色。尽管如此,dmaengine在某些情况下仍然兼容该API,并做出了一些设计选择以确保其兼容性。
关于Async TX API
的相关的信息可以参考相关的文档https://www.kernel.org/doc/html/latest/crypto/async-tx-api.html
DMAEngine APIs
struct dma_device Initialization
如同其他的内核架构一样,整个DMAEngine的注册依赖于驱动程序去填充一个结构体,并将其注册到框架中。在这里,该结构体指的是dma_device结构体。
在您的驱动程序中,首先要做的事情就是分配该结构体的空间,任何常用的内存分配函数都行,但是需要对其中的一些域:
channels: 应该初始化为一个列表list,例如INIT_LIST_HEAD宏。
src_addr_widths:应该包含了源传输位宽的bit位掩码。
dst_addr_widths:应该包含了目标传输位宽的bit掩码。
directions:应该包括从设备方向的掩码。
residue_granularity:报告给dma_set_residue的传输残渣粒度,可以是下面的一种:
-
Descriptor,您的设备不支持任何形式的残渣报告,框架只知道完成了特定的事务描述符。
-
Segment,设备能够报告已经传输了哪个块。
-
Burst,设备能够报告哪个burst已经传输了。
dev:保存了与驱动实例关联的设备结构体struct device的指针。
支持的事务类型
接下来需要设置设备(和驱动程序)支持的事务类型。
我们的dma_Device结构体有一个名为Cap_mask的字段(域),它包含支持的各种类型的事务,您需要使用dma_cap_set函数修改这个掩码,并根据您选择的事务类型填上不同的FLAG作为函数的参数。
所有的这些类型在dma_transaction_type
enum枚举类型中定义,定义的文件include/linux/dmaengine.h
目前可用的类型有:
-
DMA_MEMCPY:该设备用来执行内存到内存的的数据复制。
-
DMA_XOR:用来执行内存空间的异或操作,如用来加速RAID5密集的异或操作。
-
DMA_XOR_VAL:该设备能够使用XOR算法对存储器缓冲器执行奇偶校验。
-
DMA_PQ:该装置能够执行RAID6P+Q计算,P是一个简单的异或,Q是一个里德-所罗门算法。
-
DMA_PQ_VAL:该设备能够使用RAID6P+Q算法对存储器缓冲器执行奇偶校验。
-
DMA_INTERRUPT:该设备能够触发虚拟传输,从而产生周期性中断。客户端驱动程序注册一个回调函数,该回调函数将通过dma控制器中断定期调用。
-
DMA_PRIVATE:设备只支持从设备传输,因此不能用于异步传输。
-
DMA_ASYNC_TX:不能由设备设置,并在需要时由框架设置。TODO是关于什么的?
-
**DMA_SLAVE:**该设备可以处理设备到内存的传输,包括SG模式传输。在mem2mem的情况下,我们有两个不同的类型,(即SG模式和非SG模式),来处理要复制的单个块或它们的集合,在这里,我们只有一个事务类型来处理这两个类型。(即SG模式)如果要传输单个连续内存缓冲区,只需构建一个只有一个项的分散列表即可。
-
DMA_CYCLIC:该设备可以处理循环传输 cyclic
transfer。循环传输的块信息集合是自闭环的,其末端的字段指向首端的字段。它通常用于音频传输,您想要操作一个单一的环形缓冲区,并在那里填充您的音频数据。 -
DMA_INTERLEAVE:该设备支持交错传输。这些传输可以将数据从非连续缓冲区传输到非连续缓冲区,而不同于DMA_SLAVE类型的事务,即将数据从非连续数据集传输到连续目标缓冲区。
-
它通常用于2d内容传输,在这种情况下,您希望将部分未压缩数据直接传输到显示器以显示出来。
-
DMA_COMPLETION_NO_ORDER:设备不支持顺序完成。如果设备正在设置此功能,则驱动程序应返回device_tx_status的值为DMA_OUT_OF_ORDER。如果设备导出此功能,则应将所有cookie跟踪和检查API视为无效。此时,这与dmatest的轮询选项不兼容。如果设置了此功能,则建议用户为发送到DMA设备的每个描述符提供唯一标识符,以便正确跟踪完成情况。
-
DMA_REPEAT:该设备支持重复传输。由DMA_PREP_REPE
AT传输标志指示的重复传输。与循环传输类似,因为它在结束时会自动重复,但还可以由客户端替换。此特性仅限于交错传输,因此,如果不设置DMA_INTERLEVE标志,则不应设置此标志。这种限制是基于DMA客户端当前的需求,如果需要的话,将来应该增加对额外传输类型的支持。 -
DMA_LOAD_EOT:该设备支持在传输结束时替换重复传输(EOT),方法是使用DMA_PREP_LOAD_EOT标志集对新的传输进行排队。如果需要的话,将来将根据DMA客户端的需要,在另一个点(例如突发结束而不是传输结束)添加对替换当前运行的传输的支持。
这些不同的类型也会影响源地址和目标地址随时间的变化。指向RAM的地址通常在每次传输后递增(或递减)。在环形缓冲区的情况下,它们可以循环(DMA_CYCLIC)。指向设备寄存器(例如FIFO)的地址通常是固定的。
每个描述符元数据支持
一些数据传输体系结构(DMA控制器和外围设备)使用与事务相关的元数据。DMA控制器的作用是同时传输有效载荷和元数据。
元数据本身不被DMA引擎本身使用,但它包含给外设的或来自外设的参数、键、向量等。
DMAEngine框架提供了便利描述符元数据的通用方法。
根据架构的不同,DMA驱动程序可以实现下面这两种方法中的一种或两种,这取决于客户端驱动程序选择使用哪一种方法。
-
DESC_METADATA_CIENT
-
元数据缓冲区是由客户端驱动程序分配/提供的,它通过dmaengine_desc_attach_metadata()的助手函数绑定到描述符。
-
对于这种模式对应下面的两种DMA驱动
-
DMA_MEM_TO_DEV/DEV_MEM_TO_MEM
应为DMA控制器准备来自所提供的元数据缓冲区的数据,以便与有效载荷数据一起发送。要么通过复制到硬件描述符,要么通过高度耦合的数据包。 -
DMA_DEV_TO_MEM
在传输完成时,DMA驱动程序必须将元数据复制到客户端提供的元数据缓冲区,然后在完成操作时通知客户端。传输完成后,DMA驱动程序不得碰触客户端提供的元数据缓冲区。
-
-
-
DESC_METADATA_ENGINE
-
元数据缓冲区由DMA驱动程序分配/管理。客户端驱动程序可以请求元数据的指针、最大大小和当前使用的大小,并可以直接更新或读取它。dmaengine_desc_get_metadata_ptr()和dmaengine_desc_set_metadata_len()作为其助手函数。
-
该模式对应下面的DMA驱动
-
get_metadata_ptr()
应该返回元数据缓冲区的指针、元数据缓冲区的最大大小以及缓冲区中当前使用的/有效字节(如果有的话)。 -
set_metadata_len()
在元数据放置到缓冲区后由客户端调用该函数,以便让DMA驱动程序知道所提供的有效字节数。
-
-
注意:由于客户端将在完成回调函数时,会请求元数据指针(在DMA_DEV_to_MEM情况下),所以DMA驱动程序必须确保在调用回调之前不释放描述符。
-
设备操作
我们的dma_Device结构体还需要几个函数指针来实现实际的逻辑,下面将介绍能够执行的操作。
我们需要实现的操作函数显然取决于您之前报告说要支持的那些事务的类型。
-
device_alloc_chan_resources device_free_chan_resources
-
每当驱动程序第一次或最后一次执行通道相关的操作时,即调用dma_request_Channel或dma_Relation_Channel时,就会调用这些函数。
-
它们负责分配/释放所有所需的资源,以便在驱动程序中使用该通道。
-
-
device_prep_dma_*
-
这些函数与您之前注册的功能相匹配。这些函数都将准备传输相关的缓冲区或分散列表,并应从缓冲区或分散列表中创建一个硬件描述符或一个硬件描述符列表。
-
这些函数可以从中断上下文中调用。
-
您可能做的任何分配都应该使用GFP_NOWAIT标志,以避免潜在的进去睡眠的状况,但也不要耗尽紧急池。
-
驱动程序应尝试在探测时预先分配传输设置期间可能需要的内存,以避免对NoWait分配器造成很大压力。
-
它应该返回dma_async_tx_descriptor
结构体的唯一实例,该实例将代表之后的这个特定的传输。 -
这个结构体可以通过函数dma_async_tx_descriptor_init进行初始化,并需要设置结构体中的两个字段。
-
flags: TODO:
它是由驱动程序本身修改的,还是应该始终是参数中传递的标志? -
tx_submit:
指向您必须实现的函数的指针,该函数用来将事务描述符放入等待队列中,并等待issue_pending被调用。
-
-
在此结构体中,callback_result的函数指针可以被初始化,以便向提交者通知事务已经完成。以前的代码使用的是callback的函数指针,但是,它不为事务提供任何状态,因此不推荐在新代码中使用。定义为dmaengine_result的表示结果的结构体,传递给callback_result,其包括有下面的两个字段:
-
result:返回dmaengine_tx_result定义的传输的结果。
-
residue:为支持残渣的传输提供剩余字节。
-
-
-
device_issue_pending:获取挂起队列中的第一个事务描述符,并启动传输。无论何时完成该传输,它都应该移动到列表中的下一个事务。此函数可以在中断上下文中调用。
-
device_tx_status:应该报告整个通道还剩余的bytes数。应该只关心作为参数传递的事务描述符,而不是给定通道上当前活动的事务描述符。tx_state参数的值可能为NULL。应该使用dma_set_residue来报告它。对于循环的数据传输情,它只应考虑当前周期。
-
如果设备不支持按顺序完成,并且正在完成无序操作,则应该返回DMA_OUT_OF_ORDER。
-
此函数可以在中断上下文中调用。
-
-
device_config:使用配置参数来重新配置通道。此命令不应同步执行,也不应对任何当前排队的传输执行,而只应在后续传输上执行。在这种情况下,函数将接收一个dma_slave_config结构体指针作为参数,它详细定义了要使用的配置。尽管该结构包含一个方向字段,但该字段被弃用以支持向prep_*函数提供的方向参数。此调用仅对从操作是强制性的。不应该为memcpy操作设置配置。如果驱动程序同时支持这两种操作,那么它应该只对从操作使用这个调用,而不是对memcpy操作使用这个调用。
-
device_pause:暂停通道的传输。此命令应在通道上同步操作,立即暂停给定通道的工作。
-
device_resume:恢复通道的传输。此命令应在通道上同步操作,立即恢复给定通道的工作。
-
device_terminate_all:中止通道上所有挂起和正在进行的传输。对于终止的传输不应该调用完成的回调函数。可以从原子上下文或描述符的完成回调函数中调用。不能休眠。驱动程序必须能够正确地处理这个问题。终止可能是异步的。驱动程序不必等到当前活动的传输完全停止。参见device_synchronize。
-
device_synchronize:必须将通道的终止与当前上下文同步。必须确保以前提交的描述符的内存不再被DMA控制器访问。必须确保以前提交的描述符的所有完整回调都已完成运行,并且没有计划运行。
- 可能休眠。
杂项
-
dma_run_dependencies:应该在异步TX传输结束时调用,在从传输的情况下可以忽略。在标记为完成状态之前,确保已经运行依赖操作。
-
dma_cookie_t:它是一个DMA事务ID,将随着时间的推移而增加。自从引入了将其抽象出来的virt-dma之后,就不再有真正的相关性了。
-
DMA_CTRL_ACK:如果清除,则在客户端确认收到之前,提供者不能重用描述符,例如有机会建立任何依赖链。这可以通过调用async_tx_ack()来回复确认(ACK)。如果设置了,也不意味着描述符可以重用。
-
DMA_CTRL_REUSE:如果已设置,则可以在完成后重用描述符。如果设置了此标志,则提供程序不应释放此标志。应该通过调用dmaengine_desc_set_reuse()来设置DMA_CTRL_REUSE,从而为重用做好描述符的准备。
-
dmaengine_desc_set_reuse()只有在当通道支持可重用描述符为展示的功能时,才能成功调用。
-
因此,如果设备驱动程序希望在2次传输之间跳过dma_map_sg()和dma_unmap_sg(),因为DMA的数据未被使用,它可以在完成后立即重新提交传输。
-
描述符通过下面的几种方式释放:
-
通过调用dmaengine_desc_clear_reuse()并提交最后的TXN来清除DMA_CTRL_REUSE。
-
显式调用dmaEngine_desc_free(),只有在已经设置DMA_CTRL_REUSE时才能成功。
-
终止通道。
-
-
DMA_PREP_CMD:如果设置,客户端驱动程序告诉DMA控制器,在DMA
API中传递数据的是命令数据。命令数据的解释是DMA控制器特有的。它可以用于向其他外围设备/寄存器读/寄存器写入,发出命令,描述符应该以与普通数据描述符不同的格式。 -
DMA_PREP_REPEAT:如果设置了,则在传输结束时将自动重复传输,直到一个新的传输以DMA_PREP_LOAD_EOT标志在同一个信道上排队为止。如果在信道上排队的下一个传输没有设置DMA_prep_LOAD_EOT标志,则将重复当前传输,直到客户端终止所有传输。只有当通道报告DMA_REPEAT功能时,才支持此标志。
-
DMA_PREP_LOAD_EOT:如果已设置,则该传输将在其结束后替换当前正在进行的传输。这是非重复传输的默认行为,因此为非重复传输指定DMA_PREP_LOAD_EOT将没有任何区别。当使用重复传输时,DMA客户端通常需要在所有传输上设置DMA_PREP_LOAD_EOT标志,否则通道将继续重复上次重复传输,而忽略正在排队的新传输。如果未能设置DMA_PREP_LOAD_EOT,则表现为该信道在上一次传输中被卡住。只有当通道报告DMA_LOAD_EOT功能时,才支持此标志。
-
一般设计说明
您将看到的大多数DMAEngine驱动程序都基于类似的设计,该设计处理传输结束的中断服务函数,但将大部分工作推迟到一个微线程,包括在上次传输结束时开始新的传输。
这其实是一个相当低效率的设计,因为传输延迟不仅是中断延迟,也是任务的调度延迟,这将使通道处于空闲状态,从而减缓全局传输速率。
您应该避免这种做法,而不是在您的任务中选择一个新的传输,而是将该部分移到中断处理程序中,以便有一个更短的空闲窗口(无论如何,这是我们无法避免的)。
术语
Chunk:连续的burst的集合。
DMA Engine slave-DMA API Guide
下面介绍了如何使用DMAEngine API中的slave-DMA API来编写设备驱动。
注意:该向导只适用于slave-DMA的使用。
DMA的使用
slave DMA的使用包括如下的步骤:
-
分配一个DMA slave 通道
-
设置slave和控制器特定的参数
-
获取事务的一个描述符
-
提交事务
-
发出请求到pending队列,并等待回调通知
操作的具体细节如下
-
分配一个DMA slave 通道
-
信道分配在slave
DMA上下文中略有不同,客户端驱动程序通常只需要来自特定DMA控制器的信道,甚至在某些情况下也需要特定的信道。要请求一个通道,需要使用dma_requestChan()
API。 -
接口:struct dma_chan *dma_request_chan(struct device *dev, const char
*name); -
根据参数对应的设备,返回dma_chan通道,对应参数name为通道名称。关联通过DT设备树文件、ACPI或基于dma_from_map匹配表的板卡文件完成。
-
通过此接口分配的通道是调用方独占的,直到调用dma_release_channel()释放该通道为止。
-
-
设置从参数和控制器特定参数
-
下一步总是将一些特定信息传递给DMA驱动程序。slave
DMA可以使用的大多数通用信息都在struct dma_server_config的结构体中。 -
这允许客户端为外围设备指定DMA方向、DMA地址、总线宽度、DMA突发长度等。
-
如果某些DMA控制器有更多要发送的参数,那么它们应该尝试将struct
dma_slave_config嵌入到它们的控制器特定结构中。这使客户端可以灵活地传递更多的参数(如果需要的话)。 -
接口:int dmaengine_slave_config(struct dma_chan *chan,
struct dma_slave_config *config) -
有关struct成员的详细说明,请参阅dmaEngine.h中的dma_slave_config结构定义。
-
请注意,“direction”成员将不再推荐使用,因为它在预调用中也会设置方向参数。
-
-
获取事务的描述符
-
对于slave设备的使用,DMA引擎支持的各种从传输模式如下:
-
slave_sg:发起从/到外设的SG(分散集中)缓冲器的列表中指定的DMA传输。
-
dma_cyclic:执行从/到外围设备的循环DMA操作,直到操作显式停止为止。
-
interleaved_dma:这对于从设备和M2M客户端来说都是一种普遍的方式。驱动可能已经实现知晓了从设备FIFO的地址。可以通过设置‘dma_interleved_Template’成员为合适的值来表示各种类型的操作。如果通道通过设置DMA_PREP_REPER传输标志来支持的话,则循环交织DMA传输也是可能的。
-
-
此传输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); -
外围驱动程序在调用dmaengine_prep_slave_sg()之前应该已经映射了DMA操作的散射列表,并且必须一直保持映射散射列表,直到DMA操作完成为止。必须使用DMA
struct device结构体映射SG列表。如果稍后需要同步映射,则还必须使用DMA
struct device调用dma_sync_for()。因此,正常的设置应该如下所示:- struct device *dma_dev = dmaengine_get_dma_device(chan);
-
-
nr_sg = dma_map_sg(dma_dev, sgl, sg_len);
if (nr_sg == 0)
/* error */
desc = dmaengine_prep_slave_sg(chan, sgl, nr_sg, direction, flags);
-
一旦获得描述符,就可以添加回调信息,然后必须再提交描述符。一些DMA
Engine驱动程序可能在成功的准备和提交之间保持自旋锁,因此这两个操作紧密结合是很重要的。 -
注意:
-
尽管async_tx
API指定完成回调例程不能提交任何新操作,但从/循环DMA的情况并非如此。 -
对于从DMA,在调用回调函数之前,可能无法提交后续事务,因此允许从DMA回调准备和提交新事务。
-
对于循环DMA,回调函数可能希望通过dmaengine_terminate_async()终止DMA。
-
因此,DMA引擎驱动程序在调用可能导致死锁的回调函数之前丢弃任何锁是非常重要的。
-
请注意,回调总是在DMA引擎任务线程中调用,而不是直接在中断上下文调用。
-
-
可选:每个描述符元数据
-
DMAEngine提供了两种元数据支持方法。
-
DESC_METADATA_CLIENT:元数据Buffer由客户端驱动程序分配/提供,并附加到描述符。int
dmaengine_desc_attach_metadata(struct dma_async_tx_descriptor
*desc,
void *data, size_t len); -
DESC_METADATA_ENGINE:元数据Buffer由DMA驱动程序分配/管理。客户端驱动程序可以请求元数据的指针、最大尺寸和当前使用的大小,并可以直接更新或读取它。由于DMA驱动程序管理包含元数据的内存区域,客户端必须确保在其传输完成后回调函数获取描述符运行时,客户端不尝试访问或获取描述符指针。如果没有完成后的回调,那么,元数据不能在issue_pending之后被访问。换句话说:如果目标是在传输完成后读取元数据,那么客户端必须使用完成回调的方式。void
*dmaengine_desc_get_metadata_ptr(struct dma_async_tx_descriptor
*desc,
size_t *payload_len, size_t *max_len);
-
-
int dmaengine_desc_set_metadata_len(struct dma_async_tx_descriptor *desc,
size_t payload_len);
-
客户端驱动程序可以查询如果给定模式支持bool
dmaengine_is_metadata_mode_supported(struct dma_chan *chan,
enum dma_desc_metadata_mode mode); -
根据所使用的模式,客户端驱动程序必须遵循下面不同的流程。
-
DESC_METADATA_CLIENT
-
DMA_MEM_TO_DEV / DMA_MEM_TO_MEM:
-
准备描述符(dmaEngine_prep_*),在客户端buffer中构造元数据
-
使用dmaengine_desc_attach_metadata()将缓冲区附加到描述符
-
提交传输
-
-
DMA_DEV_TO_MEM
-
准备描述符(dmaEngine_prep_*)
-
当传输完成后,元数据应该在附加的缓冲区中可用。
-
提交传输
-
当传输完成后,元数据应该在附加的缓冲区中可用。
-
-
-
DESC_METADATA_ENGINE
-
DMA_MEM_TO_DEV / DEV_MEM_TO_MEM:
-
准备描述符(dmaEngine_prep_*)
-
使用dmaengine_desc_get_metadata_ptr()获取指向引擎元数据区域的指针
-
更新指针处的元数据
-
使用dmaengine_desc_set_metadata_len()告诉DMA引擎客户端放置在元数据缓冲区中的数据量
-
提交传输
-
-
DMA_DEV_TO_MEM
-
准备描述符(dmaEngine_prep_*)
-
提交传输
-
在传输完成后,使用dmaengine_desc_get_metadata_ptr()获取指向引擎元数据区域的指针。
-
从指针中读出元数据
-
-
-
注意:当使用DESC_METADATA_ENGINE 模式时,描述符的元数据区域在传输完成后就不再有效(如果使用了完成时回调,则在之后指针是有效的)。
不允许混合使用DESC_METADATA_ENGINE 和DESC_METADATA_CLIENT,客户端驱动必须为每个描述符使用其中的一种模式。
-
-
提交传输
-
一旦准备好了描述符并添加了回调信息,则必须将其放入DMA
Engine驱动的挂起队列中。 -
接口为:
dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc) -
这将返回一个cookie,该cookie可用于通过本文档未涉及的其他DMA引擎调用检查DMA引擎活动的进展。
-
dmaengine_submit()并不会直接启动DMA操作,而是仅仅将其添加到挂起队列中。
-
在调用了dmaengine_submit()后,提交的传输描述符(struct
dma_async_tx_descriptor)就属于DMA引擎了,因此,客户端必须考虑到指向描述符时的无效性。
-
-
发起挂起的DMA请求,并等待回调通知
-
通过调用issue_pending
API可以将挂起队列中的传输激活。如果通道空闲,则队列中的第一个传输启动,然后再轮到下一个。 -
再完成每个DMA操作后,下一个队列中的传输将启动,并触发任务进程。如果设置了回调函数,则任务进程将调用客户端驱动的回调函数来通知。
-
接口:
void dma_async_issue_pending(struct dma_chan *chan);
-