aboutsummaryrefslogtreecommitdiff
path: root/kernel/drivers/keyboard.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drivers/keyboard.c')
-rw-r--r--kernel/drivers/keyboard.c208
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;
+}