如何发送链接的 MDL 结构

本文介绍 USB 驱动程序堆栈中的链接式 MDL 功能,以及客户端驱动程序如何将传输缓冲区作为 MDL 结构链发送。

大多数 USB 主机控制器要求传输缓冲区几乎是连续的。 几乎连续意味着缓冲区可以开始和结束页中的任意位置,但缓冲区的其余部分必须在页面边界上开始和结束。 许多 USB 客户端驱动程序都能够满足该要求。 但是,对于某些客户端驱动程序,特别是那些需要向缓冲区添加或删除其他数据的客户端驱动程序,为传输缓冲区分配几乎连续的内存是不可取的。

例如,假设网络堆栈包含三个驱动程序、一个网络协议驱动程序、一个中间驱动程序和一个微型端口驱动程序。 协议驱动程序启动传输并将数据包发送到堆栈中的下一个驱动程序:中间驱动程序。 中间驱动程序希望向数据包添加单独的内存块中包含的自定义标头 () 。 中间驱动程序将该标头和收到的数据包发送到堆栈中的下一个驱动程序:微型端口驱动程序。 微型端口驱动程序与 USB 驱动程序堆栈接口,因此必须准备几乎连续的传输缓冲区。 若要创建此类缓冲区,微型端口驱动程序会分配一个大型缓冲区,添加自定义标头,然后复制有效负载。 由于有效负载通常很大,因此复制整个有效负载可能会对性能产生重大影响。

客户端驱动程序可以通过将传输缓冲区作为 内存描述符列表 链 (MDL) 来克服这种性能影响。 Windows 8 中的新 USB 驱动程序堆栈能够接受链接的 MDL (从客户端驱动程序查看 MDL) 。 通过提供链接的 MDL,客户端驱动程序可以引用内存中的不连续页面,而不是执行无关的复制操作。 此功能消除了对缓冲区的数量、大小和对齐方式的限制,从而允许在物理内存中对传输缓冲区进行分段。

若要使用链接的 MDL,客户端驱动程序必须检测 Windows 加载的基础 USB 驱动程序堆栈是否支持该功能,然后以正确的顺序生成 MDL 链。

准备工作

链式 MDL 功能仅支持批量传输、常时等传输和中断传输。 在查询链接 MDL 功能之前,请确保客户端驱动程序具有 USBD 句柄,用于向 USB 驱动程序堆栈注册驱动程序。 若要创建 USBD 句柄,请调用 USBD_CreateHandle。通常,客户端驱动程序在其 AddDevice 例程中创建 USBD 句柄。

可以在客户端驱动程序的 IRP_MN_START_DEVICE 处理程序中查询链接的 MDL 功能,或稍后随时查询。 客户端驱动程序不得在其 AddDevice 例程中查询此功能。

Instructions

  1. 调用 USBD_QueryUsbCapability 例程以确定 USB 驱动程序堆栈是否支持链接的 MDL 功能。 若要查询该功能,请将 UsbCapabilityChainedMdls 指定为 GUID。 将 OutputBuffer 参数设置为 NULL, 将 OutputBufferSize 参数设置为 0。

  2. 检查 USBD_QueryUsbCapability 返回的 NTSTATUS 值并评估结果。 如果例程成功完成,则支持链接的 MDL 功能。 任何其他值表示不支持该功能。

  3. 创建 MDL 链。 每个 MDL 都有一个指向另一个 MDLNext 指针。

    驱动程序可以通过手动设置 Next 指针来生成链 MDL。

    在前面的示例中,协议驱动程序将数据包作为 MDL 发送。 中间驱动程序可以创建另一个 MDL,该 MDL 使用标头数据引用内存块。 若要创建链,中间驱动程序可以将标头 MDL 的 Next 指针指向从协议驱动程序接收的 MDL。 然后,中间驱动程序可以将两个 MDL 的链转发到微型端口驱动程序,该驱动程序为请求的 URB 中的链接 MDL 提供引用,并将请求提交到 USB 驱动程序堆栈。 有关详细信息,请参阅使用 MDL

  4. 为使用链接的 MDL 的 I/O 请求生成 URB 时,请将关联 URB 结构的 TransferBufferMDL 成员 ((如 _URB_BULK_OR_INTERRUPT_TRANSFER_URB_ISOCH_TRANSFER) )设置为链中的第一个 MDL,并将 TransferBufferLength 设置为要传输的总字节数。 数据可以跨 MDL 链中的多个 MDL 条目。

    在 Windows 8 中添加了两种新类型的 URB 函数,使客户端驱动程序能够使用链接的 MDL 进行数据传输。 如果要使用此功能,请确保将 URB 标头的 Function 成员设置为以下 URB 函数之一:

    • URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER_USING_CHAINED_MDL
    • URB_FUNCTION_ISOCH_TRANSFER_USING_CHAINED_MDL

    有关这些 URB 函数的信息,请参阅 _URB_HEADER

注解

有关查询基础 USB 驱动程序堆栈以确定驱动程序堆栈是否可以接受链接的 MDL 的代码示例,请参阅 USBD_QueryUsbCapability