共用方式為


如何選取 USB 裝置的設定

若要選取 USB 裝置的組態,裝置的用戶端驅動程式必須至少選擇其中一個支援的組態,並指定要使用之每個介面的替代設定。 用戶端驅動程式會將這些選項封裝在選取組態要求,並將要求傳送至 Microsoft 提供的 USB 驅動程式堆棧,特別是 USB 總線驅動程式(USB 中樞 PDO)。 USB 總線驅動程式會選取指定組態中的每個介面,並將通道或 管道設定至介面內的每個端點。 要求完成之後,客戶端驅動程式會收到所選組態的句柄,以及每個介面的作用中替代設定中所定義的端點管道句柄。 然後,客戶端驅動程式可以使用收到的句柄來變更組態設定,並將 I/O 讀取和寫入要求傳送至特定端點。

用戶端驅動程式會在類型URB_FUNCTION_SELECT_CONFIGURATION的 USB 要求區塊 (URB)傳送選取組態要求。 本主題中的程式描述如何使用 USBD_SelectConfigUrbAllocateAndBuild 例程來建置該 URB。 例程會配置 URB 的記憶體、格式化選取組態要求的 URB,並將 URB 的位址傳回給用戶端驅動程式。

或者,您可以配置 URB 結構,然後手動格式化 URB,或呼叫 UsbBuildSelectConfigurationRequest 宏。

必要條件

步驟 1:建立USBD_INTERFACE_LIST_ENTRY結構的陣列

  1. 取得組態中的介面數目。 此資訊包含在 USB_CONFIGURATION_DESCRIPTOR 結構的 bNumInterfaces 成員中

  2. 建立USBD_INTERFACE_LIST_ENTRY結構的陣列 陣列中的元素數目必須大於介面數目。 呼叫 RtlZeroMemory 來初始化陣列。

    用戶端驅動程式會指定每個介面中的替代設定,以在USBD_INTERFACE_LIST_ENTRY結構的陣列中啟用。

  3. 取得組態中每個介面的介面描述元(或其替代設定)。 您可以呼叫 USBD_ParseConfigurationDescriptorEx取得這些介面描述元。

    關於 USB 複合裝置的函式驅動程式:

    如果 USB 裝置是複合裝置,Microsoft 提供的 USB 泛型父驅動程式 會選取組態(Usbccgp.sys)。 用戶端驅動程式,這是複合裝置的其中一個函式驅動程式,無法變更設定,但驅動程式仍然可以透過Usbccgp.sys傳送選取組態要求。

    傳送該要求之前,客戶端驅動程序必須提交URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE要求。 回應中,Usbccgp.sys會 擷取部分組態描述元 ,該描述項只包含介面描述項和其他描述元,這些描述項與載入客戶端驅動程式的特定函式有關。 部分組態描述元之 bNumInterfaces 欄位中報告的介面數目小於針對整個 USB 複合裝置定義的介面總數。 此外,在部分組態描述元中,介面描述元的 bInterfaceNumber 表示相對於整個裝置的實際介面編號。 例如,Usbccgp.sys可能會報告部分組態描述元,其中 bNumInterfaces 值為 2,而 第一個介面的 bInterfaceNumber 值為 4。 請注意,介面編號大於報告的介面數目。

    在部分組態中列舉介面時,請根據介面數目計算介面編號,以避免搜尋介面。 在上述範例中,如果在迴圈中呼叫USBD_ParseConfigurationDescriptorEx,其開頭為零、結尾(bNumInterfaces - 1)為 ,並在每次反覆專案中遞增介面索引(在 InterfaceNumber 參數中指定),例程就無法取得正確的介面。 相反地,請務必在 InterfaceNumber傳遞 -1,以搜尋組態描述元中的所有介面。 如需實作詳細數據,請參閱本節中的程式代碼範例。

    如需如何Usbccgp.sys處理用戶端驅動程式所傳送之選取組態要求的資訊,請參閱 設定Usbccgp.sys以選取非預設 USB 組態

  4. 針對陣列中的每個元素(除了最後一個專案之外),請將 InterfaceDescriptor 成員設定為介面描述元的位址。 針對陣列中的第一個專案,將 InterfaceDescriptor 成員設定為表示組態中第一個介面之介面描述元的位址。 同樣地, 針對陣列中的 n個元素,將 InterfaceDescriptor 成員設定為介面描述元的位址,代表 組態中的第 n個介面。

  5. 最後 一個專案的 InterfaceDescriptor 成員必須設定為 NULL。

步驟 2:取得 USB 驅動程式堆疊所配置 URB 的指標

接下來,藉由指定要選取的組態和填入USBD_INTERFACE_LIST_ENTRY結構的陣列,來呼叫USBD_SelectConfigUrbAllocateAndBuild。 例程會執行下列工作:

  • 建立 URB,並填入指定組態、其介面和端點的相關信息,並將要求類型設定為URB_FUNCTION_SELECT_CONFIGURATION。

  • 在該 URB 中,為用戶端驅動程式指定的每個介面描述項配置 USBD_INTERFACE_INFORMATION 結構。

  • 呼叫端所提供USBD_INTERFACE_LIST_ENTRY數位第 n個元素的 Interface 成員設定為 URB 中對應USBD_INTERFACE_INFORMATION結構的位址。

  • 初始化 InterfaceNumberAlternateSettingNumberOfPipesPipes[i]。MaximumTransferSizePipes[i]。PipeFlags 成員。

    注意

    在 Windows 7 和更早版本中,用戶端驅動程式會呼叫 USBD_CreateConfigurationRequestEx,為選取組態要求建立 URB。 在 Windows 2000 USBD_CreateConfigurationRequestEx 初始化 Pipes[i]。MaximumTransferSize 為單一 URB 讀取/寫入要求的默認傳輸大小上限。 用戶端驅動程式可以在 Pipes[i] 中 指定不同的傳輸大小上限。MaximumTransferSize。 USB 堆疊會在 Windows XP、Windows Server 2003 和更新版本的作業系統中忽略此值。 如需 MaximumTransferSize的詳細資訊,請參閱 USB 頻寬配置中的<設定 USB 傳輸和封包大小>。

步驟 3:將 URB 提交至 USB 驅動程式堆疊

若要將 URB 提交至 USB 驅動程式堆疊,用戶端驅動程式必須傳送 IOCTL_INTERNAL_USB_SUBMIT_URB I/O 控制要求。 如需提交 URB 的相關信息,請參閱 如何提交 URB

收到 URB 之後,USB 驅動程式堆疊會填滿每個 USBD_INTERFACE_INFORMATION 結構的其餘成員。 特別是, Pipes 陣列成員會填入與介面端點相關聯的管道相關信息。

步驟 4:在要求完成時,檢查USBD_INTERFACE_INFORMATION結構和 URB

在USB驅動程式堆疊完成要求的 IRP 之後,堆疊會傳回替代設定清單和USBD_INTERFACE_LIST_ENTRY陣列中的相關介面。

  1. 每個USBD_INTERFACE_INFORMATION結構的 Pipes 成員會指向一個USBD_PIPE_INFORMATION結構的數位,其中包含與該特定介面之每個端點相關聯的管道相關信息。 用戶端驅動程式可以從 Pipes[i] 取得管道控點 。PipeHandle 並使用它們將 I/O 要求傳送至特定管道。 Pipes[i]。PipeType 成員會指定該管道所支援的端點和傳輸類型。

  2. 在 URB 的 UrbSelectConfiguration 成員內,USB 驅動程式堆棧會傳回句柄,您可以藉由提交另一個類型 URB URB_FUNCTION_SELECT_INTERFACE (select-interface request) 來選取替代介面設定。 若要配置並建置該要求的 URB 結構,請呼叫 USBD_SelectInterfaceUrbAllocateAndBuild

    如果沒有足夠的頻寬支援已啟用介面內的不時時、控制及中斷端點,則 select-configuration 要求和 select-interface 要求可能會失敗。 在此情況下,USB 總線驅動程式會將URB標頭的 Status 成員設定為USBD_STATUS_NO_BANDWIDTH。

下列範例程式代碼示範如何建立USBD_INTERFACE_LIST_ENTRY結構的陣列,並呼叫 USBD_SelectConfigUrbAllocateAndBuild。 此範例會呼叫 SubmitUrbSync 以同步方式傳送要求。 若要查看 SubmitUrbSync 的程式代碼範例,請參閱 如何提交 URB

/*++

Routine Description:
This helper routine selects the specified configuration.

Arguments:
USBDHandle - USBD handle that is retrieved by the 
client driver in a previous call to the USBD_CreateHandle routine.

ConfigurationDescriptor - Pointer to the configuration
descriptor for the device. The caller receives this pointer
from the URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE request.

Return Value: NT status value
--*/

NTSTATUS SelectConfiguration (PDEVICE_OBJECT DeviceObject,
                              PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
{
    PDEVICE_EXTENSION deviceExtension;
    PIO_STACK_LOCATION nextStack;
    PIRP irp;
    PURB urb = NULL;

    KEVENT    kEvent;
    NTSTATUS ntStatus;    

    PUSBD_INTERFACE_LIST_ENTRY   interfaceList = NULL; 
    PUSB_INTERFACE_DESCRIPTOR    interfaceDescriptor = NULL;
    PUSBD_INTERFACE_INFORMATION  Interface = NULL;
    USBD_PIPE_HANDLE             pipeHandle;

    ULONG                        interfaceIndex;

    PUCHAR StartPosition = (PUCHAR)ConfigurationDescriptor;

    deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;

    // Allocate an array for the list of interfaces
    // The number of elements must be one more than number of interfaces.
    interfaceList = (PUSBD_INTERFACE_LIST_ENTRY)ExAllocatePool (
        NonPagedPool, 
        sizeof(USBD_INTERFACE_LIST_ENTRY) *
        (deviceExtension->NumInterfaces + 1));

    if(!interfaceList)
    {
        //Failed to allocate memory
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto Exit;
    }

    // Initialize the array by setting all members to NULL.
    RtlZeroMemory (interfaceList, sizeof (
        USBD_INTERFACE_LIST_ENTRY) *
        (deviceExtension->NumInterfaces + 1));

    // Enumerate interfaces in the configuration.
    for ( interfaceIndex = 0; 
        interfaceIndex < deviceExtension->NumInterfaces; 
        interfaceIndex++) 
    {
        interfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
            ConfigurationDescriptor, 
            StartPosition, // StartPosition 
            -1,            // InterfaceNumber
            0,             // AlternateSetting
            -1,            // InterfaceClass
            -1,            // InterfaceSubClass
            -1);           // InterfaceProtocol

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

        // Set the interface entry
        interfaceList[interfaceIndex].InterfaceDescriptor = interfaceDescriptor;
        interfaceList[interfaceIndex].Interface = NULL;

        // Move the position to the next interface descriptor
        StartPosition = (PUCHAR)interfaceDescriptor + interfaceDescriptor->bLength;

    }

    // Make sure that the InterfaceDescriptor member of the last element to NULL.
    interfaceList[deviceExtension->NumInterfaces].InterfaceDescriptor = NULL;

    // Allocate and build an URB for the select-configuration request.
    ntStatus = USBD_SelectConfigUrbAllocateAndBuild(
        deviceExtension->UsbdHandle, 
        ConfigurationDescriptor, 
        interfaceList,
        &urb);

    if(!NT_SUCCESS(ntStatus)) 
    {
        goto Exit;
    }

    // Allocate the IRP to send the buffer down the USB stack.
    // The IRP will be freed by IO manager.
    irp = IoAllocateIrp((deviceExtension->NextDeviceObject->StackSize)+1, TRUE);  

    if (!irp)
    {
        //Irp could not be allocated.
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto Exit;
    }

    ntStatus = SubmitUrbSync( 
        deviceExtension->NextDeviceObject, 
        irp, 
        urb, 
        CompletionRoutine);

    // Enumerate the pipes in the interface information array, which is now filled with pipe
    // information.

    for ( interfaceIndex = 0; 
        interfaceIndex < deviceExtension->NumInterfaces; 
        interfaceIndex++) 
    {
        ULONG i;

        Interface = interfaceList[interfaceIndex].Interface;

        for(i=0; i < Interface->NumberOfPipes; i++) 
        {
            pipeHandle = Interface->Pipes[i].PipeHandle;

            if (Interface->Pipes[i].PipeType == UsbdPipeTypeInterrupt)
            {
                deviceExtension->InterruptPipe = pipeHandle;
            }
            if (Interface->Pipes[i].PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_IN (Interface->Pipes[i].EndpointAddress))
            {
                deviceExtension->BulkInPipe = pipeHandle;
            }
            if (Interface->Pipes[i].PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_OUT (Interface->Pipes[i].EndpointAddress))
            {
                deviceExtension->BulkOutPipe = pipeHandle;
            }
        }
    }

Exit:

    if(interfaceList) 
    {
        ExFreePool(interfaceList);
        interfaceList = NULL;
    }

    if (urb)
    {
        USBD_UrbFree( deviceExtension->UsbdHandle, urb); 
    }

    return ntStatus;
}

NTSTATUS CompletionRoutine ( PDEVICE_OBJECT DeviceObject,
                            PIRP           Irp,
                            PVOID          Context)
{
    PKEVENT kevent;

    kevent = (PKEVENT) Context;

    if (Irp->PendingReturned == TRUE)
    {
        KeSetEvent(kevent, IO_NO_INCREMENT, FALSE);
    }

    KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Select-configuration request completed. \n" ));

    return STATUS_MORE_PROCESSING_REQUIRED;
}

停用USB裝置的設定

若要停用 USB 裝置,請使用 NULL 組態描述元建立並提交選取組態要求。 針對該類型的要求,您可以重複使用您針對在裝置中選取組態的要求所建立的 URB。 或者,您可以呼叫 USBD_UrbAllocate來配置新的 URB。 提交要求之前,您必須使用 UsbBuildSelectConfigurationRequest 宏來格式化URB,如下列範例程式代碼所示。

URB Urb;
UsbBuildSelectConfigurationRequest(
  &Urb,
  sizeof(_URB_SELECT_CONFIGURATION),
  NULL
);