USB コントロール転送の送信方法

この記事では、コントロール転送の構造について説明し、クライアント ドライバーがデバイスに制御要求を送信する方法を示しています。

既定のエンドポイントについて

すべての USB デバイスで、既定のエンドポイントと呼ばれているエンドポイントを少なくとも 1 つサポートする必要があります。 既定のエンドポイントを対象とする転送は、コントロール転送と呼ばれています。 コントロール転送の目的は、ホストがデバイス情報を取得したり、デバイスを構成したり、デバイスに固有のコントロール操作を実行したりできるようにすることです。

まず、既定のエンドポイントの特性について調べることから始めます。

  • 既定のエンドポイントのアドレスは 0 です。
  • 既定のエンドポイントは双方向です。つまり、ホストでは、1 回の転送内でエンドポイントにデータを送信し、エンドポイントからデータを受信できます。
  • 既定のエンドポイントはデバイス レベルで利用できます。デバイスのインターフェイスには定義されていません。
  • 既定のエンドポイントは、ホストとデバイスの間の接続が確立された直後にアクティブになります。 構成が選択される前でもアクティブになります。
  • 既定のエンドポイントの最大パケット サイズは、デバイスのバス速度によって異なります。 Low-Speed、8 バイト。全速/高速、64 バイト。SuperSpeed、512 バイト。

コントロール転送のレイアウト

コントロール転送は優先度の高い転送であるため、一定量の帯域幅がホストによってバスに予約されます。 Low-Speed 転送と全速転送のデバイスの場合、帯域幅の 10% が、高速転送と SuperSpeed 転送のデバイスの場合、20% が予約されます。 次に、コントロール転送のレイアウトを見てみましょう。

Diagram of a USB control transfer.

コントロール転送は、"セットアップ トランザクション"、"データ トランザクション"、"ステータス トランザクション" の 3 つのトランザクションに分けられます。 各トランザクションには、"トークン パケット"、"データ パケット"、"ハンドシェイク パケット" という 3 種類のパケットが含まれています。

一部のフィールドはすべてのパケットに共通しています。 これらのフィールドを次に示します。

  • パケットの先頭を示す同期フィールド。
  • パケットの種類、トランザクションの方向、ハンドシェイク パケットの場合はトランザクションの成功または失敗を示すパケット識別子 (PID)。
  • EOP フィールドは、パケットの末尾を示します。

その他のフィールドは、パケットの種類によって異なります。

トークン パケット

すべてのセットアップ トランザクションは、トークン パケットから始まります。 パケットの構造を次に示します。 ホストは常にトークン パケットを送信します。

Diagram of a token packet layout.

PID 値は、トークン パケットの種類を示します。 以下に使用可能な値を示します。

  • SETUP: 制御転送におけるセットアップ・トランザクションの開始を示します。
  • IN: ホストがデバイスからデータを要求していることを示します (大文字と小文字の読み取り)。
  • OUT: ホストによってデバイスにデータが送信されていることを示します (書き込みの場合)。
  • SOF: フレームの開始を示します。 この種類のトークン パケットには、11 ビットのフレーム番号が含まれています。 ホストから SOF パケットが送信されます。 このパケットが送信される頻度は、バス速度によって異なります。 全速の場合、ホストは 1 ミリ秒ごとにパケットを送信します。高速バスの場合、125 マイクロ秒ごとです。

データ パケット

トークン パケットの直後に続くのが、ペイロードを含むデータ パケットです。 各データ パケットで格納できるバイト数は、既定のエンドポイントの最大パケット サイズによって異なります。 データ パケットは、転送の方向に応じてホストまたはデバイスによって送信されます。

Diagram of a data packet layout.

ハンドシェイク パケット

データ パケットの直後に続くのがハンドシェイク パケットです。 パケットの PID は、パケットがホストによって受信されたのか、デバイスによって受信されたのかを示します。 ハンドシェイク パケットは、転送の方向に応じてホストまたはデバイスによって送信されます。

Diagram of a handshake packet layout.

トランザクションとパケットの構造は、Beagle、Ellisys、LeCroy USB プロトコル アナライザーなど、あらゆる USB アナライザーを使用して確認できます。 アナライザー デバイスには、通信回線経由で USB デバイスとの間でデータを送受信する方法が示されます。 この例では、LeCroy USB アナライザーによってキャプチャされたトレースをいくつか確認してみましょう。 この例は、情報提供目的のみで用意されています。 Microsoft がこの製品を推薦するものではありません。

セットアップ トランザクション

ホストから常にコントロール転送が開始されます。 これはセットアップ トランザクションを送信することによって行われます。 このトランザクションには "セットアップ トークン" と呼ばれているトークン パケットが含まれ、その後に 8 バイトのデータ パケットが続きます。 このスクリーンショットでは、セットアップ トランザクションの例を示しています。

Screenshot of a trace of a setup transaction.

前のトレースでは、セットアップ トークン パケット #434 を送信することによってホストによりコントロール転送が開始されます (H↓ がそれを示しています)。 PID によってセットアップ トークンを示す SETUP が指定されることにご注意ください。 PID の後に、デバイス アドレスとエンドポイントのアドレスが続きます。 コントロール転送の場合、そのエンドポイント アドレスは常に 0 です。

次に、ホストからデータ パケット #435 が送信されます。 PID は DATA0 であり、その値はパケット シーケンスに使用されます (これについては、これから説明します)。 PID の後には、この要求に関する主要な情報を含む 8 バイトが続きます。 これらの 8 バイトは、要求の種類と、デバイスがその応答を書き込むバッファーのサイズを示します。

すべてのバイトは逆順で受信されます。 セクション 9.3 で説明したように、次のフィールドと値が表示されます。

フィールド サイズ 説明
bmRequestType (「9.3.1 bmRequestType」を参照) 1 0x80 データ転送方向はデバイスからホストです (D7 は 1 です)

要求は標準要求です (D6...D5 は 0)

要求の受信者はデバイスです (D4 は 0)
bRequest (セクション 9.3.2 と表 9-4 を参照) 1 0x06 要求の種類は GET_DESCRIPTOR です。
wValue (表 9-5 参照) 2 0x0100 要求値は、記述子の種類が DEVICE であることを示します。
wIndex (セクション 9.3.4 を参照) 2 0x0000 方向はホストからデバイスです (D7 は 1)。

エンドポイント番号は 0 です。
wLength (セクション 9.3.5 を参照) 2 0x0012 要求は 18 バイトを取得することです。

そのため、このコントロール (読み取り) 転送では、ホストがデバイスの記述子を取得する要求を送信し、その記述子を保持するため、転送長として 18 バイトを指定するものと結論付けることができます。 デバイスが 18 バイトを送信する方法は、既定のエンドポイントが 1 回のトランザクションで送信できるデータの量によって異なります。 その情報は、データ トランザクションでデバイスによって返されるデバイスの記述子に含まれます。

応答の中で、デバイスからハンドシェイク パケットが送信されます (#436、D↓ で示されます)。 PID 値が ACK (ACK パケット) であることにご注意ください。 これは、デバイスがトランザクションを受信確認したことを示します。

データ トランザクション

次に、要求に応答してデバイスから返される内容を見てみましょう。 実際のデータは、データ トランザクションで転送されます。

データ トランザクションのトレースは次のようになります。

Screenshot that shows a trace of an example data transaction.

ACK パケットを受信すると、ホストではデータ トランザクションが開始されます。 トランザクションを開始するために、IN 方向でトークン パケット (#450) が送信されます (IN トークンと呼ばれています)。

応答の中で、IN トークンに続くデータ パケット (#451) がデバイスから送信されます。 このデータ パケットには、実際のデバイス記述子が含まれています。 最初のバイトはデバイス記述子の長さ、18 バイト (0x12) を示します。 このデータ パケットの最後のバイトは、既定のエンドポイントでサポートされる最大パケット サイズを示します。 この例の場合、デバイスが既定のエンドポイントを使用して一度に 8 バイトを送信できることがわかります。

Note

既定のエンドポイントの最大パケット サイズは、デバイスの速度によって異なります。 高速デバイスの既定のエンドポイントは 64 バイトです。Low-Speed デバイスの場合は 8 バイトです。

ホストでは ACK パケット (#452) をデバイスに送信することでデータ トランザクションを受信確認します。

返されるデータの量を計算しましょう。 セットアップ トランザクションのデータ パケット (#435) の wLength フィールドで、ホストから 18 バイトが要求されました。 データ トランザクションで、デバイス記述子の最初の 8 バイトだけがデバイスから受信されたことがわかります。 それでは、残りの 10 バイトに保存されている情報をホストはどのように受信するのでしょうか。 デバイスは、8 バイトと最後の 2 バイトの 2 つのトランザクションで行われます。

これで、既定のエンドポイントの最大パケット サイズがホストに認識されたので、ホストでは、新しいデータ トランザクションを開始し、パケット サイズに基づいて次の部分を要求します。

次のデータ トランザクションはこのようになります。

Screenshot that shows a trace of the new data transaction.

ホストでは、IN トークン (#463) を送信し、デバイスに次の 8 バイトを要求することで前述のデータ トランザクションを開始します。 デバイスは、デバイス記述子の次の 8 バイトを含むデータパケット (#464) で応答します。

8 バイトが受信されると、ホストからデバイスに ACK パケット (#465) が送信されます。

次に、ホストでは、次のように別のデータ トランザクションで最後の 2 バイトを要求します。

Screenshot that shows a trace of the new example data transaction in which the host requests the last 2 bytes.

結果的に、デバイスからホストに 18 バイトを転送するために、ホストでは転送されたバイト数を追跡記録し、3 回のデータ トランザクション (8+8+2) を開始したことがわかります。

Note

データ トランザクション 19、23、26 のデータ パケットの PID に注目してください。 PID は DATA0 と DATA1 の間で交互に切り替わります。 このシーケンスはデータ切り替えと呼ばれています。 複数のデータ トランザクションが存在するとき、パケットのシーケンスを確認する目的でデータ切り替えが利用されます。 この手法により、データ パケットが重複したり、失われたりすることはありません。

統合データ パケットをデバイス記述子の構造にマップすることで (表 9-8 参照)、次のフィールドと値が表示されます。

フィールド サイズ 説明
bLength 1 0x12 デバイス記述子の長さ (18 バイト)。
bDescriptorType 1 0x01 記述子の種類はデバイスです。
bcdUSB 2 0x0100 仕様バージョン番号は 1.00 です。
bDeviceClass 1 0x00 デバイス クラスは 0 です。 構成内の各インターフェイスにはクラス情報が与えられます。
bDeviceSubClass 1 0x00 デバイス クラスが 0 のため、サブクラスは 0 です。
bProtocol 1 0x00 プロトコルは 0 です。 このデバイスでは、いかなるクラス固有プロトコルも使用されません。
bMaxPacketSize0 1 0x08 エンドポイントの最大パケット サイズは 8 バイトです。
idVendor 2 0x0562 テレックス通信。
idProduct 2 0x0002 USB マイク。
bcdDevice 2 0x0100 デバイスのリリース番号を示します。
iManufacturer 1 0x01 製造元文字列。
iProduct 1 0x02 製品文字列。
iSerialNumber 1 0x03 シリアル番号。
bNumConfigurations 1 0x01 構成の数。

これらの値を調べることで、デバイスに関する予備情報を得られます。 このデバイスはLow-Speed の USB マイクです。 既定のエンドポイントの最大パケット サイズは 8 バイトです。 このデバイスでは、1 つの構成がサポートされます。

ステータス トランザクション

最後に、ホストでは、最後のトランザクションであるステータス トランザクションを開始し、コントロール転送を完了とします。

Screenshot of a trace of an example data transaction.

ホストは、OUT トークン パケット (#481) でトランザクションを開始します。 このパケットの目的は、要求されたすべてのデータをデバイスが送信したことを確認することです。 このステータス トランザクションで送信されたデータ パケットはありません。 デバイスは ACK パケットで応答します。 エラーが発生した場合、PID は NAK か STALL になります。

ドライバー モデル

前提条件

クライアント ドライバーでパイプを列挙するには、次の要件が満たされている必要があります。

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

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

KMDF クライアント ドライバー

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

UMDF クライアント ドライバー

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

コントロール転送の最も重要な側面は、セットアップ トークンを正しくフォーマットすることです。 要求を送信する前に、次の一連の情報を収集します。

  • 要求の方向: ホストからデバイス、またはデバイスからホスト。
  • 要求の受信者: デバイス、インターフェイス、エンドポイント、その他。
  • 要求のカテゴリ: 標準、クラス、ベンダー。
  • 要求の種類 (GET_DESCRIPTPOR 要求など)。 詳細については、USB 仕様のセクション 9.5 を参照してください。
  • wValue 値と wIndex 値。 これらの値は、要求の種類によって異なります。

この情報はすべて、公式の USB 仕様から入手できます。

UMDF ドライバーを記述している場合、UMDF Sample Driver for OSR USB Fx2 Learning Kit から Usb_hw.h というヘッダー ファイルを取得します。 このヘッダー ファイルには、コントロール転送用のセットアップ パケットをフォーマットするための便利なマクロと構造体が含まれています。

UMDF ドライバーはすべて、デバイスとの間でデータを送受信するために、カーネルモード ドライバーと通信する必要があります。 USB UMDF ドライバーの場合、カーネルモード ドライバーは常に Microsoft 提供ドライバー WinUSB (Winusb.sys) になります。

UMDF ドライバーから USB ドライバー スタックが要求されるたびに、Windows I/O マネージャーでは、WinUSB に要求を送信します。 WinUSB は要求を受信すると、要求を処理するか、USB ドライバー スタックに転送します。

コントロール転送要求を送信するための Microsoft 定義メソッド

ホスト上の USB クライアント ドライバーでは、デバイスに関する情報を取得するか、デバイスを構成するか、ベンダー コントロール コマンドを送信する目的でほとんどの制御要求を開始します。 このような要求はすべて、次に分類できます。

  • 標準要求は、USB 仕様で定義されています。 標準要求を送信する目的は、デバイス、その設定、インターフェイス、およびエンドポイントに関する情報を取得することです。 各要求の受信者は、要求の種類によって異なります。 受信者は、デバイス、インターフェイス、またはエンドポイントです。

    Note

    コントロール転送の転送先は常に既定のエンドポイントです。 受信者は、ホストがその情報 (記述子やステータスなど) に関心を持っているデバイスのエンティティです。

    要求は、構成要求、機能要求、および状態要求にさらに分類できます。

    • 構成要求は、ホストでそれを構成できるよう、デバイスから情報を取得する目的で送信されます。たとえば、GET_DESCRIPTOR 要求です。 この要求は、デバイスで特定または代替の構成を設定する目的でホストにより送信される書き込み要求になることもあります。
    • 機能要求は、デバイス、インターフェイス、またはエンドポイントでサポートされている特定のブール値デバイス設定を有効または無効にする目的で、クライアント ドライバーにより送信されます。
    • ステータス要求 を使用すると、ホストはデバイス、エンドポイント、またはインターフェイスの USB 定義のステータス ビットを取得または設定できます。

    詳細については、USB 仕様バージョン 2.0 のセクション 9.4 を参照してください。 標準要求の種類は、Usbspec.h というヘッダー ファイルに定義されています。

  • クラス要求は、特定のデバイス クラス仕様によって定義されます。

  • ベンダー要求はベンダーによって提供され、デバイスでサポートされている要求によって異なります。

Microsoft 提供の USB スタックでは、前述のトレースで確認できるように、デバイスとのあらゆるプロトコル通信が処理されます。 ドライバーからは、クライアント ドライバーからさまざまな方法でコントロール転送を送信することを可能にするデバイス ドライバー インターフェイス (DDI) が公開されます。 クライアント ドライバーが Windows Driver Foundation (WDF) ドライバーの場合、ルーチンを直接呼び出し、一般的な種類の制御要求を送信できます。 WDF は、KMDF と UMDF の両方のコントロール転送を本質的にサポートしています。

特定の種類の制御要求は、WDF 経由で公開されません。 そのような要求の場合、クライアント ドライバーでは WDF ハイブリッド モデルを使用できます。 このモデルでは、クライアント ドライバーで WDM URB スタイルの要求を作成し、フォーマットし、WDF フレームワーク オブジェクトで送信することが可能になります。 ハイブリッド モデルは、カーネルモード ドライバーにのみ適用されます。

UMDF ドライバーの場合:

usb_hw.h に定義されているヘルパー マクロと構造体を使用します。 このヘッダーは UMDF Sample Driver for OSR USB Fx2 Learning Kit に付属しています。

この表を利用し、制御要求を USB ドライバー スタックに送信するための最適な方法を決定します。 この表を表示できない場合は、この記事の表を参照してください。

制御要求を送信するには... KMDF ドライバーの場合 UMDF ドライバーの場合 WDM ドライバーの場合は、URB 構造体 (ヘルパー ルーチン) をビルドします
CLEAR_FEATURE: デバイス、その構成、インターフェイス、およびエンドポイントの特定の機能設定を無効にします。 USB 仕様のセクション 9.4.1 を参照してください。
  1. セットアップ パケットを宣言します。 WDF_USB_CONTROL_SETUP_PACKET 構造体を参照してください。
  2. WDF_USB_CONTROL_SETUP_PACKET_INIT_FEATURE を呼び出してセットアップ パケットを初期化します。
  3. WDF_USB_BMREQUEST_RECIPIENT に定義されている受信者値を指定します。
  4. 機能セレクターを指定します (wValue)。 Usbspec.h の「USB_FEATURE_XXX 定数」セクションを参照してください。 USB 仕様の表 9-6 も参照してください。
  5. SetFeatureFALSE に設定します。
  6. WdfUsbTargetDeviceSendControlTransferSynchronously または WdfUsbTargetDeviceFormatRequestForControlTransfer を呼び出して要求を送信します。
  1. セットアップ パケットを宣言します。 usb_hw.h で宣言されている WINUSB_CONTROL_SETUP_PACKET を参照してください。
  2. usb_hw.h に定義されている WINUSB_CONTROL_SETUP_PACKET_INIT_FEATURE というヘルパー マクロを呼び出してセットアップ パケットを初期化します。
  3. WINUSB_BMREQUEST_RECIPIENT に定義されている受信者値を指定します。
  4. 機能セレクターを指定します (wValue)。 Usbspec.h の「USB_FEATURE_XXX 定数」セクションを参照してください。 USB 仕様の表 9-6 も参照してください。
  5. SetFeatureFALSE に設定します。
  6. IWDFUsbTargetDevice::FormatRequestForControlTransfer メソッドを呼び出し、フレームワーク要求オブジェクトと転送バッファーを初期化されたセットアップ パケットに関連付けることで要求を作成します。
  7. IWDFIoRequest::Send メソッドを呼び出して要求を送信します。
_URB_CONTROL_FEATURE_REQUEST

(UsbBuildFeatureRequest)

URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE

URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE

URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT

URB_FUNCTION_CLEAR_FEATURE_TO_OTHER
GET_CONFIGURATION: 現在の USB 構成を取得します。 USB 仕様のセクション 9.4.2 を参照してください。 KMDF では、既定で最初の構成が選択されます。 デバイス定義の構成番号を取得するには:

  1. WDF_USB_CONTROL_SETUP_PACKET をフォーマットし、その bRequest メンバーを USB_REQUEST_GET_CONFIGURATION に設定します。
  2. WdfUsbTargetDeviceSendControlTransferSynchronously または WdfUsbTargetDeviceFormatRequestForControlTransfer を呼び出して要求を送信します。
UMDF では、既定で最初の構成が選択されます。 デバイス定義の構成番号を取得するには:

  1. セットアップ パケットを宣言します。 usb_hw.h で宣言されている WINUSB_CONTROL_SETUP_PACKET を参照してください。
  2. usb_hw.h に定義されている WINUSB_CONTROL_SETUP_PACKET_INIT というヘルパー マクロを呼び出してセットアップ パケットを初期化します。
  3. 方向として BmRequestToDevice を、受信者として BmRequestToDevice を、要求として USB_REQUEST_GET_CONFIGURATION を指定します。
  4. IWDFUsbTargetDevice::FormatRequestForControlTransfer メソッドを呼び出し、フレームワーク要求オブジェクトと転送バッファーを初期化されたセットアップ パケットに関連付けることで要求を作成します。
  5. IWDFIoRequest::Send メソッドを呼び出して要求を送信します。
  6. 転送バッファー内の構成番号を受信します。 IWDFMemory メソッドを呼び出してそのバッファーにアクセスします。
_URB_CONTROL_GET_CONFIGURATION_REQUEST

URB_FUNCTION_GET_CONFIGURATION
GET_DESCRIPTOR: デバイス、構成、インターフェイス、およびエンドポイント記述子を取得します。 USB 仕様のセクション 9.4.3 を参照してください。

詳細については、「USB 記述子」を参照してください。
以下のメソッドを呼び出します。

以下のメソッドを呼び出します。

_URB_CONTROL_DESCRIPTOR_REQUEST

(UsbBuildGetDescriptorRequest)

URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE

URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT

URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE
GET_INTERFACE: インターフェイス値の現在の代替設定を取得します。 USB 仕様のセクション 9.4.4 を参照してください。

  1. WdfUsbTargetDeviceGetInterface メソッドを呼び出し、ターゲット インターフェイス オブジェクトの WDFUSBINTERFACE ハンドルを取得します。
  2. WdfUsbInterfaceGetConfiguredSettingIndex メソッドを呼び出します。
  1. ターゲット インターフェイス オブジェクトへの IWDFUsbInterface ポインターを取得します。
  2. IWDFUsbInterface::GetConfiguredSettingIndex メソッドを呼び出します。
_URB_CONTROL_GET_INTERFACE_REQUEST

URB_FUNCTION_GET_INTERFACE
GET_STATUS: デバイス、エンドポイント、またはインターフェイスからステータス ビットを取得します。 USB 仕様のセクション 9.4.5 を ご覧ください。
  1. セットアップ パケットを宣言します。 WDF_USB_CONTROL_SETUP_PACKET 構造体を参照してください。
  2. WDF_USB_CONTROL_SETUP_PACKET_INIT_GET_STATUS を呼び出してセットアップ パケットを初期化します。
  3. WDF_USB_BMREQUEST_RECIPIENT に定義されている受信者値を指定します。
  4. デバイス、インターフェイス、またはエンドポイントのどのステータスを取得するかを指定します (wIndex)。
  5. WdfUsbTargetDeviceSendControlTransferSynchronously または WdfUsbTargetDeviceFormatRequestForControlTransfer を呼び出して要求を送信します。
  1. セットアップ パケットを宣言します。 usb_hw.h で宣言されている WINUSB_CONTROL_SETUP_PACKET を参照してください。
  2. usb_hw.h に定義されている WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS というヘルパー マクロを呼び出してセットアップ パケットを初期化します。
  3. WINUSB_BMREQUEST_RECIPIENT に定義されている受信者値を指定します。
  4. デバイス、インターフェイス、またはエンドポイントのどのステータスを取得するかを指定します (wIndex)。
  5. IWDFUsbTargetDevice::FormatRequestForControlTransfer メソッドを呼び出し、フレームワーク要求オブジェクトと転送バッファーを初期化されたセットアップ パケットに関連付けることで要求を作成します。
  6. IWDFIoRequest::Send メソッドを呼び出して要求を送信します。
  7. 転送バッファーのステータス値を受信します。 IWDFMemory メソッドを呼び出してそのバッファーにアクセスします。
  8. ステータスにより自己供給のリモートウェイクアップが示されるかどうかを判断するには、WINUSB_DEVICE_TRAITS 列挙型に定義されている値を使用します。
_URB_CONTROL_GET_STATUS_REQUEST

(UsbBuildGetStatusRequest)

URB_FUNCTION_GET_STATUS_FROM_DEVICE

URB_FUNCTION_GET_STATUS_FROM_INTERFACE

URB_FUNCTION_GET_STATUS_FROM_ENDPOINT

URB_FUNCTION_GET_STATUS_FROM_OTHER
SET_ADDRESS: USB 仕様のセクション 9.4.6 を参照してください。 この要求は USB ドライバーによって処理されます。クライアント ドライバーではこの操作を実行できません。 この要求は USB ドライバーによって処理されます。クライアント ドライバーではこの操作を実行できません。 この要求は USB ドライバーによって処理されます。クライアント ドライバーではこの操作を実行できません。
SET_CONFIGURATION: 構成を設定します。 USB 仕様のセクション 9.4.7 を参照してください。

詳細については、「USB デバイス用の構成の選択方法」を参照してください。
KMDF では既定で、既定の構成と各インターフェイスにおける最初の代替設定が選択されます。 クライアント ドライバーでは、WdfUsbTargetDeviceSelectConfigType メソッドを呼び出し、WdfUsbTargetDeviceSelectConfigTypeUrb を要求オプションとして指定することで既定の構成を変更できます。 次に、この要求の URB をフォーマットし、それを USB ドライバー スタックに提出する必要があります。 UMDF では既定で、既定の構成と各インターフェイスにおける最初の代替設定が選択されます。 クライアント ドライバーでは、構成を変更できません。 _URB_SELECT_CONFIGURATION

(USBD_SelectConfigUrbAllocateAndBuild)

URB_FUNCTION_SELECT_CONFIGURATION
SET_DESCRIPTOR: 既存のデバイス、構成、または文字列記述子を更新します。 USB 仕様のセクション 9.4.8 を参照してください。

この要求は一般的には使用されません。 ただし、USB ドライバー スタックは、クライアント ドライバーからこのような要求を受け取ります。
  1. 要求の URB を割り当て、構築します。
  2. _URB_CONTROL_DESCRIPTOR_REQUEST 構造体で転送情報を指定します。
  3. WdfUsbTargetDeviceFormatRequestForUrb または WdfUsbTargetDeviceSendUrbSynchronously を呼び出して要求を送信します。
  1. セットアップ パケットを宣言します。 usb_hw.h で宣言されている WINUSB_CONTROL_SETUP_PACKET を参照してください。
  2. USB 仕様に従って転送情報を指定します。
  3. IWDFUsbTargetDevice::FormatRequestForControlTransfer メソッドを呼び出し、フレームワーク要求オブジェクトと転送バッファーを初期化されたセットアップ パケットに関連付けることで要求を作成します。
  4. IWDFIoRequest::Send メソッドを呼び出して要求を送信します。
_URB_CONTROL_DESCRIPTOR_REQUEST

URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE

URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT

URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE
SET_FEATURE: デバイス、その構成、インターフェイス、およびエンドポイントの特定の機能設定を有効にします。 USB 仕様のセクション 9.4.9 を参照してください。
  1. セットアップ パケットを宣言します。 WDF_USB_CONTROL_SETUP_PACKET 構造体を参照してください。
  2. WDF_USB_CONTROL_SETUP_PACKET_INIT_FEATURE を呼び出してセットアップ パケットを初期化します。
  3. WDF_USB_BMREQUEST_RECIPIENT に定義されている受信者値 (デバイス、インターフェイス、エンドポイント) を指定します。
  4. 機能セレクターを指定します (wValue)。 Usbspec.h の「USB_FEATURE_XXX 定数」セクションを参照してください。 USB 仕様の表 9-6 も参照してください。
  5. SetFeatureTRUE に設定します。
  6. WdfUsbTargetDeviceSendControlTransferSynchronously または WdfUsbTargetDeviceFormatRequestForControlTransfer を呼び出して要求を送信します。
  1. セットアップ パケットを宣言します。 usb_hw.h で宣言されている WINUSB_CONTROL_SETUP_PACKET を参照してください。
  2. usb_hw.h に定義されている WINUSB_CONTROL_SETUP_PACKET_INIT_FEATURE というヘルパー マクロを呼び出してセットアップ パケットを初期化します。
  3. WINUSB_BMREQUEST_RECIPIENT に定義されている受信者値を指定します。
  4. 機能セレクターを指定します (wValue)。 Usbspec.h の「USB_FEATURE_XXX 定数」セクションを参照してください。 USB 仕様の表 9-6 も参照してください。
  5. SetFeatureTRUE に設定します。
  6. IWDFUsbTargetDevice::FormatRequestForControlTransfer メソッドを呼び出し、フレームワーク要求オブジェクトと転送バッファーを初期化されたセットアップ パケットに関連付けることで要求を作成します。
  7. IWDFIoRequest::Send メソッドを呼び出して要求を送信します。
_URB_CONTROL_FEATURE_REQUEST

(UsbBuildFeatureRequest)

URB_FUNCTION_SET_FEATURE_TO_DEVICE

URB_FUNCTION_SET_FEATURE_TO_INTERFACE

URB_FUNCTION_SET_FEATURE_TO_ENDPOINT

URB_FUNCTION_SET_FEATURE_TO_OTHER
SET_INTERFACE: インターフェイスの代替設定を変更します。 USB 仕様のセクション 9.4.9 を参照してください。

詳細については、「USB インターフェイスにおける代替設定の選択方法」を参照してください。
WdfUsbTargetDeviceSelectConfig
  1. ターゲット インターフェイス オブジェクトへの WDFUSBINTERFACE ハンドルを取得します。
  2. WdfUsbInterfaceSelectSetting メソッドを呼び出します。
  1. ターゲット インターフェイス オブジェクトへの IWDFUsbInterface ポインターを取得します。
  2. IWDFUsbInterface::SelectSetting メソッドを呼び出します。
_URB_SELECT_INTERFACE

(USBD_SelectInterfaceUrbAllocateAndBuild)

URB_FUNCTION_SELECT_INTERFACE
SYNC_FRAME: エンドポイントの同期フレーム番号を設定および取得します。 USB 仕様のセクション 9.4.10 を参照してください。 この要求は USB ドライバーによって処理されます。クライアント ドライバーではこの操作を実行できません。 この要求は USB ドライバーによって処理されます。クライアント ドライバーではこの操作を実行できません。 この要求は USB ドライバーによって処理されます。クライアント ドライバーではこの操作を実行できません。
デバイス クラス固有の要求とベンダーコマンド用。
  1. セットアップ パケットを宣言します。 WDF_USB_CONTROL_SETUP_PACKET 構造体を参照してください。
  2. WDF_USB_CONTROL_SETUP_PACKET_INIT_CLASS 固有要件または WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR (ベンダー コマンド用) を呼び出し、セットアップ パケットを初期化します。
  3. WDF_USB_BMREQUEST_RECIPIENT に定義されている受信者値 (デバイス、インターフェイス、エンドポイント) を指定します。
  4. WdfUsbTargetDeviceSendControlTransferSynchronously または WdfUsbTargetDeviceFormatRequestForControlTransfer を呼び出して要求を送信します。
  1. セットアップ パケットを宣言します。 usb_hw.h で宣言されている WINUSB_CONTROL_SETUP_PACKET を参照してください。
  2. usb_hw.h に定義されている WINUSB_CONTROL_SETUP_PACKET_INIT_CLASS または WINUSB_CONTROL_SETUP_PACKET_INIT_VENDOR というヘルパー マクロを呼び出してセットアップ パケットを初期化します。
  3. クラスまたはハードウェア仕様の説明を参照し、方向 (WINUSB_BMREQUEST_DIRECTION 列挙型を参照)、受信者 (WINUSB_BMREQUEST_RECIPIENT 列挙型を参照)、要求を指定します。
  4. IWDFUsbTargetDevice::FormatRequestForControlTransfer メソッドを呼び出し、フレームワーク要求オブジェクトと転送バッファーを初期化されたセットアップ パケットに関連付けることで要求を作成します。
  5. IWDFIoRequest::Send メソッドを呼び出して要求を送信します。
  6. 転送バッファー内のデバイスから情報を受信します。 IWDFMemory メソッドを呼び出してそのバッファーにアクセスします。
_URB_CONTROL_VENDOR_OR_CLASS_REQUEST

(UsbBuildVendorRequest)

URB_FUNCTION_VENDOR_DEVICE

URB_FUNCTION_VENDOR_INTERFACE

URB_FUNCTION_VENDOR_ENDPOINT

URB_FUNCTION_VENDOR_OTHER

URB_FUNCTION_CLASS_DEVICE

URB_FUNCTION_CLASS_INTERFACE

URB_FUNCTION_CLASS_ENDPOINT

URB_FUNCTION_CLASS_OTHER

ベンダー コマンドのコントロール転送を送信する方法 - KMDF

この手順からは、クライアント ドライバーでコントロール転送を送信するしくみがわかります。 この例では、クライアント ドライバーからベンダー コマンドが送信され、そのコマンドでデバイスからファームウェア バージョンが取得されます。

  1. ベンダー コマンドの定数を宣言します。 ハードウェア仕様を調べ、使用するベンダー コマンドを決めます。

  2. WDF_MEMORY_DESCRIPTOR 構造体を宣言し、WDF_MEMORY_DESCRIPTOR_INIT_BUFFER マクロを呼び出してそれを初期化します。 この構造は、USB ドライバーで要求が完了した後、デバイスから応答を受信します。

  3. 要求の送信を同期にするのか、非同期にするのかに合わせて送信オプションを指定します。

    • WdfUsbTargetDeviceSendControlTransferSynchronously を呼び出して要求を同期で送信する場合、タイムアウト値を指定します。 タイムアウトなしでは、スレッドが無限にブロックされる可能性があるため、この値は重要です。

      そのため、WDF_REQUEST_SEND_OPTIONS 構造体を宣言し、WDF_REQUEST_SEND_OPTIONS_INIT マクロを呼び出してそれを初期化します。 オプションとして WDF_REQUEST_SEND_OPTION_TIMEOUT を指定します。

      次に、WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT マクロを呼び出し、タイムアウト値を設定します。

    • 要求を非同期で送信する場合は、完了ルーチンを実装します。 完了ルーチンで、割り当てられているすべてのリソースを解放します。

  4. セットアップ トークンを含める WDF_USB_CONTROL_SETUP_PACKET 構造体を宣言し、構造体をフォーマットします。 これを行うには、WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR マクロを呼び出し、セットアップ パケットをフォーマットします。 呼び出しで、要求の方向、受信者、要求送信オプション (手順 3 で初期化)、ベンダー コマンドの定数を指定します。

  5. WdfUsbTargetDeviceSendControlTransferSynchronously または WdfUsbTargetDeviceFormatRequestForControlTransfer を呼び出して要求を送信します。

  6. フレームワークによって返された NTSTATUS 値を確認して受信した値を調べます。

このコード例では、コントロール転送要求が USB デバイスに送信され、そのファームウェア バージョンが取得されます。 要求は同期で送信され、クライアント ドライバーにより、5 秒 (100 ナノ秒単位) という相対的タイムアウト値が指定されます。 ドライバーにより、受信した応答がドライバー定義のデバイス コンテキストに格納されます。

enum {
    USBFX2_GET_FIRMWARE_VERSION = 0x1,
....

} USBFX2_VENDOR_COMMANDS; 

#define WDF_TIMEOUT_TO_SEC              ((LONGLONG) 1 * 10 * 1000 * 1000)  // defined in wdfcore.h

const __declspec(selectany) LONGLONG
            DEFAULT_CONTROL_TRANSFER_TIMEOUT = 5 * -1 * WDF_TIMEOUT_TO_SEC; 


typedef struct _DEVICE_CONTEXT
{

    ...
       union {
        USHORT      VersionAsUshort;
        struct {
            BYTE Minor;
            BYTE Major;
        } Version;
    } Firmware; // Firmware version.

} DEVICE_CONTEXT, *PDEVICE_CONTEXT;


__drv_requiresIRQL(PASSIVE_LEVEL)
VOID  GetFirmwareVersion(
    __in PDEVICE_CONTEXT DeviceContext
)
{
    NTSTATUS                        status;
    WDF_USB_CONTROL_SETUP_PACKET    controlSetupPacket;
    WDF_REQUEST_SEND_OPTIONS        sendOptions;
    USHORT                          firmwareVersion;
    WDF_MEMORY_DESCRIPTOR           memoryDescriptor;

    PAGED_CODE();

    firmwareVersion = 0;

    WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, (PVOID) &firmwareVersion, sizeof(firmwareVersion));

    WDF_REQUEST_SEND_OPTIONS_INIT(
                                  &sendOptions,
                                  WDF_REQUEST_SEND_OPTION_TIMEOUT
                                  );

    WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(
                                         &sendOptions,
                                         DEFAULT_CONTROL_TRANSFER_TIMEOUT
                                         );

    WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(&controlSetupPacket,
                                        BmRequestDeviceToHost,       // Direction of the request
                                        BmRequestToDevice,           // Recipient
                                        USBFX2_GET_FIRMWARE_VERSION, // Vendor command
                                        0,                           // Value
                                        0);                          // Index 

    status = WdfUsbTargetDeviceSendControlTransferSynchronously(
                                        DeviceContext->UsbDevice,
                                        WDF_NO_HANDLE,               // Optional WDFREQUEST
                                        &sendOptions,
                                        &controlSetupPacket,
                                        &memoryDescriptor,           // MemoryDescriptor
                                        NULL);                       // BytesTransferred 

    if (!NT_SUCCESS(status)) 
    {
        KdPrint(("Device %d: Failed to get device firmware version 0x%x\n", DeviceContext->DeviceNumber, status));
        TraceEvents(DeviceContext->DebugLog,
                    TRACE_LEVEL_ERROR,
                    DBG_RUN,
                    "Device %d: Failed to get device firmware version 0x%x\n",
                    DeviceContext->DeviceNumber,
                    status);
    }
    else 
    {
        DeviceContext->Firmware.VersionAsUshort = firmwareVersion;
        TraceEvents(DeviceContext->DebugLog,
                    TRACE_LEVEL_INFORMATION,
                    DBG_RUN,
                    "Device %d: Get device firmware version : 0x%x\n",
                    DeviceContext->DeviceNumber,
                    firmwareVersion);
    }

    return;
}

GET_STATUS のコントロール転送を送信する方法 - UMDF

この手順からは、GET_STATUS コマンドの場合にクライアント ドライバーでコントロール転送を送信するしくみがわかります。 要求の受信者はデバイスであり、要求によりビット D1-D0 の情報が取得されます。 詳細については、USB 仕様の図 9-4 を参照してください。

  1. UMDF Sample Driver for OSR USB Fx2 Learning Kit に付属する Usb_hw.h というヘッダー ファイルを含めます。

  2. WINUSB_CONTROL_SETUP_PACKET 構造体を宣言します。

  3. WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS というヘルパー マクロを呼び出してセットアップ パケットを初期化します。

  4. 受信者として BmRequestToDevice を指定します。

  5. Index 値に 0 を指定します。

  6. ヘルパー メソッド SendControlTransferSynchronously を呼び出し、要求を同期で送信します。

    ヘルパー メソッドが IWDFUsbTargetDevice::FormatRequestForControlTransfer メソッドを呼び出し、フレームワーク要求オブジェクトと転送バッファーを初期化されたセットアップ パケットに関連付けることで要求が作成されます。 次に、ヘルパー メソッドが IWDFIoRequest::Send メソッドを呼び出して要求が送信されます。 メソッドが戻ったら、返された値を調べます。

  7. ステータスにより自己供給のリモート ウェイクアップが示されるかどうかを判断するには、WINUSB_DEVICE_TRAITS 列挙型に定義されている次の値を使用します。

このコード例では、デバイスのステータスを取得する目的でコントロール転送要求が送信されます。 この例では、SendControlTransferSynchronously という名前のヘルパー メソッドを呼び出すことで要求が同期で送信されます。

HRESULT
CDevice::GetDeviceStatus ()
{

    HRESULT hr = S_OK;

    USHORT deviceStatus;
    ULONG bytesTransferred;

    TraceEvents(TRACE_LEVEL_INFORMATION,
                DRIVER_ALL_INFO,
                "%!FUNC!: entry");

    // Setup the control packet.

    WINUSB_CONTROL_SETUP_PACKET setupPacket;

    WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS(
                                      &setupPacket,
                                      BmRequestToDevice,
                                      0);

    hr = SendControlTransferSynchronously(
                 &(setupPacket.WinUsb),
                 & deviceStatus,
                 sizeof(USHORT),
                 &bytesReturned
                );

     if (SUCCEEDED(hr))
    {
        if (deviceStatus & USB_GETSTATUS_SELF_POWERED)
        {
             m_Self_Powered = true;
        } 
        if (deviceStatus & USB_GETSTATUS_REMOTE_WAKEUP_ENABLED)
        {
             m_remote_wake-enabled = true;
        }
    }

    return hr;
 }

次のコード例では、SendControlTransferSynchronously という名前のヘルパー メソッドの実装を確認できます。 このメソッドでは、要求が同期で送信されます。

HRESULT
CDevice::SendControlTransferSynchronously(
    _In_ PWINUSB_SETUP_PACKET SetupPacket,
    _Inout_ PBYTE Buffer,
    _In_ ULONG BufferLength,
    _Out_ PULONG LengthTransferred
    )
{
    HRESULT hr = S_OK;
    IWDFIoRequest *pWdfRequest = NULL;
    IWDFDriver * FxDriver = NULL;
    IWDFMemory * FxMemory = NULL;
    IWDFRequestCompletionParams * FxComplParams = NULL;
    IWDFUsbRequestCompletionParams * FxUsbComplParams = NULL;

    *LengthTransferred = 0;

    hr = m_FxDevice->CreateRequest( NULL, //pCallbackInterface
                                    NULL, //pParentObject
                                    &pWdfRequest);

    if (SUCCEEDED(hr))
    {
        m_FxDevice->GetDriver(&FxDriver);

        hr = FxDriver->CreatePreallocatedWdfMemory( Buffer,
                                                    BufferLength,
                                                    NULL,        //pCallbackInterface
                                                    pWdfRequest, //pParetObject
                                                    &FxMemory );
    }

    if (SUCCEEDED(hr))
    {
        hr = m_pIUsbTargetDevice->FormatRequestForControlTransfer( pWdfRequest,
                                                                   SetupPacket,
                                                                   FxMemory,
                                                                   NULL); //TransferOffset
    }

    if (SUCCEEDED(hr))
    {
        hr = pWdfRequest->Send( m_pIUsbTargetDevice,
                                WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
                                0); //Timeout
    }

    if (SUCCEEDED(hr))
    {
        pWdfRequest->GetCompletionParams(&FxComplParams);

        hr = FxComplParams->GetCompletionStatus();
    }

    if (SUCCEEDED(hr))
    {
        HRESULT hrQI = FxComplParams->QueryInterface(IID_PPV_ARGS(&FxUsbComplParams));
        WUDF_TEST_DRIVER_ASSERT(SUCCEEDED(hrQI));

        WUDF_TEST_DRIVER_ASSERT( WdfUsbRequestTypeDeviceControlTransfer ==
                            FxUsbComplParams->GetCompletedUsbRequestType() );

        FxUsbComplParams->GetDeviceControlTransferParameters( NULL,
                                                             LengthTransferred,
                                                             NULL,
                                                             NULL );
    }

    SAFE_RELEASE(FxUsbComplParams);
    SAFE_RELEASE(FxComplParams);
    SAFE_RELEASE(FxMemory);

    pWdfRequest->DeleteWdfObject(); 
    SAFE_RELEASE(pWdfRequest);

    SAFE_RELEASE(FxDriver);

    return hr;
}

デバイスの関数ドライバーとして Winusb.sys を使用している場合、アプリケーションからコントロール転送を送信できます。 WinUSB でセットアップ パケットをフォーマットするには、この記事の表で説明されている UMDF ヘルパー マクロと構造を使用します。 要求を送信するには、WinUsb_ControlTransfer 関数を呼び出します。