9kカーネルのシリアル通信実装
Plan 9の9kカーネルにおいて、シリアルポート通信は主に以下のファイルで構成される。
- 9k/386/uarti8250.c
- 9k/port/devuart.c
一般的な情報はこれがよくまとまっている。これによると、オリジナルの8250と後継の16550があるらしい。
動作としては、まず9kの main 関数で i8250console を実行する。これは Plan 9 ログを出力する前に行われる。
i8250console("0");...print("\nPlan 9\n");i8250console は plan9.ini の console= または0番目のUartを探すが、このときまだ i8250pnp を実行していないので uartconsole で取り出せるUartは1つもない。
Uart*uartconsole(int i, char *cmd){ // uartnuart=0なので、この時点では常にnilを返す if(i >= uartnuart || (p = uart[i]) == nil) return nil; ...}なので i8250console はデバイスの初期化を以下のように行う。コードは読みやすいように加工している。
uart = &i8250uart[i];if(!uart->enabled) (*uart->phys->enable)(uart, 0);uartctl(uart, cmd);consuart = uart;uart->console = 1;これだけでもシリアル通信は可能となるが、ただしこの時点では、OS側が自覚的に入出力することはできるけれど、enable の第2引数が0なのでEnable Received Data Available(Erda)レジスタ未設定となり、外からの入力に気づけない。
その後、devuart が初期化される際に uartreset が i8250pnp を呼び正しい状態を構築する。このとき返した Uart のリストが #t ドライバ以下に現れる。ここで uartlist と uartnuart 変数が初期化されるので、これ以降は uartconsole でも Uart を扱えるようになる。
// uartreset()if((p = phyuart[i]->pnp()) == nil) continue;if(uartlist != nil) tail->next = p;else uartlist = p;for(tail = p; tail->next != nil; tail = tail->next) uartnuart++;uartnuart++;では Erda レジスタはどこで設定されるのか。有効化しているのは uartenable で行う。
Uart*uartenable(Uar *p){ if(p->enabled) return p; ... (*p->phys->enable)(p, 1); p->enabled = 1;}これは uartreset で発見された consuart に対して呼ぶことになる。
// uartreset()p = uartlist;for(i = 0; i < uartnuart; i++){ if(p->console || p->special){ if(uartenable(p) != nil){ addkbdq(p->oq, uartputs, 2, 0); p->putc = kbdcr2nl; } p->opens++; } p = p->next;}