diff options
author | Richard Braun <rbraun@sceen.net> | 2017-05-28 17:31:04 +0200 |
---|---|---|
committer | Richard Braun <rbraun@sceen.net> | 2017-05-28 17:57:29 +0200 |
commit | 001ea7c0bbe1b5fb440b5e628faa47d8a58f36b7 (patch) | |
tree | 87b81ba25422dab4372696ec1821ab2ee179774b | |
parent | 5a711e07fe97d3fdbd6e322bacd41de44bdf82d3 (diff) |
x86: new atcons and atkbd modules
These modules, together with cga, implement the AT console driver.
-rw-r--r-- | arch/x86/Makefrag.am | 4 | ||||
-rw-r--r-- | arch/x86/machine/atcons.c | 56 | ||||
-rw-r--r-- | arch/x86/machine/atcons.h | 44 | ||||
-rw-r--r-- | arch/x86/machine/atkbd.c | 811 | ||||
-rw-r--r-- | arch/x86/machine/atkbd.h | 29 | ||||
-rw-r--r-- | arch/x86/machine/boot.c | 4 | ||||
-rw-r--r-- | arch/x86/machine/cga.c | 23 | ||||
-rw-r--r-- | arch/x86/machine/cga.h | 5 |
8 files changed, 955 insertions, 21 deletions
diff --git a/arch/x86/Makefrag.am b/arch/x86/Makefrag.am index 6adc769f..0971530f 100644 --- a/arch/x86/Makefrag.am +++ b/arch/x86/Makefrag.am @@ -26,6 +26,10 @@ endif AMD64 x15_SOURCES += \ arch/x86/machine/acpimp.c \ arch/x86/machine/acpimp.h \ + arch/x86/machine/atcons.c \ + arch/x86/machine/atcons.h \ + arch/x86/machine/atkbd.c \ + arch/x86/machine/atkbd.h \ arch/x86/machine/asm.h \ arch/x86/machine/atomic.h \ arch/x86/machine/biosmem.c \ diff --git a/arch/x86/machine/atcons.c b/arch/x86/machine/atcons.c new file mode 100644 index 00000000..c4c48069 --- /dev/null +++ b/arch/x86/machine/atcons.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <kern/console.h> +#include <kern/init.h> +#include <machine/atcons.h> +#include <machine/atkbd.h> +#include <machine/cga.h> + +static struct console atcons_console; + +static void +atcons_putc(struct console *console, char c) +{ + (void)console; + cga_putc(c); +} + +static const struct console_ops atcons_ops = { + .putc = atcons_putc, +}; + +void __init +atcons_bootstrap(void) +{ + cga_setup(); + + console_init(&atcons_console, "atcons", &atcons_ops); + console_register(&atcons_console); +} + +void __init +atcons_setup(void) +{ + atkbd_setup(); +} + +void +atcons_intr(char c) +{ + console_intr(&atcons_console, c); +} diff --git a/arch/x86/machine/atcons.h b/arch/x86/machine/atcons.h new file mode 100644 index 00000000..05b870fc --- /dev/null +++ b/arch/x86/machine/atcons.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * + * AT console driver. + */ + +#ifndef _X86_ATCONS_H +#define _X86_ATCONS_H + +/* + * Early initialization of the atcons module. + */ +void atcons_bootstrap(void); + +/* + * Initialize the atcons module. + * + * This function enables keyboard interrupt handling. + */ +void atcons_setup(void); + +/* + * Console interrupt handler. + * + * This function is called by the AT keyboard interrupt handler + * to handle machine-independent console management. + */ +void atcons_intr(char c); + +#endif /* _X86_ATCONS_H */ diff --git a/arch/x86/machine/atkbd.c b/arch/x86/machine/atkbd.c new file mode 100644 index 00000000..362b834c --- /dev/null +++ b/arch/x86/machine/atkbd.c @@ -0,0 +1,811 @@ +/* + * Copyright (c) 2017 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> + +#include <kern/error.h> +#include <kern/init.h> +#include <kern/intr.h> +#include <kern/macros.h> +#include <machine/atkbd.h> +#include <machine/atcons.h> +#include <machine/io.h> + +#define ATKBD_PORT_DATA 0x60 +#define ATKBD_PORT_STATUS 0x64 +#define ATKBD_PORT_CMD 0x64 + +#define ATKBD_REQ_LEDS 0xed +#define ATKBD_REQ_RESET 0xff + +#define ATKBD_REP_PASS 0xaa +#define ATKBD_REP_ACK 0xfa +#define ATKBD_REP_RESEND 0xfe + +#define ATKBD_LEDS_SCROLLLOCK 0x01 +#define ATKBD_LEDS_NUMLOCK 0x02 +#define ATKBD_LEDS_CAPSLOCK 0x04 + +#define ATKBD_STATUS_OUT_FULL 0x01 +#define ATKBD_STATUS_IN_FULL 0x02 +#define ATKBD_STATUS_PARITY_ERROR 0x80 + +#define ATKBD_CMD_RDCONF 0x20 +#define ATKBD_CMD_WRCONF 0x60 +#define ATKBD_CMD_DIS2 0xa7 +#define ATKBD_CMD_EN2 0xa8 +#define ATKBD_CMD_DIS1 0xad +#define ATKBD_CMD_EN1 0xae + +#define ATKBD_CONF_ENINT1 0x01 +#define ATKBD_CONF_ENINT2 0x02 +#define ATKBD_CONF_ENTRANS 0x40 + +#define ATKBD_INTR1 1 + +enum atkbd_key_id { + ATKBD_KEY_INVALID = 0, + ATKBD_KEY_1 = 1, + ATKBD_KEY_2, + ATKBD_KEY_3, + ATKBD_KEY_4, + ATKBD_KEY_5, + ATKBD_KEY_6, + ATKBD_KEY_7, + ATKBD_KEY_8, + ATKBD_KEY_9, + ATKBD_KEY_0, + ATKBD_KEY_DASH, + ATKBD_KEY_EQUAL, + ATKBD_KEY_BACKSLASH, + ATKBD_KEY_BACKSPACE, + + ATKBD_KEY_TAB, + ATKBD_KEY_Q, + ATKBD_KEY_W, + ATKBD_KEY_E, + ATKBD_KEY_R, + ATKBD_KEY_T, + ATKBD_KEY_Y, + ATKBD_KEY_U, + ATKBD_KEY_I, + ATKBD_KEY_O, + ATKBD_KEY_P, + ATKBD_KEY_OBRACKET, + ATKBD_KEY_CBRACKET, + ATKBD_KEY_ENTER, + + ATKBD_KEY_CAPSLOCK, + ATKBD_KEY_A, + ATKBD_KEY_S, + ATKBD_KEY_D, + ATKBD_KEY_F, + ATKBD_KEY_G, + ATKBD_KEY_H, + ATKBD_KEY_J, + ATKBD_KEY_K, + ATKBD_KEY_L, + ATKBD_KEY_SEMICOLON, + ATKBD_KEY_QUOTE, + + ATKBD_KEY_LSHIFT, + ATKBD_KEY_Z, + ATKBD_KEY_X, + ATKBD_KEY_C, + ATKBD_KEY_V, + ATKBD_KEY_B, + ATKBD_KEY_N, + ATKBD_KEY_M, + ATKBD_KEY_COMMA, + ATKBD_KEY_DOT, + ATKBD_KEY_SLASH, + ATKBD_KEY_RSHIFT, + + ATKBD_KEY_LCTRL, + ATKBD_KEY_ALT, + ATKBD_KEY_SPACE, + ATKBD_KEY_ALTGR, + ATKBD_KEY_RCTRL, + + ATKBD_KEY_INSERT, + ATKBD_KEY_DELETE, + ATKBD_KEY_HOME, + ATKBD_KEY_END, + ATKBD_KEY_PGUP, + ATKBD_KEY_PGDOWN, + + ATKBD_KEY_LEFT, + ATKBD_KEY_BOTTOM, + ATKBD_KEY_RIGHT, + ATKBD_KEY_UP, + + ATKBD_KEY_KP_NUMLOCK, + ATKBD_KEY_KP_SLASH, + ATKBD_KEY_KP_STAR, + ATKBD_KEY_KP_MINUS, + ATKBD_KEY_KP_HOME, + ATKBD_KEY_KP_UP, + ATKBD_KEY_KP_PGUP, + ATKBD_KEY_KP_PLUS, + ATKBD_KEY_KP_LEFT, + ATKBD_KEY_KP_5, + ATKBD_KEY_KP_RIGHT, + ATKBD_KEY_KP_END, + ATKBD_KEY_KP_BOTTOM, + ATKBD_KEY_KP_PGDOWN, + ATKBD_KEY_KP_ENTER, + ATKBD_KEY_KP_INS, + ATKBD_KEY_KP_DEL, +}; + +#define ATKBD_KM_SHIFT 0x01 /* Shift / caps lock modifier applies */ +#define ATKBD_KM_KP 0x02 /* Num lock modifier applies */ +#define ATKBD_KM_CTL 0x04 /* Unmodified key is a control character */ + +struct atkbd_key { + int modifiers; + enum atkbd_key_id id; +}; + +static const struct atkbd_key atkbd_keys[] = { + [0x16] = { ATKBD_KM_SHIFT, ATKBD_KEY_1 }, + [0x1e] = { ATKBD_KM_SHIFT, ATKBD_KEY_2 }, + [0x26] = { ATKBD_KM_SHIFT, ATKBD_KEY_3 }, + [0x25] = { ATKBD_KM_SHIFT, ATKBD_KEY_4 }, + [0x2e] = { ATKBD_KM_SHIFT, ATKBD_KEY_5 }, + [0x36] = { ATKBD_KM_SHIFT, ATKBD_KEY_6 }, + [0x3d] = { ATKBD_KM_SHIFT, ATKBD_KEY_7 }, + [0x3e] = { ATKBD_KM_SHIFT, ATKBD_KEY_8 }, + [0x46] = { ATKBD_KM_SHIFT, ATKBD_KEY_9 }, + [0x45] = { ATKBD_KM_SHIFT, ATKBD_KEY_0 }, + [0x4e] = { ATKBD_KM_SHIFT, ATKBD_KEY_DASH }, + [0x55] = { ATKBD_KM_SHIFT, ATKBD_KEY_EQUAL }, + [0x5d] = { ATKBD_KM_SHIFT, ATKBD_KEY_BACKSLASH }, + [0x66] = { 0, ATKBD_KEY_BACKSPACE }, + + [0x0d] = { 0, ATKBD_KEY_TAB }, + [0x15] = { ATKBD_KM_SHIFT, ATKBD_KEY_Q }, + [0x1d] = { ATKBD_KM_SHIFT, ATKBD_KEY_W }, + [0x24] = { ATKBD_KM_SHIFT, ATKBD_KEY_E }, + [0x2d] = { ATKBD_KM_SHIFT, ATKBD_KEY_R }, + [0x2c] = { ATKBD_KM_SHIFT, ATKBD_KEY_T }, + [0x35] = { ATKBD_KM_SHIFT, ATKBD_KEY_Y }, + [0x3c] = { ATKBD_KM_SHIFT, ATKBD_KEY_U }, + [0x43] = { ATKBD_KM_SHIFT, ATKBD_KEY_I }, + [0x44] = { ATKBD_KM_SHIFT, ATKBD_KEY_O }, + [0x4d] = { ATKBD_KM_SHIFT, ATKBD_KEY_P }, + [0x54] = { ATKBD_KM_SHIFT, ATKBD_KEY_OBRACKET }, + [0x5b] = { ATKBD_KM_SHIFT, ATKBD_KEY_CBRACKET }, + [0x5a] = { 0, ATKBD_KEY_ENTER }, + + [0x58] = { ATKBD_KM_CTL, ATKBD_KEY_CAPSLOCK }, + [0x1c] = { ATKBD_KM_SHIFT, ATKBD_KEY_A }, + [0x1b] = { ATKBD_KM_SHIFT, ATKBD_KEY_S }, + [0x23] = { ATKBD_KM_SHIFT, ATKBD_KEY_D }, + [0x2b] = { ATKBD_KM_SHIFT, ATKBD_KEY_F }, + [0x34] = { ATKBD_KM_SHIFT, ATKBD_KEY_G }, + [0x33] = { ATKBD_KM_SHIFT, ATKBD_KEY_H }, + [0x3b] = { ATKBD_KM_SHIFT, ATKBD_KEY_J }, + [0x42] = { ATKBD_KM_SHIFT, ATKBD_KEY_K }, + [0x4b] = { ATKBD_KM_SHIFT, ATKBD_KEY_L }, + [0x4c] = { ATKBD_KM_SHIFT, ATKBD_KEY_SEMICOLON }, + [0x52] = { ATKBD_KM_SHIFT, ATKBD_KEY_QUOTE }, + + [0x12] = { ATKBD_KM_CTL, ATKBD_KEY_LSHIFT }, + [0x1a] = { ATKBD_KM_SHIFT, ATKBD_KEY_Z }, + [0x22] = { ATKBD_KM_SHIFT, ATKBD_KEY_X }, + [0x21] = { ATKBD_KM_SHIFT, ATKBD_KEY_C }, + [0x2a] = { ATKBD_KM_SHIFT, ATKBD_KEY_V }, + [0x32] = { ATKBD_KM_SHIFT, ATKBD_KEY_B }, + [0x31] = { ATKBD_KM_SHIFT, ATKBD_KEY_N }, + [0x3a] = { ATKBD_KM_SHIFT, ATKBD_KEY_M }, + [0x41] = { ATKBD_KM_SHIFT, ATKBD_KEY_COMMA }, + [0x49] = { ATKBD_KM_SHIFT, ATKBD_KEY_DOT }, + [0x4a] = { ATKBD_KM_SHIFT, ATKBD_KEY_SLASH }, + [0x59] = { ATKBD_KM_CTL, ATKBD_KEY_RSHIFT }, + + [0x14] = { ATKBD_KM_CTL, ATKBD_KEY_LCTRL }, + [0x11] = { ATKBD_KM_CTL, ATKBD_KEY_ALT }, + [0x29] = { 0, ATKBD_KEY_SPACE }, + + [0x77] = { ATKBD_KM_CTL, ATKBD_KEY_KP_NUMLOCK }, + [0x7c] = { 0, ATKBD_KEY_KP_STAR }, + [0x7b] = { 0, ATKBD_KEY_KP_MINUS }, + [0x6c] = { ATKBD_KM_KP, ATKBD_KEY_KP_HOME }, + [0x75] = { ATKBD_KM_KP, ATKBD_KEY_KP_UP }, + [0x7d] = { ATKBD_KM_KP, ATKBD_KEY_KP_PGUP }, + [0x79] = { 0, ATKBD_KEY_KP_PLUS }, + [0x6b] = { ATKBD_KM_KP, ATKBD_KEY_KP_LEFT }, + [0x73] = { 0, ATKBD_KEY_KP_5 }, + [0x74] = { ATKBD_KM_KP, ATKBD_KEY_KP_RIGHT }, + [0x69] = { ATKBD_KM_KP, ATKBD_KEY_KP_END }, + [0x72] = { ATKBD_KM_KP, ATKBD_KEY_KP_BOTTOM }, + [0x7a] = { ATKBD_KM_KP, ATKBD_KEY_KP_PGDOWN }, + [0x70] = { ATKBD_KM_CTL | ATKBD_KM_KP, ATKBD_KEY_KP_INS }, + [0x71] = { ATKBD_KM_KP, ATKBD_KEY_KP_DEL }, +}; + +static const struct atkbd_key atkbd_e0_keys[] = { + [0x11] = { ATKBD_KM_CTL, ATKBD_KEY_ALTGR }, + [0x14] = { ATKBD_KM_CTL, ATKBD_KEY_RCTRL }, + + [0x70] = { ATKBD_KM_CTL, ATKBD_KEY_INSERT }, + [0x71] = { 0, ATKBD_KEY_DELETE }, + [0x6c] = { 0, ATKBD_KEY_HOME }, + [0x69] = { 0, ATKBD_KEY_END }, + [0x7d] = { 0, ATKBD_KEY_PGUP }, + [0x7a] = { 0, ATKBD_KEY_PGDOWN }, + + [0x6b] = { 0, ATKBD_KEY_LEFT }, + [0x72] = { 0, ATKBD_KEY_BOTTOM }, + [0x74] = { 0, ATKBD_KEY_RIGHT }, + [0x75] = { 0, ATKBD_KEY_UP }, + + [0x4a] = { 0, ATKBD_KEY_KP_SLASH }, + [0x5a] = { 0, ATKBD_KEY_KP_ENTER }, +}; + +static const char *atkbd_chars[] = { + [ATKBD_KEY_1] = "1", + [ATKBD_KEY_2] = "2", + [ATKBD_KEY_3] = "3", + [ATKBD_KEY_4] = "4", + [ATKBD_KEY_5] = "5", + [ATKBD_KEY_6] = "6", + [ATKBD_KEY_7] = "7", + [ATKBD_KEY_8] = "8", + [ATKBD_KEY_9] = "9", + [ATKBD_KEY_0] = "0", + [ATKBD_KEY_DASH] = "-", + [ATKBD_KEY_EQUAL] = "=", + [ATKBD_KEY_BACKSLASH] = "\\", + [ATKBD_KEY_BACKSPACE] = "\b", + + [ATKBD_KEY_TAB] = "\t", + [ATKBD_KEY_Q] = "q", + [ATKBD_KEY_W] = "w", + [ATKBD_KEY_E] = "e", + [ATKBD_KEY_R] = "r", + [ATKBD_KEY_T] = "t", + [ATKBD_KEY_Y] = "y", + [ATKBD_KEY_U] = "u", + [ATKBD_KEY_I] = "i", + [ATKBD_KEY_O] = "o", + [ATKBD_KEY_P] = "p", + [ATKBD_KEY_OBRACKET] = "[", + [ATKBD_KEY_CBRACKET] = "]", + [ATKBD_KEY_ENTER] = "\n", + + [ATKBD_KEY_A] = "a", + [ATKBD_KEY_S] = "s", + [ATKBD_KEY_D] = "d", + [ATKBD_KEY_F] = "f", + [ATKBD_KEY_G] = "g", + [ATKBD_KEY_H] = "h", + [ATKBD_KEY_J] = "j", + [ATKBD_KEY_K] = "k", + [ATKBD_KEY_L] = "l", + [ATKBD_KEY_SEMICOLON] = ";", + [ATKBD_KEY_QUOTE] = "'", + + [ATKBD_KEY_Z] = "z", + [ATKBD_KEY_X] = "x", + [ATKBD_KEY_C] = "c", + [ATKBD_KEY_V] = "v", + [ATKBD_KEY_B] = "b", + [ATKBD_KEY_N] = "n", + [ATKBD_KEY_M] = "m", + [ATKBD_KEY_COMMA] = ",", + [ATKBD_KEY_DOT] = ".", + [ATKBD_KEY_SLASH] = "/", + + [ATKBD_KEY_SPACE] = " ", + + [ATKBD_KEY_DELETE] = "\e[3~", + + [ATKBD_KEY_LEFT] = "\e[D", + [ATKBD_KEY_BOTTOM] = "\e[B", + [ATKBD_KEY_RIGHT] = "\e[C", + [ATKBD_KEY_UP] = "\e[A", + + [ATKBD_KEY_KP_SLASH] = "/", + [ATKBD_KEY_KP_STAR] = "*", + [ATKBD_KEY_KP_MINUS] = "-", + [ATKBD_KEY_KP_PLUS] = "+", + [ATKBD_KEY_KP_5] = "5", + [ATKBD_KEY_KP_ENTER] = "\n", + [ATKBD_KEY_KP_DEL] = "\e[3~", +}; + +static const char *atkbd_shift_chars[] = { + [ATKBD_KEY_1] = "!", + [ATKBD_KEY_2] = "@", + [ATKBD_KEY_3] = "#", + [ATKBD_KEY_4] = "$", + [ATKBD_KEY_5] = "%", + [ATKBD_KEY_6] = "^", + [ATKBD_KEY_7] = "&", + [ATKBD_KEY_8] = "*", + [ATKBD_KEY_9] = "(", + [ATKBD_KEY_0] = ")", + [ATKBD_KEY_DASH] = "_", + [ATKBD_KEY_EQUAL] = "+", + [ATKBD_KEY_BACKSLASH] = "|", + + [ATKBD_KEY_Q] = "Q", + [ATKBD_KEY_W] = "W", + [ATKBD_KEY_E] = "E", + [ATKBD_KEY_R] = "R", + [ATKBD_KEY_T] = "T", + [ATKBD_KEY_Y] = "Y", + [ATKBD_KEY_U] = "U", + [ATKBD_KEY_I] = "I", + [ATKBD_KEY_O] = "O", + [ATKBD_KEY_P] = "P", + [ATKBD_KEY_OBRACKET] = "{", + [ATKBD_KEY_CBRACKET] = "}", + + [ATKBD_KEY_A] = "A", + [ATKBD_KEY_S] = "S", + [ATKBD_KEY_D] = "D", + [ATKBD_KEY_F] = "F", + [ATKBD_KEY_G] = "G", + [ATKBD_KEY_H] = "H", + [ATKBD_KEY_J] = "J", + [ATKBD_KEY_K] = "K", + [ATKBD_KEY_L] = "L", + [ATKBD_KEY_SEMICOLON] = ":", + [ATKBD_KEY_QUOTE] = "\"", + + [ATKBD_KEY_Z] = "Z", + [ATKBD_KEY_X] = "X", + [ATKBD_KEY_C] = "C", + [ATKBD_KEY_V] = "V", + [ATKBD_KEY_B] = "B", + [ATKBD_KEY_N] = "N", + [ATKBD_KEY_M] = "M", + [ATKBD_KEY_COMMA] = "<", + [ATKBD_KEY_DOT] = ">", + [ATKBD_KEY_SLASH] = "?", +}; + +static const char *atkbd_kp_chars[] = { + [ATKBD_KEY_KP_HOME] = "7", + [ATKBD_KEY_KP_UP] = "8", + [ATKBD_KEY_KP_PGUP] = "9", + [ATKBD_KEY_KP_LEFT] = "4", + [ATKBD_KEY_KP_RIGHT] = "6", + [ATKBD_KEY_KP_END] = "1", + [ATKBD_KEY_KP_BOTTOM] = "2", + [ATKBD_KEY_KP_PGDOWN] = "3", + [ATKBD_KEY_KP_INS] = "0", + [ATKBD_KEY_KP_DEL] = ".", +}; + +#define ATKBD_KF_E0 0x01 +#define ATKBD_KF_F0 0x02 +#define ATKBD_KF_LSHIFT 0x04 +#define ATKBD_KF_RSHIFT 0x08 +#define ATKBD_KF_NUMLOCK 0x10 +#define ATKBD_KF_CAPSLOCK 0x20 +#define ATKBD_KF_SCROLLLOCK 0x40 + +#define ATKBD_KF_SHIFT (ATKBD_KF_CAPSLOCK \ + | ATKBD_KF_RSHIFT \ + | ATKBD_KF_LSHIFT) + +/* + * These flags are only accessed during interrupt handling and don't + * require additional synchronization. + */ +static unsigned int atkbd_flags; + +static uint8_t +atkbd_read_data(void) +{ + return io_read_byte(ATKBD_PORT_DATA); +} + +static void +atkbd_write_data(uint8_t data) +{ + io_write_byte(ATKBD_PORT_DATA, data); +} + +static uint8_t +atkbd_read_status(void) +{ + return io_read_byte(ATKBD_PORT_STATUS); +} + +static void +atkbd_write_cmd(uint8_t cmd) +{ + io_write_byte(ATKBD_PORT_CMD, cmd); +} + +static int +atkbd_out_wait(void) +{ + uint8_t status; + + for (;;) { + status = atkbd_read_status(); + + if (status & ATKBD_STATUS_OUT_FULL) { + break; + } + } + + if (status & ATKBD_STATUS_PARITY_ERROR) { + printf("atkbd: parity error\n"); + return ERROR_IO; + } + + return 0; +} + +static void +atkbd_in_wait(void) +{ + uint8_t status; + + for (;;) { + status = atkbd_read_status(); + + if (!(status & ATKBD_STATUS_IN_FULL)) { + break; + } + } +} + +static int +atkbd_read(uint8_t *datap, bool wait) +{ + uint8_t status; + int error; + + if (wait) { + error = atkbd_out_wait(); + + if (error) { + return error; + } + } else { + status = atkbd_read_status(); + + if (!(status & ATKBD_STATUS_OUT_FULL)) { + return ERROR_AGAIN; + } + } + + *datap = atkbd_read_data(); + return 0; +} + +static void +atkbd_write(uint8_t data) +{ + atkbd_in_wait(); + atkbd_write_data(data); +} + +static void +atkbd_flush(void) +{ + uint8_t status; + + atkbd_read(&status, false); +} + +static void __init +atkbd_reset_kbd(void) +{ + uint8_t reply; + int error; + +resend: + atkbd_write(ATKBD_REQ_RESET); + error = atkbd_read(&reply, true); + + if (error) { + return; + } + + if (reply == ATKBD_REP_RESEND) { + goto resend; + } + + if ((reply != ATKBD_REP_PASS) && (reply != ATKBD_REP_ACK)) { + printf("atkbd: warning: unable to reset keyboard\n"); + } +} + +static int __init +atkbd_disable(void) +{ + uint8_t byte; + int error; + + atkbd_write_cmd(ATKBD_CMD_DIS1); + atkbd_write_cmd(ATKBD_CMD_DIS2); + + atkbd_flush(); + + atkbd_write_cmd(ATKBD_CMD_RDCONF); + error = atkbd_read(&byte, true); + + if (error) { + return error; + } + + byte &= ~(ATKBD_CONF_ENTRANS | ATKBD_CONF_ENINT2 | ATKBD_CONF_ENINT1); + atkbd_write_cmd(ATKBD_CMD_WRCONF); + atkbd_write(byte); + + return 0; +} + +static int __init +atkbd_enable(void) +{ + uint8_t byte; + int error; + + atkbd_write_cmd(ATKBD_CMD_EN1); + + atkbd_write_cmd(ATKBD_CMD_RDCONF); + error = atkbd_read(&byte, true); + + if (error) { + return error; + } + + byte &= ~(ATKBD_CONF_ENTRANS | ATKBD_CONF_ENINT2); + byte |= ATKBD_CONF_ENINT1; + atkbd_write_cmd(ATKBD_CMD_WRCONF); + atkbd_write(byte); + + atkbd_flush(); + + return 0; +} + +static void +atkbd_set_leds(void) +{ + uint8_t leds, reply; + int error; + + leds = ((atkbd_flags & ATKBD_KF_NUMLOCK) ? ATKBD_LEDS_NUMLOCK : 0) + | ((atkbd_flags & ATKBD_KF_CAPSLOCK) ? ATKBD_LEDS_CAPSLOCK : 0) + | ((atkbd_flags & ATKBD_KF_SCROLLLOCK) ? ATKBD_LEDS_SCROLLLOCK : 0); + +resend: + atkbd_write(ATKBD_REQ_LEDS); + atkbd_write(leds); + error = atkbd_read(&reply, true); + + if (error) { + return; + } + + if (reply == ATKBD_REP_RESEND) { + goto resend; + } + + if (reply != ATKBD_REP_ACK) { + printf("atkbd: warning: invalid reply from keyboard\n"); + } +} + +static void +atkbd_toggle_numlock(void) +{ + atkbd_flags ^= ATKBD_KF_NUMLOCK; + atkbd_set_leds(); +} + +static void +atkbd_toggle_capslock(void) +{ + atkbd_flags ^= ATKBD_KF_CAPSLOCK; + atkbd_set_leds(); +} + +static void +atkbd_key_process_chars(const struct atkbd_key *key, + const char **chars, size_t size) +{ + const char *s; + + if (key->id >= size) { + return; + } + + if (atkbd_flags & ATKBD_KF_F0) { + return; + } + + s = chars[key->id]; + + if (s != NULL) { + while (*s != '\0') { + atcons_intr(*s); + s++; + } + } +} + +static void +atkbd_key_process_shift(const struct atkbd_key *key) +{ + atkbd_key_process_chars(key, atkbd_shift_chars, + ARRAY_SIZE(atkbd_shift_chars)); +} + +static void +atkbd_key_process_kp(const struct atkbd_key *key) +{ + atkbd_key_process_chars(key, atkbd_kp_chars, + ARRAY_SIZE(atkbd_kp_chars)); +} + +static void +atkbd_key_process_ctl(const struct atkbd_key *key) +{ + switch (key->id) { + case ATKBD_KEY_LSHIFT: + if (atkbd_flags & ATKBD_KF_F0) { + atkbd_flags &= ~ATKBD_KF_LSHIFT; + } else { + atkbd_flags |= ATKBD_KF_LSHIFT; + } + + break; + case ATKBD_KEY_RSHIFT: + if (atkbd_flags & ATKBD_KF_F0) { + atkbd_flags &= ~ATKBD_KF_RSHIFT; + } else { + atkbd_flags |= ATKBD_KF_RSHIFT; + } + + break; + case ATKBD_KEY_KP_NUMLOCK: + if (!(atkbd_flags & ATKBD_KF_F0)) { + atkbd_toggle_numlock(); + } + + break; + case ATKBD_KEY_CAPSLOCK: + if (!(atkbd_flags & ATKBD_KF_F0)) { + atkbd_toggle_capslock(); + } + default: + break; + } +} + +static void +atkbd_key_process(const struct atkbd_key *key) +{ + if (key->id == ATKBD_KEY_INVALID) { + return; + } + + if ((key->modifiers & ATKBD_KM_SHIFT) && (atkbd_flags & ATKBD_KF_SHIFT)) { + atkbd_key_process_shift(key); + } else if ((key->modifiers & ATKBD_KM_KP) + && (atkbd_flags & ATKBD_KF_NUMLOCK)) { + atkbd_key_process_kp(key); + } else if (key->modifiers & ATKBD_KM_CTL) { + atkbd_key_process_ctl(key); + } else { + atkbd_key_process_chars(key, atkbd_chars, ARRAY_SIZE(atkbd_chars)); + } + + atkbd_flags &= ~ATKBD_KF_F0; +} + +static void +atkbd_process_e0_code(uint8_t code) +{ + if (code == 0xf0) { + atkbd_flags |= ATKBD_KF_F0; + return; + } + + if (code >= ARRAY_SIZE(atkbd_keys)) { + return; + } + + atkbd_key_process(&atkbd_e0_keys[code]); + atkbd_flags &= ~ATKBD_KF_E0; +} + +static void +atkbd_process_code(uint8_t code) +{ + if (code == 0xe0) { + atkbd_flags |= ATKBD_KF_E0; + return; + } else if (code == 0xf0) { + atkbd_flags |= ATKBD_KF_F0; + return; + } + + if (code >= ARRAY_SIZE(atkbd_keys)) { + return; + } + + atkbd_key_process(&atkbd_keys[code]); +} + +static int +atkbd_intr(void *arg) +{ + uint8_t code; + int error; + + (void)arg; + + for (;;) { + error = atkbd_read(&code, false); + + if (error) { + return 0; + } + + if (atkbd_flags & ATKBD_KF_E0) { + atkbd_process_e0_code(code); + } else { + atkbd_process_code(code); + } + } + + return 0; +} + +void __init +atkbd_setup(void) +{ + int error; + + error = atkbd_disable(); + + if (error) { + return; + } + + error = intr_register(ATKBD_INTR1, atkbd_intr, NULL); + + if (error) { + printf("atkbd: error: unable to register interrupt handler\n"); + return; + } + + error = atkbd_enable(); + + if (error) { + return; + } + + atkbd_reset_kbd(); +} diff --git a/arch/x86/machine/atkbd.h b/arch/x86/machine/atkbd.h new file mode 100644 index 00000000..0bed7ada --- /dev/null +++ b/arch/x86/machine/atkbd.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * + * Tiny AT keyboard driver. + */ + +#ifndef _X86_ATKBD_H +#define _X86_ATKBD_H + +/* + * Initialize the atkbd module. + */ +void atkbd_setup(void); + +#endif /* _X86_ATKBD_H */ diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 0df96747..b7a9b0ee 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -63,6 +63,7 @@ #include <kern/syscnt.h> #include <kern/thread.h> #include <kern/turnstile.h> +#include <machine/atcons.h> #include <machine/biosmem.h> #include <machine/boot.h> #include <machine/cga.h> @@ -468,7 +469,7 @@ boot_main(void) cpu_setup(); thread_bootstrap(); console_setup(); - cga_setup(); + atcons_bootstrap(); uart_bootstrap(); printf_setup(); boot_show_version(); @@ -485,6 +486,7 @@ boot_main(void) intr_setup(); cpu_mp_probe(); pic_setup(); + atcons_setup(); uart_setup(); kernel_main(); diff --git a/arch/x86/machine/cga.c b/arch/x86/machine/cga.c index 3f1525c2..1ec80354 100644 --- a/arch/x86/machine/cga.c +++ b/arch/x86/machine/cga.c @@ -18,7 +18,6 @@ #include <stdint.h> #include <string.h> -#include <kern/console.h> #include <kern/init.h> #include <kern/macros.h> #include <kern/param.h> @@ -70,8 +69,6 @@ static uint8_t *cga_memory __read_mostly; static uint16_t cga_cursor; -static struct console cga_console; - static uint16_t cga_get_cursor_position(void) { @@ -119,8 +116,8 @@ cga_scroll_lines(void) } } -static void -cga_write_char(char c) +void +cga_putc(char c) { if (c == '\r') { return; @@ -143,7 +140,7 @@ cga_write_char(char c) int i; for(i = 0; i < CGA_TABULATION_SPACES; i++) { - cga_write_char(' '); + cga_putc(' '); } } else { if ((cga_cursor + 1) >= CGA_COLUMNS * CGA_LINES) { @@ -158,17 +155,6 @@ cga_write_char(char c) } } -static void -cga_console_putc(struct console *console, char c) -{ - (void)console; - cga_write_char(c); -} - -static const struct console_ops cga_console_ops = { - .putc = cga_console_putc, -}; - void __init cga_setup(void) { @@ -194,7 +180,4 @@ cga_setup(void) } cga_cursor = cga_get_cursor_position(); - - console_init(&cga_console, "cga", &cga_console_ops); - console_register(&cga_console); } diff --git a/arch/x86/machine/cga.h b/arch/x86/machine/cga.h index a6771621..f5a5d285 100644 --- a/arch/x86/machine/cga.h +++ b/arch/x86/machine/cga.h @@ -26,4 +26,9 @@ */ void cga_setup(void); +/* + * Append a character to the CGA screen. + */ +void cga_putc(char c); + #endif /* _X86_CGA_H */ |