USB CDC ACMシリアルのデバッグ:Line Coding、コントロールライン状態、データ欠落

SET_LINE_CODING、SET_CONTROL_LINE_STATE、バルクエンドポイント、ファームウェア挙動を観察して、USB CDC ACM仮想シリアルデバイスをデバッグする方法を解説します。

USB, CDC ACM, シリアル, ラインコーディング, ファームウェア

USB CDC ACMデバイスは、仮想シリアルポート・デバイスコンソール・ファームウェアツール・テレメトリ・テスト治具・組み込み診断に使われています。アプリ側からはとても単純で、COMポートまたは /dev/ttyACM* を開いて、ボーレートを設定して、バイトを読み書きするだけです。その裏側でも、ホストとデバイスはクラス固有のUSBリクエストとバルク転送をやり取りしています。

CDCデバイスが列挙されてもデータが流れないとき、キャプチャを見れば問題がディスクリプタ配置なのか、ラインコーディングなのか、コントロールライン状態なのか、エンドポイントトラフィックなのか、ファームウェアのバッファリングなのかを切り分けられます。

CDC ACMは通信用とデータ用の2つのインターフェースを持つ

典型的なCDC ACMデバイスは、Communication InterfaceとData Interfaceを公開します。ホストはデータ転送が始まる前にクラス固有のリクエストを送るかもしれません。

重要な証拠:

  • Communication Interfaceディスクリプタ
  • Data Interfaceディスクリプタ
  • CDC Functional Descriptor
  • Notificationエンドポイント
  • バルクINエンドポイント
  • バルクOUTエンドポイント
  • SET_LINE_CODING
  • GET_LINE_CODING
  • SET_CONTROL_LINE_STATE

これらのリクエストが一度も来ないなら、ホストが想定どおりのCDCドライバをバインドできていない可能性があります。

ボーレートは「物理UART」ではなく「シグナル」であることが多い

多くのUSB CDCデバイスでは、ボーレートはUARTと同じ意味での物理値ではありません。それでもホストはラインコーディングを送ってきます。ファームウェアはそれを使ってブリッジを設定したり、無視したり、検証に使ったりします。

キャプチャで確認したい問い:

  • ホストは SET_LINE_CODING を送ったか?
  • 要求されたボーレート・パリティ・ストップビット・データビットは何か?
  • ファームウェアはそのリクエストを受け付けたか?
  • ホストは SET_CONTROL_LINE_STATE でDTR/RTSをセットしたか?
  • ファームウェアは送信前にDTRを待っているか?

「シリアル出力が出ない」問題の実態は、「ファームウェアがDTRを待っているのにホストが一度もアサートしない」または「アプリがポートを開いたが想定どおり設定していない」であることが多いです。

バルクエンドポイントが「データの動き」を証明する

セットアップが終わると、CDCデータは通常バルクエンドポイントで流れます。ホストからバルクOUTへの書き込みが見えるのにバルクINの応答がないなら、ファームウェアが送っていない可能性があります。バルクINにデータが出ているのにアプリに表示されないなら、ホストアプリ側の挙動が疑わしい。

確認ポイント:

  • エンドポイントの方向
  • 転送長
  • 繰り返されるNAK/タイムアウト挙動
  • 実際のペイロードバイト
  • 転送のステータス
  • ライン状態リクエストとの相対順序

USBキャプチャが、ターミナルのスクリーンショットより圧倒的に価値があるのはこのためです。

Bus Scopeが活きる場面

Bus Scopeは、ファームウェアチームがディスクリプタ・クラスリクエスト・生エンドポイントデータを1つのセッションに保てるように設計されています。CDC ACMのデバッグでは、次の問いに答えを出してくれるはずです。

  • ホストはCDCをバインドしたか?
  • どんなラインコーディングを送ったか?
  • DTR/RTSは変化したか?
  • バルクOUTにはコマンドが乗っていたか?
  • バルクINには応答が乗っていたか?
  • 問題はシリアル的なトラフィックが始まる前と後、どちらで起きたか?

「USB CDC ACM no data」「virtual COM port no output」「SET_CONTROL_LINE_STATE DTR」といった検索の答えは、ターミナルの中だけにあるのではなく、USBクラスリクエストとエンドポイント転送の中にあります。