HIDレポートディスクリプタのデバッグ:デバイスは列挙できるのにホストが読み違える原因
USBデバイスが正常に列挙されてもホスト側で誤動作するHIDレポートディスクリプタの不備を診断する方法を解説します。
HIDの魅力は、なんといっても「独自ドライバなしでも動く」点ですよね。キーボード、センサ、ノブ、バーコードリーダ、操作パネル、ベンダー固有のHID機器など、いずれも標準ホストスタックの恩恵を受けられます。その一方で、HIDは複雑さをレポートディスクリプタ側に押し込みます。そのため、デバイスは正常に列挙されたのに、ホスト側では誤ったデータとして解釈される、というケースが頻発します。
これはUSBファームウェアで最も多い落とし穴のひとつで、「列挙に成功=HIDも正しい」と誤解されてしまうパターンです。
レポートディスクリプタは「データ契約」を定義する
HIDレポートディスクリプタは、ホストに対して「バイト列をどう解釈するか」を伝えます。Usage、Report Size、Report Count、Logical Minimum/Maximum、Physical Minimum/Maximum、Collection、Report IDなどを定義します。ディスクリプタがAと言っているのにファームウェアがBを送ってきた場合、ホストは必ずディスクリプタ側を信頼します。
よくある不具合には、次のようなものがあります。
- ファームウェアは8バイト送っているが、ディスクリプタは7バイトと記述している
- Report IDが抜けている/余分に入っている
- 符号付き値を符号なしとして記述している
- Logical Min/Maxが実際のレンジと合っていない
- Usage Pageを間違えている
- パディングビットのカウントがズレている
- 複数レポートのレイアウトが分かりにくい
- InputレポートとOutputレポートを取り違えている
症状としては、アプリケーション側で「値がおかしい」「ボタンが反応しない」「レポートが無視される」「不定期に欠落する」といった形で現れます。
ディスクリプタとレポートを同時にキャプチャする
HIDのデバッグを、レポートディスクリプタだけでは不十分ですし、ペイロードの生バイトだけでも不十分です。両方が揃って初めて議論できます。
有用なHIDキャプチャには、次の情報が含まれます。
- デバイスディスクリプタ
- コンフィギュレーション/インターフェースディスクリプタ
- HIDディスクリプタ
- レポートディスクリプタのリクエストと応答
- Interrupt IN レポート
- (必要に応じて)Interrupt OUT レポート
- フィーチャーレポート用のコントロール転送
- Report IDとペイロード長
こうして初めて、宣言されたレイアウトと実際のバイト列を比較できます。ディスクリプタで Report Count 3 と言っているのに、Interruptのペイロードが4つの値を載せているなら、キャプチャ上でその不一致が一目で分かるべきです。
「ホスト側が正しく見えるのに間違って見える」こともある
ファームウェア開発者の方が「ホストがデータを捨てている」と感じるとき、実際には「ホストがディスクリプタに従ってパースしているだけ」というケースがよくあります。ディスクリプタがパディングや異なるReport IDを宣言していれば、データの位置がズレたり、切り詰められたり、無視されたりして見えます。
だからこそ、サポートレポートには「生バイト」を残しておくべきです。デコード済みの解釈も便利ですが、議論を最終的に決めるのは生バイトです。突き詰めるべき問いは次の4つに集約されます。
- ファームウェアは何を送ったか?
- ファームウェアは何を宣言したか?
- ホストは何を要求したか?
- ホストは何を受け取ったか?
この4点が、HIDデバッグの正しい境界線です。
複合HIDデバイスは特に慎重に
複合デバイスでは、HIDとCDC、ストレージ、ベンダー固有インターフェースが同居することがあります。HID部分単体としては正しくても、インタフェース番号やエンドポイント割り当て、ディスクリプタ全体の長さエラーなどの影響を受けて、不具合が出るケースがあります。
複合HIDをデバッグするときは、次の点を確認しましょう。
- 該当する場合はInterface Association
- インターフェース番号
- エンドポイントアドレスの一意性
- HIDディスクリプタの位置
- レポートディスクリプタの長さ
- クラス固有リクエストのルーティング
ホストがレポートディスクリプタを誤ったインターフェースから取得したり、長さを間違って受け取ったりすると、そのあとのレポートトラフィック全体が誤解を招くものになります。
Bus Scopeが活きる場面
Bus Scopeは、証拠に基づいてUSBをデバッグしたいファームウェア/デバイスチームのために設計されています。HIDレポートディスクリプタのケースでは、ディスクリプタツリー・生バイト・エンドポイントトラフィック・保存済み.bscopeセッションを、同じ場所でまとめて確認できるように作られています。
最終的に残すべきレポートは、次のような構造になっているはずです。
- HIDレポートディスクリプタが要求され、返されたこと
- ディスクリプタが宣言するレポート長
- 実際のInterruptペイロード長
- Report IDの挙動
- 宣言とトラフィックの一致/不一致
- ファームウェアのディスクリプタ、レポートパッキング、ホストパーサ側の想定に対する次のアクション
これは「HIDデバイスが動かない」という漠然とした課題ではなく、USBの契約(contract)の不一致という具体的な問題に変換します。