使用 MapTransferEx 例程

MapTransferEx 例程初始化一组以前分配的 DMA 资源,并启动 DMA 传输。 此例程在 DMA 操作接口的版本 3 中可用。 从 Windows 8 开始,支持此接口的版本 3。 有关 DMA 操作接口的详细信息,请参阅 DMA_OPERATIONS

MapTransferEx 与 MapTransfer 的比较

MapTransferExMapTransfer 例程的改进版本。 从 Windows 2000 中的版本 1 开始,MapTransfer 在所有版本的 DMA 操作接口中都可用。 一次对 MapTransfer 的 调用可以从 MDL 映射一个连续的物理内存块。 但是,复杂 DMA 传输的数据缓冲区可能由 MDL 链描述,而链中的每个 MDL 可能描述多个物理连续内存块。 若要使用 MapTransfer 传输此类缓冲区,驱动程序必须多次调用 MapTransfer。 通常,这些调用是在一对嵌套循环内进行的。 内部循环从一个连续物理内存块循环访问每个 MDL 中的下一个,外部循环从 MDL 链中的一个 MDL 循环访问下一个 MDL。

相比之下,对 MapTransferEx 的 一次调用可以传输整个数据缓冲区,以便进行复杂的 DMA 传输。 以下三个 MapTransferEx 参数描述了要用于传输的缓冲区内存。

参数 说明
Mdl

指向一个或多个 MDL 链中的第一个 MDL 的指针。 有关 MDL 链的详细信息,请参阅 使用 MDL

Offset

缓冲区从 MDL 链描述的内存开始的字节偏移量。

长度

指向包含数据缓冲区长度(以字节为单位)的位置的指针。

MapTransferEx 调用开始时, MapTransferEx 例程通过 MDL 链前进,以查找缓冲区的开头。 缓冲区的开头由 Offset 参数指定。 接下来,从缓冲区的开始到结束, MapTransferEx 构造散点/收集列表,其中列表中的每个缓冲区片段都是来自 MDL 链的物理连续内存块。 为了构造此列表, MapTransferEx 从一个物理连续的内存块逐步到每个 MDL 中的下一个 MDL,从一个 MDL 到 MDL 链中的下一个 MDL。 当散点/收集列表描述的缓冲区内存总量等于 *Length 输入参数指定的字节数时,列表构造完成。 生成的散点/收集列表中缓冲区片段的顺序与 MDL 链中物理连续块的顺序匹配。

多次调用 MapTransferEx

MapTransferEx 可能无法始终在一次调用中传输整个 DMA 数据缓冲区。 以下列表描述了可能需要多次调用 MapTransferEx 才能完成传输的一些条件:

  • DMA 适配器需要映射寄存器,并且分配给适配器的映射寄存器数不足以描述整个缓冲区。
  • 驱动程序为包含散点/收集列表而分配的存储不够大,无法包含整个缓冲区的散点/收集列表。
  • 传输使用系统 DMA 控制器,该控制器限制可在硬件散点/收集列表中指定的缓冲区片段数。

在所有这些情况下, MapTransferEx 在一次调用中映射尽可能多的数据缓冲区,并告知驱动程序该调用映射了多少缓冲区。 前面的列表不包括可能需要多次调用 MapTransferEx 才能完成传输的其他条件,例如特定于平台的缓存行为。 未来的硬件平台可能会对 DMA 传输长度施加其他限制。 出于这些原因,驱动程序开发人员应将其驱动程序设计为正确处理 MapTransferEx 无法在一次调用中映射整个 DMA 数据缓冲区的情况。

在调用 MapTransferEx 之前,调用方将 *Length 参数设置为 DMA 数据缓冲区中仍需要映射的字节数。 在返回之前, MapTransferEx 将 *Length 设置为缓冲区中由调用实际映射的字节数。 如果 MapTransferEx 调用无法映射 *Length 输入值指定的整个缓冲区长度,则 *Length 的输出值小于其输入值。 如果 DMA 传输需要两次或更多 个 MapTransferEx 调用,则调用驱动程序必须先从一个调用获取 *Length 输出值,然后才能为下一次调用指定 *Length 输入值。

例如,如果 MapTransferEx 调用在输入) 上只能将 X 字节传输到缓冲区或从缓冲区传输 X 个字节,其 Offset = B 和 *Length = N (,则返回时,*Length = X。对于下一次调用 MapTransferEx,驱动程序应设置 Offset = B + X 和 *Length = N - X。在这两个调用中,使用相同的 MDL 链,而无需修改。

如果调用方指定 DmaCompletionRoutine则 MapTransferEx 在计划运行 DmaCompletionRoutine 之前写入 *Length 输出值。 此行为可确保更新后的 *Length 值在 DmaCompletionRoutine 运行之前始终可用。 例如,如果 DMA 传输需要两个 MapTransferEx 调用,则第一次调用计划的 DmaCompletionRoutine 可以从第一次调用获取 *Length 输出值。 然后,例程可以使用此值来计算第二次调用的 *Length 输入值。 通常, Length 参数指向 *CompletionContext 值中作为参数提供给 DmaCompletionRoutine 的位置。