USBコントロール転送とSetupパケットのデバッグ:bmRequestType、bRequest、wValue、wIndexの読み方
USB STALLエラーとコントロール転送の失敗を修正。bmRequestType、bRequest、wValue、wIndex、ファームウェアデバッグ用ディスクリプタリクエストの証拠を使って、Setupパケットの問題を診断します。
USBコントロール転送は、ホストとデバイスとの「最初の真剣な会話」です。列挙はこれに依存し、クラスセットアップもこれに依存し、ベンダー固有の初期化も多くの場合これに依存します。コントロール転送が失敗したとき、ユーザーの目には「デバイスが認識されない」「ドライバが失敗した」としか映りませんが、証拠は大抵Setupパケットの中にあります。
ファームウェアエンジニアにとって、bmRequestType、bRequest、wValue、wIndex、wLength を読めるようになるのは、推測から「ピンポイントの修正」へ進む最短ルートです。
Setupパケットはリクエストの「契約」
USB Setupパケットはデバイスに以下を伝えます。
- 転送の方向
- リクエスト種別:standard、class、vendor、reserved
- 受信者:device、interface、endpoint、other
- リクエストコード
- valueフィールド
- indexフィールド
- 期待されるデータ長
ファームウェアがこれらのフィールドを誤って解釈すると、誤ったディスクリプタを返したり、正しいリクエストをストールさせたり、無効なコマンドを受け付けたりします。ホストが想定外のリクエストを送ってきた場合も、キャプチャにはそれが映ります。
GET_DESCRIPTORが最初のチェックポイント
列挙中、ホストは標準ディスクリプタリクエストを送ります。典型的なシーケンスは次のとおりです。
- デバイスディスクリプタのリクエスト
- コンフィギュレーションディスクリプタのリクエスト
- 文字列ディスクリプタのリクエスト
- HIDデバイスの場合HIDレポートディスクリプタのリクエスト
- 新しいホストではBOSディスクリプタのリクエスト
Setupパケット内では、bRequest が GET_DESCRIPTOR を示し、wValue にディスクリプタ種別とインデックスが入ります。wIndex は文字列ディスクリプタの言語IDや、クラス固有ディスクリプタのインターフェース番号を示すことがあります。wLength はホストが期待するバイト数です。
ディスクリプタ応答の長さが間違っている、あるいはファームウェアがホストの必要バイト数より少なく返した場合、列挙は後段で「まったく別件」のように見える失敗をすることがあります。
方向の取り違えは高くつく
コントロール転送には方向があります。Device-to-Hostリクエストはデータを返し、Host-to-Deviceリクエストはデータを運ぶか状態を構成します。ファームウェアが読み取りリクエストを書き込みとして扱ったり、書き込みリクエスト中にデータを送信したりすると、ホスト側は忖度してくれません。
注目ポイント:
- IN方向だがデータステージがない
- OUT方向なのにファームウェアがデータ送信を待っている
- Zero-Length Status Stageが抜けている
- 妥当な標準リクエストをSTALLしている
- クラスリクエストを誤ったインターフェースで処理している
キャプチャには、リクエスト・データステージ・ステータスステージが並びます。
クラス/ベンダーリクエストにはインターフェース文脈が必要
列挙後、クラスドライバがクラス固有リクエストを送ります。CDCならラインコーディング、HIDならレポートディスクリプタやフィーチャーレポート、ベンダーツールなら初期化コマンド。同じ bRequest 値でも、リクエスト種別と受信者によって意味が変わります。
確認ポイント:
- リクエスト種別
- 受信者
wIndex内のインターフェース番号- 受信者がエンドポイントの場合はエンドポイント番号
- ペイロードバイト
- 応答またはSTALL
複合デバイスで複数のインターフェースがある場合、誤ったインターフェースにリクエストをルーティングするのは定番バグです。
Bus Scopeがどう役立つか
Bus Scopeは「USBの証拠」のために作られています。コントロール転送のデバッグには、デコード済みのセットアップフィールドと生バイトが両方必要です。ファームウェアエンジニアが意味的なフィールドを読みながら、正確なパケットバイトを検証できるビューが理想です。
コントロール転送デバッグ向けの良いBus Scopeセッションは、次の問いに答えてくれます。
- どのSetupパケットが失敗したか?
- standard、class、vendorのどれか?
- どのディスクリプタまたはインターフェースが要求されたか?
- デバイスは期待された長さを返したか?
- ファームウェアのSTALLは意図的か、不適切か?
- 次の列挙ステップはこの応答に依存していたか?
問題は「USBコントロール転送失敗」と書かれますが、修正点は5フィールドのSetupパケットの中にあります。