diff options
Diffstat (limited to 'kernel/drivers/keyboard.c')
-rw-r--r-- | kernel/drivers/keyboard.c | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/kernel/drivers/keyboard.c b/kernel/drivers/keyboard.c new file mode 100644 index 0000000..c0c4b5e --- /dev/null +++ b/kernel/drivers/keyboard.c @@ -0,0 +1,208 @@ +#include "drivers/keyboard.h" + +#include "drivers/tty/tty.h" + +#include "main/interrupt.h" +#include "main/io.h" + +#define IRQ_KEYBOARD 1 + +/* Indicates that one of these is "being held down" */ +#define SHIFT_MASK 0x1 +#define CTRL_MASK 0x2 +/* Indicates that an escape code was the previous key received */ +#define ESC_MASK 0x4 +static int curmask = 0; + +/* Where to read from to get scancodes */ +#define KEYBOARD_IN_PORT 0x60 +#define KEYBOARD_CMD_PORT 0x61 + +/* Scancodes for special keys */ +#define LSHIFT 0x2a +#define RSHIFT 0x36 +#define CTRL 0x1d +/* Right ctrl is escaped */ +/* Our keyboard driver totally ignores ALT */ + +#define ESC0 0xe0 +#define ESC1 0xe1 + +/* If the scancode & BREAK_MASK, it's a break code; otherwise, it's a make code + */ +#define BREAK_MASK 0x80 + +#define NORMAL_KEY_HIGH 0x39 + +/* Some sneaky value to indicate we don't actually pass anything to the terminal + */ +#define NO_CHAR 0xff + +#define F1_SCANCODE 0x3b +#define F12_SCANCODE (F1_SCANCODE + 11) + +/* Scancode tables copied from + http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html */ + +/* The scancode table for "normal" scancodes - from 02 to 39 */ +/* Unsupported chars are symbolized by \0 */ +static const char *normal_scancodes = + "\0" /* Error */ + "\e" /* Escape key */ + "1234567890-=" /* Top row */ + "\b" /* Backspace */ + "\tqwertyuiop[]\n" /* Next row - ish */ + "\0" /* Left ctrl */ + "asdfghjkl;\'`" + "\0" /* Lshift */ + "\\" + "zxcvbnm,./" + "\0\0\0" /* Rshift, prtscrn, Lalt */ + " "; /* Space bar */ +/* As above, but if shift is pressed */ +static const char *shift_scancodes = + "\0" + "\e" + "!@#$%^&*()_+" + "\b" + "\tQWERTYUIOP{}\n" + "\0" + "ASDFGHJKL:\"~" + "\0" + "|" + "ZXCVBNM<>?" + "\0\0\0" + " "; + +static keyboard_char_handler_t keyboard_handler = NULL; + +/* This is the function we register with the interrupt handler - it reads the + * scancode and, if appropriate, call's the tty's receive_char function */ +static long keyboard_intr_handler(regs_t *regs) +{ + uint8_t sc; /* The scancode we receive */ + int break_code; /* Was it a break code */ + /* the resulting character ('\0' -> ignored char) */ + uint8_t c = NO_CHAR; + /* Get the scancode */ + sc = inb(KEYBOARD_IN_PORT); + /* Separate out the break code */ + break_code = sc & BREAK_MASK; + sc &= ~BREAK_MASK; + + /* dbg(DBG_KB, ("scancode 0x%x, break 0x%x\n", sc, break_code)); */ + + /* The order of this conditional is very, very tricky - be careful when + * editing! */ + + /* Most break codes are ignored */ + if (break_code) + { + /* Shift/ctrl release */ + if (sc == LSHIFT || sc == RSHIFT) + { + curmask &= ~SHIFT_MASK; + } + else if (sc == CTRL) + { + curmask &= ~CTRL_MASK; + } + } + /* Check for the special keys */ + else if (sc == LSHIFT || sc == RSHIFT) + { + curmask |= SHIFT_MASK; + } + else if (sc == CTRL) + { + curmask |= CTRL_MASK; + } + /* All escaped keys past this point (anything except right shift and right + * ctrl) will be ignored */ + else if (curmask & ESC_MASK) + { + /* Escape mask only lasts for one key */ + curmask &= ~ESC_MASK; + } + /* Now check for escape code */ + else if (sc == ESC0 || sc == ESC1) + { + curmask |= ESC_MASK; + } + + else if (sc >= F1_SCANCODE && sc <= F12_SCANCODE) + { + c = (uint8_t)(F1 + (sc - F1_SCANCODE)); + } + /* Check for Ctrl+Backspace which indicates scroll down */ + else if ((curmask & CTRL_MASK) && (curmask & SHIFT_MASK) && + sc == SCROLL_DOWN) + { + c = SCROLL_DOWN_PAGE; + } + + else if ((curmask & CTRL_MASK) && (curmask & SHIFT_MASK) && + sc == SCROLL_UP) + { + c = SCROLL_UP_PAGE; + } + + else if ((curmask & CTRL_MASK) && sc == SCROLL_DOWN) + { + c = SCROLL_DOWN; + } + /* Check for Ctrl+Enter which indicates scroll down */ + else if ((curmask & CTRL_MASK) && sc == SCROLL_UP) + { + c = SCROLL_UP; + } + /* Check to make sure the key isn't high enough that it won't be found in + * tables */ + else if (sc > NORMAL_KEY_HIGH) + { + /* ignore */ + } + /* Control characters */ + else if (curmask & CTRL_MASK) + { + /* Because of the way ASCII works, the control chars are based on the + * values of the shifted chars produced without control */ + c = (uint8_t)shift_scancodes[sc]; + /* Range of chars that have corresponding control chars */ + if (c >= 0x40 && c < 0x60) + { + c -= 0x40; + } + else + { + c = NO_CHAR; + } + } + /* Capitals */ + else if (curmask & SHIFT_MASK) + { + c = (uint8_t)shift_scancodes[sc]; + } + else + { + c = (uint8_t)normal_scancodes[sc]; + } + + if (c != NO_CHAR) + { + keyboard_handler(c); + } + else + { + // panic("get rid of me: char was: %c (%d) (%x)\n", c, c, c); + } + dbg(DBG_KB, "received scancode 0x%x; resolved to char 0x%x\n", sc, c); + return 0; +} + +void keyboard_init(keyboard_char_handler_t handler) +{ + intr_map(IRQ_KEYBOARD, INTR_KEYBOARD); + intr_register(INTR_KEYBOARD, keyboard_intr_handler); + keyboard_handler = handler; +} |