RustでUSBマウスドライバを実装する
MikanOSのDay06C内で出てくるマウスドライバをRustで自作中です。
実装中に躓いた箇所などをここにメモする予定です。
xHCホストコントローラーのリセット後にEventRingにTRBが登録されない
- xHCのリセット
- xHCの初期化(ここでERDPなども登録)
- xHCをRun
- PortReset
上記の手順を踏むことで、通常ならEventRingにPortStatusChangeEventが登録されますが、全く反応せず...。
いろいろ試行錯誤した末に仕様書を読み直したところ、428PのEvent Ring Segment Table Base Address Register(ERSTBA)の項目に以下のような記載がありました。
Writing this register sets the Event Ring State Machine:EREP Advancement to the Start state.
ERSTBAに書き込む前に、ERDPとセグメントテーブルにセグメントのアドレスを書き込む必要があるようです。
英語力がなさ過ぎて読み飛ばしていました...。英語勉強しないといかんなぁ
SET_PROTOCOL後にNormalTRBを送信してもイベントリングにデータが一向に来ない!
ポーリング中に毎回ドアベルレジスタを鳴らすようにしたら来るため、エンドポイント自体は有効化できている...はず。
マウスの位置もしっかり記録されていました。
ドアベルレジスタのターゲットとなるDCIの値が違っただけでした。
マウスカーソルの位置が取得されない
動作環境
エミュレータ: QEMU
OS: Windows11(WSL + XLaunch)
マウス: G703(ワイヤレス)
QEMU上でマウスを動かしてもマウスカーソルの位置が取得できませんでした。ボタンは反応します。
本家MikanOSでも同様の動作になりました。
いろいろ試したところ、以下のような対応をした場合には取得できることを確認しました。
- QEMU上でCTRL + ALT + Gを押しながらマウスを動かす
- セカンダリモニタ側にQEMUウィンドウを移す
2に関しては、最も左に位置するモニタ側にウィンドウを移すことが条件?
ディスプレイの絶対座標が関係している?
うーん、やっぱりカーソル位置がおかしい
本家のほうでも位置ずれは発生しましたが、軌道が全然違うためどこかでミスってるっぽいです。
あと、途中から位置が取得できなくなる!
マウスカーソルの位置情報は前回からの相対位置とのこと。
データバッファをi8型の配列にして相対位置の計算処理を加えることで期待する動きに(若干位置ずれはあるが)!
ただ、途中で取得できなくなる問題はまだ未解決のため引き続き調査...したいところですがかなーーーりコードが乱雑になったため、リファクタリングを優先しよう...。
途中で取得できなくなる不具合は、コマンドリングの終端を示すLINK TRBにリングの先頭アドレスを指定していなかったのが問題だったみたいです。
無事動かせるようになりました!
ブートプロトコルのデータ形式は下のサイトの71Pageを参考にしました。
4~7Byteはオプションのようで、手元のマウスだと常に0になりました。
あと、本家MikanOSと同様に、マウスのサブスクライバを実装しました。
地味に躓いた箇所として、引数にはMouseSubscribableというトレイトを実装したものを渡しているのですが、このトレイトにCloneを付与した状態でBox<dyn MouseSubscribable>という形に出来ませんでした。
完全に理解できているわけではありませんが、dynは実行時まで型を判別できないため、Cloneのような具象型が返り値(またはレシーバ)になるメソッドを持つトレイトは使用できないということだと思います。
端的に言えば動的ディスパッチの場合、完全に抽象化されていることを保証しなければならない?
この問題は、dyn-cloneというライブラリを使って解決しました。