osdev-jpでは、OS開発に有用な情報を収集し公開しています

View My GitHub Profile

Initialize a USB host controller

このページ及び後続のページ群は xHCI 規格に準拠したホストコントローラを対象とする. 動作確認は MinnowBoard Turbot で行った.

説明用のコードは C 言語風の疑似コードで示す.

References

参考文献を挙げる.コロンの前はこの記事中で参照するときの略称.

Reset a host controller

まずハードウェアリセットを行う.

リセットを行うには USBSTS レジスタの HCHalted ビットが 1 でなければならない.

if ((USBSTS & 1u) == 0) error();

USBCMD レジスタの Host Controller Reset ビットに 1 を書き,0 になるまで待つ. 続けて USBSTS レジスタの Controller Not Ready ビットが 0 になるまで待つ.

USBCMD |= 1u << 1;
while (USBCMD & (1u << 1));
while (USBSTS & (1u << 11));

Prepare data structures

ドライバで有効化する Device Slot の数を設定する.この数を大きくするとメモリ使用量が増え,xHC がより多くの USB デバイスを同時に扱える.設定できる数は 0 から MaxSlots まで.MaxSlots は HCSPARAMS1 レジスタから読める.

#define NUM_DEVICE_SLOT 8
CONFIG = (CONFIG & 0xffffff00u) | NUM_DEVICE_SLOT;

Device Context ベースアドレス配列(DCBAA: Device Context Base Address Array)を用意する.これは Device Context へのポインタの配列である.DCBAAP レジスタにその配列のポインタを設定する.

alignas(64) struct DeviceContext* dcbaa[NUM_DEVICE_SLOT];
DCBAAP = dcbaa;

struct DeviceContext の具体的な構造は xHCI Spec 6.2.1 を参照.alignas(64) は,その変数が 64 バイト境界に配置されるようにコンパイラへ指示するという意味だ.xHCI で規定されたデータ構造それぞれにバイト境界の制約があるので注意すること.

Command Ring を管理するための構造体を作る.

#define RING_SIZE 8
struct RingManager
{
    struct TRB buf[RING_SIZE];
    int cycle_bit;
    int write_index;
};

Command Ring を用意し,そのポインタを CRCR レジスタに設定する.

alignas(64) struct RingManager cr;
memset(cr.buf, 0, sizeof(cr.buf));
cr.cycle_bit = 1;
cr.write_index = 0;
CRCR = (uint64_t)cr.buf | cr.cycle_bit;

CRCR レジスタの Ring Cycle State ビットは,ホストコントローラが Command Ring から Command TRB を取得する際に参照する cycle bit の初期値として設定される.

Event Ring セグメントテーブルと,個々のセグメントを用意する.xHCI Spec Figure 19 を見ると複数のセグメントを数珠繋ぎにする構成にもできるが,動作速度などを気にせずに動かすだけならセグメント数は 1 で良い.

#define ERSEGM_SIZE 16
alignas(64) TRB er_segment[ERSEGM_SIZE];
alignas(64) struct ERSegmentTableEntry erst[1] = {
    (uint64_t)er_segment, ERSEGM_SIZE
};

これで要素数 1 の Event Ring セグメントテーブルが用意された.ERSTBA レジスタと ERSTSZ レジスタへ,セグメントテーブルのベースアドレスと要素数を設定する.

ERSTSZ = 1;
ERSTBA = (uint64_t)erst;

ERSTBA レジスタへ書き込むと,ホストコントローラ内部の Event Ring ステートマシンが初期化され Start 状態に戻る.xHCI Spec Firure 20 参照.

Event Ring の producer cycle state を準備する.この変数は Event Ring が一周するたびに 1,0 が入れ替わる.

int er_cycle_bit = 1;

Start a host controller

Device Slot,Command Ring,Event Ring の準備が整ったらホストコントローラの動作を開始させることができる.USBCMD レジスタの Run/Stop ビットに 1 を書き込むことで開始させる.

USBCMD |= 1u;