USB インターフェイスにおける代替設定の選択方法

このトピックでは、USB インターフェイスで代替設定をアクティブ化するためにインターフェイス選択要求を発行する手順について説明しています。 クライアント ドライバーは、USB 構成を選択した後にこの要求を発行する必要があります。 既定では、構成を選択すると、その構成に含まれる各インターフェイスの最初の代替設定も同時にアクティブになります。

各 USB 構成では、1 つ以上の複数の USB インターフェイスをサポートする必要があります。 各インターフェイスは、デバイスとの間でデータを転送するために使用される 1 つ以上のエンドポイントを公開します。 USB インターフェイスには、インターフェイスを識別するために使用されるデバイス定義のインターフェイス インデックスが必要です。 インターフェイスには、インターフェイスのエンドポイントをグループ化する 1 つ以上の代替設定も必要です。 デバイス構成の一部として、クライアント ドライバーは、インターフェイスの代替設定のいずれかを選択する必要があります。 エンドポイントは代替設定間で共有できるため、一度にアクティブにできる設定は 1 つだけです。 代替設定がアクティブになると、そのエンドポイントがデータ転送に使用できるようになります。

複数のインターフェイス デバイスの場合、特定の時点で 2 つのインターフェイスをアクティブにすることができます。 クライアント ドライバーは、各インターフェイスで代替設定をアクティブにする必要があります。 エンドポイントはインターフェイス間で共有されないため、各同時データ転送は各インターフェイスで実行できます。

代替設定はデバイス定義であり、設定インデックスと呼ばれる番号で識別されます。 インデックス 0 の代替設定は、このドキュメント セットの既定の代替設定と呼ばれます。 代替設定は、USB_INTERFACE_DESCRIPTOR 構造で説明します。 この構造には、設定が関連付けられているインターフェイス インデックスと、設定によって定義されたエンドポイントの数が含まれています。 また、インターフェイスの機能が準拠するクラス仕様に関する情報も含まれています。 エンドポイントをグループ化する方法は、デバイスの機能によって異なります。

たとえば、インターフェイスは、3 つの代替設定 (インデックス 0、1、2) を使用して、2 つの等時性エンドポイントと 2 つの一括エンドポイントを公開します。 代替設定 0 では、エンドポイントは定義されません。代替設定 1 では、一括エンドポイントが定義されます。代替設定 2 では、等時性エンドポイントが定義されます。 代替設定 0 にはエンドポイントがないため、クライアント ドライバーはこの設定を選択して、データ転送を無効にして帯域幅を節約することができます。 他の設定のいずれかがアクティブな場合、デバイスはデータ転送の準備ができています。 代替設定 1 を使用すると、一括データを転送できます。 代替設定 2 は、デバイスがストリーミング モードのときに選択できます。 そのため、代替設定により、クライアント ドライバーは、必要に応じてデバイス構成を柔軟に変更することができます。 この例では、クライアント ドライバーは、代替設定を選択するだけで、デバイスの機能を一括転送からストリーミングに切り替えることができます。

別の設定を使用して、帯域幅の要件を設定することもできます。 例については、「USB デバイスのレイアウト」をご覧ください。

Windows Driver Foundation (WDF) には、別の代替設定を選択するためにクライアント ドライバーが呼び出すことができるカーネル モード ドライバー フレームワークユーザー モード ドライバー フレームワークのメソッドが用意されています。 KMDF クライアント ドライバーは、設定インデックス、設定のインターフェイス記述子を指定するか、要求を含む URB を送信することにより、設定を選択できます。 UMDF クライアント ドライバーは、その設定インデックスを指定することによってのみ、代替設定を選択できます。

選択構成要求が正常に完了すると、以前にアクティブだった代替設定が非アクティブになります。

知っておくべきこと

この記事では、次のフレームワークを利用します。

開始する前に

クライアント ドライバーで代替設定を選択する前に、以下の要件を満していることを確認します。

  • クライアント ドライバーによって、フレームワーク USB ターゲット デバイス オブジェクトが作成されている必要があります。

    • KMDF クライアント ドライバーでは、WdfUsbTargetDeviceCreateWithParameters メソッドを呼び出すことで WDFUSBDEVICE ハンドルを取得する必要があります。 詳細については、「USB クライアント ドライバー コード構造について (KMDF)」の「デバイスのソース コード」を参照してください。

    • UMDF クライアント ドライバーは、フレームワーク ターゲット デバイス オブジェクトを問い合わせることで、IWDFUsbTargetDevice ポインターを取得する必要があります。 詳しくは、「USB クライアント ドライバー コード構造について (UMDF)」の「IPnpCallbackHardware 実装と USB 固有のタスク」をご覧ください。

      Microsoft Visual Studio Professional 2012 に付属する USB テンプレートを使用している場合、テンプレート コードでこれらのタスクが実行されます。 テンプレート コードによりターゲット デバイス オブジェクトのハンドルが取得され、デバイス コンテキストに格納されます。

  • デバイスにはアクティブな構成が必要です。

    • KMDF クライアント ドライバーは、WdfUsbTargetDeviceSelectConfig メソッドを呼び出す必要があります。

    • UMDF クライアント ドライバーの場合、フレームワークは最初の構成と、その構成内の各インターフェイスの既定の代替設定を選択します。

      USB テンプレートを使用している場合、コードは各インターフェイスの最初の構成と既定の代替設定を選択します。

KMDF クライアント ドライバーでの代替設定の選択

  1. 代替設定を持つインターフェイスへの WDFUSBINTERFACE ハンドルを取得します。

    ハンドルを取得するには、まず WdfUsbTargetDeviceGetNumInterfaces を呼び出して選択した構成のインターフェイスの数を取得し、ループ内のインターフェイスを列挙します。 各イテレーションで WdfUsbTargetDeviceGetInterface メソッドを呼び出し、インデックスをインクリメントします (ゼロから始まる)。

    デバイスの列挙時、USB ドライバー スタックは代替設定に番号を割り当てます。 インターフェイス番号は 0 から始まるシーケンシャルです。 これらの数値は、デバイス定義の設定インデックスとは異なる場合があります。 デバイス定義の設定インデックスを取得するには、WdfUsbInterfaceGetInterfaceNumber メソッドを呼び出します。

  2. WdfUsbInterfaceSelectSetting メソッドを呼び出すことにより、インターフェイスの選択要求を開始します。 呼び出しの Params パラメーターで、次のいずれかのオプションを選択します。

    • USB ドライバー スタックによって割り当てられる代替設定番号を指定します。 通常は、設定を列挙するために手順 1 で使用したのと同じインデックスを渡します。

    • 代替設定を記述するインターフェイス記述子をポインターで指定します。 その後、ドライバーは、WdfUsbInterfaceGetDescriptor メソッドを呼び出すことによって、インターフェイスの代替設定を列挙するときにインターフェイス記述子を取得できます。 列挙が完了すると、ドライバーは、USB_INTERFACE_DESCRIPTOR 構造内のすべての列挙代替設定に関する情報を取得します。

    • 選択インターフェイス要求に必要なすべての情報を含む URB へのポインターを指定します。

      1. USBD_INTERFACE_LIST_ENTRY 構造体の配列を割り当てます。 この配列内の要素の数は、選択した構成のインターフェイスの数によって異なります。 この配列の初期化については、「USB デバイスの構成を選択する方法」をご覧ください。
      2. USBD_SelectInterfaceUrbAllocateAndBuild ルーチンを呼び出して、選択インターフェイス要求に URB を割り当てます。 この呼び出しでは、構成を選択した後に取得されたインターフェイス リスト配列と構成ハンドルを指定します。 このハンドルを取得するには、WdfUsbTargetDeviceWdmGetConfigurationHandle メソッドを呼び出します。
      3. WdfUsbInterfaceSelectSetting を呼び出し、URB を指定します。

      **WDM ドライバー:**URB を送信するには、URB を IRP に関連付けて、IRP を USB ドライバー スタックに送信します。 詳細については、「URB の送信方法」に関する記事を参照してください。

    一覧のオプションは、選択基準を柔軟に指定できるクライアント ドライバーを提供します。 代替設定のエンドポイント機能を既に認識している場合、一覧の最初のオプション (代替設定番号付き) を選択します。 それ以外の場合、インターフェイス記述子を指定する 2 番目のオプションを選択します。 すべての代替設定の USB_INTERFACE_DESCRIPTOR 構造を検査します。 各設定について、エンドポイントとその特性 (エンドポイントの種類、最大パケット サイズなど) を列挙します。 データ転送に必要なエンドポイントのセットが見つかると、そのインターフェイス記述子へのポインターを指定することにより WdfUsbInterfaceSelectSetting を呼び出します。 通常、3 つ目のオプションは必要ありません。WDM ベースのクライアント ドライバーで、URL を送信することによってのみ USB ドライバー スタックに要求を送信できる場合を除きます。

    USB ドライバー スタックは、クライアント ドライバーによって提供される情報に基づいて、標準のコントロール要求 (SET INTERFACE) を構築し、デバイスに送信します。 要求が正常に完了した場合、USB ドライバー スタックは代替設定のエンドポイントへのパイプ ハンドルを取得します。

    別の設定を選択した後、クライアント ドライバーは常に新しい設定でエンドポイントのパイプ ハンドルを取得する必要があります。 そうしないと、ドライバーが古いパイプ ハンドルを使用してデータ転送要求を送信する可能性があります。 パイプ ハンドルの取得については、「USB パイプを列挙する方法」をご覧ください。

NTSTATUS  FX3SelectInterfaceSetting(  
    _In_ WDFDEVICE Device,
    _In_ UCHAR SettingIndex)

{
    NTSTATUS                 status;  
    PDEVICE_CONTEXT          pDeviceContext;  
    WDF_OBJECT_ATTRIBUTES               pipeAttributes;

    WDF_USB_INTERFACE_SELECT_SETTING_PARAMS settingParams;

    PAGED_CODE();  

    pDeviceContext = GetDeviceContext(Device);

    if (pDeviceContext->UsbInterface == NULL)
    {
        status = USBD_STATUS_BAD_NUMBER_OF_INTERFACES;
        goto Exit;
    }

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pipeAttributes, PIPE_CONTEXT);  

    pipeAttributes.EvtCleanupCallback = FX3EvtPipeContextCleanup;

    WDF_USB_INTERFACE_SELECT_SETTING_PARAMS_INIT_SETTING (&settingParams, SettingIndex);

    status = WdfUsbInterfaceSelectSetting (
        pDeviceContext->UsbInterface,
        &pipeAttributes,
        &settingParams);

    if (status != STATUS_SUCCESS)
    {
        goto Exit;
    }

    if (WdfUsbInterfaceGetNumConfiguredPipes (pDeviceContext->UsbInterface) > 0)
    {
        status = FX3EnumeratePipes (Device);

        if (status != STATUS_SUCCESS)
        {
            goto Exit;
        }
    }

Exit:
    return status;
}

UMDF クライアント ドライバーでの代替設定の選択

  1. IWDFUsbTargetDevice::GetNumInterfaces メソッドを呼び出すことにより、アクティブな構成でサポートされる USB インターフェイスの数を取得します。

  2. 構成内の各インターフェイスの IWDFUsbInterface ポインターを取得します。

    関数が NULL を返すまで、ループ内で IWDFUsbTargetDevice::RetrieveUsbInterface メソッドを呼び出して、すべてのインターフェイスを列挙します。 反復処理のたびに、メンバー インデックス (0 から始まる) をインクリメントします。 ループは、列挙されたすべてのインターフェイスへの IWDFUsbInterface ポインターを取得します。

  3. インターフェイスごとに、IWDFUsbInterface::GetWinUsbHandle を呼び出して WinUSB ハンドルを取得します。 このハンドルは、次の手順で必要になります。

  4. WinUsb_GetAssociatedInterface を呼び出して、インターフェイスへのハンドルを取得します。 AssociatedInterfaceIndex パラメーターで、手順 2 でインデックスを指定します。

  5. インターフェイスの代替設定の数を決定します。

    ループで WinUsb_QueryInterfaceSettings 関数を呼び出し、各イテレーションでインデックス (0 から始まる) をインクリメントします。 すべての設定が列挙されると、関数は ERROR_NO_MORE_ITEMS を返します。 この関数は、各設定のインターフェイス記述子も返します。

  6. 各インターフェイス記述子の bNumEndpoints メンバーで受信した値を使用して、そのエンドポイントを列挙します。 エンドポイント記述子を検査し、要件を満たす設定を決定します。

  7. WinUsb_SetCurrentAlternateSetting 関数を呼び出すことにより、インターフェイスの選択要求を開始します。 呼び出しにおいて、手順 4 でインデックスに関連付けられている代替設定番号を指定します。

  8. WinUsb_Free 関数を呼び出すことにより、手順 4 で取得したインターフェイス ハンドルを解放します。

  9. WinUsb_Free 関数を呼び出すことにより、手順 3 で取得した WinUSB ハンドルを解放します。

  10. IWDFUsbInterface メソッドの使用が完了した場合、手順 2 で取得したすべてのインターフェイス ポインターを解放します。

解説

KMDF クライアント ドライバーの場合、ドライバーは WdfUsbInterfaceSelectSetting 呼び出しで、ドライバー定義のパイプ コンテキストへのポインターを提供できます。 クライアント ドライバーは、パイプ コンテキストにパイプに関する情報を格納できます。 パイプ情報について詳しくは、「USB パイプを列挙する方法」をご覧ください。