USB デバイス用の構成の選択方法

USB デバイスの構成を選択するには、デバイスのクライアント ドライバーがサポートされている構成を少なくとも 1 つ選択し、使用する各インターフェイスの代替設定を指定する必要があります。 クライアント ドライバーは、これらの選択肢を選択構成要求にパッケージ化して、Microsoft が提供する USB ドライバー スタック (具体的には USB バス ドライバー (USB ハブ PDO) に要求を送信します。 USB バス ドライバーは、指定された構成の各インターフェイスを選択し、インターフェイス内の各エンドポイントへのコミュニケーション チャネル ( パイプ) を設定します。 要求が完了すると、クライアント ドライバーは、選択した構成のハンドルと、各インターフェイスのアクティブな代替設定で定義されているエンドポイントのパイプ ハンドルを受け取ります。 クライアント ドライバーは、受信したハンドルを使用して構成設定を変更し、特定のエンドポイントに I/O 読み取りおよび書き込み要求を送信できます。

クライアント ドライバーは、URB_FUNCTION_SELECT_CONFIGURATION の種類の USB 要求ブロック (URB) で選択構成要求を送信します。 このトピックの手順では、USBD_SelectConfigUrbAllocateAndBuild ルーチンを使用してその URB をビルドする方法について説明します。 このルーチンは、URB のメモリを割り当て、選択構成要求の URB を書式設定し、URB のアドレスをクライアント ドライバーに返します。

または、URB 構造体を割り当ててから、手動で、または UsbBuildSelectConfigurationRequest マクロを呼び出して URB の書式を設定できます。

前提条件

手順 1: USBD_INTERFACE_LIST_ENTRY 構造体の配列を作成する

  1. 構成内のインターフェイスの数を取得します。 この情報は、USB_CONFIGURATION_DESCRIPTOR 構造体の bNumInterfaces メンバーに含まれています。

  2. USBD_INTERFACE_LIST_ENTRY 構造体の配列を作成します。 配列内の要素の数は、インターフェイスの数よりも 1 つ多くする必要があります。 RtlZeroMemory を呼び出して配列を初期化します。

    クライアント ドライバーは、有効にする各インターフェイスの代替設定を、USBD_INTERFACE_LIST_ENTRY 構造体の配列で指定します。

    • 各構造体の InterfaceDescriptor メンバーは、代替設定を含むインターフェイス記述子を指します。
    • 各構造体のInterface メンバーは、Pipes メンバー内のパイプ情報を含むUSBD_INTERFACE_INFORMATION 構造体を指します。 Pipes は、代替設定で定義されている各エンドポイントに関する情報を格納します。
  3. 構成内の各インターフェイス (またはその代替設定) のインターフェイス記述子を取得します。 これらのインターフェイス記述子は、USBD_ParseConfigurationDescriptorEx を呼び出すことによって取得できます。

    USB 複合デバイスのファンクション ドライバーについて:

    USB デバイスが複合デバイスの場合、構成はマイクロソフトが提供する USB 汎用親ドライバー (Usbccgp.sys) によって選択されます。 複合デバイスのファンクション ドライバーの 1 つであるクライアント ドライバーは構成を変更できませんが、Usbccgp.sys を通じて構成の選択要求を送信することはできます。

    その要求を送信する前に、クライアント ドライバーは URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE 要求を送信する必要があります。 応答として、Usbccgp.sys は、クライアント ドライバーが読み込まれる特定の関数に関連するインターフェイス記述子とその他の記述子のみを含む部分構成記述子を取得します。 部分構成記述子の [bNumInterfaces] フィールドで報告されるインターフェイスの数が、USB 複合デバイス全体に対して定義されているインターフェイスの合計数よりも少なくなります。 さらに、部分的な構成記述子では、インターフェイス記述子の bInterfaceNumber は、デバイス全体に対する実際のインターフェイス番号を示します。 たとえば、Usbccgp.sysは、最初のインターフェイスの bNumInterfaces 値が 2、bInterfaceNumber 値が 4 の部分的な構成記述子を報告できます。 インターフェイス番号が報告されたインターフェイスの数より大きいことに注意してください。

    部分構成でインターフェイスを列挙する場合は、インターフェイスの数に基づいてインターフェイス番号を計算してインターフェイスを検索することを避けてください。 前の例では、0 から始まり、(bNumInterfaces - 1) で終わるループで USBD_ParseConfigurationDescriptorEx が呼び出され、各反復処理でインターフェイス インデックス (InterfaceNumber パラメーターで指定) がインクリメントされると、ルーチンは正しいインターフェイスを取得できません。 代わりに、InterfaceNumber で -1 を渡して、構成記述子内のすべてのインターフェイスを検索します。 実装の詳細については、このセクションのコード例を参照してください。

    クライアント ドライバーによって送信された構成の選択要求を Usbccgp.sys が処理する方法については、「既定以外の USB 構成を選択するための Usbccgp.sys の構成」を参照してください。

  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 構造体のアドレスに設定します。

  • InterfaceNumber、AlternateSettingNumberOfPipesPipes[i].MaximumTransferSize、および Pipes[i].PipeFlags メンバーを初期化します。

    Note

    Windows 7 以前では、クライアント ドライバーは、USBD_CreateConfigurationRequestEx を呼び出して選択構成要求の URB を作成していました。 Windows 2000 では、USBD_CreateConfigurationRequestExPipes[i].MaximumTransferSize を単一の URB 読み取り/書き込み要求の既定の最大転送サイズに初期化します。 クライアント ドライバーは、Pipes[i].MaximumTransferSize で異なる最大転送サイズを指定できます。 USB スタックは、Windows XP、Windows Server 2003、およびそれ以降のバージョンのオペレーティング システムでは、この値を無視します。 MaximumTransferSize の詳細については、「USB 帯域幅の割り当て」の「USB 転送とパケット サイズの設定」を参照してください。

手順 3: USB ドライバー スタックに URB を送信する

USB ドライバー スタックに URB を送信するには、クライアント ドライバーは、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_FUNCTION_SELECT_INTERFACE (select-interface request) 型の別の URB を送信することによって代替インターフェイス設定を選択するために使用できるハンドルを返します。 その要求の URB 構造体を割り当てて構築するには、USBD_SelectInterfaceUrbAllocateAndBuild を呼び出します。

    有効なインターフェイス内で等時性エンドポイント、制御エンドポイント、割り込みエンドポイントをサポートするのに十分な帯域幅がない場合、構成選択要求とインターフェイス選択要求は失敗する可能性があります。 その場合、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
);