如何在 USB 界面中选择备用设置

本主题介绍发出选择接口请求以激活 USB 接口中的备用设置的步骤。 客户端驱动程序必须在选择 USB 配置后发出此请求。 默认情况下,选择配置还会激活该配置中每个接口中的第一个备用设置。

每个 USB 配置必须支持一个或多个多个 USB 接口。 每个接口都会公开一个或多个终结点,这些终结点用于向设备传输数据或从设备传输数据。 USB 接口必须具有用于标识接口的设备定义的 接口索引 。 接口还必须具有一个或多个对接口终结点进行分组的 备用设置 。 作为设备配置的一部分,客户端驱动程序必须在接口中选择一个备用设置。 由于终结点可以在备用设置之间共享,因此在给定时间只能有一个设置处于活动状态。 备用设置处于活动状态后,其终结点将可用于数据传输。

对于多接口设备,两个接口可以在给定的时间处于活动状态。 客户端驱动程序必须在每个接口中激活备用设置。 终结点不在接口之间共享,因此,每个同时的数据传输都可以在每个接口上执行。

备用设置是设备定义的,并使用一个名为 设置索引的数字进行标识。 索引 0 处的备用设置称为本文档集中 的默认备用设置USB_INTERFACE_DESCRIPTOR结构中介绍了备用设置。 结构包含与设置关联的接口索引以及设置定义的终结点数。 它还包含有关接口功能符合的类规范的信息。 终结点的分组方式取决于设备的功能。

例如,接口通过三个备用设置 (索引 0、1、2) 公开两个常时等量终结点和两个批量终结点。 备用设置 0 不定义任何终结点;备用设置 1 定义批量终结点;备用设置 2 定义常量终结点。 由于备用设置 0 没有终结点,因此客户端驱动程序可以选择此设置来禁用数据传输,以节省带宽。 当其他任一设置处于活动状态时,设备已准备好进行数据传输。 备用设置 1 可用于传输批量数据。 当设备处于流模式时,可以选择备用设置 2。 因此,备用设置使客户端驱动程序能够根据需要灵活地更改设备配置。 在此示例中,客户端驱动程序只需选择备用设置即可将设备功能从批量传输切换到流式传输。

备用设置还可用于设置带宽要求。 有关示例,请参阅 USB 设备布局

Windows Driver Foundation (WDF) 在 内核模式驱动程序框架用户模式驱动程序框架 中提供方法,客户端驱动程序可以调用这些方法以选择其他备用设置。 KMDF 客户端驱动程序可以通过指定设置索引、设置的接口描述符或提交包含请求的 URB 来选择设置。 UMDF 客户端驱动程序只能通过指定其设置索引来选择备用设置。

选择配置请求成功完成后,将停用以前处于活动状态的备用设置。

需要了解的事项

本文利用以下框架:

准备工作

在客户端驱动程序可以选择备用设置之前,请确保满足以下要求:

  • 客户端驱动程序必须已创建框架 USB 目标设备对象。

  • 设备必须具有活动配置。

    • KMDF 客户端驱动程序必须调用 WdfUsbTargetDeviceSelectConfig 方法。

    • 对于 UMDF 客户端驱动程序,框架为该配置中的每个接口选择第一个配置和默认备用设置。

      如果使用 USB 模板,则代码会选择每个接口中的第一个配置和默认备用设置。

在 KMDF 客户端驱动程序中选择备用设置

  1. 获取具有备用设置的接口的 WDFUSBINTERFACE 句柄。

    若要获取句柄,首先通过调用 WdfUsbTargetDeviceGetNumInterfaces 获取所选配置的接口数,然后在循环中枚举接口。 在每个迭代中,调用 WdfUsbTargetDeviceGetInterface 方法,并从零) 开始递增索引 (。

    注意 在设备枚举期间,USB 驱动程序堆栈会将数字分配给备用设置。 接口编号是从零开始的和顺序的。 这些数字可能与设备定义的设置索引不同。 若要获取设备定义的设置索引,请调用 WdfUsbInterfaceGetInterfaceNumber 方法。

  2. 通过调用 WdfUsbInterfaceSelectSetting 方法启动 select-interface 请求。 在调用的 Params 参数中,选择以下选项之一:

    • 指定 USB 驱动程序堆栈分配的备用设置编号。 通常,传递在步骤 1 中用于枚举设置的相同索引。

    • 指定描述备用设置的接口描述符的指针。 然后,驱动程序可以获取接口描述符,同时通过调用 WdfUsbInterfaceGetDescriptor 方法枚举接口中的备用设置。 枚举完成后,驱动程序将获取 有关USB_INTERFACE_DESCRIPTOR 结构中所有枚举的备用设置的信息。

    • 指定指向 URB 的指针,该 URLB 包含选择接口请求所需的所有信息。

      1. 分配 USBD_INTERFACE_LIST_ENTRY 结构的数组。 此数组中的元素数取决于所选配置中的接口数。 有关初始化此数组的信息,请参阅 如何为 USB 设备选择配置
      2. 通过调用 USBD_SelectInterfaceUrbAllocateAndBuild 例程为 select 接口请求分配 URB。 在此调用中,指定在选择配置后获取的接口列表数组和配置句柄。 可以通过调用 WdfUsbTargetDeviceWdmGetConfigurationHandle 方法获取该句柄。
      3. 调用 WdfUsbInterfaceSelectSetting 并指定 URB。

      **WDM 驱动程序:**若要提交 URB,请将 URB 与 IRP 关联,然后将 IRP 提交到 USB 驱动程序堆栈。 有关详细信息,请参阅 如何提交 URB

    列表中的选项为客户端驱动程序提供了指定选择条件的灵活性。 如果已知道备用设置的终结点功能,请选择第一个选项 (,其中备用设置编号) 列表中。 否则,请选择指定接口描述符的第二个选项。 检查 USB_INTERFACE_DESCRIPTOR 结构中是否有所有备用设置。 对于每个设置,枚举其终结点及其特征,例如终结点类型、最大数据包大小等。 找到数据传输所需的终结点集时,请通过指定指向该接口描述符的指针来调用 WdfUsbInterfaceSelectSetting 。 通常,不需要第三个选项,除非你是一个基于 WDM 的客户端驱动程序,该驱动程序只能通过提交 URL 将请求发送到 USB 驱动程序堆栈。

    然后,根据客户端驱动程序提供的信息,USB 驱动程序堆栈 (SET INTERFACE) 生成标准控制请求并将其发送到设备。 如果请求成功完成,USB 驱动程序堆栈将获取指向备用设置终结点的管道句柄。

    选择备用设置后,客户端驱动程序必须始终获取新设置中终结点的管道句柄。 如果不这样做,可能会导致驱动程序使用过时的管道句柄发送数据传输请求。 有关检索管道句柄的信息,请参阅 如何枚举 USB 管道

NTSTATUS  FX3SelectInterfaceSetting(  
    _In_ WDFDEVICE Device,
    _In_ UCHAR SettingIndex)

{
    NTSTATUS                 status;  
    PDEVICE_CONTEXT          pDeviceContext;  
    WDF_OBJECT_ATTRIBUTES               pipeAttributes;

    WDF_USB_INTERFACE_SELECT_SETTING_PARAMS settingParams;

    PAGED_CODE();  

    pDeviceContext = GetDeviceContext(Device);

    if (pDeviceContext->UsbInterface == NULL)
    {
        status = USBD_STATUS_BAD_NUMBER_OF_INTERFACES;
        goto Exit;
    }

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pipeAttributes, PIPE_CONTEXT);  

    pipeAttributes.EvtCleanupCallback = FX3EvtPipeContextCleanup;

    WDF_USB_INTERFACE_SELECT_SETTING_PARAMS_INIT_SETTING (&settingParams, SettingIndex);

    status = WdfUsbInterfaceSelectSetting (
        pDeviceContext->UsbInterface,
        &pipeAttributes,
        &settingParams);

    if (status != STATUS_SUCCESS)
    {
        goto Exit;
    }

    if (WdfUsbInterfaceGetNumConfiguredPipes (pDeviceContext->UsbInterface) > 0)
    {
        status = FX3EnumeratePipes (Device);

        if (status != STATUS_SUCCESS)
        {
            goto Exit;
        }
    }

Exit:
    return status;
}

在 UMDF 客户端驱动程序中选择备用设置

  1. 通过调用 IWDFUsbTargetDevice::GetNumInterfaces 方法获取活动配置支持的 USB 接口数。

  2. 获取配置中每个接口的 IWDFUsbInterface 指针。

    通过在循环中调用 IWDFUsbTargetDevice::RetrieveUsbInterface 方法枚举所有接口,直到函数返回 NULL。 每次迭代时, (从零开始) 递增成员索引。 循环检索指向所有枚举接口的 IWDFUsbInterface 指针。

  3. 对于每个接口,通过调用 IWDFUsbInterface::GetWinUsbHandle 获取 WinUSB 句柄。 下一步需要此句柄。

  4. 调用 WinUsb_GetAssociatedInterface 以获取接口的句柄。 在 AssociatedInterfaceIndex 参数中,指定步骤 2 中的索引。

  5. 确定接口中的备用设置数。

    在循环中调用 WinUsb_QueryInterfaceSettings 函数,并在每次迭代中递增索引 (从零开始的) 。 枚举所有设置后,函数将返回ERROR_NO_MORE_ITEMS。 函数还为每个设置返回接口描述符。

  6. 通过使用在每个接口描述符的 bNumEndpoints 成员中收到的值,并枚举其终结点。 检查终结点描述符并确定哪个设置符合要求。

  7. 通过调用 WinUsb_SetCurrentAlternateSetting 函数启动选择接口请求。 在调用中,指定与步骤 4 中的索引关联的备用设置编号。

  8. 释放通过调用 WinUsb_Free 函数在步骤 4 中获取的接口句柄。

  9. 通过调用 WinUsb_Free 函数释放在步骤 3 中获取的 WinUSB 句柄。

  10. 如果已完成 IWDFUsbInterface 方法的用法,请释放步骤 2 中检索到的所有接口指针。

注解

对于 KMDF 客户端驱动程序,在其 WdfUsbInterfaceSelectSetting 调用中,驱动程序可以提供指向驱动程序定义的管道上下文的指针。 客户端驱动程序可以在管道上下文中存储有关管道的信息。 有关管道信息的详细信息,请参阅 如何枚举 USB 管道