windows USB 设备驱动开发-如何获取描述符

如何获取配置描述符

配置描述符是通过标准设备请求 (GET_DESCRIPTOR) 从设备获取的,该请求由 USB 驱动程序堆栈作为控制传输发送。 USB 客户端驱动程序可以通过以下方式之一启动请求:

  • 如果设备仅支持一种配置,最简单的方法是调用框架提供的 WdfUsbTargetDeviceRetrieveConfigDescriptor 方法;
  • 对于支持多个配置的设备,如果客户端驱动程序想要获取第一个配置以外的配置描述符,则驱动程序必须提交 URB。 若要提交 URB,驱动程序必须分配、格式化 URB,然后将 URB 提交到 USB 驱动程序堆栈;

若要分配 URB,客户端驱动程序必须调用 WdfUsbTargetDeviceCreateUrb 方法。 方法接收指向 USB 驱动程序堆栈分配的 URB 的指针。

若要设置 URB 的格式,客户端驱动程序可以使用 UsbBuildGetDescriptorRequest 宏。 宏在 URB 中设置所有必要的信息,例如要检索其描述符的设备定义的配置编号。 URB 函数设置为 URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE (see _URB_CONTROL_DESCRIPTOR_REQUEST) ,并将描述符的类型设置为 USB_CONFIGURATION_DESCRIPTOR_TYPE。 通过使用 URB 中包含的信息,USB 驱动程序堆栈生成标准控制请求并将其发送到设备。

若要提交 URB,客户端驱动程序必须使用 WDF 请求对象。 若要以异步方式将请求对象发送到 USB 驱动程序堆栈,驱动程序必须调用 **WdfRequestSend** 方法。 若要以同步方式发送,请调用 WdfUsbTargetDeviceSendUrbSynchronously 方法。

WDM 驱动程序: Windows 驱动程序模型 (WDM) 客户端驱动程序只能通过提交 URB 来获取配置描述符。 若要分配 URB,驱动程序必须调用 USBD_UrbAllocate 例程。 若要设置 URB 的格式,驱动程序必须调用 UsbBuildGetDescriptorRequest 宏。 若要提交 URB,驱动程序必须将 URB 与 IRP 相关联,并将 IRP 提交到 USB 驱动程序堆栈。 有关详细信息,请参阅 如何提交 URB。

在 USB 配置中,接口数及其备用设置是可变的。 因此,很难预测保存配置描述符所需的缓冲区大小。 客户端驱动程序必须通过两个步骤收集所有这些信息。 首先,确定保存所有配置描述符所需的大小缓冲区,然后发出检索整个描述符的请求。 客户端驱动程序可以通过以下方式之一获取大小:

若要通过调用 WdfUsbTargetDeviceRetrieveConfigDescriptor 获取配置描述符,请执行以下步骤:

  • 通过调用 WdfUsbTargetDeviceRetrieveConfigDescriptor 获取保存所有配置信息所需的缓冲区大小。 驱动程序必须在缓冲区中传递 NULL,并传递一个变量来保存缓冲区的大小;
  • 根据通过上一个 WdfUsbTargetDeviceRetrieveConfigDescriptor 调用接收的大小分配更大的缓冲区;
  • 再次调用 WdfUsbTargetDeviceRetrieveConfigDescriptor ,并指定指向步骤 2 中分配的新缓冲区的指针;
NTSTATUS RetrieveDefaultConfigurationDescriptor (
    _In_  WDFUSBDEVICE  UsbDevice,
    _Out_ PUSB_CONFIGURATION_DESCRIPTOR *ConfigDescriptor 
    )
{
    NTSTATUS ntStatus = -1;

    USHORT sizeConfigDesc;

    PUSB_CONFIGURATION_DESCRIPTOR fullConfigDesc = NULL;

    PAGED_CODE();

    *ConfigDescriptor  = NULL;

    ntStatus = WdfUsbTargetDeviceRetrieveConfigDescriptor (
        UsbDevice, 
        NULL,
        &sizeConfigDesc);

    if (sizeConfigDesc == 0)
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, 
            "%!FUNC! Could not retrieve the configuration descriptor size.");

        goto Exit;
    }
    else
    {
        fullConfigDesc = (PUSB_CONFIGURATION_DESCRIPTOR) ExAllocatePoolWithTag (
            NonPagedPool, 
            sizeConfigDesc,
            USBCLIENT_TAG);

        if (!fullConfigDesc)
        {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
            goto Exit;
        }  
    }

    RtlZeroMemory (fullConfigDesc, sizeConfigDesc);

    ntStatus = WdfUsbTargetDeviceRetrieveConfigDescriptor (
        UsbDevice, 
        fullConfigDesc,
        &sizeConfigDesc);

    if (!NT_SUCCESS(ntStatus))
    {           
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, 
            "%!FUNC! Could not retrieve the configuration descriptor.");

        goto Exit;
    }

    *ConfigDescriptor = fullConfigDesc;

Exit:

    return ntStatus;   
}

若要通过提交 URB 获取配置描述符,请执行以下步骤:

  • 通过调用 WdfUsbTargetDeviceCreateUrb 方法分配 URB ;
  • 通过调用 UsbBuildGetDescriptorRequest 宏设置 URB 的格式。 URB 的传输缓冲区必须指向足以容纳 USB_CONFIGURATION_DESCRIPTOR 结构的缓冲区;
  • 通过调用 WdfRequestSend 或 WdfUsbTargetDeviceSendUrbSynchronously 将 URB 作为 WDF 请求对象提交;
  • 请求完成后,检查 USB_CONFIGURATION_DESCRIPTOR 的 wTotalLength 成员。 该值指示包含完整配置描述符所需的缓冲区大小;
  • 根据 wTotalLength 中检索到的大小分配更大的缓冲区;
  • 对较大的缓冲区发出相同的请求;

以下示例代码演示请求的 UsbBuildGetDescriptorRequest 调用,以获取第 i 个配置的配置信息:

NTSTATUS FX3_RetrieveConfigurationDescriptor (
    _In_ WDFUSBDEVICE  UsbDevice,
    _In_ PUCHAR ConfigurationIndex,
    _Out_ PUSB_CONFIGURATION_DESCRIPTOR *ConfigDescriptor 
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;

    USB_CONFIGURATION_DESCRIPTOR configDesc;
    PUSB_CONFIGURATION_DESCRIPTOR fullConfigDesc = NULL;

    PURB urb = NULL;

    WDFMEMORY urbMemory = NULL;

    PAGED_CODE();

    RtlZeroMemory (&configDesc, sizeof(USB_CONFIGURATION_DESCRIPTOR));
    *ConfigDescriptor = NULL;

    // Allocate an URB for the get-descriptor request. 
    // WdfUsbTargetDeviceCreateUrb returns the address of the 
    // newly allocated URB and the WDFMemory object that 
    // contains the URB.

    ntStatus = WdfUsbTargetDeviceCreateUrb (
        UsbDevice,
        NULL,
        &urbMemory,
        &urb);

    if (!NT_SUCCESS (ntStatus))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, 
            "%!FUNC! Could not allocate URB for an open-streams request.");

        goto Exit;
    }

       // Format the URB.
    UsbBuildGetDescriptorRequest (
        urb,                                                        // Points to the URB to be formatted
        (USHORT) sizeof( struct _URB_CONTROL_DESCRIPTOR_REQUEST ),  // Size of the URB.
        USB_CONFIGURATION_DESCRIPTOR_TYPE,                          // Type of descriptor
        *ConfigurationIndex,                                        // Index of the configuration
        0,                                                          // Not used for configuration descriptors
        &configDesc,                                                // Points to a USB_CONFIGURATION_DESCRIPTOR structure
        NULL,                                                       // Not required because we are providing a buffer not MDL
        sizeof(USB_CONFIGURATION_DESCRIPTOR),                       // Size of the USB_CONFIGURATION_DESCRIPTOR structure.
        NULL                                                        // Reserved.
        );

       // Send the request synchronously.
    ntStatus = WdfUsbTargetDeviceSendUrbSynchronously (
        UsbDevice,
        NULL,
        NULL,
        urb);

    if (configDesc.wTotalLength == 0)
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, 
            "%!FUNC! Could not retrieve the configuration descriptor size.");

        ntStatus = USBD_STATUS_INAVLID_CONFIGURATION_DESCRIPTOR;

        goto Exit;
    }

    // Allocate memory based on the retrieved size. 
       // The allocated memory is released by the caller.
    fullConfigDesc = (PUSB_CONFIGURATION_DESCRIPTOR) ExAllocatePoolWithTag (
        NonPagedPool, 
        configDesc.wTotalLength,
        USBCLIENT_TAG);

    RtlZeroMemory (fullConfigDesc, configDesc.wTotalLength);

    if (!fullConfigDesc)
    {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;

        goto Exit;
    }

       // Format the URB.
    UsbBuildGetDescriptorRequest (
        urb,                                                        
        (USHORT) sizeof( struct _URB_CONTROL_DESCRIPTOR_REQUEST ),  
        USB_CONFIGURATION_DESCRIPTOR_TYPE,                          
        *ConfigurationIndex,                                         
        0,                                                          
        fullConfigDesc,                                                 
        NULL,                                                       
        configDesc.wTotalLength,                       
        NULL                                                        
        );

       // Send the request again.
    ntStatus = WdfUsbTargetDeviceSendUrbSynchronously (
        UsbDevice,
        NULL,
        NULL,
        urb);

    if ((fullConfigDesc->wTotalLength == 0) || !NT_SUCCESS (ntStatus))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, 
            "%!FUNC! Could not retrieve the configuration descriptor.");

        ntStatus = USBD_STATUS_INAVLID_CONFIGURATION_DESCRIPTOR;

        goto Exit;
    }

       // Return to the caller.
    *ConfigDescriptor = fullConfigDesc;

Exit:

    if (urbMemory)
    {
        WdfObjectDelete (urbMemory);
    }

    return ntStatus;
}

当设备返回配置描述符时,请求缓冲区中将填充所有备用设置的接口描述符,以及特定备用设置中所有端点的端点描述符。 对于 USB 设备布局中所述的设备,下图演示了配置信息在内存中的布局方式。

USB_INTERFACE_DESCRIPTOR的从零开始的 bInterfaceNumber 成员区分配置中的接口。 对于给定接口,从零开始的 bAlternateSetting 成员区分接口的备用设置。 设备按 bInterfaceNumber 值的顺序返回接口描述符,然后按 bAlternateSetting 值的顺序返回接口描述符。

若要在配置中搜索给定的接口描述符,客户端驱动程序可以调用 USBD_ParseConfigurationDescriptorEx。 在 调用中,客户端驱动程序在配置中提供起始位置。 (可选)驱动程序可以指定接口号、备用设置、类、子类或协议。 例程返回指向下一个匹配接口描述符的指针。

若要检查端点或字符串描述符的配置描述符,请使用 USBD_ParseDescriptors 例程。 调用方在配置中提供起始位置和描述符类型,例如USB_STRING_DESCRIPTOR_TYPE或USB_ENDPOINT_DESCRIPTOR_TYPE。 例程返回指向下一个匹配描述符的指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值