かつては「自宅から車で行けるOSCには出る」という方針でイベント駆動で NetBSD謎マシン展示をしていたのですが、2020年1月のOSC大阪を最後に物理OSC開催がなくなってしまい、2021年はついに一度もOSCその他のNetBSDマシン展示発表をしない年になってしまいました。10月以降はmikutterにどっぷりでしたが
じゃあ昨年はNetBSD的に何をやっていたのか、ということで、 NetBSD CVSリポジトリのコミットログメッセージが流れてくる source-changes の mailing list の情報をベースに、2021年のNetBSD活動を振り返ってみようと思います。
2021年のコミット数
source-changes の mailing list はそのままでは集計や検索がしづらいので、 NetBSD developer の ryo@ さんがメンテしている NetBSD Source-Changes commit log search から情報を拾ってみると、2021年に自分のコミット数は 86回 でした。
これが多いのか少ないのかと考えると少なくはないのでしょうが、2020年は NetBSD/sun3 の Xorg サーバー実装とか MIPS Windows NTマシン Express5800/230 の電源修理からの NetBSD/arc 復活とか フランスから送られてきた HP9000/300 フレームバッファの Xサーバー用ビットマップオペレーション実装とか自分的に満足度の高い作業をやっていた反動で、2021年はなんとなく地味に感じてしまっていたのでした。いろいろやってるのにそれを展示してないからなおさらか
とはいえ、改めて 2021年の自分のコミットを見ていると、それなりに力を入れて書いたものもあります。そういうものをいくつかピックアップしてみました。
NetBSD/x68k Xサーバー 3ボタンマウスエミュレーション
まず 2021年2月7日コミットのこれ。
X68030の純正マウスは2ボタンなのですが、過去に自分が X11R7対応したXサーバーでは真ん中ボタンのいわゆる3ボタンエミュレーション(左右ボタン同時押しで中ボタンが押された扱いになる)が実装されていませんでした。
これ自体はだいぶ前から気になっていたのですが、2020年秋ごろからサーベイ自体はしていて、「各Xサーバー共通で使える3ボタンエミュレーション実装はない」ということはわかっていました。実態としては、旧XFree86ベースのxf86-input-mouseドライバ、 Linux evdev用ドライバ、 Windows Xサーバー実装の Xwin それぞれで別の実装がされている状態でした。
それぞれがまた歴史が積み重なった実装で読むのが大変なのですが、最終的には状態遷移をきっちり設計してあるっぽい xf86-input-mouse のマウスドライバ実装の状態遷移部分を他のインターフェースから分離する形で持ってきて、 NetBSD/x68k に限らずなるべく汎用な形で流用ができるインタフェース設計と「読めばわかる」構造体その他の実装定義を考えた上で NetBSD/x68k サーバーに組み込んだ形でコミットしました。
「左右同時押しで中ボタン扱い」という要件自体は簡単なように見えるのですが、「同時押しといってもどれだけのズレを許容するのか」「同時押しを待っている間に離されたらどうするのか」「同時押しの後に片方だけ離したらどうするのか」「中ボタンのダブルクリックは可能なのか」と言ったことを考え出すと結構難しい設計が求められます。
また、マウスのイベント自体も、PS/2やバスマウス、各種ワークステーションで定義が異なる、OSのデバイスドライバごとでも定義が異なる、Xサーバー側プロトコルも明示的なドキュメントがない、という混沌状態で、「コードから定義を推測する」という練習にもよいかと思いました。
なお、最初は X68030 エミュレータである XM6i 上でテストをしたところまったく意図通りに動かず調べるのに苦労したのですが、「ホストである NetBSD/i386 側の Xサーバーでも 3ボタンエミュレーションが有効になっているため先にそちらにイベントを取られてしまっている」というしょうもない罠にハマりました。3ボタンエミュレーションを無効にするには /etc/X11/xorg.conf
のマウス以外の各セクションをすべて書く必要があり、これも結構面倒でした……。
NetBSD/x68k X68kサーバーに xf86-input-mouse の 3ボタンエミュレーションを持ってきて、30年前の謎3次元配列ステートマシン実装を凡人が読んでもわかるように構造体に直したりして一応意図通り動いているっぽいのだけれど、様々な困難によりエミュレータではUX試験ができないという問題 pic.twitter.com/ogpU82rIO1
— Izumi Tsutsui (@tsutsuii) 2021年2月3日
Emulate3Buttons tests for NetBSD/x68k Xorg 1.20.5 based monolithic X server
— Izumi Tsutsui (@tsutsuii) 2021年2月3日
とりあえずそれっぽく動いている感じ。
これをちゃんと動くようにするにはカーネル側の ms(4) ドライバも変更しないといけないけど、いまさら従来動作との互換性とか気にしなくてもいいですかね……。 pic.twitter.com/SdWDBL9Bct
NetBSD/m68k カーネルスタックオーバーフロー問題修正
次に結構苦労したのが、この 2021年2月23日の NetBSD/m68k カーネル全般のスタックリークの修正。
前述の 2020年の NetBSD/sun3用 X11R7サーバーを動かしていると「おもむろにカーネルスタックオーバーフローっぽい症状で panicする」という現象が起きていました。
kernel: Address error trap
— Izumi Tsutsui (@tsutsuii) 2020年8月11日
NetBSD/sun3 9.0 で Xserver を起動してクライアントをいろいろ動かしていると結構な頻度でこの panic が発生するのだけれど、 stack trace がまともに出ないのは kernel stack overflow なのか KVA shortage なのか pic.twitter.com/sEeO5euBaY
ただ、カーネルスタックが壊れているためにバックトレースが取れないという問題がありました。カーネルスタックを2倍以上に増やしても発生するため「単なるスタック不足ではない」ということは見えていたのですが、そもそも「スタックがリークする」という現象がどういうシナリオで起こり得るのかがずっとわからないままでした。
その後、 NetBSD/x68k の3ボタンマウス修正に伴うカーネルクラッシュダンプ修正の作業を経て、前項の NetBSD/x68kサーバーでも起きていたカーネルハングアップの現象が NetBSD/sun3 のスタックオーバーフローと同じ現象らしい、ということがわかってきました。
マウスの中ボタンエミュレーションのテストをしたいのに 撮影用に仕込みをしてると Xサーバーが落ちたりカーネルがハングったりで 中ボタン押し模擬状態の撮影ができない問題 pic.twitter.com/HInvSGG3ND
— Izumi Tsutsui (@tsutsuii) 2021年2月4日
https://t.co/mkr6aR8Egj
— Izumi Tsutsui (@tsutsuii) 2021年2月6日
src/sys/arch/x68k/x68k/machdep.c r1.195 の変更のうち cpu_init_kcore_hdr() の変更を元に戻したら crash dump も savecore(8) も正常に動くようになった。 crash dump にしか影響しないとは言え、こんなマージミスに 4年も気づかないようではダメダメ(ヽ´ω`) pic.twitter.com/Evgmmxry4R
NetBSD/x68k で kernel crash dump と savecore(8) 動くようになって Xサーバー起動中の panic 後のダンプも取れたけれど、 Address error ってそこはかとなくカーネルスタックオーバーフローという予感がする……(5年くらい前にもそんなのが出ていた気がする) pic.twitter.com/36bwqa8Pqh
— Izumi Tsutsui (@tsutsuii) 2021年2月7日
XM6i上でも再現するということがわかったので、ひたすら頭悪く実行トレースを取るという手法で調査を進めました。
m68k/sig_machdep.c の cpu_setmcontext() から reenter_syscall() に飛ぶ直前に Debugger() に落とした画面。
— Izumi Tsutsui (@tsutsuii) 2021年2月15日
&sz のスタックは 0x3999ab0 で正常(?)な値。目trace すると
3999ab8 の 161520 が setucontext()
3999ae4 の 19334e が sys_setcontext() で、これが 1200バイトくらいスタックを使う pic.twitter.com/DPcE1Lh7Wk
breakpoint を張れないので、何も考えずにひたすら step/p を連打して syscall() → syscall_plain() → sys_setcontext() → setucontext() → cpu_setmcontext() にたどり着くまでひたすら目でトレース pic.twitter.com/LjPjzUaE7w
— Izumi Tsutsui (@tsutsuii) 2021年2月15日
いろいろやっていたら
— Izumi Tsutsui (@tsutsuii) 2021年2月16日
cpu_setmcontext() リターン直前は stackadj=0, pc=0x0415cb4a, format=b
syscall() リターン時は stackadj=0x54, pc=0x000f2408, format=0
さらに次に呼ばれた cpu_setmcontext() で trap0 時点のカーネルスタックが 0x54バイト上にズレている
というのは観測できた pic.twitter.com/Zz8EQes4aw
MC68030マニュアルや各種実装と突き合わせた結果、「シグナルハンドラ関連でコンテキストを切り替える際に m68kのトラップ要因によってはカーネルスタックをずらす必要があるがその処理が抜けている」らしい、という仮説にたどり着きました。
NetBSD/m68k の stackadj がどこで更新されているかを調べる元気が出ないので、対処療法的に reenter_syscall.s で syscall() から戻ってきた後に stackadj が 0じゃなかったら雑にスタックをその分ずらすコードを入れたらとりあえずスタック消費はなくなったような……https://t.co/RGNKllp3qu pic.twitter.com/3LvTF6aOuO
— Izumi Tsutsui (@tsutsuii) 2021年2月20日
今回問題の cpu_setmcontext() → reenter_syscall() の流れで何が起きているかを整理すると
— Izumi Tsutsui (@tsutsuii) 2021年2月20日
(1) format 0 分の stack しかないけど format B で戻りたい (address error 関連なのでメモリ不足の時起きる?)
(2) reenter_syscall() で format B 分の stack を確保して format B 分の stack 領域を用意
(3) reenter_syscall() から cpu_setmcontext() に戻ってきて stackadj が format B 分確保されているのを確認して従来退避した format B の内容を戻して stackadj を 0 に戻す
— Izumi Tsutsui (@tsutsuii) 2021年2月20日
(4) stackadj=0 のまま cpu_setmcontext() はリターンする
(5) 実際に system call からユーザーランドに戻る時点ではなぜか stackadj=84 かつ format B の例外フレームがスタックにある状態になっている。これは恐らく cpu_getmcontext() が呼ばれている?
— Izumi Tsutsui (@tsutsuii) 2021年2月20日
(6) このまま trap0 から rei に戻ると ast trap の処理をしない限り stackadj の調整がされない?
というわけでとりあえず reenter_syscall から system call を読んだ後に戻ってきた場合(実際に reenter_sysacall を呼ぶのは COMPAT_LINUX を除けば cpu_setmcontext() だけ)に stackadj を見て 0 でなければ戻すという処理もおかしくはないと思うのだけれど
— Izumi Tsutsui (@tsutsuii) 2021年2月20日
cpu_getmcontext() にも debug printf を入れたところ
— Izumi Tsutsui (@tsutsuii) 2021年2月20日
① setcontext(2) 呼び出し
② format B 条件で reenter_syscall() 呼び出し
③ 再入した cpu_setmcontext() で format B 設定後 stackadj=0 で return
④ syscall の return 前に cpu_getmcontext() で FMTB の stackadj 調整
の動作は観測できた pic.twitter.com/fZXAjgnj94
よって https://t.co/XlTgGCG7Zl で書いた仮説の裏付けにはなっている?
— Izumi Tsutsui (@tsutsuii) 2021年2月20日
なおこの引用ツイート中の (5) の「stackadj=84 かつ format B の例外フレーム」の「format B」は誤記で「format 0 の例外フレーム」が正解だったので、お詫びして修正します
このあたりのツイートを拾い出すとエンドレス Togetter セルフまとめになるので詳細はまた別途
確信には至らない状態でバグレポとして投げたところ、 NetBSD開発者の thorpej氏からも「その修正で妥当」というコメントもいただくことができました。
こうやって結果が見えてくると理屈もわかるのですが、メモリフォールトのトラップがトリガであるため「メモリが極端に少ないマシン」で「激しくページアウトするような負荷をかけた場合」にしか起きないこともあり、結果としてこのバグは10年以上も残ったままになっていたのでした。今でも m68kマシンを「使っている」ような人はたいてい MC68060でメモリフル実装だったりする
いずれにせよ、これで X68030 や sun3/60での Xサーバー展示デモにおける「そもそもXを起動すると落ちる」という問題が一つ解消したはずなので、早く物理OSCが復活してほしいところです。
なお「目スタックトレース」という作業は一部の方には異様なものとして映った模様。慣れればそんなに大変じゃないんですけどね
目スタックトレース… https://t.co/eRxLY0NL1k
— きくちゃん (@kikuchan98) 2021年2月15日
HP9000/300 HP-IBディスクサポート改善
- Pull HP-IB probe fixes from OpenBSD/hp300.
- Add support of multiple rd(4) disks on all punits for HPDisk.
これはカーネルの改善というよりは実ハードウェアのテスト作業がメインでした。
HP-IBというよりも GP-IB あるいは IEEE 488 といったほうが通じやすいかと思いますが、もともとは HPが自社計測機用に作ったバスプロトコルおよび物理層の規格が HP-IBです。計測機用規格といいつつ、パソコン用にディスクドライブやプリンタなども接続可能で、 NetBSD/hp300 でもそれらのデバイスのサポートが入っています。
私自身は 2001年頃に NetBSD/hp300 を動かし始めてからいろいろあって今では NetBSD/hp300 の機種担当という立場にあるのですが、 2020年まで一度も HP-IBのデバイスを NetBSD/hp300でテストしたことがないまま、という状況でした。
HPDrive
機種担当であるにも関わらず HP-IBの動作がずっと未確認というのはよろしくないなと思っていて HP-IB関連情報を過去にサーベイした際に、Windows PC + HP-IBインターフェースの組み合わせで HP-IB デバイスを模擬する HPDrive というのものを見つけていました。
この HPDriveで使える National Instruments製 PCI GP-IBインターフェースボードがヤフオクで手頃な値段で出ているのを見かけたのが 2020年の年末。
その後の GP-IB用ケーブル手配その他を経て、無事に NetBSD/hp300 で動作確認できたというの以下の 2021年1月9日のツイート。
HP-IB 7937H 544MB hard drive emulated by "HPDrive" on Windows 10 with PCI NI GPIB I/F just works on NetBSD/hp300 9.1 via nhpib(4) on HP 9000/425thttps://t.co/8M3tkU9j9B pic.twitter.com/x0xEEdNiTP
— Izumi Tsutsui (@tsutsuii) January 8, 2021
HP-IB interface on HP9000/425t and Win10 PC with National Instruments PCI-GPIB TNT5004 board pic.twitter.com/M17DK4jJef
— Izumi Tsutsui (@tsutsuii) January 8, 2021
とはいえ、このときは「何もしなくても動いた」という状態だったので、特にこれといったコミットはありませんでした。
HPDisk
HP-IB関連サーベイの中では、前述の HPDrive の他に「PICマイコンとSDカードで HP-IBディスクを模擬する」という HPDisk というものも見つけていました。
別PC設定というひと手間が必要な HPDriveと違い実デバイスと同じようにつなぐだけで使えるこちらもいいなと思ってはいたのですが、配線基板を起こす、もしくは入手する手間を考えると「すぐに試してみる」というわけにも行かず、いったん保留にしていたのでした。
そんな過去経緯のあと 2021年4月上旬になって、この HPDiskの作者の方から「HPDiskのテストのために HP9000/340 に入れた NetBSD/hp300 が起動しないのだけれど」という相談を受けました。この起動問題自体についてはざっと調べて「古いモノクロフレームバッファの probeルーチンで存在しないカラーパレットをアクセスしているため」という既知の問題が放置されていただけというのがわかり、修正して無事に起動するようになりました。
このやり取りの中で「自分でハンダ付け工作できるなら基板送るよ」と言われて「ぜひぜひ」とお願いして部品発注その他を経て作り出したのが 2021年5月。
"GPIB disk emulator" https://t.co/gXvtdeQdym
— Izumi Tsutsui (@tsutsuii) 2021年5月2日
NetBSD/hp300 つながりでこちらの HPDisk MKII の PCB を送っていただいていたのですが、 Mouser および秋月その他に発注していた PICマイコン以外の部品が届いたので、とりあえず作り始めます #HPDisk
https://t.co/gXvtdeQdym
— Izumi Tsutsui (@tsutsuii) 2021年5月2日
"We now have bare boards for sale. See below for details."
ということで基板通販もあります #HPDisk pic.twitter.com/afiibYKkxA
部品は 1608 その他の面実装品が多数必要なので Mouser で手配したほうが手っ取り早い感じ。MicroSDソケットその他の一部部品は秋月電子で手配。肝心の PICマイコンはまだ届いてなかったり #HPDisk pic.twitter.com/WIE16sQijQ
— Izumi Tsutsui (@tsutsuii) 2021年5月2日
じわじわ始まっていた半導体需要逼迫の影響で PICマイコンの手配に手間取りましたが、PICライタも入手して動かし始めたのが5月連休明けくらい。
はんだ付けチェックもそこそこに、おもむろに PICKit3 をつないで書き込み用の MPLAB IPE を起動して雑に #HPDisk pic.twitter.com/1AsbBpIaEY
— Izumi Tsutsui (@tsutsuii) 2021年5月9日
んー。
— Izumi Tsutsui (@tsutsuii) 2021年5月9日
rd0 としてなにかしら認識しているようなのでなんとなく応答は返しているっぽいけど、中身が正しくない感じ。これは設定ファイルの書き方の問題なのか #HPDisk pic.twitter.com/A8PvRDeh9t
これも個別作業のツイート引用をしだすと終わらなくなるので詳細は別の機会に回しますが、「NetBSD/hp300ほど HP-IBプロトコルを強烈に使う testsuit はない」というコメントをもらう状況で、こちらからデバッグログを送っては更新版の Firmwareを送ってもらう、ということを10回近く繰り返すこととなりました。
最終的には 2021年7月8日の Firmware v0.20で NetBSD/hp300 でも HPDisk の一通りの機能が動くようになりました。この作業の中で、従来の NetBSD/hp300で正しくサポートされていなかった「複数スレーブデバイスディスクのサポート」についても HPDisk側で任意のドライブを指定できるように改善してもらったため、別途教えてもらっていた OpenBSD/hp300 の修正も合わせて持ってくることにしました。
Yes, part of https://t.co/yYBtSiXomT . Note the problem only arises when the "multiple-function" device has multiple functions of the same type. i.e. ct/rd will attach, but not rd/rd or ct/ct which would only attach punit 0.
— Miod Vallat (@MiodVallat) January 9, 2021
Pulling HP-IB probe fixes from OpenBSD/hp300 to NetBSD/hp300 to use multiple rd(4) disk images on the same slave using HPDiskhttps://t.co/QqTIOCbFTW pic.twitter.com/4vYurUC6K9
— Izumi Tsutsui (@tsutsuii) June 29, 2021
コミット内容としては微妙に地味だったりするのですが、上述のように NetBSD/hp300に対する問い合わせがきっかけで久しぶりにハードウェア工作をしたり実機デバッグをしたりという交流を含めて楽しく作業をさせてもらいました。
NetBSD/hp300 HP-IB improvements on 2021 😀 https://t.co/DOj68z7H2a
— Izumi Tsutsui (@tsutsuii) July 9, 2021
なお、HPDiskの作者の方にもこの更新の件を取り上げていただいていますのでこちらもどうぞ。
LUNAキーボードのCAP/かなLEDおよびブザーコマンドサポート
- Ignore mouse packets properly even if wsmouse(4) isn't configured.
- Implement transmitting keyboard LED and buzzer control commands.
- Handle CAP and Kana modifier key specification quirk of LUNA's keyboard.
- Refactor and cleanup sio (uPD7201) drivers.
- Handle wskbd(9) .set_leds op in cngetc(9) using wskbd_cnattach(9) cookie.
最後は9月から10月にかけて作業したこれら OMRON LUNAの NetBSD/luna68k キーボードドライバ関連のコミットです。
マイナーすぎるにもほどがある と言われそうですが、きっかけは LUNAエミュレータnonoの作者の一人である @moveccr さんのこのレポート。
- NetBSD/luna68k ブートローダでマウスを動かすと誤入力が発生する問題のパッチ · GitHub
- NetBSD/luna68k NWSMOUSE=0 のときにマウスが接続されているとキーボードに誤入力が発生する問題のパッチ · GitHub
LUNAキーボード マウスON/OFFコマンド
この問題を説明しだすとまた記事が長くなってしまうのですが、ざっと書くと
「LUNAのキーボードとマウスは同じシリアル通信ラインで信号が送られる」
「マウス信号のデータはキーボード送信データ 0x00〜0xFF のバイトデータのうち 0x80〜0x87 のデータをヘッダマークとして使ってボタン情報と移動量情報を送る」
「NetBSD/luna68kカーネルはコンフィグでマウスが無効だとマウスデータの処理を行わない」
の3つを合わせると
「(マウスを使わない)ブートローダーとマウス無効のカーネルでマウスを動かすとマウスデータをキーデータと誤認する」
という問題です。
LUNAキーボードの仕様については過去に仕様書を見てざっと把握していたので「そう言われればそうだよな」と思いつつ実機でテストしてみたのですが、どうも再現しません。
この原因は
「LUNAのキーボードにはマウスON/OFFコマンドがある」
「実キーボードは起動時にマウスON状態の仕様だが、LUNA実機のROMは起動後にマウスOFFコマンドを送っている」
「NetBSD/luna68kのカーネルも起動時にマウスOFFコマンドを送っていて /dev/wsmouse
を open(2)
するときにマウスONコマンドを送っている」
「エミュレータであるnonoではマウスON/OFFコマンドが未実装」
ということでした。
ただ、実機でも起動後にキーボードを抜き差しした場合は同じ現象が再現します。
というわけでこの修正をしたのが冒頭のコミット5件のうち最初のもの。
CAP/かなLEDコマンドとブザーコマンド
この「マウスON/OFFコマンド」の修正と見出しの「キーボードのCAP/かなLEDおよびブザーコマンド」に何の関係があるかというと、「LUNA本体→キーボードにシリアルデータとしてコマンドを送信する」という処理の部分です。
この問題が持ち上がった 2021年9月時点の NetBSD/luna68kカーネルでは、LUNAのキーボードの仕様として定義されている機能のうち、キーボードLEDコマンドの制御とブザーコマンドの制御については未実装で、マウスON/OFFコマンドのみが暫定に近い形で実装されているのみでした。
マウスON/OFFのコマンドの実装としては「カーネルが起動時にマウスOFF」「デバイスの open 時にマウスON、 close時にマウスOFF」「同時にオープンできるデバイスは1つのみ」というものだったので、同時に複数のコマンド送信要求が発生することはありません。このため、マウスON/OFFのコマンドはシリアル通信ICのuPD7201のレジスタを調停無しで直接アクセスする実装になっていました。
マウスコマンド以外のLEDコマンドやブザーコマンドはユーザーランドプロセスからの入力をトリガにシステムコールで呼ばれる場合があります。また、CAPキー入力とブザー出力とが同時に発生するケースもあるため、これらを適切に処理するためには「デバイスドライバ側で送信データキューを用意してソフトウェア割り込み等を用いて送信キューデータを順次送信する」といった実装が必要になります。
この送信キュー処理については NetBSD/sgimips の R3000系マシンの実装を参考にサクッと(?)記述しました。
wscons(4) の LED・ブザー実装
詳細実装以前のインターフェース設計の問題として、NetBSDの MI (Machine Independent つまり各機種共通仕様) のスクリーン+キーボードのコンソールドライバである wscons(4) の仕様として、キーボードLEDのON/OFFやブザー出力要求がどのような経路でデバイスドライバまで到達するのかも調べる必要がありました。
調査においては、もともと wscons の仕様を説明した文書がほとんどないこと、LEDやブザーをキーボード側にコマンドとして送信する機種(主に NetBSD/alpha, NetBSD/pmax, NetBSD/sparc, NetBSD/sgimips といった Sun, DEC, SGIといったワークステーション)でドライバをちゃんと実装したものがほぼなかったことから NetBSD の src/sys/dev/wscons および各機種のコードをかなり読み込むハメになりました。
またセルフまとめ状態で長くなってしまいますが、調査の過程についてはTwitterで適当にメモっていたのでそのまま引用します。
wscons(4) まわりのハマりポイントメモを転記
— Izumi Tsutsui (@tsutsuii) 2021年9月15日
- wscons(9) ではブザーが wskbd(4) に紐付いている
- ttyE0 の wsdisplay(4) でブザーを鳴らす時は WSKBDIO_BELL の ioctl が呼ばれる
- wsbell(4) というものがあるがこれはキーボードと独立したデバイスの spkr(4) を wscons のブザーに関連付けるもの
- ブザーが wskbd(4) に紐付いているのはワークステーションではそういうキーボードが多かったから?
— Izumi Tsutsui (@tsutsuii) 2021年9月15日
- 一方 WSKBDIO_BELL の ioctl(4) がまともに実装されているキーボードデバイスは1つもない?
https://t.co/GwIMkAD2uo
- mac68k/dev/akbd.c にはあるが WSKBDIO_COMPLEXBELL と同じI/Fなのは間違い
- wskbd(4) の man の WSKBDIO_*BELL の説明で (struct wsmouse_repeat) とあるのは (struct wskbd_bell_data) の誤り
— Izumi Tsutsui (@tsutsuii) 2021年9月15日
- wskbd(4) の man に WSKBDIO_*BELL の struct wskbd_bell_data の説明がなく wsbell(4) には説明があるが場所が適切でない気がする
- wsbell(4) にはそもそも major の定義がない?
WSKBDIO_BELL, WSKBDIO_GETBELL, WSKBDIO_SETBELL, WSKBDIO_COMPLEXBELL, WSKBDIO_GETDEFAULTBELL, WSKBDIO_SETDEFAULTBELL の各 ioctl(2) の仕様は wsbell(4) の man に一通り書いてあるhttps://t.co/lUImf19Unm
— Izumi Tsutsui (@tsutsuii) 2021年9月15日
WSKBDIO_BELL の ioctl を呼んでもブザーの音が the default bell にならないので調べたら
— Izumi Tsutsui (@tsutsuii) 2021年9月17日
「wskbd に WSKBDIO_BELL の ioctl を投げるとキーボードドライバの WSKBDIO_COMPLEXBELL ioctl が pitch=1500Hz period=100ms で呼ばれる」
(つまりドライバ側 WSKBDIO_BELL は呼ばれない)ということが判明
echo ^G しても lunaws.c では WSKBDIO_COMPLEXBELL が呼ばれているのだけれど、 wsdisplay(4) 的には WSKBDIO_BELL を呼んでいるように見える。https://t.co/eqTx8lokN4https://t.co/a9vLa29jEj
— Izumi Tsutsui (@tsutsuii) 2021年9月17日
どこから何がどう呼ばれているのやら
とりあえずでユーザーランドプロセスからの要求に対しては以下のように対処すれば良いということはわかったのでこれで実装。
- CapsLockその他のLEDについては、デバイス固有の処理として
WSKBDIO_SETLEDS
(とWSKBDIO_GETLEDS
) の ioctl(2) だけを実装すれば良い (それより上位は wscons(4) 側で対処される) - ブザーについては、デバイス固有の処理として
WSKBDIO_COMPLEXBELL
の ioctl(2) だけを実装すれば良い (それ以外は wscons(4) 側で対処される)
この修正が冒頭の5コミットのうち2つ目の修正。
LUNAキーボード CAP/かなキー挙動対応
前項のLED調査の過程で LUNAの CAPキー および かなキー の動作は
「キーを押してLEDが点灯するときに PRESSイベントを送信」
「キーを押してLEDが消灯するときにRELEASEイベントを送信」
「キーを離したときはイベントを送信しない」
という「いにしえのメカニカルロックキーをエミュレーション(?)した動作」であることが改めて確認されました。PS/2やUSBではこれらの modifier キーについても物理イベントは他のキーと共通(なのでドライバレベルでの入れ替えも可能)
NetBSD/luna68k カーネルではこれに対応しておらず CAPキーの操作とLED状態とCapsLock状態が一致しないという問題があったため、今回合わせて修正しました。これが3つ目のコミット。
また、nonoのキーボードエミュレーション実装の挙動が実機と異なるようだったので報告。これは nonoさんの次のバージョンで修正されることになりました。
https://t.co/597wRUD4WC
— Izumi Tsutsui (@tsutsuii) 2021年9月15日
LUNAのキーボードCAP・かな押下時に送出されるキーコードが実機と異なる?
NetBSD/luna68k のキーボードドライバをいじっている最中にハマったので投げました
uPD7201 sio(4) ドライバ整理
このあたりの uPD7201シリアルドライバの実装を見ていると、過去から気になっていた記述が思い出されて気になってしまいました。せっかくだから、と過去経緯を含めて調べて「コードを読めば何をしているかわかる」という方向にすべくいろいろ調査した上で見直しを検討。
NetBSD/luna68k sio 整理続きというか実装バグメモ。
— Izumi Tsutsui (@tsutsuii) 2021年9月19日
- sioreg.h で WR2A を 0x02, WR2B を 0x12 で定義している
https://t.co/RE0gHdOOlB
- それをそのまま setsioreg() のレジスタ番号に渡している
https://t.co/Xe3GHoUhlO
→これは誤り。CR0 (WR0) の仕様としてレジスタ番号は3ビットしかない。
CR2B に書くなら channel B 側の CR2 にアクセスする必要があり 4.4BSD ではそうなっている。https://t.co/t0RiKFwz1Q
— Izumi Tsutsui (@tsutsuii) 2021年9月19日
4.4BSD の sioreg.h には CHANNEL(r) というマクロがあり、これがチャンネル指定を意図していたが未使用のまま WR2A, WR2B の定義が残っていると思われるhttps://t.co/Ldm68UJOr9
未使用なので影響はないが、 sioreg.h に RR0〜RR4 が 0x08〜0x0C で定義されているhttps://t.co/OQcEiF9t47
— Izumi Tsutsui (@tsutsuii) 2021年9月19日
- 読み出しは SR0〜SR2 まで。 RR3, RR4 は余計
- ビット 0x8 は uPD7201 の仕様ではなく 4.4BSD の sioreg.h の isStatusReg(r) マクロ用https://t.co/aPpuLIC1ubhttps://t.co/w5BbJqXmQS
本題の NetBSD/luna68k の sio (uPD7201 シリアル) の気になっていた構造を見直してみた差分 (WIP)https://t.co/E30bMkNVBj
— Izumi Tsutsui (@tsutsuii) 2021年9月19日
とりあえず LUNA実機で キーボード/フレームバッファスクリーンコンソールは ddb(4) 含め動いているけれど、シリアルコンソールは未テスト
このあたりを整理してコミットしたのが4つ目のコミット。
カーネルメッセージ出力とコンソールドライバ実装
この uPD7201ドライバの整理と並行して考える必要があったのが「ユーザーランドプロセスだけではなく、カーネル内 printf(9) によるコンソール処理についてもLEDとブザーの処理が必要」という部分。
シリアルコンソールの場合、 LEDの処理はなく、ブザーについても単にブザーに相当する ASCII制御コードを送信するだけです。よってカーネル側 printf(9) についても特別な処理はなく、 cnputc(9) 相当の関数でデータを送信しているだけです。
一方、キーボードコンソールの場合は LED制御もブザー吹鳴もキーボード側にコマンドを送信する必要があるものの、一般的にカーネル printf(9) からの呼び出しでは割り込みが使えません。よって、ユーザーランド側処理と同様の送信キューによる実装では対応が難しいという問題があります。
真面目に実装しようとすると、ストレージデバイスのように「割り込み使用不可の場合のポーリング処理実装」を入れる必要がありますが、超マイナー機種、かつ、使用頻度が高いとは言えないキーボードの 1〜3バイトのコマンドのためだけにどこまで工数をかけるのか、というのが悩みどころでした。
このあたりも細かく説明すると終わらないので、セルフまとめツイートで雰囲気だけ感じてもらえればと思います。
うげー。 NetBSD/luna68k の sio 全体を整理するかと眺めてたら今の LED のコードはNGと判明。
— Izumi Tsutsui (@tsutsuii) 2021年9月19日
wskbd(4) がコンソールの場合 wskbd_cngetc() → wskbd_translate() → update_leds() → omkbd_set_leds() のパスがあるので attach 前に cngetc() が呼ばれて CAP を押すと死んでしまうと思われる
これを真面目に対処するには
— Izumi Tsutsui (@tsutsuii) 2021年9月19日
- コンソールアクセスに必要な情報を softc 構造体から分離して別の構造体にする
- cnattach() でその構造体を初期化する
- softc 構造体の中にそのアクセス用構造体を含めて attach 時に代入
という実装が必要だが、LEDのためだけにそこまでやるのかというと、うーん……
そもそも bell コマンド送信に対してはコンソール用の cnbell 関数が別にあるのに set_leds はコンソール用も通常時も同じ関数というのは、API設計としてレイヤーの一貫性が無くてイケてない
— Izumi Tsutsui (@tsutsuii) 2021年9月19日
と思ったけれど、 set_leds の送信は送信キューと送信完了割り込みを使っている、かつ、 cngetc() の間は割り込み禁止なので、 cngetc() から set_leds が呼ばれたときは直接書き込まないとだめじゃん。これ API を直してくれないとどうしようもない気がするけど、他のデバイスはどうやってるんだ?
— Izumi Tsutsui (@tsutsuii) 2021年9月19日
今回の作業時に参考にした src/sys/arch/sgimips/dev/zs_kbd.c でも同じ問題があるように見える。
— Izumi Tsutsui (@tsutsuii) 2021年9月19日
そもそもカーネルいじるような人が CAPSLOCK を押すことなんてほぼないから気にするなという話はあるけど、 PS/2 や USBキーボードあたりのメジャーどころだとどうなっているのか(読むのがめんどい)
set_leds に渡される cookie は cngetc(9) のときは cnattach() 時のもの、通常時はデバイス attach 時の cookie だから、一応判別は可能なのか。とはいえ、カーネルコンソール用 cngetc() や cnputc() ではアドレス関連は手抜きで即値埋め込みしてたりするので、 LEDだけなぜ共用という疑問は消えない
— Izumi Tsutsui (@tsutsuii) 2021年9月19日
今の NetBSD/luna68k のキーボードドライバだと wskbd_cnattach() に対して渡す cookie はダミーの値、つまり cngetc() その他では参照しておらずわりと雑な作りhttps://t.co/r3e1aSNxG6
— Izumi Tsutsui (@tsutsuii) 2021年9月19日
今回書いた cnbell() では cookie を softc として参照しているように見えるものの、いろいろいじった結果としてその先のブザーコマンド取得関数では参照が無いので問題なし。 cnbell() での送信データ書き込みは通常時の omkbd_send() とは別関数で直接書き込みなので問題なし。
— Izumi Tsutsui (@tsutsuii) 2021年9月19日
テキトー対処といっても wskbd_cnattach(9) に渡す cookie を明示的に定義して omkbd_set_leds() の中で cookie の値を見て cnattach 時のものだったら softc を使わない(つまり omkbd_send() ではなく syscnputc() を使う)というだけだけど、すでに sio まわりの修正をガッツリ入れてしまっててアレ
— Izumi Tsutsui (@tsutsuii) 2021年9月19日
よくよく考えると wskbd_cnattach() では set_leds の情報を渡していないので cngetc() から update_leds() を呼ぶとどうなるのかと見たらキーボードデバイスが正規に attach されて id->t_sc の softc が初期化された後だけ .set_leds 関数を呼ぶ構成になっていたhttps://t.co/SwiJzMilGs
— Izumi Tsutsui (@tsutsuii) 2021年9月19日
sio の修正をコミットしたのでこの .set_leds のことを考えていたのだけれど、 cons(9) の cngetc(9) の説明を見ると
— Izumi Tsutsui (@tsutsuii) 2021年9月25日
"cnpollc() must be called before cngetc() could be used."
なので、 cnpollc() で割り込みかそうでないかを切り替える処理を入れて、その後で cngetc(9) を呼ぶという設計なのか? https://t.co/v8oFXAY0BZ
今回の luna68k キーボード LEDコマンド送信と、今回参考にした sgimips R3000 系のキーボード https://t.co/wUiGDuN7nF と、同様に sgimips を参考にした ews4800mips https://t.co/YKyZ0TELDJ だけは cnpollc(9) で処置すれば LED送信コマンドを polling で処理可能……だけど、いまさら感満載である
— Izumi Tsutsui (@tsutsuii) 2021年9月25日
cnpollc(9) で wskbd(9) の .set_leds の動作を切替可能と言われても、 struct wskbd_consops ws_consops の .pollc の cookie (= wskbd_cnattach(9) に渡したもの)と struct wskbd_accessops の .set_leds の cookie (= wskbddev_attach_args で渡したもの) は別物なので壮絶に分かりづらいな……
— Izumi Tsutsui (@tsutsuii) 2021年9月25日
struct wskbd_consops の .pollc の設定で struct wskbd_accessops の .set_leds で polling させるコードを極力暗黙仮定が無い形で書いてみたhttps://t.co/YcSd5sBQum
— Izumi Tsutsui (@tsutsuii) 2021年9月25日
LEDは動いてるっぽいけれど、 LUNA キーボードの CAP の特殊動作を処置できないので LEDだけ処置しても意味ないのであった(完) https://t.co/GRGEWfZwcq
やはり無駄に長い引用になってしまう
最終的な妥協案としては、既存の cnpollc(9) の仕組みを使ってカーネル cngetc(9) からの呼び出しかどうかを判定した上で、カーネル内 cngetc(9) の場合は送信キューを使わずに従来のマウスON/OFFコマンド同様に直接 uPD7201のレジスタを操作してコマンドを送信する実装としました。
これが最後の5つ目のコミット。
まとめ
こうやって改めて思い出してみると、それぞれの作業で結構頭を使って考えていたのかな、とは思わないでもないですね。
「OSCがなくなったのでイベント駆動がなくなったと言いながらこれらの作業はどうなんだ」と言われてしまいそうですが、x68k Xサーバーの3ボタンエミュレーションを除くと「ユーザーの見た目にはほとんど現れないが内部的なあるべきハードウェア対応実装設計およびインターフェース設計」という感じで、このたぐいの「詳細実装設計メインの面白さ」をOSCネタで表現できたかというと微妙です。どんなもんでしょう
2022年以降もOSC含めた世情がどうなっていくのか見えていませんが、これまでと同じようなペースでぼちぼち NetBSDをいじっていけたらなあ、と思っています。今回のエントリで書いた内容に限らず「こんな中身の話を聞いてみたい!」というご意見があれば聞かせていただけると幸いです。