版本 3 DMA 例程的基本调用模式

若要执行使用 DMA 操作接口版本 3 中例程的 DMA 传输,您的驱动程序应遵循以下列表中介绍的步骤。 从属设备和总线主设备通常执行这些步骤。 从 Windows 8 开始,此接口的版本 3 可用。 有关此接口中的例程的详细信息,请参阅 DMA_OPERATIONS

步骤 1:获取 DMA 适配器对象

为准备 DMA 传输,驱动程序调用 IoGetDmaAdapter 例程以获取 DMA 适配器对象。 DMA 适配器对象是表示总线主设备或系统 DMA 控制器上的请求行的软件对象。 此对象包含用于从设备与设备之间传输数据的总线的DMA操作接口。 此外,此对象将同步驱动程序对执行传输所需的共享资源的访问权限。 有关详细信息,请参阅 适配器对象简介

步骤 2:获取所需 DMA 资源的说明

驱动程序调用 GetDmaTransferInfo 例程来获取执行传输所需的 DMA 资源的说明。

此调用的输入参数描述用于传输的内存缓冲区以及传输的方向(读取或写入)。

从此调用获取的资源要求包括映射寄存器的数量以及用于描述传输数据缓冲区所需的散布/聚集列表的大小。 在对 AllocateAdapterChannelEx 例程的后续调用中(请参阅 步骤 3),驱动程序将映射寄存器计数作为输入参数提供。

步骤 3:请求所需的 DMA 资源

驱动程序调用 AllocateAdapterChannelEx 例程来分配要分配给 DMA 适配器对象的资源。 这些资源包括 DMA 通道和映射寄存器。

AllocateAdapterChannelEx 调用可以是异步调用,也可以是同步调用。

如果未设置DMA_SYNCHRONOUS_CALLBACK标志,则调用是异步的。 在这种情况下, ExecutionRoutine 参数指向调用方提供的执行例程,该例程在请求的资源可用时调用。 如果成功,异步 AllocateAdapterChannelEx 调用将返回STATUS_SUCCESS,而无需等待执行例程运行。

如果设置了DMA_SYNCHRONOUS_CALLBACK标志, AllocateAdapterChannelEx调用是同步的。 在这种情况下,调用中的 ExecutionRoutine 参数是可选的, AllocateAdapterChannelEx 的行为如下所示:

  • 如果 ExecutionRoutine 为非 NULL,并且可以立即分配 DMA 资源, 则 AllocateAdapterChannelEx 将在调用线程的上下文中调用执行例程。 执行例程完成运行后, AllocateAdapterChannelEx 返回STATUS_SUCCESS。 如果资源未立即可用,AllocateAdapterChannelEx 将失败,并返回错误状态代码 STATUS_INSUFFICIENT_RESOURCES。

  • 如果 ExecutionRoutine 为 NULL,并且 AllocateAdapterChannelEx 可以立即分配 DMA 资源, 则 AllocateAdapterChannelEx 将返回STATUS_SUCCESS。 如果所有资源没有立即可用,调用将失败,并返回错误状态代码 STATUS_INSUFFICIENT_RESOURCES。

对于返回STATUS_SUCCESS的同步调用,如果 AllocateAdapterChannelExMapRegisterBase 参数为非 NULL,则 AllocateAdapterChannelEx 会将分配的映射寄存器的基址写入 MapRegisterBase 参数指向的地址。 如果 ExecutionRoutine 为 NULL, 则 MapRegisterBase 必须为非 NULL。 如果 ExecutionRoutine 为非 NULL,则 AllocateAdapterChannelExMapRegisterBase 参数是可选的,并且执行例程接收映射寄存器基址作为输入参数。

对于异步 AllocateAdapterChannelEx 调用, ExecutionRoutine 必须为非 NULL,执行例程接收映射寄存器基址作为输入参数。

在对 MapTransferEx 例程的后续调用中(请参阅 步骤 5),驱动程序将映射寄存器基址作为输入参数提供。

如果 ExecutionRoutine 为非 NULL,则执行例程返回状态值以指示已分配资源的处置。 对于系统 DMA 传输,此返回值必须是 KeepObject。 此值将通知操作系统,适配器对象及其所有已分配的资源正在使用中,因此不应释放。 如果未提供执行例程,驱动程序必须改为调用 FreeAdapterObject 例程,并提供 KeepObject 作为 AllocationOption 参数。

步骤 4:如有必要,请取消挂起的资源请求

在调用 AllocateAdapterChannelEx 后,DMA 适配器会排队等待 DMA 资源,如果需要,驱动程序可以调用 CancelAdapterChannel 例程来取消挂起的资源请求。

如果 CancelAdapterChannel 返回 TRUE,则已成功取消资源请求。 如果在 AllocateAdapterChannelEx 调用中提供了执行例程,则此例程不会运行。

如果 CancelAdapterChannel 返回 FALSE,则无法取消资源请求,因为它已被授予。 如果在 AllocateAdapterChannelEx 调用中提供了执行例程,将调用此例程。

步骤 5:初始化 DMA 资源并启动 DMA 传输

驱动程序调用 MapTransferEx 来初始化 DMA 资源并启动 DMA 传输。 此调用可能发生在调用 AllocateAdapterChannelEx 的同一驱动程序线程中,或者在驱动程序提供给 AllocateAdapterChannelEx 的执行例程中发生。 如果需要多个 MapTransferEx 调用来传输整个 DMA 数据缓冲区,则以后的 MapTransferEx 调用可能会在上一 个 MapTransferEx 调用的完成例程中发生。

MapTransferEx 支持链接的 MDL 作为输入参数。 每个 MDL 描述虚拟内存中连续的 DMA 缓冲区区域。 当 MapTransferEx 生成散点/收集列表时,它会自动处理从一个几乎连续的缓冲区区域到下一个缓冲区区域的转换,而无需驱动程序干预。 有关详细信息,请参阅 使用 MapTransferEx 例程

对于系统 DMA 传输,可以将指向 DMA 完成例程的指针传递到可选的 DmaCompletionRoutine 参数中的 MapTransferEx。 此例程计划以调度级别运行,以响应系统 DMA 控制器的中断,指示 DMA 传输已完成。

如果 MapTransferEx 无法映射整个请求的传输大小,它将 *Length 输出参数设置为映射的长度,并返回STATUS_SUCCESS。

步骤 6:如有必要,请执行特定于硬件的操作

MapTransferEx 返回STATUS_SUCCESS,指示已成功启动 DMA 传输。 在某些平台上,驱动程序可能需要在 MapTransferEx 调用之外执行一些额外的作才能启动传输,但并非所有平台都不需要这种类型的延迟启动。 驱动程序必须不依赖于此类延迟来决定是否使用和释放已分配的资源。

DMA作接口中的例程以对使用这些例程的驱动程序透明的方式维护 DMA 传输的缓存一致性。 在未在硬件中强制实施缓存一致性的平台上, MapTransferEx 可确保在写入(内存到设备)传输之前刷新处理器数据缓存。 对于读取(设备到内存)传输,当调用 FlushAdapterBuffersEx 例程时,缓存会失效(请参阅每个 MapTransferEx 调用后的步骤 8)。

步骤 7:在 DMA 传输完成时接收通知

DMA 传输完成后,可通过以下两种方式之一通知驱动程序:

  • 总线主设备的设备驱动程序中断
  • 为使用系统 DMA 控制器的从属设备执行驱动程序提供的完成例程

对于系统 DMA 传输,驱动程序可以将完成例程作为输入参数提供给 MapTransferEx

步骤 8:清除缓存中剩余的任何数据

DMA 传输完成后,驱动程序必须调用 FlushAdapterBuffersEx 例程来刷新缓存中保留的任何数据。 驱动程序必须在每次 MapTransferEx 调用后调用 FlushAdapterBuffersEx

如果 MapTransferEx 仅映射 DMA 数据缓冲区的一部分,驱动程序必须再次调用 MapTransferEx 来映射剩余数据。 复杂的传输可能需要多个 MapTransferEx 调用。 对于每个额外的 MapTransferEx 调用,重复步骤 5 到 8。

步骤 9:释放 DMA 通道和映射寄存器

成功映射整个 DMA 数据缓冲区并完成最终传输后,驱动程序必须调用 FreeAdapterChannel 例程来释放 DMA 通道和以前分配的任何映射寄存器。

步骤 10:释放 DMA 适配器对象

完成所有 DMA 传输并释放以前分配的任何映射寄存器后,驱动程序将调用 PutDmaAdapter 例程以释放适配器对象。