借り初めのひみつきち

仮ブログです。

UEFI自作OS日記 v0.6.1 ゆ。

前回の記事で紹介したように v0.6 は大幅なリメイクとなりましたが、それには大きな目的がありました。

github.com

現代の PC ではペリフェラル接続に主に USB を利用しています。
キーボードやマウスもデスクトップ PC では USB 接続が主流です。
モバイル PC は 10 年くらい前は内部的に PS/2 接続のものが多かったですが、ここ数年は USB 接続のものが増えてきています。
BIOS の時代は PS/2 ポートは正義でしたが、まもなく完全に消滅してしまうでしょう。

自作 OS も BIOSPS/2 の時代が終わって UEFI と USB の時代がきます。

USB が使えるようになれば色々なペリフェラルが接続できるようになります。 USB には未来があります。 USB に対応しないと未来はありません。

実は v0.5 の非公開バージョンで USB 対応を初めていてある程度のところまで動いていたものの、あと一歩のところで不可解なバグに悩まされる日々が続いていました。

そんなわけで、余計な肉を削ぎ落として安定させた上で USB 対応するのが v0.6 の最大の目的でした。

f:id:neriring16:20190825114723p:plain

PS/2 の時代は決まった I/O ポート (0x60 など) から入出力するだけでかんたんにキーコードを取得できましたが、 USB はいくつかの層に分かれた比較的複雑な構造になっており、「デバイスと1ビットのデータをやりとりする」状態にいくまでが大変です。

一方で「デバイスと1ビットのデータがやりとりできる」状態になってしまえばキーボードなんてたかだか数バイトのデータやりとりするだけので特別難しいことはありません。

USB キーボードは HID という規格の一部です。 HID 規格自体は人間とデバイスの間のあらゆるインターフェースを定義しようとした巨大な規格なので全てを網羅するのはけっこう大変ですが、多くの機種ではキーボードとマウスはブートプロトコルという機能を制限したモードで使えるようになっているので、そちらを使えば比較的かんたんに利用できます。*1

というわけで USB 対応のうちの多くの時間は xHCI との格闘の歴史だったわけですが、筆者がはまった箇所を覚えてる限りいくつか紹介しておきます。

ポート ID とスロット ID のオリジン

xHCI では USB デバイスはルートハブの各ポートに接続しており、これをポート ID で区別します。ルートハブのポートは全て筐体の外に出てるとは限らず内蔵のデバイスの接続にも利用しています。
各ポートに接続されたデバイスは適切な初期化をすると xHCI 内部のスロットという内部データ構造に割り当てられ、スロット ID で区別します。
初期化以降はスロット ID を通して通信する事になります。

初期化時やデバイスの抜き差しでポートの状態に変化があると Port Status Change Event というイベントが発生しますが、このイベントで通知されるポート ID は1から始まります。
一方、実際の xHCI のポートに相当する PORTSC などのレジスタ

ベース + 0x10 * (ポートID - 1)

のような計算式で計算します。実質0オリジンです。
この「-1」を忘れると、イベントの発生したポートと実際にレジスタを操作するポートが1ずれる事になっておかしな事になります。

また、スロット ID も1から始まりますが、実際のスロットに相当する DCBAA という配列の項目も1から始まります。
0番目の項目は Scratchpad Buffer という xHC が内部で利用する内部で利用するバッファーの指定に使います。
これが指定されていないと、ポートの初期化などは一見普通に動作するものの xHC が Scratchpad Buffer に依存しているコマンドを実行しようとした時に再起動などの怪しい挙動します。

コントロール転送の Status Stage

USB のコントロール転送には Setup, Data, Status という3つのステージがあります。
USB プロトコルでは全ての転送命令の方向が決まっていて、 Setup Stage は常にホストからデバイスに対する出力となります。
Data Stage の方向は Setup Stage で指定する値によって異なりますが、入力したいのか出力したいのかはあらかじめ決まっているのでそんなに難しくないでしょう。
問題は最後の Status Stage ですが、なんと Data Stage の方向によって変わります。また Data Stage は存在しないこともよくあります。

これをまとめると以下のようになります。

f:id:neriring16:20190825124100p:plain

方向を正しくを設定しないと USB Transaction Error となって以降の通信がうまくいかなくなります。

キーコードの変換

実は moe は当初から USB 対応を見込んで設計されていてキーコードも PS/2 のコードではなく HID 規格のものをベースとして変換しているので、特に問題なく動作しました。

TL; DR

まだまだ問題も多いですが、とりあえず USB キーボードから入力できるようになりました。

*1:ブートプロトコルにしか対応してないキーボードや逆にブートプロトコルに対応していないキーボードもあります。