Skip to content

USB_Enumeration

askn37 edited this page Oct 20, 2026 · 3 revisions

USB 2.0 制御エニュメレーション

USB エニュメレーション(列挙)の流れ。USBホストから見て;

  1. 接続検知と物理バス速度判定。USB 周辺機能が自動で行う
  2. USBアドレスの提供。⇨ SET_ADDRESS
  3. デバイス(装置)記述子の取得。⇨ GET_DESCRIPTOR
  4. 構成・インタフェース記述子の取得。⇨ GET_DESCRIPTOR
  5. 構成・インタフェースの選択と有効化。⇨ SET_CONFIGURATION、SET_INTERFACE
  6. OSドライバの読み込み・有効化。

ホスト-装置 トランザクションは 制御専用(Control)エンドポイント=EP0 を通じて行う。

設定パケット Setup Packet

EP0_OUT に着信する EP0_SETUP パケットは、必ず 8バイトからなる。ワード値はリトルエンディアン(下位バイト先行;以後共通)で表現される。

0 1 2,3 4,5 6,7
bmRequestType bRequest wValue wIndex wLength
要求形式 要求種別 機能選択値 指標値 データ量
/* SETUPパケット 8byteを表現する構造体 */
typedef struct {
  uint8_t  bmRequestType;
  uint8_t  bRequest;
  uint16_t wValue;
  uint16_t wIndex;
  uint16_t wLength;
} __attribute__((packed)) Setup_Packet;

bmRequestType 要求種別 は 1+2+5 ビットのフィールドからなる。

Bit Selector Value Description
7 転送方向 0 ホスト⇨装置指令
1 USB_REQTYPE_DIRECTION_bm 装置⇨ホスト要求
6,5 種別 0 USB_REQTYPE_STANDARD_gc Standard
1 USB_REQTYPE_CLASS_gc Class
2 USB_REQTYPE_VENDOR_gc Vendor
3 予約
4-0 受取先 0 USB_RECIPIENT_DEVICE_gc Device
1 USB_RECIPIENT_INTERFACE_gc Interface
2 USB_RECIPIENT_ENDPOINT_gc Endpoint
3 USB_RECIPIENT_OTHER_gc Other
4-31 予約

転送方向が 1であれば、wLength で示されたバイト数の DATA応答を、ホストに返さなければならない。そうでなければ ZLP(ゼロ長応答)を返すことで 1パケットのトランザクションを完了する。

  • wLength を超える大きさの応答を返すべきではない。ホスト側では その大きさのバッファメモリしか用意していないと想定するべき。

以上の情報から、標準要求までのフローの実装コードは以下のように書き下せる。転送方向が0の場合は EP0_SETUP パケットに続いて追加の DATA領域(9バイト目以降、ディスクリプタの本体部分等)が存在しうるので、その転送(DATAバッファ充填)完了を確実にしてから先に進める。

  • SETUP 割込は EP0_SETUP パケットの 8バイトが揃うと TRNCOMPL 割込に先んじて発生する。
  • ベンダー要求は USB標準には含まれず、装置固有の OSドライバが ホスト側で有効化されない限り使用されない。
ISR(USB0_TRNCOMPL_vect) {
  USB0_INTFLAGSB |= USB_SETUP_bm;
  if (bit_is_set(EP0_OUT.STATUS, USB_EPSETUP_bp)) {
    /* EP0_OUT先頭バイトを読む */
    uint8_t bmRequestType = EP0_SETUP.bmRequestType;

    /* DIRECTION属性が偽なら制御OUT */
    if (bit_is_clear(bmRequestType, USB_REQTYPE_DIRECTION_bp)) {  /* bp=7 */
      EP0_OUT_flush();  /* EP0_OUTに残りのDATAを読む(TRNCOMPL 解決) */
      /* DATAが格納されるのは EP0_OUT_BUFFの位置:EP0_SETUP の後ろから */
    }

    /* REQTYPE属性をマスク抽出 */
    bmRequestType &= USB_REQTYPE_TYPE_gm;                 /* gm=0x60 */

    if (bmRequestType == USB_REQTYPE_STANDARD_gc) {       /* gc=0x00 */
      /* 標準要求を処理:制御エニュメレーション本体 */
      control_request_standard();
    }

    else if (bmRequestType == USB_REQTYPE_CLASS_gc) {     /* gc=0x20 */
      /* クラス要求を処理:例えばUSB-CDCやHIDクラス */
      control_request_class();
    }

    else if (bmRequestType == USB_REQTYPE_VENDOR_gc) {    /* gc=0x40 */
      /* 必要ならここにベンダー要求処理 */
      control_request_vender();
    }

  }
  /* 割込バルク転送 処理等 */
}

標準要求 Standard Request

標準要求の bRequest 要求種別 には、標準装置要求、標準インターフェース要求、標準エンドポイント要求 の 3分類がある。

/* 標準要求 応答例 */
void control_request_standard (void) {
  uint8_t bRequest = EP0_SETUP.bRequest;

  if (bRequest == USB_REQ_GetStatus) {              /* 0x00 */
    EP0_IN.CNT = EP0_SETUP.wLength;
    USB_BUFF.wValue = 0;
  }

  else if (bRequest == USB_REQ_GetDescriptor) {     /* 0x06 */
    size_t _length = (size_t)memcpy_descriptor(&EP0_IN_BUFF, EP0_SETUP.wValue);
    EP0_IN.CNT = EP0_SETUP.wLength < _length ? EP0_SETUP.wLength : _length;
  }

  else if (bRequest == USB_REQ_GetConfiguration) {  /* 0x08 */
    EP0_IN.CNT = 1;
    EP0_IN_BUFF[0] = get_configuration();
  }

  else if (bRequest == USB_REQ_SetConfiguration) {  /* 0x09 */
    EP0_IN.CNT = 0;
    set_configuration((uint8_t)EP0_SETUP.wValue);
  }

  else if (bRequest == USB_REQ_GetInterface) {      /* 0x0A */
    EP0_IN.CNT = 1;
    EP0_IN_BUFF[0] = get_interface((uint8_t)EP0_SETUP.wIndex);
  }

  else if (bRequest == USB_REQ_SetInterface) {      /* 0x0B */
    EP0_IN.CNT = 0;
    set_interface((uint8_t)EP0_SETUP.wIndex, (uint8_t)EP0_SETUP.wValue);
  }

  else if (bRequest == USB_REQ_SetAddress) {        /* 0x05 */
    /* Initiate EP0_IN and, if there is a response, activate the assigned address. */
    EP0_IN.CNT = 0;
    EP0_IN_flush();     /* STATUS (ZLP) sending */
    EP0_IN_pending();   /* wait */
    USB0_ADDR = EP0_SETUP.wValue; /* Get 7bit USB Device Address */
    return;
  }

  else if (bRequest == USB_REQ_ClearFeature) {  /* 0x01 */
    EP0_IN.CNT = 0;
    clear_feature();
  }

  else if (bRequest == USB_REQ_SetFeature) {    /* 0x03 */
    EP0_IN.CNT = 0;
    set_feature();
  }

  else if (bRequest == USB_REQ_SynchFrame) {    /* 0x0C */
    /* Isochronous 転送デバイスのみ実装 */
    EP0_IN.CNT = 2;
    USB_BUFF.wValue = USB_EP_WORK.FRAMENUM;
  }

  EP0_IN_flush();     /* DATA (ZLP) sending */
}

標準装置要求 Standard Device Request

標準装置要求は、8種類ある。うち 一般的な(HID や CDC といった)装置実装では使用されない SET_DESCRIPTOR を除く 7種類はすべて実装され、応答できなくてはならない。

bmRequestType bRequest wValue wIndex wLength Data
1 00 00000 0x00 GET_STATUS 0 0 2 装置状態値
0 00 00000 0x01 CLEAR_FEATURE 機能選択値 0 0 -
0 00 00000 0x03 SET_FEATURE 機能選択値 0 0 -
0 00 00000 0x05 SET_ADDRESS 装置アドレス 0 0 -
1 00 00000 0x06 GET_DESCRIPTOR 記述子種別/指標番号 0または言語ID * 記述内容
0 00 00000 0x07 SET_DESCRIPTOR 記述子種別/指標番号 0または言語ID * 記述内容
1 00 00000 0x08 GET_CONFIGURATION 0 0 1 構成番号
0 00 00000 0x09 SET_CONFIGURATION 構成番号 0 0 -

標準インターフェース要求 Standard Interface Request

wIndex 指標値 にインタフェース番号が指定される 標準インターフェース要求は、5種類ある。

bmRequestType bRequest wValue wIndex wLength Data
1 00 00001 0x00 GET_STATUS 0 IF番号 2 IF状態値
0 00 00001 0x01 CLEAR_FEATURE 機能選択値 IF番号 0 -
0 00 00001 0x03 SET_FEATURE 機能選択値 IF番号 0 -
1 00 00001 0x0A GET_INTERFACE 0 IF番号 1 代替IF値
0 00 00001 0x11 SET_INTERFACE 代替設定値 IF番号 0 -

このときの wIndex は、上位バイトは予約値で、下位バイト(Bit[7-0])だけが有効。番号0はホスト状態が未設定であることを表す。

Bit Selector Value
15-8 予約 0
7-0 インターフェース番号 0x00-0xFF

標準エンドポイント要求 Standard Endpoint Request

wIndex 指標値 にエンドポイント番号が指定される 標準エンドポイント要求は、4種類ある。うち SYNCH_FRAME は Isochronous 転送装置でのみ使用される。

bmRequestType bRequest wValue wIndex wLength Data
1 00 00010 0x00 GET_STATUS 0 エンドポイント番号 2 エンドポイント状態値
0 00 00010 0x01 CLEAR_FEATURE 機能選択値 エンドポイント番号 0 -
0 00 00010 0x03 SET_FEATURE 機能選択値 エンドポイント番号 0 -
1 00 00010 0x12 SYNCH_FRAME 0 エンドポイント番号 2 フレーム番号

このときの wIndex 指標値 は、エンドポイント番号と方向を指示する。

Bit Selector Value Description
15-8 予約 0
7 転送方向 0 ホスト⇨装置(OUT)
1 装置⇨ホスト(IN)
6-4 予約 0
3-0 EP番号 0-15

GET_STATUS

GET_STATUS は(どの要求分類でも)wLength で求められる 2バイトの状態値を常に返却する必要がある。後述の詳細な実装を必要としないなら、すべての分類で単純に 0 を返せば良い。

if (bRequest == USB_REQ_GetStatus) {  /* 0x00 */
  EP0_IN.CNT = 2;       /* = EP0_SETUP.wLength */
  USB_BUFF.wValue = 0;  /* Response Value */
  EP0_IN_flush();       /* DATA sending */
}

装置要求での返値

この要求での返値は、以下のビットフィールドからなる。

Bit Selector Value Description
15-2 予約 0
1 REMOTE_WAKEUP 0 不可
1 可能
0 SELF_POWERED 0 バスパワー
1 セルフパワー

給電形態ビット(SELF_POWERED)は、ホスト側から VUSB 給電を遮断したとしても、装置が支障なく動作し続けられるかを表明する。

遠隔起動ビット(REMOTE_WAKEUP)は、スリープ/サスペンド状態の ホストPCをレジュームできることを表明する。これは 装置要求 SET_FEATURE の当該ビットの結果を反映すべきだ。

インターフェース要求での返値

この要求は将来用に予約されている。要求された場合は(要求インタフェース番号に関わらず)常に 2バイトの 0を返せば良い。

Bit Selector Value
15-0 予約 0

エンドポイント要求での返値

この要求には、要求されたエンドポイントの活動状態を 最下位 1ビットで返却する。

Bit Selector Value Description
15-1 予約 0
0 ENDPOINT_HALT 0 活動
1 停止

SET_ADDRESS

この指令は USB装置に 7ビットの USBアドレスを供給する。指示された USBアドレスは次回の(SOFから始まる)通信フレームから使用可能になるため、これを設定反映する前に ZLP応答を完遂しなければならない。

if (bRequest == USB_REQ_SetAddress) {   /* 0x05 */
  EP0_IN.CNT = 0;
  EP0_IN_flush();     /* STATUS (ZLP) sending */
  EP0_IN_pending();   /* wait */
  USB0_ADDR = EP0_SETUP.wValue; /* Get 7bit USB Device Address */
  return;
}

CLEAR_FEATURE

この指令の wIndex 指標値 は、以下のように解釈する。そして指定された機能区分を既定状態にリセットする。例えば ENDPOINT_HALT 指令ならば、当該エンドポイントの STALED や NAK応答状態を解除して再度正常応答可能にするということである。

  • 装置要求:常にゼロ - wValue のみを使用
  • インターフェース要求:インターフェース番号
  • エンドポイント要求:エンドポイント番号

ホストに対しては ZLP で応答する。

if (bRequest == USB_REQ_ClearFeature) {   /* 0x01 */
  EP0_IN.CNT = 0;
  clear_feature();
  EP0_IN_flush();
}

この指令の wValue 機能選択値 は、下位バイトを以下のように解釈する。インターフェース要求に対しては常に 0であるはずだ。

wValue.low Selector Target
0 ENDPOINT_HALT エンドポイント要求
1 DEVICE_REMORE_WAKEUP 装置要求
2 TEST_MODE 装置要求

装置要求での TEST_MODE は、wValue 機能選択値 の上位バイトを以下のように解釈する。TEST_MODE は High-Speed 以上の USBデバイス認証試験を行うために、実装が必要とされる。

wValue.high Selector Description
0x00 Reserved (テストモード終了)
0x01 Test_J D+/D-線を Jステート状態にする
0x02 Test_K D+/D-線を Kステート状態にする
0x03 Test_SE0_NAK D+/D-線を SE0 状態にし、かつ NAK を返す
0x04 Test_Packet 特定のテスト信号パターンを送信し続ける
0x05 Test_Force_Enable 受信信号をそのまま送り返す(loopback)
0x06-0x3F Reserved (標準テストセレクタ)
0x3F-0xBF Reserved (未定義)
0xC0-0xFF Reserved (ベンダー定義テストセレクタ)
  • Low/Full-Speed USBデバイスに対してはこの規約はなく、コンプライアンス認証試験だけが要求されるので TEST_MODE の実装は必要ない。
  • Test_K は、装置側からの REMORE_WAKEUP 信号送信(URESUME)に等価。
  • Test_J は、装置接続開始時の D+/D- プルアップ有効=アイドル状態(ATTACH)に等価。
  • Test_SE0_NAK は、アイドル+全NAK 応答状態(GNACK)と換言できる。
  • つまりこの3種類は、AVR DU 系統の場合はレジスタ設定で不完全ながら対応可能。ただし SETUP転送も塞ぐ GNAUTOは、通信での解除手段も失うことに注意。

SET_FEATURE

この指令は CLEAR_FEATURE とは逆に、指定の機能区分をそうあるように変更することが期待される。wIndex 指標値 と wValue 機能選択値 の様式は CLEAR_FEATURE と同じ。

ホストに対しては ZLP で応答する。

if (bRequest == USB_REQ_SetFeature) {   /* 0x03 */
  EP0_IN.CNT = 0;
  set_feature();
  EP0_IN_flush();
}
  • インターフェース要求に対する指令は、当該インターフェース番号を新たな既定値として有効にすることを求める。現在選択されている当該インタフェース状態は インターフェース要求の GET_STATUS で取得される。これは前述のように普通は常に 0 である。
  • エンドポイント要求に対する ENDPOINT_HALT 指令は、CLEAR_FEATURE 指令がなされるまで、当該エンドポイントを通信不可(停止)に維持すべきだ。現在の当該エンドポイント状態は エンドポイント要求の GET_STATUS で取得される。実際には停止状態のエンドポイントに対して ホスト側は何もしてこなくなるので、装置側は何の対応もしなくても問題はない。
  • 装置要求に対する DEVICE_REMORE_WAKEUP 指令は、装置の遠隔起動機能を有効にする。現在の装置状態は 装置要求の GET_STATUS で取得される。実際には(装置側が REMORE_WAKEUP に対応していてもいなくても)何の対応もしなくても実害はない。
  • 装置要求に対する TEST_MODE 指令は、CLEAR_FEATURE 指令が指示されるまで(あるいは別の TEST_MODE 状態に変更されるまで)装置を指示された状態で維持すべきだ。これは前述したように Low/Full-Speed 装置では実装されていなくても支障はなく、単に無視してよい。
  • 装置要求に対する CLEAR_FEATURE 指令の wValue == 0 セレクタは、TEST_MODE 終了と解釈してよい。

GET_CONFIGURATION

この 装置要求 には、0x00 以外の 現在選択されている 構成番号 を 1バイトで返さなければならない。

if (bRequest == USB_REQ_GetConfiguration) {  /* 0x08 */
  EP0_IN.CNT = 1;
  EP0_IN_BUFF[0] = get_configuration();
  EP0_IN_flush();
}
Bit Selector Result
7-0 構成番号 0x01-0xFF

SET_CONFIGURATION

この 装置指令 は、0x00 以外の 構成番号 が wValue の下位バイトに指定される。wIndex には 常に 0 が指定される。

ホストに対しては ZLP で応答する。

if (bRequest == USB_REQ_SetConfiguration) {  /* 0x09 */
  EP0_IN.CNT = 0;
  set_configuration((uint8_t)EP0_SETUP.wValue);
  EP0_IN_flush();
}
Bit Selector wValue
7-0 構成番号 0x01-0xFF

GET_INTERFACE

この インターフェース要求 には、0x00 以外の(現在有効である)インターフェース番号 を 1バイトで返さなければならない。wValue には 常に 0 が指定される。

if (bRequest == USB_REQ_GetInterface) {   /* 0x0A */
  EP0_IN.CNT = 1;
  EP0_IN_BUFF[0] = get_interface((uint8_t)EP0_SETUP.wIndex);
  EP0_IN_flush();
}
Bit Selector wIndex
7-0 インターフェース番号 0x01-0xFF

SET_INTERFACE

この インターフェース指令 は、wIndex の下位バイトに インターフェース番号 が、下位バイトに 代替インタフェース番号 が指定される。これらは インターフェース記述子の bInterfaceNumber と bAlternateSetting を反映している。

ホストに対しては ZLP で応答する。

if (bRequest == USB_REQ_SetInterface) {   /* 0x0B */
  EP0_IN.CNT = 0;
  set_interface((uint8_t)EP0_SETUP.wIndex, (uint8_t)EP0_SETUP.wValue);
  EP0_IN_flush();
}
Bit Selector wIndex
15-8 代替インターフェース番号 0x01-0xFF
7-0 インターフェース番号 0x01-0xFF

SYNCH_FRAME

この エンドポイント要求 は、Isochronous 転送形態でのみ実装する必要がある。wIndex で示されるエンドポイントの、最新のフレーム番号(実際は 12ビットのみ有効)を 2バイトで返さなければならない。

if (bRequest == USB_REQ_SynchFrame) {   /* 0x0C */
  EP0_IN.CNT = 2;
  USB_BUFF.wValue = USB_EP_WORK.FRAMENUM;
  EP0_IN_flush();
}
Bit Selector Result
11-0 フレーム番号 0x000-0x7FF

実際には、エンドポイント毎に個別のフレーム番号があるわけではなく 装置全体で共通の通番であるから、FRAMENUM 仮想レジスタの値を返すだけで良い。ホスト側はこれを見て Isochronous 転送パケットの途中欠損(ホストが送ったのに装置が受け取れなかった)を知ることができる。

SET_DESCRIPTOR

この 装置指令は、wIndex と wValue で指定される構成記述(ディスクリプタ)を、wLength の長さを持つ Data で上書きすることを求める。しかし一般に構成記述は "読み出し専用" であるので、USB 規約上は存在する機能ではあるものの、使用される場面は稀、あるいは皆無である。仮に使用されても無視して ZLP応答で済ませればよい。

if (bRequest == USB_REQ_SetDescriptor) {  /* 0x07 */
  EP0_IN.CNT = 0;
  EP0_IN_flush();
}
  • 稀な用途としては、装置の 既定の 言語ID 変更や、シリアル番号文字列の書き換えがありうる。

GET_DESCRIPTOR

この 装置要求は、さまざまな構成記述子(ディスクリプタ)の返答を要求する。USB エニュメレーションの根幹。

詳細は別項。⇨ USB 記述子

LINKS

multix.jp/てくにかるむ(休眠中)
Multix Zinnia Product SDK [*AVR]
AVR.JP(日本語訳)
AVR-LIBC(日本語訳)

Clone this wiki locally