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的替代設定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"
在上述範例中,請注意 bInterfaceNumber 和 bAlternateSetting 欄位值。 這些欄位包含用戶端驅動程式用來啟用介面的索引值,以及其中一個替代設定。 若要啟用,驅動程式會將選取介面要求傳送至 USB 驅動程式堆疊。 然後,驅動程式堆疊會建置標準控制要求 (SET INTERFACE) ,並將它傳送至裝置。 請注意 bInterfaceClass 欄位。 介面描述元或其任何替代設定的描述元會指定類別碼、子類別及通訊協定。 0x0E 的值表示介面適用於視訊裝置類別。 另外,請注意 iInterface 欄位。 該值表示有兩個字串描述元附加至介面描述元。 字串描述元包含裝置列舉期間用來識別功能的 Unicode 描述。 如需字串描述元的詳細資訊,請參閱 USB 字串描述元。
介面中的每個端點都會描述裝置的單一輸入或輸出資料流程。 支援不同類型功能串流的裝置有多個介面。 支援多個與功能相關的資料流的裝置可以在單一介面上支援多個端點。
所有類型的端點 (預設端點除外) 都必須提供端點描述元,以便主機可以取得端點的相關資訊。 端點描述符包含資訊,例如其位址、類型、方向,以及端點可以處理的資料量。 到端點的資料傳輸是基於該資訊。
下列範例顯示網路攝影機裝置的端點描述元:
Endpoint Descriptor:
bEndpointAddress: 0x82 IN
bmAttributes: 0x01
wMaxPacketSize: 0x0080 (128)
bInterval: 0x01
bEndpointAddress 欄位會指定包含端點號碼 (位元 3..0) 和端點方向 (位元 7) 的唯一端點位址。 藉由讀取上述範例中的這些值,我們可以判斷描述元描述端點號碼為 2 的 IN 端點。 bmAttributes 屬性指出端點類型是同步的。 wMaxPacketSize欄位指出端點可以在單一交易中傳送或接收的位元組數目上限。 位元 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 來取得設定描述元,請執行下列步驟:
- 呼叫 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。 常式會傳回下一個相符描述元的指標。