USBが認識されないとき
USB機器を組み込み機器側で実装したのに、ホスト側のPCに認識されないという場面はよくあります。主な原因は以下の3つに潜んでいることが多い傾向です。
- デバイス側の実装
- ホスト側のドライバ
- 物理層
実際に認識されなかった場合に、それぞれについて闇雲に手を入れても解決へは遠回りになってしまうでしょう。
そこでこの記事では、USBが認識されない原因となり得る上記3つの項目について、チェックすべきポイントを解説します。
- USB列挙がどこで止まっているかを特定する
- デバイス側で最初に疑う実装(記述子・構成・エンドポイント)
- ホスト(PC)側で切り分ける観点(OS・ドライバ・ポート)
- 物理・電気・ケーブルまわり(見落としがちな層)
- デバッグが難しいときの追加観点
USB列挙がどこで止まっているかを特定する
- USBデバイスの接続を検知
- 通信速度(Low/Full/Hight)を特定
- デバイス記述子(Device)を取得
- デバイスをリセットしアドレスを割り当て
- 構成記述子とインタフェース記述子の取得
- 構成を設定(SET_CONFIGURATION)してデバイスを有効化
- 適切なドライバが読み込まれデバイスが利用可能に
最初に確認したいのは、列挙のプロセスのうちどの段階で問題が起きているか特定することです。USBの列挙シーケンスは、接続検知、速度の特定、デバイス記述子の取得、デバイスのリセットとアドレスの割当、構成記述子とインタフェース記述子の取得、そしてUSBドライバのロードという流れで進みます。この一連の段階のどこで止まったかを把握できれば、原因はかなり絞り込めるでしょう。
ホスト側がデバイスの接続自体は検知できているのに記述子取得で失敗しているなら、デバイス側の応答に原因が潜んでいる可能性があります。アドレス割り当てまでは成功するのに構成設定の段階で躓いているなら、構成記述子の中身に矛盾がある可能性が高いと考えられます。Windows環境ではEvent Tracing for Windowsを活用すると、ホスト側の各段階のイベントが拾えるため、どの段階で止まったかを見極める助けになるでしょう。
デバイス側で最初に疑う実装(記述子・構成・エンドポイント)
列挙の早い段階で躓いている場合、まずデバイス側の実装が疑いの対象です。ここではCDC ACMとHIDという2つの代表的なクラスを念頭に置きつつ、記述子の整合性、転送の組み方、Full Speedの規格制約という3つの観点から順に見ていきましょう。CDCとHIDは前提となる転送タイプもインタフェース構成も異なるため、双方を同じ枠組みで書くと記述子レベルで矛盾を抱えやすくなります。
デバイス/構成/インタフェース記述子の整合
最も基本となるのが、デバイス記述子と構成記述子の整合確認です。VID(ベンダーID)やPID(製品ID)が未設定のままだと、ホストOSはどの製品が接続されたか認識できないかもしれません。この結果、列挙の失敗につながる可能性があります。
次に見ておきたいのは、構成記述子のbNumInterfacesと、実際に配置したインタフェース記述子の数の対応関係です。宣言と実体がずれていれば、構成設定の段階で失敗する可能性が高まります。
クラスコードも要注意で、CDC ACMの通信インタフェースならbInterfaceClassを0x02、HIDなら0x03に設定するのが基本です。文字列インデックスを無効値のままにしておくと、ホスト側の表示が不正になるだけでなく、ドライバ判定に影響するケースもあるため、ここも確認しておきたい項目でしょう。
CDCとHIDで違う転送の組み方
CDC ACMは、通信インタフェース(クラス0x02)とデータインタフェース(クラス0x0A)の二つで構成されます。通信側に通知用の割り込みINエンドポイントを1個、データ側にバルクINとバルクOUTを各1個という構成が定番です。Windowsではこの構成を満たすと、互換ID USB\Class_02&SubClass_02 に対してUsbser.sysがバインドされ、仮想COMポートとして列挙されます。
一方のHIDは単一インタフェースで、制御転送と割り込み転送だけで完結する構造です。レポート記述子の内容によってマウスやキーボード、あるいはカスタムデバイスとしてホストに見え方が変わるという特徴があります。CDCのつもりでHIDの構成を書いてしまえば、当然ながらどちらとも認識されません。
エンドポイント数・最大パケットサイズ・転送間隔の見直し
Full Speed USBにはエンドポイント仕様に明確な制約があります。制御転送のEP0は最大パケットサイズが8、16、32、64バイトのいずれかで、Full Speedでは64バイトを選ぶのが一般的でしょう。バルク転送のwMaxPacketSizeも8、16、32、64バイトに限られ、128バイトを宣言してしまうと列挙の途中で弾かれます。
割り込み転送のbIntervalはFull Speedで1~255までの値を取り、単位はミリ秒です。記述子の数値はすべて、ここで挙げた範囲に収まっているかを上から検算しておきたいところです。
特に組み込み機器では、対向するホスト側との相性で接続できない、といったことが起きがちです。テストを行う際にも、ある程度の種類のホストを用意して試すことが必要になるでしょう。
ホスト(PC)側で切り分ける観点(OS・ドライバ・ポート)
デバイス側の記述子に矛盾がなさそうなら、次はホスト側に目を向けましょう。Windowsの場合、デバイスマネージャに不明なデバイスとして表示されているか、黄色の感嘆符が付いているかが切り分けの最初のヒントになります。
互換IDが想定どおりに生成されていないと、目当てのクラスドライバが読み込まれません。CDC ACMならUSB\Class_02&SubClass_02でUsbser.sysが、複合デバイスならUSB\COMPOSITEでUsbccgp.sysが自動的にロードされる仕組みです。複合デバイスとして扱われるためには、デバイス記述子側で複合の意思を示す宣言が必要ですが、その具体値については後述の「デバッグが難しいときの追加観点」で詳しく見ていきましょう。なお、デバイスマネージャの「プロパティ」-「詳細」から互換IDを確認すれば、想定とのズレが見えてきます。
物理・電気・ケーブルまわり(見落としがちな層)
実装も互換IDも正しいのに認識しない場合、物理・電気・ケーブルといった物理層を疑ってみてください。給電が不足していると、列挙の途中でVBUSが落ちて切断が繰り返されることもあります。バスパワードで設計したのにホスト側のポートが供給できる電流を超えていれば、不安定な挙動が出るのも不思議ではありません。
ケーブルの問題も意外と多く、充電専用と書かれたケーブルではD+とD-が結線されておらず、データ通信そのものができません。ハブを介すると速度のネゴシエーションが入るため、想定したFull Speed動作にならない場面もあります。コネクタの接触不良は、間欠的な再列挙という形で症状が表れがちです。
デバッグが難しいときの追加観点
一通りの切り分けでも原因がつかめない場合は、初期化のタイミングと複合デバイスの設計を見直してみてください。D+のプルアップを電源投入直後に立ててしまうと、ホスト側がまだ準備できていない段階でアタッチ検知が走り、不安定な挙動を招きます。リセット直後の応答タイミングが規格を逸脱している場合にも、再現性の低い列挙失敗が起こりがちです。
複合デバイスでよく見かけるのが、Interface Association Descriptorの設置漏れでしょう。CDCとHIDを同一機器に同居させる場合、IADを各機能インタフェース群の直前に配置し、デバイス記述子のクラスコードを0xEF/0x02/0x01に設定する必要があります。これを忘れると、Windowsはひとまとめの複合として扱わず、片方しか認識されません。


