diff options
Diffstat (limited to 'roms/openbios/drivers/pc_kbd.c')
-rw-r--r-- | roms/openbios/drivers/pc_kbd.c | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/roms/openbios/drivers/pc_kbd.c b/roms/openbios/drivers/pc_kbd.c new file mode 100644 index 000000000..a98e57e66 --- /dev/null +++ b/roms/openbios/drivers/pc_kbd.c @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2003, 2004 Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +#include "config.h" +#include "libopenbios/bindings.h" +#include "kernel/kernel.h" +#include "drivers/drivers.h" +#include "libc/vsprintf.h" + +/* ****************************************************************** + * simple polling video/keyboard console functions + * ****************************************************************** */ + +#define SER_SIZE 8 + +/* + * keyboard driver + */ + +static const char normal[] = { + 0x0, 0x1b, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', + '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', + 'p', '[', ']', 0xa, 0x0, 'a', 's', 'd', 'f', 'g', 'h', 'j', + 'k', 'l', ';', 0x27, 0x60, 0x0, 0x5c, 'z', 'x', 'c', 'v', 'b', + 'n', 'm', ',', '.', '/', 0x0, '*', 0x0, ' ', 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, '0', 0x7f +}; + +static const char shifted[] = { + 0x0, 0x1b, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', + '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', + 'P', '{', '}', 0xa, 0x0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', + 'K', 'L', ':', 0x22, '~', 0x0, '|', 'Z', 'X', 'C', 'V', 'B', + 'N', 'M', '<', '>', '?', 0x0, '*', 0x0, ' ', 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, '7', '8', + '9', 0x0, '4', '5', '6', 0x0, '1', '2', '3', '0', 0x7f +}; + +static int key_ext; +static int key_lshift = 0, key_rshift = 0, key_caps = 0; + +static char last_key; + +static void pc_kbd_cmd(unsigned char cmd, unsigned char val) +{ + outb(cmd, 0x60); + /* wait until keyboard controller accepts cmds: */ + while (inb(0x64) & 2); + outb(val, 0x60); + while (inb(0x64) & 2); +} + +static void pc_kbd_controller_cmd(unsigned char cmd, unsigned char val) +{ + outb(cmd, 0x64); + /* wait until keyboard controller accepts cmds: */ + while (inb(0x64) & 2); + outb(val, 0x60); + while (inb(0x64) & 2); +} + +static char pc_kbd_poll(void) +{ + unsigned int c; + if (inb(0x64) & 1) { + c = inb(0x60); + switch (c) { + case 0xe0: + key_ext = 1; + return 0; + case 0x2a: + key_lshift = 1; + return 0; + case 0x36: + key_rshift = 1; + return 0; + case 0xaa: + key_lshift = 0; + return 0; + case 0xb6: + key_rshift = 0; + return 0; + case 0x3a: + if (key_caps) { + key_caps = 0; + pc_kbd_cmd(0xed, 0); + } else { + key_caps = 1; + pc_kbd_cmd(0xed, 4); /* set caps led */ + } + return 0; + } + + if (key_ext) { + // void printk(const char *format, ...); + printk("extended keycode: %x\n", c); + + key_ext = 0; + return 0; + } + + if (c & 0x80) /* unhandled key release */ + return 0; + + if (key_lshift || key_rshift) + return key_caps ? normal[c] : shifted[c]; + else + return key_caps ? shifted[c] : normal[c]; + } + return 0; +} + +int pc_kbd_dataready(void) +{ + if (last_key) + return 1; + + last_key = pc_kbd_poll(); + + return (last_key != 0); +} + +unsigned char pc_kbd_readdata(void) +{ + char tmp; + while (!pc_kbd_dataready()); + tmp = last_key; + last_key = 0; + return tmp; +} + +static void +pc_kbd_reset(void) +{ + /* Reset first port */ + outb(0xae, 0x64); + while (inb(0x64) & 2); + + /* Write mode command, translated mode */ + pc_kbd_controller_cmd(0x60, 0x40); + + /* Reset keyboard device */ + outb(0xff, 0x60); + while (inb(0x64) & 2); + inb(0x60); /* Should be 0xfa */ + while (inb(0x64) & 2); + inb(0x60); /* Should be 0xaa */ +} + +/* ( addr len -- actual ) */ +static void +pc_kbd_read(void) +{ + unsigned char *addr; + int len; + + len = POP(); + addr = (unsigned char *)POP(); + + if (len != 1) + printk("pc_kbd_read: bad len, addr %lx len %x\n", (unsigned long)addr, len); + + if (pc_kbd_dataready()) { + *addr = pc_kbd_readdata(); + PUSH(1); + } else { + PUSH(0); + } +} + +static void +pc_kbd_close(void) +{ +} + +static void +pc_kbd_open(unsigned long *address) +{ + PUSH(find_ih_method("address", my_self())); + fword("execute"); + *address = POP(); + + RET ( -1 ); +} + +DECLARE_UNNAMED_NODE(pc_kbd, 0, sizeof(unsigned long)); + +NODE_METHODS(pc_kbd) = { + { "open", pc_kbd_open }, + { "close", pc_kbd_close }, + { "read", pc_kbd_read }, +}; + +void +ob_pc_kbd_init(const char *path, const char *kdev_name, const char *mdev_name, + uint64_t base, uint64_t offset, int kintr, int mintr) +{ + phandle_t chosen, aliases; + char nodebuff[128]; + + fword("new-device"); + + push_str("8042"); + fword("device-type"); + + push_str("8042"); + fword("device-name"); + + /* Make openable */ + fword("is-open"); + + PUSH((base + offset) >> 32); + fword("encode-int"); + PUSH((base + offset) & 0xffffffff); + fword("encode-int"); + fword("encode+"); + PUSH(SER_SIZE); + fword("encode-int"); + fword("encode+"); + + if (mdev_name != NULL) { + PUSH((base + offset) >> 32); + fword("encode-int"); + fword("encode+"); + PUSH((base + offset) & 0xffffffff); + fword("encode-int"); + fword("encode+"); + PUSH(SER_SIZE); + fword("encode-int"); + fword("encode+"); + } + + push_str("reg"); + fword("property"); + + chosen = get_cur_dev(); + set_int_property(chosen, "#address-cells", 1); + set_int_property(chosen, "#size-cells", 0); + + PUSH(kintr); + fword("encode-int"); + + if (mdev_name != NULL) { + PUSH(mintr); + fword("encode-int"); + fword("encode+"); + } + + push_str("interrupts"); + fword("property"); + + /* Keyboard */ + fword("new-device"); + + push_str(kdev_name); + fword("device-name"); + + push_str("serial"); + fword("device-type"); + + PUSH(0); + fword("encode-int"); + push_str("reg"); + fword("property"); + + PUSH(-1); + fword("encode-int"); + push_str("keyboard"); + fword("property"); + + PUSH(offset); + fword("encode-int"); + push_str("address"); + fword("property"); + + BIND_NODE_METHODS(get_cur_dev(), pc_kbd); + + PUSH(offset); + feval("value address"); + + fword("finish-device"); + + snprintf(nodebuff, sizeof(nodebuff), "%s/8042/%s", path, kdev_name); + chosen = find_dev("/chosen"); + push_str(nodebuff); + fword("open-dev"); + set_int_property(chosen, "keyboard", POP()); + + aliases = find_dev("/aliases"); + set_property(aliases, "keyboard", nodebuff, strlen(nodebuff) + 1); + + pc_kbd_reset(); + + /* Mouse (optional) */ + if (mdev_name != NULL) { + fword("new-device"); + + push_str(mdev_name); + fword("device-name"); + + push_str("mouse"); + fword("device-type"); + + PUSH(1); + fword("encode-int"); + push_str("reg"); + fword("property"); + + PUSH(-1); + fword("encode-int"); + push_str("mouse"); + fword("property"); + + PUSH(offset); + fword("encode-int"); + push_str("address"); + fword("property"); + + fword("finish-device"); + } + + fword("finish-device"); +} |