キーボードコントローラ(以下、KBCと略)とは、キーボードとCPU間のデータのやり取りを制御する、ワンチップマイコンの事でIntel i8042などが有名です。しかし最近では、周辺チップの統合化が進みI/O Controller Hubなどのチップセットの一部として内蔵されているので、i8042単体で存在事は無くなってきています。
キーボードコントローラ(以下、KBCと略)とは、キーボードとCPU間のデータのやり取りを制御する、ワンチップマイコンの事でIntel i8042などが有名です。しかし最近では、周辺チップの統合化が進みI/O Controller Hubなどのチップセットの一部として内蔵されているので、i8042単体で存在事は無くなってきています。
キーボードは、KBCに繋がっていて、この間の通信はシリアル通信(歩調同期)でやり取りが行われます。そして、キーボードが押されたとき、キーボード・KBC間では次のことが行われます。
コンピュータのキーボードは、キーを押した場合、キーの文字の文字コードが直接送られるわけではありません(例えば"0" Keyを押下した場合"0x20"が送られる訳ではない)。つまりスキャンコードは、キーボードのキーを物理的に識別するもので、そのキーの表す文字や機能のコードそのものでは無いと言うことです。
つまり、コントローラが受信したスキャンコードを、キー配列の設定や修飾キー・ロックキーなどの状態を参照して、該当する文字の入力として処理を行うのはOS側の仕事になります。
キーボードは、キーの押下あるいは解放を検出すると、そのキーに固有のスキャンコードをKBCに送ります。
同じキーのメイクとブレイクはMSB(スキャンコードの7bit)やプレフィクスコードによって区別されることが多く、オートリピートの処理をキーボード側で行う場合、キーが押されている間、メイクコードを周期的に発生させたりします。
スキャンコードはコンピュータの種類により異なり、またインターフェースによっても異なります。このためOSは、受け取ったコードをOS内部の共通コードに変換して扱っています。
例えばOpenBSDはPC/AT・PS/2用キーボードのスキャンコードマップとして wskbdmap_mfii.cを持っています。またUSB 用キーボードのスキャンコードマップ(PS/2とはコード体系が異なり、スキャンコードの名称もUsage IDと呼ばれる) ukbdmap.cを持っています。
KBCには、CPUとの通信を行うための8bitレジスタを持っています。 これらのレジスタはI/O port(0x60,0x64)にマップされていて、CPUはこのアドレスにread/writeすることにより、KBCとやり取りを行います。
また、キーボードの入力はランダムに来るので、KBCはキーボードからのデータを受信すると、割込みコントローラを介してCPUに対して割込み(IRQ 1)を発生させます。
アドレス | 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 /* レスポンスを入力レジスタから読込む */
各コマンドは下図になります。
コマンド | データの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:変換しない
KBC type #2の時1:Scan Code2をScan Code1に変換
常に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し自己診断を走らせる。自己診断結果が返る。 |