-
Notifications
You must be signed in to change notification settings - Fork 5
USB Memo
USB のホスト側の制御(特に xHCI)についてのメモ書き
2017/08/05 by uchan 2017/10/27 updated by uchan
USB を制御するには,いくつかの層のドライバを書く必要がある.
- USB クラスドライバ:例えば HID(キーボードやマウス)とか,Web カメラとか用のドライバ
- USB ドライバ:USB 規格で規定された,データのやり取りとかを実装する部分
- ホストコントローラドライバ:xHCI など,ホストコントローラを制御する部分
- PCI ドライバ:ホストコントローラは PCI バスに繋がることが前提になっているので,PCI ドライバも必要
USB 規格はあくまでもデータのやり取りの形式(フレームの構造,USB 機器とやり取りする際のプロトコルなど)を規定している. USB 規格と物理層をつなぐのがホストコントローラで,これは USB 規格とは別物.
ホストコントローラ規格には幾つか種類がある:UHCI, OHCI, EHCI, xHCI
- USB
- USB 規格の解説 http://wiki.osdev.org/USB
- xHCI
- インテルの xHCI 規格書 xHCI for Universal Serial Bus: Specification
- PCI
xHCI のレジスタは大きく 2 箇所に存在する.
- PCI コンフィギュレーション空間のレジスタ:MMIO 空間のスタートアドレスを設定する BAR を含む.
- MMIO 空間のレジスタ:こちらがメイン.USB 機器に送るデータをためるキューなどはこちらにある.
PCI コンフィギュレーション空間の BAR で MMIO 空間のアドレスを設定し,後は MMIO 空間のレジスタでいろいろ制御する感じになる.
"xHCI for Universal Serial Bus: Specification"(以降 "xHCI Spec" と書く)5.3 節によれば,規格書に出てくる MMIO レジスタは,すべて MMIO 空間の先頭を基準に配置されているらしい.規格書の中では一貫して MMIO 空間の先頭のことを Base と呼んでいる.
MMIO 空間の先頭(Base offset 00h)には xHCI Capability Registers が配置されている. Capability Registers の直後から Operational Registers が続く.
xHCI 規格書の 1.6 "Terms and Abbreviations" 参照.
- Device Slot
- "Device Slot" は個々の USB デバイスに紐づけられた xHC のインターフェース(DCBAA の 1 つのエントリ,1 つの Doorbell Array レジスタ,1 つの Device Context)を意味する.
- Device Context Base Address Array (DCBAA)
- 1 つの Device Slot を 1 つの Device Context データ構造と紐づけるもの.
- DCBAA の場所は Operational Registers の DCBAAP から指される.
- DCBAA は MaxSlotsEn + 1 個のエントリを持つ配列(となるようにプログラマがメモリを用意する).
- Device Context
- 個々の USB デバイスを記述するデータ構造.
- 1 つの Device Context はコンテキストデータ構造の配列.1 つの Slot Context と 31 個以下の Endpoint Context を持つ.
xHCI 規格書の 4.2 "Host Controller Initialization" にいろいろ書いてある. レジスタやメモリ上のデータ構造を初期化し,最終的に USBCMD レジスタの Run/Stop ビットを 1 にするとコントローラが動き出す.
- Host Controller Reset(USBCMD.HCRST に 1 を書き込む)
- PCI コンフィグレーションレジスタを除くレジスタ群が初期化される
- Primary Interrupter(Interrupter Register Set 0)の初期化
- R/S ビットを 1 にする前に Primary Interrupter レジスタ群を初期化する必要がある
- 最低限 Event Ring 関係のレジスタに設定を行う
この後,接続されているデバイスの列挙とそれぞれのデバイスの初期化を行う. やはり,電源投入時から接続されている USB デバイスについては UEFI が初期化してくれているので,やるべき作業はとても少ない. これは UEFI の実装に依るかもしれないが,UEFI の OSS 実装である OVMF(を載せた QEMU)では,USB デバイスの一般的な初期化は完了した状態で OS に処理が渡ってくる.
QEMU に -device usb-kbd
を指定すると PS/2 キーボードの代わりに USB キーボードが接続される.
OS に処理が渡った時点でこの USB キーボードは状態 "Configured" になっており,USB デバイスアドレスやいくつかのエンドポイントなどが設定済みだ.
ただ,USB キーボードから PC 方向のエンドポイントが Disabled になっているようなので,Configure Endpoint コマンドを発行する必要がある.
xHC に対するコマンドの発行は Command Ring というキューにより行う. Command Ring にコマンドを積み,xHC にコマンド発行の合図を送ると xHC がコマンドを読み取って動作する. OS が「プロデューサ」,xHC が「コンシューマ」の役目である.
Command Ring は 1 つの xHC インスタンスにつき 1 つだけ存在する. Command Ring は TRB(Transfer Request Block)というデータ構造をやり取りするためのキューだ. TRB にはいくつも種類があるが,Command Ring で処理できる TRB の種類は限られている.
Command Ring にコマンドを発行するやり方:
- 1 つ以上の Command Descriptor(Command TRB の別名)を Command Ring に配置する.
- Host Controller Doorbell を鳴らす.
コマンドを発行するためには,発行先の Slot をまず有効化する必要がある.Enable Slot コマンドにより有効化できる(xHCI 規格書 4.5.3).
コマンドに対する結果などが通知されるキュー. xHCI 規格書 4.9.4 Figure 19: Segmented Event Ring Example
Interrupter 毎に Event Ring が用意されている. 0 番目の Interrupter(Interrupter 0)に対する Event Ring を "Primary Event Ring",その他の Interrupter に対するものを "Secondary Event Ring" と呼ぶ.
各 Interrupter は Interrupter Register Set というレジスタセットを持っていて,その中の ERSTBA,ERDP がそれぞれ Event Ring のベースアドレス,Event Ring の読み出しポインタを示す. Interrupter Register Set は Runtime Registers(Capability Registers の RTSOFF でオフセットを取得できる)に存在する. Interrupter 0 のための Interrupter Register Set 0 は,Runtime Registers の 0x0020h にある.
各 Event Ring は実際にはいくつものセグメント(Event Ring Segment)からなる. 各セグメントのベースアドレスとサイズ(Event Ring Segment に格納できる TRB の数)は,Event Ring Segment Table(ERST)に設定されている. ERST がどこにあるかは,Event Ring Segment Table Base Address(ERSTBA,上述)レジスタに設定されている.
TRB のためのキューは 3 種類ある.Transfer Ring,Command Ring,Event Ring. Event Ring だけ,xHC がプロデューサ,OS がコンシューマとなる.他の 2 つは逆.
xHC は Producer Cycle State ビット(=PCS,初期値 1)を持っていて,書き込みポインタが Event Ring を 1 週するたびにビットを反転させる. xHC が Event TRB をキューに突っ込むときに PCS ビットを TRB の Cycle ビットに設定する.
OS も Consumer Cycle State ビット(=CCS,初期値 1)を保持し,読み込みポインタが Event Ring を 1 週するたびにビットを反転させる. 読み込んだ TRB の Cycle ビットが CCS と一致すれば有効な要素,一致しなければ無効な要素と判断できる. ビットが一致しない TRB を読み込んだら,その TRB の先頭アドレスを Event Ring Dequeue Pointer(xHC 側が保持する読み込みポインタ)に設定すると,xHC はそこまでのデータが全部 OS 側で処理されたと認識する.
例えば USB デバイスのディスクリプタを得たいときには,コントロールパイプ経由で Setup/Data/Status ステージを実行する. xHCI ではそれぞれ Setup Stage TRB, Data Stage TRB, Status Stage TRB というのが用意されている.
これらステージの実行例が Figure 7 にのっている.