トレーニング
ラーニング パス
Use advance techniques in canvas apps to perform custom updates and optimization - Training
Use advance techniques in canvas apps to perform custom updates and optimization
このブラウザーはサポートされなくなりました。
Microsoft Edge にアップグレードすると、最新の機能、セキュリティ更新プログラム、およびテクニカル サポートを利用できます。
この記事では、コントロール転送の構造について説明し、クライアント ドライバーがデバイスに制御要求を送信する方法を示しています。
すべての USB デバイスで、既定のエンドポイントと呼ばれているエンドポイントを少なくとも 1 つサポートする必要があります。 既定のエンドポイントを対象とする転送は、コントロール転送と呼ばれています。 コントロール転送の目的は、ホストがデバイス情報を取得したり、デバイスを構成したり、デバイスに固有のコントロール操作を実行したりできるようにすることです。
まず、既定のエンドポイントの特性について調べることから始めます。
コントロール転送は優先度の高い転送であるため、一定量の帯域幅がホストによってバスに予約されます。 Low-Speed 転送と全速転送のデバイスの場合、帯域幅の 10% が、高速転送と SuperSpeed 転送のデバイスの場合、20% が予約されます。 次に、コントロール転送のレイアウトを見てみましょう。
コントロール転送は、"セットアップ トランザクション"、"データ トランザクション"、"ステータス トランザクション" の 3 つのトランザクションに分けられます。 各トランザクションには、"トークン パケット"、"データ パケット"、"ハンドシェイク パケット" という 3 種類のパケットが含まれています。
一部のフィールドはすべてのパケットに共通しています。 これらのフィールドを次に示します。
その他のフィールドは、パケットの種類によって異なります。
すべてのセットアップ トランザクションは、トークン パケットから始まります。 パケットの構造を次に示します。 ホストは常にトークン パケットを送信します。
PID 値は、トークン パケットの種類を示します。 以下に使用可能な値を示します。
トークン パケットの直後に続くのが、ペイロードを含むデータ パケットです。 各データ パケットで格納できるバイト数は、既定のエンドポイントの最大パケット サイズによって異なります。 データ パケットは、転送の方向に応じてホストまたはデバイスによって送信されます。
データ パケットの直後に続くのがハンドシェイク パケットです。 パケットの PID は、パケットがホストによって受信されたのか、デバイスによって受信されたのかを示します。 ハンドシェイク パケットは、転送の方向に応じてホストまたはデバイスによって送信されます。
トランザクションとパケットの構造は、Beagle、Ellisys、LeCroy USB プロトコル アナライザーなど、あらゆる USB アナライザーを使用して確認できます。 アナライザー デバイスには、通信回線経由で USB デバイスとの間でデータを送受信する方法が示されます。 この例では、LeCroy USB アナライザーによってキャプチャされたトレースをいくつか確認してみましょう。 この例は、情報提供目的のみで用意されています。 Microsoft がこの製品を推薦するものではありません。
ホストから常にコントロール転送が開始されます。 これはセットアップ トランザクションを送信することによって行われます。 このトランザクションには "セットアップ トークン" と呼ばれているトークン パケットが含まれ、その後に 8 バイトのデータ パケットが続きます。 このスクリーンショットでは、セットアップ トランザクションの例を示しています。
前のトレースでは、セットアップ トークン パケット #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 パケット) であることにご注意ください。 これは、デバイスがトランザクションを受信確認したことを示します。
次に、要求に応答してデバイスから返される内容を見てみましょう。 実際のデータは、データ トランザクションで転送されます。
データ トランザクションのトレースは次のようになります。
ACK パケットを受信すると、ホストではデータ トランザクションが開始されます。 トランザクションを開始するために、IN 方向でトークン パケット (#450) が送信されます (IN トークンと呼ばれています)。
応答の中で、IN トークンに続くデータ パケット (#451) がデバイスから送信されます。 このデータ パケットには、実際のデバイス記述子が含まれています。 最初のバイトはデバイス記述子の長さ、18 バイト (0x12) を示します。 このデータ パケットの最後のバイトは、既定のエンドポイントでサポートされる最大パケット サイズを示します。 この例の場合、デバイスが既定のエンドポイントを使用して一度に 8 バイトを送信できることがわかります。
注意
既定のエンドポイントの最大パケット サイズは、デバイスの速度によって異なります。 高速デバイスの既定のエンドポイントは 64 バイトです。Low-Speed デバイスの場合は 8 バイトです。
ホストでは ACK パケット (#452) をデバイスに送信することでデータ トランザクションを受信確認します。
返されるデータの量を計算しましょう。 セットアップ トランザクションのデータ パケット (#435) の wLength フィールドで、ホストから 18 バイトが要求されました。 データ トランザクションで、デバイス記述子の最初の 8 バイトだけがデバイスから受信されたことがわかります。 それでは、残りの 10 バイトに保存されている情報をホストはどのように受信するのでしょうか。 デバイスは、8 バイトと最後の 2 バイトの 2 つのトランザクションで行われます。
これで、既定のエンドポイントの最大パケット サイズがホストに認識されたので、ホストでは、新しいデータ トランザクションを開始し、パケット サイズに基づいて次の部分を要求します。
次のデータ トランザクションはこのようになります。
ホストでは、IN トークン (#463) を送信し、デバイスに次の 8 バイトを要求することで前述のデータ トランザクションを開始します。 デバイスは、デバイス記述子の次の 8 バイトを含むデータパケット (#464) で応答します。
8 バイトが受信されると、ホストからデバイスに ACK パケット (#465) が送信されます。
次に、ホストでは、次のように別のデータ トランザクションで最後の 2 バイトを要求します。
結果的に、デバイスからホストに 18 バイトを転送するために、ホストでは転送されたバイト数を追跡記録し、3 回のデータ トランザクション (8+8+2) を開始したことがわかります。
注意
データ トランザクション 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 つの構成がサポートされます。
最後に、ホストでは、最後のトランザクションであるステータス トランザクションを開始し、コントロール転送を完了とします。
ホストは、OUT トークン パケット (#481) でトランザクションを開始します。 このパケットの目的は、要求されたすべてのデータをデバイスが送信したことを確認することです。 このステータス トランザクションで送信されたデータ パケットはありません。 デバイスは ACK パケットで応答します。 エラーが発生した場合、PID は NAK か STALL になります。
クライアント ドライバーでパイプを列挙するには、次の要件が満たされている必要があります。
クライアント ドライバーによって、フレームワーク USB ターゲット デバイス オブジェクトが作成されている必要があります。
Microsoft Visual Studio Professional 2012 に付属する USB テンプレートを使用している場合、テンプレート コードでこれらのタスクが実行されます。 テンプレート コードによりターゲット デバイス オブジェクトのハンドルが取得され、デバイス コンテキストに格納されます。
KMDF クライアント ドライバーでは、WdfUsbTargetDeviceCreateWithParameters メソッドを呼び出すことで WDFUSBDEVICE ハンドルを取得する必要があります。 詳細については、「USB クライアント ドライバー コード構造について (KMDF)」の「デバイスのソース コード」を参照してください。
UMDF クライアント ドライバーは、フレームワーク ターゲット デバイス オブジェクトを問い合わせることで、IWDFUsbTargetDevice ポインターを取得する必要があります。 詳細については、「USB クライアント ドライバー コード構造について (UMDF)」の「IPnpCallbackHardware 実装と USB 固有のタスク」を参照してください。
コントロール転送の最も重要な側面は、セットアップ トークンを正しくフォーマットすることです。 要求を送信する前に、次の一連の情報を収集します。
この情報はすべて、公式の 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 ドライバー スタックに転送します。
ホスト上の USB クライアント ドライバーでは、デバイスに関する情報を取得するか、デバイスを構成するか、ベンダー コントロール コマンドを送信する目的でほとんどの制御要求を開始します。 このような要求はすべて、次に分類できます。
標準要求は、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 を参照してください。 |
|
|
_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 では、既定で最初の構成が選択されます。 デバイス定義の構成番号を取得するには:
|
UMDF では、既定で最初の構成が選択されます。 デバイス定義の構成番号を取得するには:
|
_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 を参照してください。 |
|
|
_URB_CONTROL_GET_INTERFACE_REQUEST URB_FUNCTION_GET_INTERFACE |
GET_STATUS: デバイス、エンドポイント、またはインターフェイスからステータス ビットを取得します。 USB 仕様のセクション 9.4.5 を ご覧ください。 |
|
|
_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 ドライバー スタックは、クライアント ドライバーからこのような要求を受け取ります。 |
|
|
_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 を参照してください。 |
|
|
_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
|
|
_URB_SELECT_INTERFACE (USBD_SelectInterfaceUrbAllocateAndBuild) URB_FUNCTION_SELECT_INTERFACE |
SYNC_FRAME: エンドポイントの同期フレーム番号を設定および取得します。 USB 仕様のセクション 9.4.10 を参照してください。 | この要求は USB ドライバーによって処理されます。クライアント ドライバーではこの操作を実行できません。 | この要求は USB ドライバーによって処理されます。クライアント ドライバーではこの操作を実行できません。 | この要求は USB ドライバーによって処理されます。クライアント ドライバーではこの操作を実行できません。 |
デバイス クラス固有の要求とベンダーコマンド用。 |
|
|
_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 |
この手順からは、クライアント ドライバーでコントロール転送を送信するしくみがわかります。 この例では、クライアント ドライバーからベンダー コマンドが送信され、そのコマンドでデバイスからファームウェア バージョンが取得されます。
ベンダー コマンドの定数を宣言します。 ハードウェア仕様を調べ、使用するベンダー コマンドを決めます。
WDF_MEMORY_DESCRIPTOR 構造体を宣言し、WDF_MEMORY_DESCRIPTOR_INIT_BUFFER マクロを呼び出してそれを初期化します。 この構造は、USB ドライバーで要求が完了した後、デバイスから応答を受信します。
要求の送信を同期にするのか、非同期にするのかに合わせて送信オプションを指定します。
WdfUsbTargetDeviceSendControlTransferSynchronously を呼び出して要求を同期で送信する場合、タイムアウト値を指定します。 タイムアウトなしでは、スレッドが無限にブロックされる可能性があるため、この値は重要です。
そのため、WDF_REQUEST_SEND_OPTIONS 構造体を宣言し、WDF_REQUEST_SEND_OPTIONS_INIT マクロを呼び出してそれを初期化します。 オプションとして WDF_REQUEST_SEND_OPTION_TIMEOUT を指定します。
次に、WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT マクロを呼び出し、タイムアウト値を設定します。
要求を非同期で送信する場合は、完了ルーチンを実装します。 完了ルーチンで、割り当てられているすべてのリソースを解放します。
セットアップ トークンを含める WDF_USB_CONTROL_SETUP_PACKET 構造体を宣言し、構造体をフォーマットします。 これを行うには、WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR マクロを呼び出し、セットアップ パケットをフォーマットします。 呼び出しで、要求の方向、受信者、要求送信オプション (手順 3 で初期化)、ベンダー コマンドの定数を指定します。
WdfUsbTargetDeviceSendControlTransferSynchronously または WdfUsbTargetDeviceFormatRequestForControlTransfer を呼び出して要求を送信します。
フレームワークによって返された 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 コマンドの場合にクライアント ドライバーでコントロール転送を送信するしくみがわかります。 要求の受信者はデバイスであり、要求によりビット D1-D0 の情報が取得されます。 詳細については、USB 仕様の図 9-4 を参照してください。
UMDF Sample Driver for OSR USB Fx2 Learning Kit に付属する Usb_hw.h というヘッダー ファイルを含めます。
WINUSB_CONTROL_SETUP_PACKET 構造体を宣言します。
WINUSB_CONTROL_SETUP_PACKET_INIT_GET_STATUS というヘルパー マクロを呼び出してセットアップ パケットを初期化します。
受信者として BmRequestToDevice を指定します。
Index 値に 0 を指定します。
ヘルパー メソッド SendControlTransferSynchronously を呼び出し、要求を同期で送信します。
ヘルパー メソッドが IWDFUsbTargetDevice::FormatRequestForControlTransfer メソッドを呼び出し、フレームワーク要求オブジェクトと転送バッファーを初期化されたセットアップ パケットに関連付けることで要求が作成されます。 次に、ヘルパー メソッドが IWDFIoRequest::Send メソッドを呼び出して要求が送信されます。 メソッドが戻ったら、返された値を調べます。
ステータスにより自己供給のリモート ウェイクアップが示されるかどうかを判断するには、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 関数を呼び出します。
トレーニング
ラーニング パス
Use advance techniques in canvas apps to perform custom updates and optimization - Training
Use advance techniques in canvas apps to perform custom updates and optimization