USBベンダー固有コントロールリクエストのタイムアウト:ファームウェアコマンド、bmRequestType、エンドポイントゼロのデバッグ
USBベンダー固有コントロールリクエストのタイムアウト、bmRequestType、bRequest、wValue、wIndex、エンドポイントゼロのファームウェア処理、ブートローダーコマンド、デバイス状態をデバッグする方法を解説します。
ベンダー固有のUSBコントロールリクエストは、ファームウェアツール、キャブレーションユーティリティ、工場テストソフト、ブートローダー、デバッグモード、独自デバイスで広く使われています。これが失敗すると、アプリは「control transfer timeout」「vendor request failed」「device not responding」「LIBUSB_ERROR_TIMEOUT」としか報告しないことが多いです。失敗がOSには説明できないプライベートプロトコルの中にあるため、USB vendor request timeout、bmRequestType debugging、control transfer endpoint zero timeout、vendor-specific USB command failed で検索することになります。
Bus Scopeが効くのは、ベンダー固有コントロールリクエストでも「標準のSetupパケット」は付いているからです。コマンドの意味がプライベートでも、転送構造は可視です。
Setupパケットのフィールド
コントロールリクエストには以下が含まれます。
bmRequestTypebRequestwValuewIndexwLength
ベンダーリクエストでは、bmRequestType がベンダータイプと方向を示します。bRequest、wValue、wIndex はデバイスファームウェアが定義します。
方向や長が誤っていると、デバイスはストール/タイムアウトします。
タイムアウト vs STALL
STALLは「デバイスが明示的に拒否した」という意味。タイムアウトは「時間内に完了応答が来なかった」という意味。
タイムアウトが意味しうるもの:
- ファームウェアがコマンド処理でハング
- リクエスト中にデバイスリセット
- 方向の不一致
- ホストはデータを期待したがデバイスから来ない
- デバイスはOUTデータを期待したがホストがINを要求
- コマンドが別状態でだけ有効
- フラッシュ消去やセンサ処理が時間超過
トレースは、データステージがあったか、デバイスがその後消えたかを示してくれます。
ブートローダーとファームウェア更新コマンド
ベンダーリクエストは、ブートローダー入り、フラッシュ消去、ファームウェア書き込み、リセット、ステータスポーリングをトリガすることが多いです。これらのコマンドは時間がかかっても不思議ありませんが、ホストのタイムアウトは期待挙動に合う必要があります。
毎回タイムアウト後に再接続するなら、デバイスは実はリセットに成功している可能性。タイムアウト後に再列挙されないなら、ファームウェアが固まっているかもしれません。
デバッグチェックリスト
次の流れで作業します。
- ベンダーコマンド送信前にキャプチャする
- Setupパケットのフィールドをデコードする
- 方向が期待されるデータステージと一致しているかを確認する
wLengthを確認する- データステージのバイトを確認する
- STALL/タイムアウト/リセット/切断を探す
- デバイスが別モードで再列挙するかを確認する
- 既知の正常なツールとコマンドシーケンスを比較する
- コマンドが正当に長時間を要することを証明してからタイムアウトを延ばす
- 失敗前後のベンダーリクエストシーケンスを保全する
最終的な診断
ベンダー固有コントロールリクエストのタイムアウトはプライベートプロトコルの失敗ですが、USBの証拠は引き続き可視です。Setupパケット/方向/長さ/タイミング/リセット挙動/エンドポイントゼロ応答を見れば、ホストリクエストの形かデバイスファームウェアの状態か、どちらが犯人かが分かります。
Bus Scopeは、プライベートなファームウェアコマンド失敗を「観察可能なUSBの証拠」に変換する手伝いをします。