最佳做法:使用 URB

本主题介绍客户端驱动程序分配、生成 URB 并将其发送到Windows 8随附的 USB 驱动程序堆栈的最佳做法。

Windows 8包括一个新的 USB 驱动程序堆栈,用于支持通用串行总线 (USB) 3.0 设备。 根据 USB 3.0 规范,新的 USB 3.0 驱动程序堆栈实现了多项新功能。 此外,驱动程序堆栈还包含其他功能,使客户端驱动程序能够有效地执行常见任务。 例如,新的驱动程序堆栈接受链式 MDL,允许客户端驱动程序在物理内存中的不连续页中发送传输缓冲区。

在客户端驱动程序可以使用 USB 驱动程序堆栈的新功能进行Windows 8之前,驱动程序必须将自身注册到由 Windows 为设备加载的基础 USB 驱动程序堆栈。 若要注册客户端驱动程序,请调用 USBD_CreateHandle 并指定 协定版本。 如果客户端驱动程序旨在生成、运行和使用Windows 8上的改进和新功能,则会USBD_CLIENT_CONTRACT_VERSION_602客户端协定版本。

对于USBD_CLIENT_CONTRACT_VERSION_602版本客户端驱动程序,USB 驱动程序堆栈假定客户端驱动程序符合以下规则集:

USB 驱动程序堆栈对收到的请求执行验证,并尽可能处理冲突。 如果不这样做,可能会导致未定义的行为。

不要使用过时或无效的管道句柄发送 I/O 请求

客户端驱动程序 不得 使用陈旧的管道句柄将 I/O 请求发送到 USB 驱动程序堆栈。 过时的管道句柄是指在请求中获取的管道句柄,用于选择设备中不再选择的配置、接口或备用设置。 为了避免过时的管道句柄,每次客户端驱动程序选择配置或接口时,驱动程序都必须刷新其通常存储在设备上下文) 中的管道句柄缓存 (。 某些争用条件也可能导致管道句柄过时。 例如,客户端驱动程序通过使用所选接口上的管道句柄发送 I/O 请求。 在请求完成之前,客户端驱动程序会选择不使用与正在使用的管道句柄关联的同一终结点的备用设置。 这两个挂起的请求都可能导致争用条件,使管道句柄无效。

通过在 Windows 8 中调用分配例程来分配 URB

Windows 8提供了新的例程,用于分配、生成和释放 USB 请求块 (URB) 。 若要分配 URB,Windows 驱动程序模型 (WDM) 客户端驱动程序必须始终使用以下列表中所示的新例程:

上述列表中的例程可能会将不透明的 URB 上下文附加到分配的 URB,以便改进跟踪和处理。 客户端驱动程序无法查看或修改 URB 上下文的内容。 有关 Windows 8 中的 URB 分配的详细信息,请参阅分配和生成 URB

如果 Windows 驱动程序框架 (WDF) 在注册期间将其版本标识为USBD_CLIENT_CONTRACT_VERSION_602的客户端驱动程序 (请参阅 WdfUsbTargetDeviceCreateWithParameters) ,则 USB 驱动程序堆栈要求客户端驱动程序通过调用新的 WdfUsbTargetDeviceCreateUrb 来为 URB 分配内存。

不要重复使用与挂起请求关联的活动 URL

如果 USB 驱动程序堆栈检测到在与 URB 关联的请求之前重新提交的活动 URB,则会故意进行 bug 检查。 只要请求处于挂起状态,并且尚未调用客户端驱动程序的 IRP 完成例程,URB 就处于活动状态。 不要在活动 URB 上执行以下任务。

  • 不要为另一个请求重新提交活动 URB, (将 URB 与另一个 IRP) 相关联。
  • 请勿修改活动 URB 的内容。
  • 不要释放活动 URB。

调用客户端驱动程序的完成例程后,驱动程序可以在完成例程中重新提交特定类型的请求的 URL。 以下规则适用于重新提交:

  • 客户端驱动程序不得对选择配置请求以外的任何类型的请求重复使用 由 USBD_SelectConfigUrbAllocateAndBuild 分配的 URB 来选择相同的配置。

  • 客户端驱动程序不得对除选择接口请求以外的任何类型的请求重复使用 由 USBD_SelectInterfaceUrbAllocateAndBuild 分配的 URB,以在接口中选择相同的备用设置。 有关示例,请参阅 USBD_SelectInterfaceUrbAllocateAndBuild 中的备注。

  • USBD_IsochUrbAllocate分配的 URB 只能用于常时等量传输请求。 相反,为其他类型的 I/O 请求分配的 URB (控制、批量或中断) 不得用于常时等量请求。

    例如,客户端驱动程序为批量传输请求分配并生成 URB 结构。 客户端驱动程序还希望将数据发送到设备中的常时等量终结点。 批量传输请求完成后,客户端驱动程序 不得 重新格式化并提交常时常量请求的 URB。 这是因为与常时等量请求关联的 URB 具有可变长度,具体取决于数据包数。 此外,需要在帧边界上启动和结束数据包。 为批量传输) 分配的 URB (可能不符合常时常量传输所需的缓冲区布局,并且请求可能会失败。

  • USBD_UrbAllocate 分配的 URB 不得重复用于常时常量请求、选择配置请求或选择接口请求。 可以重复使用 URB 来选择 NULL 配置,以禁用设备中的所选配置。 URB 不得处于活动状态,并且客户端驱动程序必须通过调用 UsbBuildSelectConfigurationRequest 宏并在 ConfigurationDescriptor 参数中传递 NULL 来重新设置 URB 格式。

  • 在重新提交 URB 之前,客户端驱动程序必须使用为请求类型定义的相应 UsbBuildXxx 宏重新设置 URB 格式。 驱动程序必须格式化 URB,因为 USB 堆栈可能已更改其某些内容。

    例如,假设驱动程序调用 UsbBuildInterruptOrBulkTransferRequest 来初始化批量传输请求的 URB, (请参阅 _URB_BULK_OR_INTERRUPT_TRANSFER) 。 如果驱动程序将 URB 结构的 TransferBufferMDL 成员初始化为 NULL,则 USB 驱动程序堆栈将使用中指定的 TransferBuffer 传输缓冲区与设备交换数据,而不是 MDL。 但是,在内部,USB 驱动程序堆栈可能会创建 MDL,在 TransferBufferMDL 中存储指向 MDL 的指针,并使用 MDL 将数据向下传递堆栈。 即使 USB 驱动程序堆栈释放 MDL 内存,当客户端驱动程序在完成例程中处理 URB 时 ,TransferBufferMDL 可能不是 NULL。 为了确保 URB 的成员格式正确,驱动程序必须在提交请求之前调用 UsbBuildInterruptOrBulkTransferRequest 以重新设置 URB 的格式,

对于高速和超高速常时等时传输,不要使用大于 8 的轮询周期

USB 驱动程序堆栈支持轮询周期数为 1、2、4 或 8 的高速和超高速常时等量管道。 客户端驱动程序不得将 IO 发送到时间段大于 8 的终结点。 这样做可能会导致 bug 检查。

确保常时常量数据包数是每帧数据包数的倍数

对于高速和 SuperSpeed 常时等时传输,每帧的常时等量数据包数计算为 8 /轮询周期。 客户端驱动程序必须确保 URB 中指定的 NumberOfPackets 值 (看到 _URB_ISOCH_TRANSFER) 是每帧数据包数的倍数。

USB 驱动程序堆栈不支持常时等量传输 URL,其中 NumberOfPackets 不是每帧数据包数的倍数。

在记录的 IRQL 级别调用例程

如果将客户端驱动程序注册到 USBD_CLIENT_CONTRACT_VERSION_602 作为协定版本,则 USB 驱动程序堆栈假定客户端驱动程序在适当的 IRQL 级别发送请求。 如果客户端驱动程序在 DISPATCH_LEVEL 发送请求,则应在 PASSIVE_LEVEL 发送请求。 在某些情况下,收到请求后,USB 驱动程序堆栈会验证 IRQL 值,并使请求失败。 但是,在其他情况下,USB 驱动程序堆栈可能会生成 bug 检查。

向 USB 设备发送请求