Skip to content

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");

i8250consoleplan9.iniconsole= または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 が初期化される際に uartreseti8250pnp を呼び正しい状態を構築する。このとき返した Uart のリストが #t ドライバ以下に現れる。ここで uartlistuartnuart 変数が初期化されるので、これ以降は 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;
}