キーボード コントローラ(KBC)

キーボードコントローラ(以下、KBCと略)とは、キーボードとCPU間のデータのやり取りを制御する、ワンチップマイコンの事でIntel i8042などが有名です。しかし最近では、周辺チップの統合化が進みI/O Controller Hubなどのチップセットの一部として内蔵されているので、i8042単体で存在事は無くなってきています。

KBCの構成

KBC周辺のH/Wは下記にある図のようになります。
KBCの構成
KBCの構成

キーボード・KBC間

キーボードは、KBCに繋がっていて、この間の通信はシリアル通信(歩調同期)でやり取りが行われます。そして、キーボードが押されたとき、キーボード・KBC間では次のことが行われます。

  1. キーボードは押されたスキャンコードの前にスタートビットを、後ろにパリティビットとストップビットを付加してLSBでコントローラに送信します。
  2. コントローラは受け取ったデータのパリティをチェックして、受け取ったデータが正しくない場合は再送を要求します。
  3. 再度データが正しくない場合は、パリティエラーを報告します。
  4. 送信要求より15msecの間、データが送られてこないされない場合、または2msecの間にデータの受信が完了しない場合は、タイムアウトが報告されます。
  5. パリティエラー・タイムアウトが発生した場合、データは0xffにセットされます。

スキャンコード

コンピュータのキーボードは、キーを押した場合、キーの文字の文字コードが直接送られるわけではありません(例えば"0" Keyを押下した場合"0x20"が送られる訳ではない)。つまりスキャンコードは、キーボードのキーを物理的に識別するもので、そのキーの表す文字や機能のコードそのものでは無いと言うことです。

つまり、コントローラが受信したスキャンコードを、キー配列の設定や修飾キー・ロックキーなどの状態を参照して、該当する文字の入力として処理を行うのはOS側の仕事になります。

キーボードは、キーの押下あるいは解放を検出すると、そのキーに固有のスキャンコードをKBCに送ります。

同じキーのメイクとブレイクはMSB(スキャンコードの7bit)やプレフィクスコードによって区別されることが多く、オートリピートの処理をキーボード側で行う場合、キーが押されている間、メイクコードを周期的に発生させたりします。

スキャンコードはコンピュータの種類により異なり、またインターフェースによっても異なります。このためOSは、受け取ったコードをOS内部の共通コードに変換して扱っています。

例えばOpenBSDはPC/AT・PS/2用キーボードのスキャンコードマップとして wskbdmap_mfii.cを持っています。またUSB 用キーボードのスキャンコードマップ(PS/2とはコード体系が異なり、スキャンコードの名称もUsage IDと呼ばれる) ukbdmap.cを持っています。

CPU・KBC間

KBCには、CPUとの通信を行うための8bitレジスタを持っています。 これらのレジスタはI/O port(0x60,0x64)にマップされていて、CPUはこのアドレスにread/writeすることにより、KBCとやり取りを行います。

また、キーボードの入力はランダムに来るので、KBCはキーボードからのデータを受信すると、割込みコントローラを介してCPUに対して割込み(IRQ 1)を発生させます。

KBCのレジスタ
アドレス size R/W 名称 説明
0x60 byte R 出力 レジスタ KBCからのデータが出力されます。
ステータスレジスタのbit0が1の時に読込み可能。
0x60 byte W 入力 レジスタ KCBにデータを入力します。
ステータスレジスタのbit1が1の時に書込み可能。
0x64 byte R ステータス レジスタ KBCのステータス情報
0x64 byte W コマンド レジスタ コマンドを書込み、どの様な制御を行うか指定します。

ステータスレジスタ

CPU側ではI/O ポートの64H番地を読む(inb命令)ことで、ステータス情報を得ることが出来ます。読んだステータスの値は下記のようになります。

ステータスレジスタ
bit 意味
0 出力 レジスタ ステータス 0:出力バッファが空(キーボードからのデータが無い)
1:出力バッファにデータ有り(キーボードからのデータが有る)
1 入力 レジスタ ステータス 0:入力バッファが空(キーボードコントローラに送信可能)
1:入力バッファにデータ有り(キーボードコントローラに送信不可)
2 システムフラグ reset時0がセットされる
self-test成功時1がセットされる
3 コマンド/データ 0:最後にデータが入力バッファに書き込まれた。
1:最後にコマンドが入力バッファに書き込まれた。
4 Keyboard lock 0:Locked
1:Not Locked
5 出力 buffer full 0:Not Full
1:Full 出力バッファのデータが,次のデータに上書きされた場合に1になる
6 Timeout 0:OK
1:キーボード・KCB間でデータ通信時にタイムアウトが発生した
7 Parity error 0:OK
1:キーボード・KCB間でデータ通信時にパリティエラーが発生した

コマンドレジスタ

コマンドレジスタには、コマンドを書込みどの様な制御を行うか指定します。

コマンドには、コマンドの後に1byte以上のデータが続くものと、コマンドの後ろにデータが無いものが存在します。コマンドの後ろにデータが無いものは、コマンドレジスタにコマンドを書き込んで制御が完了します。

下に示したのは、sysytem rebootを行うコードです。このコマンドは、後ろにデータが続かないのでこれで制御は完了します。

/* Try to use the KBD to reboot system */
movb    $0xfe, %al     /* コマンドをalレジスタにセット */
outb    %al,   $0x64   /* alレジスタの値をコマンドレジスタ(0x64 port)にセット */	

コマンドの後に1byte以上のデータが続くものは、コマンドレジスタにコマンドを書き込んだ後に、入力(KCBに渡すデータ)・出力(KBCからのレスポンス) レジスタ(0x60 port)を使い,続くデータの読書きを行います。

下に示したのは、KCBの出力ポート(P2)の状態を読込むコードです。このコマンドの後ろにはデータが続くので、コマンドをコマンドレジスタ(0x64)に書いた後、レスポンス(出力ポート(P2)状態)を読込みます。

movb    $0xd1, %al    /* コマンドをalレジスタにセット */
outb    %al,   $0x64  /* alレジスタの値をコマンドレジスタ(0x64 port)にセット */
/* 実際は、この間にもう少し複雑な制御が入ります */
inb     $0x60, $al    /* レスポンスを入力レジスタから読込む */	

各コマンドは下図になります。

KBC制御コマンド
コマンド データのI/Oとsize コマンドの意味
R/W width
0x20 R 1byte コマンドバイトの内容を出力 レジスタ(port 0x60)から読み込む
0x60 W 1byte コマンドバイトの内容を入力 レジスタ(port 0x60)に書込む。
0xA4 R 1byte パスワードがインストールされているかチェック
出力 レジスタ(port 0x60)の値
0xFA:パスワードがインストールされている
0xF1:パスワードがインストールされていない
0xA5 W n byte パスワードを出力する(PS/2のみ)。
入力 レジスタ(port 0x60)にNULLを終端文字として書込む。この書込むパスワードのコードはscancodeである。
0xA6 ? ? パスワード チェック
パスワードがインストールされているなら、パスワードと入力キーが一致するかチェックする。
0xA7 - - マウスを無効にする(PS/2のみ)
コマンドバイトをbit5=1にするのと同じ
0xA8 - - マウスを有効にする(PS/2のみ)
コマンドバイトをbit5=0にするのと同じ
0xA9 R 1byte マウス テスト(PS/2のみ)
出力 レジスタ(port 0x60)の値
0:OK
1:Mouse clock line low
2:Mouse clock line high
3:Mouse data line low
4:Mouse data line high
0xAA R 1byte 自己テスト
出力 レジスタ(port 0x60)の値
0x55:OK
0xfc:NG
0xAB R 1byte キーボード テスト
出力 レジスタ(port 0x60)の値
0:OK
1:keyboard clock line low
2:keyboard clock line high
3:keyboard data line low
4:keyboard data line high
0xAC R 16byte 自己診断(Diagnostic)
0xAD - - キーボードを無効にする。
コマンドバイトをbit=1するのと同じ。
0xAE - - キーボードを有効にする。
コマンドバイトをbit=1するのと同じ。
0xC0 R 1byte input port (P1)の読出し
出力 レジスタ(port 0x60)の値
bit7:キーボードLock (0=Not Lock,1=Lock)
bit6:Display (0=カラー,1=モノクロ)
bit5:ジャンパ (0:installed, 1:not installed)
bit4:システムボードのRAM(0:512 KB, 1:256 KB )
bit3-0:未定義
0xC1 - - input port (P1)のbit0-3をステータスレジスタにコピーする
0xC2 - - input port (P1)のbit4-7をステータスレジスタにコピーする
0xD0 - - output port(P2)の読出し
出力 レジスタ(port 0x60)の値
bit7:キーボードのデータ信号線
bit6:キーボードのクロック信号線
bit5:input buffer empty
bit4:output buffer full
bit3:Mouse clock
bit2:Mouse data
bit1:ゲートA20信号線(0:close,1:open)
bit0:システムリセット(0:reset CPU, 1:normal)
0xD1 W 1byte output port(P2)への書出し
入力 レジスタ(port 0x60)へ書込んだ値がoutput port(P2)へ出力される
内容は0xD0と同じ
bit7:キーボードのデータ信号線
bit6:キーボードのクロック信号線
bit5:input buffer empty
bit4:output buffer full
bit3:Mouse clock
bit2:Mouse data
bit1:ゲートA20信号線(0:close,1:open)
bit0:システムリセット(0:reset CPU, 1:normal)
0xD2 W 1byte キーボードデータを入力レジスタに書込む(PS/2)
入力 レジスタ(port 0x60)へ書込んだ値がループバックされる(らしい)
0xD3 W 1byte Mouse データを入力レジスタに書込む(PS/2)
入力 レジスタ(port 0x60)へ書込んだ値がループバックされる(らしい)
0xD4 W 1byte Mouse側に値を出力(PS/2のみ)
入力 レジスタ(port 0x60)へ書込んだ値が、Mouse deviceに送られる。
0xDD W 1byte disable A20
0xDF W 1byte enable A20
0xE0 W 1byte Test port(T)の読出し
出力 レジスタ(port 0x60)の値
bit1:Keyboard data
bit0:Keyboard clock
0xF* - - output port(P2)のクロックをパルスする。
入力 レジスタ(port 0x60)へ書込んだ値のbit0-3が出力ポートにパルスされる。bit0=0の時はSystem Resetされる。
コマンド バイト

コマンド0x20と0x60で読書きするコマンドバイトの内容は、次のようになります。

コマンド バイト
bit 意味
0 キーボード割込み 0:IRQ 1を発生
1:IRQ 1を発生させない
1 PS/2 Mouseの割込み 0:IRQ 12を発生
1:IRQ 12を発生させない
2 システム フラグ 0:cold reboot
1:hot reboot
3 キーボードロックの制御 0:キーボード ロック キーの制御を有効
1:キーボード ロック キーの制御を無効 PS2の場合常に0
4 キーボード 0:有効
1:無効 CLK Lineが常にLなる
5 PS/2 Mouse 0:有効
1:無効 CLK Lineが常にLなる
6 Scan Code変換 KBC type #1の時
0:変換しない
1:Scan Code2をScan Code1に変換
KBC type #2の時
常に0
7 reserved 常に0

入力 レジスタ

入力レジスタ(port 0x60)に下記のコマンドを書く事により、キーボードに下記のキーボードコマンドを送信することができます。

コマンドの後にデータが有る場合は、続けて入力レジスタ(port 0x60)にそのデータを書込みます。また、キーボードからのレスポンスがある場合は、出力レジスタ(port 0x60)から読出します。

キーボード コマンド
コマンド データのI/Oとsize コマンドの意味
R/W width
0xED W 1byte LEDを点灯させる.
データの内容
bit0 : Scroll Lock(1=点灯,0=消灯)
bit1 : Num Lock(1=点灯,0=消灯)
bit2 : Caps Lock(1=点灯,0=消灯)
bit3-7 : 0固定
0xEE R 1byte Diagnostic Echo
0xF0 R/W 1/2byte 他のスキャンコードを選択する(PS/2のみ). データの内容
0x00: 現在のスキャンコードを取得
 (このデータセット後、出力バッファをリード)
0x01: スキャンコード#1を選択(デフォルト)
0x02: スキャンコード#2を選択
0x03: スキャンコード#3を選択
0xF2 R 3byte キーボードIDの読込み(PS/2のみ)
出力レジスタ(0x60)にACK (FAh)と2byteのキーボードIDが入る。
0xF3 W 1byte オートリピートのrate/delay
データの内容
bit0-4:rate
bit5-6:delay
bit7:0固定
0xF4 - - キーボードのイネーブルと出力バッファのクリア
0xF5 - - キーボードをリセットし、スキャンを開始しない。
0xF6 - - キーボードをリセットし、スキャンを開始する。
0xF7 - - 全てののキーをオートリピートに設定(PS/2)
0xF8 - - 全てのキーがMAKE/BREAKコードを生成するように設定(PS/2)
0xF9 - - 全てのキーがMAKEコードのみを生成するように設定(PS/2)
0xFA - - 全てのキーがMAKE/BREAKコードを生成し,かつオートリピートに設定(PS/2)
0xFB W 1byte 指定したスキャンコードをオートリピートに設定(PS/2のみ)
0xFC W 1byte 指定したスキャンコードがMAKE/BREAKコードを生成するように設定(PS/2)
0xFD W 1byte 指定したスキャンコードがBREAKコードのみを生成するように設定(PS/2)
0xFE - - 再送
0xFF R? 1byte? Resetし自己診断を走らせる。自己診断結果が返る。

参考文献

http://www.microsoft.com/whdc/device/input/Scancode.mspx
PS/2キーボードインターフェース
Last modified: Wed Apr 23 19:00:45 2008 JST