DMA 例程版本 3 的基本调用模式
若要执行使用 DMA 操作接口版本 3 中的例程的 DMA 传输,驱动程序应遵循以下列表中所述的步骤。 这些步骤对于从属设备和总线主设备都是常见的。 此接口的版本 3 从 Windows 8 开始可用。 有关此接口中的例程的详细信息,请参阅 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的同步调用,如果 AllocateAdapterChannelEx 的 MapRegisterBase 参数为非 NULL,则 AllocateAdapterChannelEx 会将分配的映射寄存器的基址写入 MapRegisterBase 参数指向的地址。 如果 ExecutionRoutine 为 NULL, 则 MapRegisterBase 必须为非 NULL。 如果 ExecutionRoutine 为非 NULL,则 AllocateAdapterChannelEx 的 MapRegisterBase 参数是可选的,并且执行例程将接收映射寄存器基址作为输入参数。
对于异步 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 例程来释放适配器对象。