分配和构建 URB

USB 客户端驱动程序可以使用 Windows 驱动程序模型 (WDM) 驱动程序例程来分配和格式化 URB,然后再将请求发送到 Microsoft 提供的 USB 驱动程序堆栈。

客户端驱动程序使用 URB 来打包 USB 驱动程序堆栈中低级驱动程序处理请求所需的所有信息。 在 Windows 操作系统中,URB 以 URB 结构进行描述。

Microsoft 提供了一个 USB 客户端驱动程序的例程库。 通过使用这些例程,USB 客户端驱动程序可以为某些指定操作生成 URB 请求,并将其向下转发到 USB 堆栈。 如果愿意,可以在设计客户端驱动程序时,调用库例程来执行所支持的操作,而不是自行创建 URB 请求。

Windows 7 及更早版本中的 URB 分配

要使用 Windows 7 和 Windows 早期版本的 Windows 驱动程序工具包 (WDK) 中包含的例程发送 USB 请求,客户端驱动程序通常会分配和填充 URB 结构,将 URB 结构与新的 IRP 关联,并将 IRP 发送到 USB 驱动程序堆栈。

对于某些类型的请求,Microsoft 提供了帮助程序例程(由 Usbd.sys 导出),用于分配和格式化 URB 结构。 例如,USBD_CreateConfigurationRequestEx 例程为 URB 结构分配内存,为选择配置请求格式化 URB,并将 URB 结构的地址返回给客户端驱动程序。 但是,帮助程序例程不能用于所有类型的请求。

Microsoft 还为某些类型的请求提供了格式化 URB 的宏。 对于这些宏,客户端驱动程序必须通过调用 ExAllocatePoolWithTag 或在堆栈上分配结构来分配 URB 结构。 例如,在客户端驱动程序分配 URB 之后,驱动程序可以调用 UsbBuildSelectConfigurationRequest 来格式化 URB 以用于选择配置请求或清除配置。

对于其他请求,客户端驱动程序必须根据请求类型,通过设置 URB 结构的各种成员来手动分配和格式化 URB。

USB 请求完成后,客户端驱动程序必须释放 URB 结构。 如果 URB 是在堆栈上分配的,则 URB 在超出范围时会被释放。 如果 URB 在非分页池中分配,客户端驱动程序必须调用 ExFreePool 才能释放 URB。

Windows 8 中的 URB 分配

适用于 Windows 8 的 WDK 提供了一个新的静态库 Usbdex.lib,该库可导出用于分配、格式化和释放 URB 的例程。 此外,还有一种将 URB 与 IRP 联系起来的新方法。 新的例程可由针对 Windows Vista 及更高版本 Windows 的客户端驱动程序调用。

在 Windows Vista 及更高版本上运行的客户端驱动程序必须使用新例程,这样基础 USB 驱动程序堆栈才能利用某些性能和可靠性改进。 这些改进适用于 Windows 8 中引入的新 USB 驱动程序堆栈,以支持 USB 3.0 设备和主机控制器。 对于 USB 2.0 主机控制器,Windows 会加载不支持改进的早期版本驱动程序堆栈。 无论基础驱动程序堆栈的版本或主机控制器支持的协议版本如何,都必须始终调用新的 URB 例程。

在调用任何新例程之前,请确保客户端驱动程序已在 USB 驱动程序堆栈中注册 USBD 句柄。 要获取 USBD 句柄,请调用 USBD_CreateHandle

适用于 Windows 8 的 WDK 提供以下例程。 这些例程在 Usbdlib.h 中定义。

前面列表中的分配例程会返回一个指向新的 URB 结构的指针,该结构由 USB 驱动程序堆栈分配。 根据 Windows 加载的 USB 驱动程序堆栈版本,URB 结构可以与不透明的 URB 上下文配对。 URB 上下文是有关 URB 的信息块。 无法查看 URB 标头的内容;USB 驱动程序堆栈内部使用这些信息来改进 URB 跟踪和处理。 URB 上下文用于 Windows 8 的 USB 驱动程序堆栈。 如果 URB 上下文可用,USB 驱动程序堆栈就会使用它,以便让 URB 处理更安全、更高效。 例如,USB 驱动程序堆栈必须确保客户端驱动程序不会在提交 URB 后,又在第一个请求完成前尝试重复使用同一个 URB。 为了检测这类错误,USB 驱动程序堆栈会在 URB 上下文中存储状态信息。 如果没有状态信息,USB 驱动程序堆栈就必须将传入的 URB 与当前正在进行的所有 URB 进行比较。 当客户端驱动程序尝试释放 URB 时,USB 驱动程序堆栈也会使用该状态信息。 在释放 URB 之前,USB 驱动程序堆栈会验证状态,以确保 URB 未挂起。

URB 上下文为存储额外的 URB 信息提供了一个官方机制。 使用 URB 上下文比根据需要分配额外内存或在 URB 结构的保留成员中存储额外信息更可取。 USB 驱动程序堆栈在非分页池中分配 URB 及其相关的 URB 上下文,因此将来如果需要更大的 URB 上下文,只需调整池分配的大小即可。

URB 例程迁移

下表概述了 URB 例程的更改。

用例 在适用于 Windows 7 及更早版本的 WDK 中可用 适用于 Windows 8 及更高版本的 WDK 中提供
  针对 Windows 7 及更早版本的操作系统 针对 Windows 8 及更新版本的操作系统
要创建 URB... 客户端驱动程序会分配一个 URB 结构,并根据请求格式化该结构。

客户端驱动程序会在堆栈上分配 URB 结构,或者通过调用 ExAllocatePoolWithTag 在非分页池中分配该结构。
客户端驱动程序调用 USBD_UrbAllocate 并接收指向新 URB 结构的指针,该结构由 USB 驱动程序堆栈分配。 URB 可能与 URB 上下文相关联,具体取决于基础 USB 驱动程序堆栈的 USBD 接口版本。
要为选择配置请求创建 URB... 客户端驱动程序会调用 USBD_CreateConfigurationRequestEx 例程,该例程会返回一个指向 USB 驱动程序堆栈创建和格式化的新 URB 的指针。 客户端驱动程序调用 USBD_SelectConfigUrbAllocateAndBuild 并接收指向新 URB 结构的指针,该结构由 USB 驱动程序堆栈分配和格式化(用于选择配置请求)。 URB 可能与 URB 上下文相关联,具体取决于基础 USB 驱动程序堆栈的 USBD 接口版本。
要为选择接口请求创建 URB... 客户端驱动程序会分配一个 URB 结构,并使用 _URB_SELECT_INTERFACE 结构来定义 USB 设备的选择接口命令格式。 客户端驱动程序调用 USBD_SelectInterfaceUrbAllocateAndBuild 并接收指向新 URB 结构的指针,该结构由 USB 驱动程序堆栈分配和格式化(用于选择接口请求)。 URB 可能与 URB 上下文相关联,具体取决于基础 USB 驱动程序堆栈的 USBD 接口版本。
要将 URB 与 IRP 关联... 客户端驱动程序通过调用 IoGetNextIrpStackLocation 获得指向下一个 IRP 堆栈位置的指针。 然后,客户端驱动程序会手动将堆栈位置的 Parameters.Others.Argument1 成员设置为 URB 结构的地址。 客户端驱动程序通过调用 IoGetNextIrpStackLocation 获得指向下一个 IRP 堆栈位置的指针。 然后,客户端驱动程序会调用 USBD_AssignUrbToIoStackLocation 将 URB 与堆栈位置关联起来。
要释放 URB... 如果客户端驱动程序在堆栈上分配了 URB,则该变量会在请求完成后超出范围。

要释放客户端驱动程序或 USB 驱动程序堆栈在非分页池中分配的 URB 结构,客户端驱动程序会调用 ExFreePool
客户端驱动程序调用 USBD_UrbFree