使用 MapTransferEx 例程
MapTransferEx 例程初始化一组以前分配的 DMA 资源,并启动 DMA 传输。 此例程在 DMA 操作接口的版本 3 中可用。 从 Windows 8 开始,支持此接口的版本 3。 有关 DMA 操作接口的详细信息,请参阅 DMA_OPERATIONS。
MapTransferEx 与 MapTransfer 的比较
MapTransferEx 是 MapTransfer 例程的改进版本。 从 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 的位置。