最近, USB CDCについて勉強したのでメモがてら書きます. (間違えてる箇所があったらご連絡ください.)
最初に: 幸福とは何か
USBの規格を読まずに日々を過ごせること( ˘ω˘)スヤァ
USB CDCについて
USB CDCとはUniversal Serial Bus Communications Device Classの略称で, USB上でデバイス間のデータのやりとりを行うための通信規格です. RS-232CとかがCDC-ACMの規格で通信をやっています.
具体的にはFT232やマイコンのデバッガとかがこの通信規格を使ってPCにデータを投げたり受け取りしてます. (TeraTermを開いてやるやつね
何を読むべきか
ブログ記事とかもネットにたくさんありますが, (https://usb.org/)に公式のドキュメントが置いてるので, 正確なことが知りたい人はこれを読んでください.
また, こちらのブログでCDCについて大変分かりやすく翻訳されています.(私もお世話になりました. ありがとうございます.)
とりメモ: (https://sites.google.com/site/toriaezunomemo/home/communication-device-class)
以下の話は
Class definitions for Communication Devices 1.2 (https://www.usb.org/document-library/class-definitions-communication-devices-12)
を読んでマイコンのUSB機能とUARTを使ってFT232みたいな仮想COMポート(シリアル変換モジュール)みたいなのを作るときに必要になりそうなものをまとめていきます.
Functional Overview
CDCではCommunicatios Class InterfaceとData Class Interfaceの2つを持っています.(CDC3章に記載)
Communications Class Interfaceでは管理と通知(これはoptionally)の2つを行っていてデバイスの設定や管理を行います. これはデバイスがコール管理を実装しているか否かで挙動が変わるので, 規格書の3.4.1を読んでください.
例えばデバイス側でコール管理をしていると管理や設定の情報はendpoint0越しにデータのやり取りをして, 通知はinterrupt endpointで行われます.
Data Class Interfaceでは実際の通信データのやり取りを行っていて, bulk endpointで行われます.
これらの実装はCommunication Class Interfaceとしてintterupt input endpointが1つ(otionallyでoutputのものが1つ.), bulk inputとbulk outputのそれぞれが1つという3つのendpointでの実装になります.
Descriptor
DescriptorはPCからUSBケーブルをデバイスに接続した際にPCに対して送信されるもので, 「このデバイスはこういうデバイス(CDCであることや使うendpointとか)ですよー」みたいな情報が含まれています.
送信するデータはC言語でいう構造体みたいな形をとっていて, 「先頭から1バイト目に〇〇の設定データ, 2バイト目に〇〇の設定データ」といった形式になります. このとき, クラス固有の情報を記述するInterface Descriptorの各要素をFunctional Descriptorといいます. 実際に送信する際にはこれらFunctional Descriptorを続けて送信します.
シリアル変換モジュールを作る際, UART越しに対象のマイコンと通信するのでその制御のために, CDCではSubClassとしてPSTN(公衆電話回線) Device Modelのサブクラスである ACM(Abstract Control Model) を採用します. (ACMのプロトコルを使うとレガシーモデムとUSBデバイス間の橋渡しが可能になる.)
USB CDCとして使用するために以下のFunctional Descriptorが必要になります.
- ヘッダ情報を示すHeader Functional Descriptor
- 通信で使われるインターフェイスの番号について記述するUnion Functional Descriptor
- これにはCDC以外のインターフェイス(HID, Audio)などのインターフェイスについてもまとめて記述できます.
- 動作が認められた国なのかを識別するCountry Selection Functional Descriptor
- (法規制目的なので今回は使いません.
また, PSTNサブクラスのACMを使用する際には以下の2つのFunctional Descriptorが必要になります.
- コール管理(ATコマンドをどこから送るか)を示すCall Management Functional Descriptor
- ACMでサポートするコマンドを示すAbstract Control Model Functional Descriptor
ACMについての詳細はUSBPSTNの3.2.2.1で詳しく書かれているのでそこを読んでください.
ディスクリプタはCDCの5章にCDCとして設定する際に送るサンプルも一緒に乗ってるので参考にしてみてください.
以下に実際にCDCとして設定する際のDescriptorの例を載せます.
Offset | Field | Size | Value | Description |
---|---|---|---|---|
0 | bFunctionLength | 1 | 0x05 | Header Functional Descriptorのサイズ(USBのどの通信を使うか決める) |
1 | bDescriptorType | 1 | 0x24 | CS_INTERFACE(固定値です) |
2 | bDescriptorSubtype | 1 | 0x00 | Header Functional Descriptorであることを示す |
3 | bcdCDC | 2 | 0x0110 | BCD(binary-coded decimal)表記で書かれたCDCのバージョン.(1.2) |
5 | bFunctionLength | 1 | 0x04 | ACM Functional Descriptorのサイズ(ACMの設定) |
6 | bDescriptorType | 1 | 0x24 | CS_INTERFACE(固定値です) |
7 | bDescriptorSubtype | 1 | 0x02 | ACM Functional Descriptorであることを示す |
8 | bmCapabilities | 1 | 0x0F | 対応する機能の設定(全部盛り) |
9 | bFunctionLength | 1 | 0x05 | Union Functional Descriptorのサイズ(Unionの設定) |
10 | bDescriptorType | 1 | 0x24 | CS_INTERFACE(固定値です) |
11 | bDescriptorSubtype | 1 | 0x06 | Union Functional Descriptorであることを示す |
12 | bControlInterface | 1 | 0x00 | コントロールインターフェイス番号(ここではCommunications Class Interface)を設定 |
13 | bSubordinateInterface0 | 1 | 0x01 | それ以外のインターフェイス番号(ここではData Class Interface)を設定 |
14 | bFunctionLength | 1 | 0x05 | Call Management Functional Descriptorのサイズ(コールの設定) |
15 | bDescriptorType | 1 | 0x24 | CS_INTERFACE(固定値です) |
16 | bDescriptorSubtype | 1 | 0x01 | Call Management Functional Descriptorであることを示す |
17 | bmCapabilities | 1 | 0x03 | コール管理を行うデータインターフェイス上でコマンドとデータを多重化する |
18 | bDataInterface | 1 | 0x03 | データとコマンドが送られるインターフェイス番号(Data Class Interfaceと同じ) |
ちなみにオフセットが8バイト目のbmCapabilitiesで送信する1バイトデータの各bitはそれぞれ
- MSB=7 - 4: RESERVED(0にすること)
- 3: NetWork_Connectionをサポート
- 2: Send_Breakをサポート
- 1: Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, Serial_Stateをサポート
- 0: Set_Comm_Feature, Clear_CommFeature, Get_Comm_Featureをサポート
になります.
Communications Class Specific Messages
これはCommunications Interface Classにおいてサポートしているリクエスト群です. これらのリクエストを使ってUART通信(半二重モデムなど)などの設定を行います.
例えば, FT232とかをPCに接続してTeraTermでUARTの通信速度やパリティビットの設定を行うとこのリクエストたちが送られます.
以下にCDCの通信でよく使われるものを書きます.
SerialState
シリアル通信の状態をホスト側が要求するときに送られるリクエストでACM固有のリクエストです. (ACMのリクエストタイプは0xa1になります)
デバイス側はシリアル通信の状態(パリティエラーやTXRXが動作しているか否か)をホストに返します.
今でも電話のリングの状態を送っていたりします. (いい話)
USBの仕様, 密林状態になってて, UARTのコンフィグをするときに電話のリング状態を送ったりする.(RS-232Cの一環なので
— イェーイ (@dango_bot) March 6, 2019
リクエストコードは0x20でデバイス側は以下のような2バイトのデータを送ります.
Bits | Field | Description |
---|---|---|
MSB=15-7 | RESERVED | |
6 | bOverRun | 受信したデータがデバイスのオーバーランによって破棄されたか否か |
5 | bParity | パリティエラーが発生したか否か |
4 | bFraming | フレーミングエラーが発生しているか否か |
3 | bRingSignal | デバイスのリング状態 |
2 | bBreak | デバイスのブレーク状態 |
1 | bTxCarrier | 送信キャリアの状態. V.24の106信号やRS-232のDSR信号にあたります. |
0 | bRxCarrier | 受信キャリアの状態. V.24の109信号やRS-232のDCD信号にあたります. |
Set(Get)LineCoding
シリアル通信の設定をホスト側が要求するときに送られるPSTN固有のリクエストです. (PSTNのリクエストタイプは0x21になります)
デバイス側はシリアル通信の設定(パリティビットや通信レート)をホストに返したり, ホストから送られた設定データを受け取ったりします.
それぞれSetLineCodingのリクエストコードは0x20, GetLineCodingのリクエストコードは0x21になります.
送られるデータ(Line Coding 構造体)は以下のようになります.
Offset | Field | Size | Value | Description |
---|---|---|---|---|
0 | dwDTERate | 4 | Number | 通信速度(bps) |
4 | bCharFormat | 1 | Number | ストップビット(0:1bit, 1:1.5bit, 2:2bit) |
5 | bParityType | 1 | Number | パリティビット(0:None, 1:Odd, 2:Even, 3:Mark, 4:Space) |
6 | bDataBits | 1 | Number | データビットの長さ(5, 6, 7, 8, 16) |
SetControlLineState
RS-232のフロー制御の信号を送るPSTN固有のリクエストです. UART通信でいうところのRTSやDTRに相当します. リクエストコードは0x22です.
以下のような2バイトのデータをホストから送信します.
Bits | Description |
---|---|
MSB=15-2 | RESERVED |
1 | 半二重モデムのためのキャリア制御. V.24の105信号やRS-232のRTSなどに対応しています. 0:キャリア不使用, 1:キャリア使用 |
0 | DTEが存在するかをDCEに示す. V.24の108/2信号やRS-232のDTRに対応しています. 0:なし, 1:あり |
SendBreak
シリアル通信に対してブレイク信号を送るためのPSTN固有のリクエストです. リクエストコードは0x23です.
リクエストと一緒に送信される値(wValue)ミリ秒間ブレイク時間が継続されます.
ただし, wValueが0xFFffのとき, 次にwValueが0x0000のSendBreakが送られるまでブレイク時間は継続されます.
デバッグ
USB製品のデバッグをするために実際に流れてるパケットを見るUSB Analyzerが必要になります. TOTAL PhaseがBeagle USBという高性能なUSB Analyzerを出していますが, 値段ェ…(https://www.totalphase.com/products/beagle-usb480/)
おススメのものがあったら教えてください…
気になるところ
Data Class Interfaceはプロプラエイタリでもよい
USB CDCの3.4.2を見るとData Class Interfaceの通信はプロプラエイタリでもいいことが明示されてます.
実際にFT232の通信しているパケットを見ているとCDCに準拠しているものとは全く違うリクエストとかが飛んでいることが分かります. (これらを解釈するためにデバイスドライバーをPCにインストールするんですね.)
Tera TermやPuttyではSetControlLineStateに対応していない?
CDCデバイスをTera TermやPuttyでフロー制御をON, OFFしても同じパケットが流れます. (なぜ?)
しかし, デバイスドライバーなどを要求するFT232とかをつなげてやると正しくパケットが変わってる.(なぜ?)
2020/03/15追記
この問題は修正済みとのこと(圧倒的感謝)
TeraTermでDSRとDTRが使えなかったやつ.
— イェーイ (@dango_bot) November 24, 2019
こんな感じですね.
USBの規格はかなり昔から存在していて今では密林状態のようになっていますが, 規格自体は結構面白いのでぜひ読んでCDCデバイスを作ってみてください.