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

このトピックでは、ユニバーサル シリアル バス (USB) デバイスで構成を選択する方法について説明します。

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

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

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

前提条件

Instructions

手順 1: 構造体の配列をUSBD_INTERFACE_LIST_ENTRYします。

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

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

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

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

    **USB 複合デバイスの関数ドライバーについて: **

    USB デバイスが複合デバイスの場合、構成は Microsoft 提供の 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)から始まり、 で終わり、各イテレーションでインターフェイス インデックス ( InterfaceNumber パラメーターで指定) をインクリメントするループで USBD_ParseConfigurationDescriptorEx が呼び出された場合、ルーチンは正しいインターフェイスを取得できません。 代わりに、 InterfaceNumber で -1 を渡して、構成記述子内のすべてのインターフェイスを検索してください。 実装の詳細については、このセクションのコード例を参照してください。

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

  4. 配列内の要素 (最後の要素を除く) ごとに、 InterfaceDescriptor メンバーをインターフェイス記述子のアドレスに設定します。 配列内の最初の要素に対して、 InterfaceDescriptor メンバーを、構成内の最初のインターフェイスを表すインターフェイス記述子のアドレスに設定します。 同様に、配列内の n 番目の要素の場合は、 InterfaceDescriptor メンバーを、構成内の n 番目のインターフェイスを表すインターフェイス記述子のアドレスに設定します。

  5. 最後 の要素の InterfaceDescriptor メンバーは NULL に設定する必要があります。

手順 2: USB ドライバー スタックによって割り当てられた URB へのポインターを取得します。

次に、選択USBD_SelectConfigUrbAllocateAndBuild構成と、指定された構造体の設定された配列を指定して、USBD_INTERFACE_LIST_ENTRYします。 ルーチンは、次のタスクを実行します。

  • URB を作成し、指定した構成、そのインターフェイス、エンドポイントに関する情報を入力し、要求の種類を URB_FUNCTION_SELECT_CONFIGURATION。

  • その URB 内で、 は、クライアント ドライバー USBD_INTERFACE_INFORMATION 指定するインターフェイス記述子ごとに 1 つの構造体を割り当てる必要があります。

  • 呼びし元が指定した USBD_INTERFACE_LIST_ENTRY 配列の n 番目の要素の Interface メンバーを、URB 内の対応する USBD_INTERFACE_INFORMATION 構造体のアドレスに設定します。

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

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

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

URB を USB ドライバー スタックに送信するには、クライアント ドライバーが I/O 制御要求 IOCTL_INTERNAL_USB_SUBMIT_URBを送信 する必要があります。 URB の送信の詳細については、「URB を 送信する方法」を参照してください

URB を受け取った後、USB ドライバー スタックは各メモリ構造の残りの メンバー USBD_INTERFACE_INFORMATION します。 特に、 パイプ 配列メンバーには、インターフェイスのエンドポイントに関連付けられているパイプに関する情報が入力されます。

手順 4: 要求の完了時に、要求の構造USBD_INTERFACE_INFORMATION URB を検査します。

USB ドライバー スタックが要求の IRP を完了すると、スタックは代替設定の一覧と、USBD_INTERFACE_LIST_ENTRY 配列内の関連インターフェイス を返 します。

  1. USBD_INTERFACE_INFORMATIONパイプ メンバー は、その特定のインターフェイスの各エンドポイントに 関連付 けられているパイプに関する情報を含む USBD_PIPE_INFORMATION 構造体の配列を参照します。 クライアント ドライバーは、パイプ [i] からパイプ ハンドルを取得できます。PipeHandle と を使用して、特定のパイプに I/O 要求を送信します。 パイプ [i]。PipeType メンバーは、そのパイプでサポートされているエンドポイントと転送の種類を指定します。

  2. URB の UrbSelectConfiguration メンバー内では、USB ドライバー スタックからハンドルが返されます。このハンドルを使用して、URB_FUNCTION_SELECT_INTERFACE (select-interface request) 型の別の URB を送信することで、代替インターフェイス設定を選択できます。 その要求に 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 を再利用できます。 または、 を呼び出して新しい URB を割 り当USBD_UrbAllocate。 要求を送信する前に、次のコード例に示すように 、UsbBuildSelectConfigurationRequest マクロを使用して URB を書式設定する必要があります。

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

既定以外の USB 構成を選択するための Usbccgp.sys の構成
USB デバイスの構成
URB の割り当てと構築