USB Hub Class を動かしてみる

USB Hub Class の動作をQEMUで確認してみました。

QEMUのRaspberry Pi 3モデルのUSB動作を確認しています。USB Hubの初期化をしないと先に進めないので、ベアメタルのプログラムを作成してUSB Hub classの動作確認を確認してみました。

「[QEMUのRaspberry Pi 3モデルでUSBホストエミュレーション](https://qiita.com/eggman/items/0089feaf55f8910adc1e)」
「[ベアメタルでRaspberry Pi 3のUSBコントロール転送](https://qiita.com/eggman/items/5b9e4f5802f0ec22921f)」
の続きです。

# ソースコード

* https://github.com/eggman/raspberrypi/tree/master/qemu-raspi3/usb03

# はじめ

まず 今回動作確認した際のUSB のトポロジーは　BroadcomのSOCのUSB portにhubが接続されてい、hubのport 1にUSB keyboardが接続されています。

```
(qemu) info usb
  Device 0.0, Port 1, Speed 12 Mb/s, Product QEMU USB Hub*
  Device 0.0, Port 1.1, Speed 12 Mb/s, Product QEMU USB Keyboard
```

USB Hubの仕様はUSB仕様書のUSB Hub Classの章で定義されています。

今回の説明では事前にポートにデバイスが接続されていることを知っているものとしていて最低限の初期化処理をしてみました。

今回の説明はUSBのコントロール転送のみです。コントロール転送なのでEP0を使います。
コントロール転送のパラメータはログ中の値をみてください。

# USB Hub の初期設定

* USBのバスリセットをすると、USB Hubがdev 0に現れます。
* dev 0 の DEVICE Descriptor を取得してClass IDがHub(0x09)であることを確認する。
* dev 0 のデバイスのアドレスを 2 に設定します。以降はdev 2としてアクセスします。
* dev 2 のconfig を1に設定する。(Hubはconfig 1しかない）
* dev 2 の HUB Descriptorを取得する。Hub Descriptoはクラスで定義されているのでreqに0xa006を指定します。応答の3バイト目がUSB Hubのポート数です。この例だと8です。


トレースのログ

```
GET DEVICE_DESCRIPTOR
  usb_hub_control dev 0, req 0x8006, value 256, index 0, langth 64
  12 01 10 01 09 00 00 08 09 04 AA 55 01 01 01 02 03 01 
SET ADDRESS 2
  usb_hub_control dev 0, req 0x5, value 2, index 0, langth 0
  usb_set_addr dev 2
GET CONFIG_DESCRIPTOR
  usb_hub_control dev 2, req 0x8006, value 512, index 0, langth 64
  09 02 19 00 01 01 00 E0 00 09 04 00 00 01 09
SET CONFIG 1
  usb_hub_control dev 2, req 0x9, value 1, index 0, langth 0
GET HUB_DESCRIPTOR
  usb_hub_control dev 2, req 0xa006, value 10496, index 0, langth 64
　0A 29 08 0A 00 01 00 00 00 FF 00 00 00 00 00 00 00 00
```

# USB Hub port 1の設定

* portの設定はSetPortFeatureを追加居ます。req が 0x0203 です。
* port 1 の電源を入れる。　(value 8, index 1)
* port 1 をリセットする。(value 4, index 1)
* リセットをするとリセットをしたポートに接続してあるUSBキーボードがdev 0でアクセスできるようになります。port 1  に接続されたUSBキーボードのDevice Descritorを取得する。9バイト目が

トレースのログ

```
SET PORT 1 POWER ON
  usb_hub_control dev 2, req 0x2303, value 8, index 1, langth 0
  usb_hub_set_port_feature dev 2, port 1, feature power
SET PORT 1 RESET
  usb_hub_control dev 2, req 0x2303, value 4, index 1, langth 0
  usb_hub_set_port_feature dev 2, port 1, feature reset
GET DEVICE_DESCRIPTOR on Port 1.1
  usb_desc_device dev 0 query device, len 64, ret 18
  12 01 00 02 00 00 00 08 27 06 01 00 00 00 01 04 0B 01
```

# メモ

* 最後にリセットしたデバイスがdev 0に現れるという仕様は面白いです。
* device addressが7bitなのでUSBの最大接続台数が127台になってる。
* インタラプト転送を使うとどのポートで変更があったか検知できる。sttatus が0x2だとポート１に何か要因がある。

```
usb_dwc2_handle_packet ch 3 dev 0x55b99b16fdf0 pkt 0x7f1c7b99c3c8 ep 1 type Intr dir In mps 64 len 64 pcnt 1
  usb_hub_status_report dev 2, status 0x2
  02 00
```
* CLEAR_PORT_FEATUREで要因をクリアすることができる。
* ポートの電源ONとポートのリセット後は要因クリアした方が良い。
* 電源ONの後は時間待ちが必要。
* USBのドライバを書くにはUSBのプロトコルを勉強しないとツライ。
* パケットを投げるにも、パラメータが多くて、応答が無い場合に、どのパラメータが間違っているのか調査するのがシンドイ。 

# 参考
* [Problem Enumerating Hub-Attached Devices : microchip forum](https://www.microchip.com/forums/m523103.aspx)
  * このスレッドのchinzeiさんの回答が一番分かりやすい説明でした。
* USB 2.0 仕様書の Chapter 11 Hub Specification
  * 各種ビットの仕様は仕様書を見た方が良い (11.23以降に記載)
* インターフェース　2005年12月号 「ハブ標準デバイス・クラスの制御法」
  * TECH I 40 「組み込み機器へのUSBホスト実装技法」にも再掲されてる。
* [最小限の USB Hub 対応 - 借り初めのひみつきち](https://neriring.hatenablog.jp/entry/2019/10/24/120034)