共用方式為


USB 組態描述元

USB 裝置會以稱為USB組態的一系列介面形式公開其功能。 每個介面都包含一或多個替代設定,而每個替代設定是由一組端點所組成。 本主題描述與 USB 組態相關聯的各種描述項。

組態描述項會描述 USB 組態, (請參閱 USB_CONFIGURATION_DESCRIPTOR 結構) 。 組態描述元包含組態及其介面、替代設定及其端點的相關信息。 每個介面描述元或替代設定都會在 USB_INTERFACE_DESCRIPTOR 結構中描述。 在設定中,每個介面描述元都會在記憶體中接著介面和替代設定的所有端點描述元。 每個端點描述元都會儲存在 USB_ENDPOINT_DESCRIPTOR 結構中。

例如,請考慮 USB 裝置配置中所述的 USB 網路攝影機裝置。 裝置支援具有兩個介面的設定,而第一個介面 (索引 0) 支援兩個替代設定。

下列範例顯示 USB 網路攝影機裝置的組態描述元:

Configuration Descriptor:
wTotalLength:         0x02CA
bNumInterfaces:       0x02
bConfigurationValue:  0x01
iConfiguration:       0x00
bmAttributes:         0x80 (Bus Powered )
MaxPower:             0xFA (500 mA)

bConfigurationValue 字段會指出裝置韌體中定義的組態數目。 用戶端驅動程式會使用該數位值來選取使用中的組態。 如需 USB 裝置設定的詳細資訊,請參閱 如何選取 USB 裝置的設定。 USB 組態也會指出特定電源特性。 bmAttributes 包含位掩碼,指出組態是否支援遠端喚醒功能,以及裝置是否為總線電源或自我電源。 MaxPower 欄位會指定裝置在總線電源時,裝置可從主機繪製的最大電源 () 。 組態描述元也會指出裝置支援 (bNumInterfaces) 介面總數。

下列範例顯示網路攝影機裝置之替代設定 0 的介面描述元:

Interface Descriptor:
bInterfaceNumber:     0x00
bAlternateSetting:    0x00
bNumEndpoints:        0x01
bInterfaceClass:      0x0E
bInterfaceSubClass:   0x02
bInterfaceProtocol:   0x00
iInterface:           0x02
0x0409: "Microsoft LifeCam VX-5000"
0x0409: "Microsoft LifeCam VX-5000"

在上述範例中,請注意 bInterfaceNumberbAlternateSetting 域值。 這些欄位包含客戶端驅動程式用來啟動介面及其其中一個替代設定的索引值。 針對啟用,驅動程式會將選取介面要求傳送至USB驅動程式堆疊。 然後驅動程式堆疊會建置標準控制要求 (SET INTERFACE) ,並將其傳送至裝置。 請注意 bInterfaceClass 欄位。 任何替代設定的介面描述元或描述元會指定類別代碼、子類別和通訊協定。 0x0E的值表示介面適用於視訊裝置類別。 此外,請注意 iInterface 欄位。 該值表示有兩個字串描述元附加至介面描述元。 字串描述元包含 Unicode 描述,這些描述會在裝置列舉期間用來識別功能。 如需字串描述元的詳細資訊,請參閱 USB 字串描述元

介面中的每個端點都會描述裝置的輸入或輸出單一數據流。 支援不同函式數據流的裝置具有多個介面。 支持數個與函式相關的數據流的裝置,可以在單一介面上支援多個端點。

除了預設端點) 必須提供端點描述元以外,所有端點類型 (,讓主機可以取得端點的相關信息。 端點描述元包含資訊,例如其位址、類型、方向,以及端點可以處理的數據量。 數據傳輸至端點是以該資訊為基礎。

下列範例顯示網路攝影機裝置的端點描述元:

Endpoint Descriptor:
bEndpointAddress:   0x82  IN
bmAttributes:       0x01
wMaxPacketSize:     0x0080 (128)
bInterval:          0x01

bEndpointAddress 字段會指定唯一端點位址,其中包含端點號碼 (Bits 3..0) ,以及端點的方向 (位 7) 。 藉由在上述範例中讀取這些值,我們可以判斷描述項描述其端點編號為 2 的 IN 端點。 bmAttributes 屬性表示端點類型為時序。 wMaxPacketSizefield 指出端點可以在單一交易中傳送或接收的最大位元組數目。 位12..11表示每個微框架可以傳送的交易總數。 bInterval 指出端點可以傳送或接收數據的頻率。

如何取得設定描述元

組態描述元是透過標準裝置要求 (GET_DESCRIPTOR) 從裝置取得,此要求會以USB驅動程式堆疊的控制傳輸的形式傳送。 USB 用戶端驅動程式可以透過下列其中一種方式來起始要求:

  • 如果裝置只支援一個設定,最簡單的方式就是呼叫架構提供的 WdfUsbTargetDeviceRetrieveConfigDescriptor 方法。

  • 對於支援多個設定的裝置,如果用戶端驅動程式想要取得第一個以外的組態描述元,驅動程式必須提交 URB。 若要提交 URB,驅動程式必須配置、格式化,然後將 URB 提交至 USB 驅動程式堆疊。

    若要配置 URB,用戶端驅動程序必須呼叫 WdfUsbTargetDeviceCreateUrb 方法。 方法會接收 USB 驅動程式堆疊所配置的 URB 指標。

    若要格式化 URB,用戶端驅動程式可以使用 UsbBuildGetDescriptorRequest 宏。 宏會設定 URB 中的所有必要資訊,例如要擷取描述元的裝置定義組態編號。 URB 函式設定為 URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE (请参阅 _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 來取得設定描述元,請執行下列步驟:

  1. 藉由呼叫 WdfUsbTargetDeviceRetrieveConfigDescriptor,取得保存所有設定資訊所需的緩衝區大小。 驅動程式必須在緩衝區中傳遞NULL,以及用來保存緩衝區大小的變數。
  2. 根據透過先前 WdfUsbTargetDeviceRetrieveConfigDescriptor 呼叫所接收的大小,配置較大的緩衝區。
  3. 再次呼叫 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 來取得組態描述元,請執行下列步驟:

  1. 呼叫 WdfUsbTargetDeviceCreateUrb 方法,以配置 URB。
  2. 呼叫 UsbBuildGetDescriptorRequest 宏來格式化 URB。 URB 的傳輸緩衝區必須指向足以保存 USB_CONFIGURATION_DESCRIPTOR 結構的緩衝區。
  3. 呼叫 WdfRequestSendWdfUsbTargetDeviceSendUrbSynchronously,將 URB 提交為 WDF 要求物件。
  4. 要求完成之後,請檢查 USB_CONFIGURATION_DESCRIPTORwTotalLength 成員。 該值表示包含完整組態描述元所需的緩衝區大小。
  5. 根據 wTotalLength 中擷取的大小來配置較大的緩衝區。
  6. 對較大的緩衝區發出相同的要求。

下列範例程式代碼顯示 要求之 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。 例程會傳回下一個相符描述項的指標。