diff options
Diffstat (limited to 'console-client/pc-kbd.c')
-rw-r--r-- | console-client/pc-kbd.c | 1194 |
1 files changed, 1194 insertions, 0 deletions
diff --git a/console-client/pc-kbd.c b/console-client/pc-kbd.c new file mode 100644 index 00000000..d66e94b3 --- /dev/null +++ b/console-client/pc-kbd.c @@ -0,0 +1,1194 @@ +/* pc-kbd.c - The PC Keyboard input driver. + Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. + Written by Marcus Brinkmann. + + This file is part of the GNU Hurd. + + The GNU Hurd 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 2, or (at + your option) any later version. + + The GNU Hurd 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <errno.h> +#include <assert.h> +#include <string.h> +#include <iconv.h> +#include <sys/mman.h> +#include <argp.h> + +#include <device/device.h> +#include <cthreads.h> + +#include <hurd/console.h> +#include <hurd/cons.h> + +#include "driver.h" +#include "mach-inputdev.h" + + +/* The default name of the node of the repeater. */ +#define DEFAULT_REPEATER_NODE "kbd" + +/* The keyboard device in the kernel. */ +static device_t kbd_dev; + +/* The converter. */ +static iconv_t cd; + +/* The status of various LEDs. */ +struct { + int scroll_lock : 1; + int num_lock : 1; + int caps_lock : 1; +} led_state; + +/* True if we are in the GNU Mach v1 compatibility mode. */ +int gnumach_v1_compat; + +/* Forward declaration. */ +static struct input_ops pc_kbd_ops; + +/* The name of the repeater node. */ +static char *repeater_node; + +/* The repeater node. */ +static consnode_t cnode; + +/* A list of scan codes generated by the keyboard, in the set 2 encoding. */ +enum scancode + { + SC_F9 = 0x01, + SC_F5 = 0x03, + SC_F3 = 0x04, + SC_F1 = 0x05, + SC_F2 = 0x06, + SC_F12 = 0x07, + SC_F10 = 0x09, + SC_F8 = 0x0A, + SC_F6 = 0x0B, + SC_F4 = 0x0C, + SC_TAB = 0x0D, + SC_BACKQUOTE = 0x0E, /* ` */ + SC_LEFT_ALT = 0x11, + SC_LEFT_SHIFT = 0x12, + SC_LEFT_CTRL = 0x14, + SC_Q = 0x15, + SC_1 = 0x16, + SC_Z = 0x1A, + SC_S = 0x1B, + SC_A = 0x1C, + SC_W = 0x1D, + SC_2 = 0x1E, + SC_C = 0x21, + SC_X = 0x22, + SC_D = 0x23, + SC_E = 0x24, + SC_4 = 0x25, + SC_3 = 0x26, + SC_SPACE = 0x29, + SC_V = 0x2A, + SC_F = 0x2B, + SC_T = 0x2C, + SC_R = 0x2D, + SC_5 = 0x2E, + SC_N = 0x31, + SC_B = 0x32, + SC_H = 0x33, + SC_G = 0x34, + SC_Y = 0x35, + SC_6 = 0x36, + SC_M = 0x3A, + SC_J = 0x3B, + SC_U = 0x3C, + SC_7 = 0x3D, + SC_8 = 0x3E, + SC_COMMA = 0x41, /* , */ + SC_K = 0x42, + SC_I = 0x43, + SC_O = 0x44, + SC_0 = 0x45, + SC_9 = 0x46, + SC_PERIOD = 0x49, /* . */ + SC_SLASH = 0x4A, /* / */ + SC_L = 0x4B, + SC_SEMICOLON = 0x4C, /* ; */ + SC_P = 0x4D, + SC_MINUS = 0x4E, /* - */ + SC_APOSTROPHE = 0x52, /* ' */ + SC_LEFT_BRACKET = 0x54, /* [ */ + SC_EQUAL = 0x55, /* = */ + SC_CAPSLOCK = 0x58, + SC_RIGHT_SHIFT = 0x59, + SC_ENTER = 0x5A, + SC_RIGHT_BRACKET = 0x5B, /* ] */ + SC_BACKSLASH = 0x5D, /* \ */ + SC_BACKSPACE = 0x66, + SC_PAD_1 = 0x69, + SC_PAD_4 = 0x6B, + SC_PAD_7 = 0x6C, + SC_PAD_0 = 0x70, + SC_PAD_DECIMAL = 0x71, + SC_PAD_2 = 0x72, + SC_PAD_5 = 0x73, + SC_PAD_6 = 0x74, + SC_PAD_8 = 0x75, + SC_ESC = 0x76, + SC_NUMLOCK = 0x77, + SC_F11 = 0x78, + SC_PAD_PLUS = 0x79, + SC_PAD_3 = 0x7A, + SC_PAD_MINUS = 0x7B, + SC_PAD_ASTERISK = 0x7C, + SC_PAD_9 = 0x7D, + SC_SCROLLLOCK = 0x7E, + SC_F7 = 0x83, + SC_EXTENDED1 = 0xE0, /* One code follows. */ + SC_EXTENDED2 = 0xE1, /* Two codes follow (only used for Pause). */ + SC_ERROR = 0xFF, /* Too many keys held down. */ + SC_FLAG_UP = 0xF000 /* ORed to basic scancode. */ + }; + +/* In set 2 function keys don't have a logical order. This macro can + determine if a function key was pressed. */ +#define IS_FUNC_KEY(c) ((sc >= SC_F9 && sc <= SC_F4) || \ + sc == SC_F7 || sc == SC_F11) + +/* Codes which can follow SC_EXTENDED1. */ +enum scancode_x1 + { + SC_X1_RIGHT_ALT = 0x11, + SC_X1_PRTSC = 0x12, +/* SC_X1_PRTSC = 0x7C, */ + SC_X1_RIGHT_CTRL = 0x14, + SC_X1_LEFT_GUI = 0x1F, + SC_X1_RIGHT_GUI = 0x27, + SC_X1_APPS = 0x2F, + SC_X1_POWER = 0x37, + SC_X1_SLEEP = 0x3F, + SC_X1_PAD_SLASH = 0x4A, + SC_X1_PAD_ENTER = 0x5A, + SC_X1_WAKEUP = 0x5E, + SC_X1_END = 0x69, + SC_X1_LEFT = 0x6B, + SC_X1_HOME = 0x6C, + SC_X1_INS = 0x70, + SC_X1_DEL = 0x71, + SC_X1_DOWN = 0x72, + SC_X1_RIGHT = 0x74, + SC_X1_UP = 0x75, + SC_X1_PGDN = 0x7A, + SC_X1_PGUP = 0x7D + }; + +/* Codes which can follow SC_EXTENDED2. */ +enum scancode_x2 + { + SC_X2_BREAK = 0x1477, + }; + + +/* Scancode to Unicode mapping. The empty string stands for the NULL + character. */ +char *sc_to_kc[][7] = + { + /*None, Shift, Ctrl, LAlt, S+LAlt, C+LAlt, RAlt */ + { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_F9, 0, 0, 0, 0, 0, 0 }, /* SC_F9. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_F5, CONS_KEY_F17, 0, 0, 0, 0, 0 }, /* SC_F5. */ + { CONS_KEY_F3, CONS_KEY_F15, 0, 0, 0, 0, 0 }, /* SC_F3. */ + { CONS_KEY_F1, CONS_KEY_F13, 0, 0, 0, 0, 0 }, /* SC_F1. */ + { CONS_KEY_F2, CONS_KEY_F14, 0, 0, 0, 0, 0 }, /* SC_F2. */ + { CONS_KEY_F12, 0, 0, 0, 0, 0, 0 }, /* SC_F12. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_F10, 0, 0, 0, 0, 0, 0 }, /* SC_F10. */ + { CONS_KEY_F8, CONS_KEY_F20, 0, 0, 0, 0, 0 }, /* SC_F8. */ + { CONS_KEY_F6, CONS_KEY_F18, 0, 0, 0, 0, 0 }, /* SC_F6. */ + { CONS_KEY_F4, CONS_KEY_F16, 0, 0, 0, 0, 0 }, /* SC_F4. */ + { "\t", "\t", "\t", "\e\t", "\e\t", "\e\t", "\t" }, /* SC_TAB. */ + { "`", "~", 0, "\e`", "\e~", 0, 0 }, /* SC_BACKQUOTE. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_LEFT_ALT. XXX */ + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_LEFT_SHIFT. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_LEFT_CTRL. XXX */ + { "q", "Q", "\x11", "\eq", "\eQ", "\e\x11", "q" }, /* SC_Q. */ + { "1", "!", 0, "\e1", "\e!", 0, "1" }, /* SC_1. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { "z", "Z", "\x1a", "\ez", "\eZ", "\e\x1a", "z" }, /* SC_Z. */ + { "s", "S", "\x13", "\es", "\eS", "\e\x13", "s" }, /* SC_S. */ + { "a", "A", "\x01", "\ea", "\eA", "\e\x01", "a" }, /* SC_A. */ + { "w", "W", "\x17", "\ew", "\eW", "\e\x17", "w" }, /* SC_W. */ + { "2", "@", "", "\e2", "\e@", 0, "2" }, /* SC_2. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { "c", "C", "\x03", "\ec", "\eC", "\e\x03", "\xc2\xa2" }, /* SC_C. */ + { "x", "X", "\x18", "\ex", "\eX", "\e\x18", "x" }, /* SC_X. */ + { "d", "D", "\x04", "\ed", "\eD", "\e\x04", "d" }, /* SC_D. */ + { "e", "E", "\x05", "\ee", "\eE", "\e\x05","\xe2\x82\xac" }, /* SC_E. */ + { "4", "$", "\x1c", "\e4", "\e$", "\e\x1c", "4" }, /* SC_4. */ + { "3", "#", "\e", "\e3", "\e#", 0, "3" }, /* SC_3. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { " ", " ", "", "\e ", "\e ", /*XXX*/0, " " }, /* SC_SPACE. */ + { "v", "V", "\x16", "\ev", "\eV", "\e\x16", "v" }, /* SC_V. */ + { "f", "F", "\x06", "\ef", "\eF", "\e\x06", "f" }, /* SC_F. */ + { "t", "T", "\x14", "\et", "\eT", "\e\x14", "t" }, /* SC_T. */ + { "r", "R", "\x12", "\er", "\eR", "\e\x12", "r" }, /* SC_R. */ + { "5", "%", "\x1d", "\e5", "\e%", 0, "5" }, /* SC_5. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { "n", "N", "\x0e", "\en", "\eN", "\e\x0e", "n" }, /* SC_N. */ + { "b", "B", "\x02", "\eb", "\eB", "\e\x02", "b" }, /* SC_B. */ + { "h", "H", "\x08", "\eh", "\eH", "\e\x08", "h" }, /* SC_H. */ + { "g", "G", "\x07", "\eg", "\eG", "\e\x07", "g" }, /* SC_G. */ + { "y", "Y", "\x19", "\ey", "\eY", "\e\x19", "y" }, /* SC_Y. */ + { "6", "^", "\x1e", "\e6", "\e^", 0, "6" }, /* SC_6. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { "m", "M", "\x0d", "\em", "\eM", "\e\x0d", "m" }, /* SC_M. */ + { "j", "J", "\x0a", "\ej", "\eJ", "\e\x0a", "j" }, /* SC_J. */ + { "u", "U", "\x15", "\eu", "\eU", "\e\x15", "u" }, /* SC_U. */ + { "7", "&", "\x1f", "\e7", "\e&", "\e\x1f", "7" }, /* SC_7. */ + { "8", "*", "\x7f", "\e8", "\e*", 0, "8" }, /* SC_8. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { ",", "<", 0, "\e,", "\e<", 0, 0 }, /* SC_COMMA. */ + { "k", "K", "\x0b", "\ek", "\eK", "\e\x0b", "k" }, /* SC_K. */ + { "i", "I", "\x09", "\ei", "\eI", "\e\x09", "i" }, /* SC_I. */ + { "o", "O", "\x0f", "\eo", "\eO", "\e\x0f", "o" }, /* SC_O. */ + { "0", ")", 0, "\e0", "\e)", 0, "0" }, /* SC_0. */ + { "9", "(", 0, "\e9", "\e(", 0, "9" }, /* SC_9. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { ".", ">", 0, "\e.", "\e>", 0, 0 }, /* SC_PERIOD. */ + { "/", "?", "\x7f", "\e/", "\e?", 0, 0 }, /* SC_SLASH. */ + { "l", "L", "\x0c", "\el", "\eL", "\e\x0c", "l" }, /* SC_L. */ + { ";", ":", 0, "\e;", "\e:", 0, 0 }, /* SC_SEMICOLON. */ + { "p", "P", "\x10", "\ep", "\eP", "\e\x10", "p" }, /* SC_P. */ + { "-", "_", "\x1f", "\e-", "\e_", "\e\x1f", "-" }, /* SC_MINUS. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { "'", "\"", "\x07", "\e'", "\e\"", 0, 0 }, /* SC_APOSTROPHE. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { "[", "{", "\e", "\e[", "\e{", 0, 0 }, /* SC_LEFT_BRACKET. */ + { "=", "+", 0, "\e=", "\e+", 0, "=" }, /* SC_EQUAL. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_CAPSLOCK. */ + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_RIGHT_SHIFT. */ + {"\x0d","\x0d", "\x0d","\e\x0d","\e\x0d","\e\x0d","\x0d" }, /* SC_ENTER. */ + { "]", "}", "\x1d", "\e]", "\e}", "\e\x1d", "~" }, /* SC_RIGHT_BRACKET. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { "\\", "|", "\x1c", "\e\\", "\e|", 0, 0 }, /* SC_BACKSLASH. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_BACKSPACE, CONS_KEY_BACKSPACE, CONS_KEY_BACKSPACE, + "\e" CONS_KEY_BACKSPACE, "\e" CONS_KEY_BACKSPACE, + "\e" CONS_KEY_BACKSPACE, CONS_KEY_BACKSPACE }, /* SC_BACKSPACE. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_END, CONS_KEY_END, CONS_KEY_END, 0, 0, 0, 0 }, /* SC_PAD_1. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT, 0, 0, 0, 0 }, /* SC_PAD_4. */ + { CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME, 0, 0, 0, 0 }, /* SC_PAD_7. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC, 0, 0, 0, 0 }, /* SC_PAD_0. */ + { CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC, 0, 0, 0, 0 }, /* SC_PAD_DECIMAL. */ + { CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN, 0, 0, 0, 0 }, /* SC_PAD_2. */ + {/* XXX */ "\e[G", "\e[G", "\e[G", 0, 0, 0, 0 }, /* SC_PAD_5. */ + { CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT,0, 0, 0, 0 }, /* SC_PAD_6. */ + { CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP, 0, 0, 0, 0 }, /* SC_PAD_8. */ + { "\e", "\e", "\e", "\e\e", "\e\e", "\e\e", "\e" }, /* SC_ESC. */ + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_NUMLOCK. */ + { CONS_KEY_F11, 0, 0, 0, 0, 0, 0 }, /* SC_F11. */ + { "+", "+", "+", "+", "+", "+", "+" }, /* SC_PAD_PLUS. */ + { CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE,0, 0, 0, 0 }, /* SC_PAD_3. */ + { "-", "-", "-", "-", "-", "-", "-" }, /* SC_PAD_MINUS. */ + { "*", "*", "*", "*", "*", "*", "*" }, /* SC_PAD_ASTERISK. XXX */ + { CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE,0, 0, 0, 0 }, /* SC_PAD_9. */ + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_SCROLLLOCK. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_F7, CONS_KEY_F19, 0, 0, 0, 0, 0 } /* SC_F7. */ + }; + +char *sc_x1_to_kc[][7] = + { + /* None, Shift, Ctrl, LAlt, S+LAlt, C+LAlt, RAlt */ + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_RIGHT_ALT. */ + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_PRTSC. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_RIGHT_CTRL. */ + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_LEFT_GUI. */ + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_RIGHT_GUI. */ + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_APPS. */ + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_POWER. */ + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_SLEEP. */ + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { "/", "/", "/", "/", "/", "/", 0 }, /* SC_X1_PAD_SLASH. */ + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { "\n", "\n", "\n", "\n", "\n", "\n", 0 }, /* SC_X1_PAD_ENTER. */ + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_WAKEUP. */ + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_END, CONS_KEY_END, CONS_KEY_END, CONS_KEY_END, + CONS_KEY_END, CONS_KEY_END, CONS_KEY_END }, /* SC_X1_END. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT, + CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT }, /* SC_X1_LEFT. */ + { CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME, + CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME }, /* SC_X1_HOME. */ + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC, + CONS_KEY_IC, CONS_KEY_IC, CONS_KEY_IC }, /* SC_X1_INS. */ + { CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC, + CONS_KEY_DC, CONS_KEY_DC, CONS_KEY_DC }, /* SC_X1_DEL. */ + { CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN, + CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN }, /* SC_X1_DOWN. */ + { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT, + CONS_KEY_RIGHT, CONS_KEY_RIGHT, CONS_KEY_RIGHT }, /* SC_X1_RIGHT. */ + { CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP, + CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP }, /* SC_X1_UP. */ + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE, + CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE }, /* SC_X1_PGDN. */ + { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, + { CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE, + CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE } /* SC_X1_PGUP. */ + }; + +char *sc_x2_to_kc[][7] = + { + /* We don't add all those zero entries here. It's just one key, + so it's special cased. */ + { "\e[P", "\e[P", "\e[P", "\e[P", "\e[P", "\e[P","\e[P" }, /* SC_X1_BREAK. */ + }; + + +/* GNU Mach v1 compatibility code. */ + +/* This is a conversion table from i8042 scancode set 1 to set 2. */ +enum scancode sc_set1_to_set2[] = + { + 0x00, + SC_ESC, + SC_1, + SC_2, + SC_3, + SC_4, + SC_5, + SC_6, + SC_7, + SC_8, + SC_9, + SC_0, + SC_MINUS, + SC_EQUAL, + SC_BACKSPACE, + SC_TAB, + SC_Q, + SC_W, + SC_E, + SC_R, + SC_T, + SC_Y, + SC_U, + SC_I, + SC_O, + SC_P, + SC_LEFT_BRACKET, + SC_RIGHT_BRACKET, + SC_ENTER, + SC_LEFT_CTRL, + SC_A, + SC_S, + SC_D, + SC_F, + SC_G, + SC_H, + SC_J, + SC_K, + SC_L, + SC_SEMICOLON, + SC_APOSTROPHE, + SC_BACKQUOTE, + SC_LEFT_SHIFT, + SC_BACKSLASH, + SC_Z, + SC_X, + SC_C, + SC_V, + SC_B, + SC_N, + SC_M, + SC_COMMA, + SC_PERIOD, + SC_SLASH, + SC_RIGHT_SHIFT, + SC_PAD_ASTERISK, + SC_LEFT_ALT, + SC_SPACE, + SC_CAPSLOCK, + SC_F1, + SC_F2, + SC_F3, + SC_F4, + SC_F5, + SC_F6, + SC_F7, + SC_F8, + SC_F9, + SC_F10, + SC_NUMLOCK, + SC_SCROLLLOCK, + SC_PAD_7, + SC_PAD_8, + SC_PAD_9, + SC_PAD_MINUS, + SC_PAD_4, + SC_PAD_5, + SC_PAD_6, + SC_PAD_PLUS, + SC_PAD_1, + SC_PAD_2, + SC_PAD_3, + SC_PAD_0, + SC_PAD_DECIMAL, + 0x00, /* XXX SYSREQ */ + 0x00, + 0x00, + SC_F11, + SC_F12, + }; + +/* Conversion table for codes which can follow SC_EXTENDED1. */ +enum scancode sc_set1_to_set2_x1[] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + SC_X1_PAD_ENTER, + SC_X1_RIGHT_CTRL, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + SC_X1_PAD_SLASH, + 0x00, + SC_X1_PRTSC, + SC_X1_RIGHT_ALT, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, /* XXX SC_X1_BREAK */ + SC_X1_HOME, + SC_X1_UP, + SC_X1_PGUP, + 0x00, + SC_X1_LEFT, + 0x00, + SC_X1_RIGHT, + 0x00, + SC_X1_END, + SC_X1_DOWN, + SC_X1_PGDN, + SC_X1_INS, + SC_X1_DEL + }; + +static enum scancode +gnumach_v1_input_next () +{ + kd_event data_buf; + int up; + enum scancode sc; + + do + { + /* io_buf_ptr_t is (char *), not (void *). So I have a few + casts to quiet warnings. */ + mach_msg_type_number_t data_cnt = sizeof (data_buf); + error_t err = device_read_inband (kbd_dev, 0, -1, sizeof (kd_event), + (void *) &data_buf, &data_cnt); + + /* XXX The error occurred likely because KBD_DEV was closed, so + terminate. */ + if (err) + return 0; + + if (kbd_repeater_opened && data_buf.type == KEYBD_EVENT) + { + kbd_repeat_key (&data_buf); + data_buf.type = 0; + continue; + } + } + while (data_buf.type != KEYBD_EVENT); + + /* Some fixed codes which are the same in set 1 and set 2, and have + the UP flag set. */ + if (data_buf.value.sc == SC_EXTENDED1 + || data_buf.value.sc == SC_EXTENDED2 + || data_buf.value.sc == SC_ERROR) + return data_buf.value.sc; + +#define SC_SET1_FLAG_UP 0x80 + up = data_buf.value.sc & SC_SET1_FLAG_UP; + sc = sc_set1_to_set2[data_buf.value.sc &~ SC_SET1_FLAG_UP]; + + return sc | (up ? SC_FLAG_UP : 0); +} + + +static void +update_leds (void) +{ + error_t err; + + if (gnumach_v1_compat) + { + int led = (led_state.scroll_lock ? 1 : 0) + | (led_state.num_lock ? 2 : 0) + | (led_state.caps_lock ? 4 : 0); + + err = device_set_status (kbd_dev, KDSETLEDS, &led, 1); + /* Just ignore the error, GNUMach 1.3 and older cannot set the + keyboard LEDs. */ + } + else + { + char leds[2]; + mach_msg_type_number_t data_cnt = 2; + + leds[0] = '\xed'; + leds[1] = (led_state.scroll_lock ? 1 : 0) + | (led_state.num_lock ? 2 : 0) + | (led_state.caps_lock ? 4 : 0); + + err = device_write_inband (kbd_dev, 0, -1, (void *) leds, 2, &data_cnt); + if (!err && data_cnt == 1) + err = device_write_inband (kbd_dev, 0, -1, (void *) &leds[1], 1, + &data_cnt); + } +} + +static enum scancode +input_next () +{ + enum scancode sc = 0; + unsigned char next; + + /* GNU Mach v1 does provide keyboard input in a different format. */ + if (gnumach_v1_compat) + return gnumach_v1_input_next (); + + /* XXX This should read several characters at once. */ + do + { + mach_msg_type_number_t data_cnt = 1; + error_t err = device_read_inband (kbd_dev, 0, -1, 1, + (void *) &next, &data_cnt); + + /* XXX The error occurred likely because KBD_DEV was closed, so + terminate. */ + if (err) + return 0; + + if (next == 0xF0) + /* XXX Magic constant. */ + sc |= SC_FLAG_UP; + } + while (next == 0xF0); /* XXX Magic constant. */ + + sc |= next; + return sc; +} + + +/* The input loop. */ +static any_t +input_loop (any_t unused) +{ + while (1) + { + enum scancode fsc = input_next (); + enum scancode sc = fsc & ~SC_FLAG_UP; + int down = !(fsc & SC_FLAG_UP); + char buf[100]; + size_t size = 0; + int modifier = -1; + + static struct { + wchar_t direct; + unsigned int extended : 2; + unsigned int left_shift : 1; + unsigned int right_shift : 1; + unsigned int caps_lock : 1; + unsigned int caps_lock_pressed : 1; + unsigned int left_ctrl : 1; + unsigned int right_ctrl : 1; + unsigned int left_alt : 1; + unsigned int right_alt : 1; + unsigned int num_lock : 1; + unsigned int num_lock_pressed : 1; + } state; + + if (!state.left_alt && !state.right_alt) + { + if (state.left_ctrl || state.right_ctrl) + modifier = 2; + else if (state.left_shift || state.right_shift) + modifier = 1; + else + modifier = 0; + } + else if (state.left_alt) + { + if (state.left_ctrl || state.right_ctrl) + modifier = 5; + if (state.left_shift || state.right_shift) + modifier = 4; + else + modifier = 3; + } + else if (state.right_alt) + { + if (!state.left_ctrl && !state.right_ctrl + && !state.left_shift && !state.right_shift) + modifier = 6; + } + + if (!state.extended) + { + if (fsc == SC_EXTENDED1) + state.extended = 1; + else if (fsc == SC_EXTENDED2) + state.extended = 2; + else if (sc == SC_LEFT_SHIFT) + state.left_shift = down; + else if (sc == SC_RIGHT_SHIFT) + state.right_shift = down; + else if (sc == SC_CAPSLOCK) + { + if (down && !state.caps_lock_pressed) + { + state.caps_lock = !state.caps_lock; + state.caps_lock_pressed = 1; + led_state.caps_lock = state.caps_lock; + update_leds (); + } + else if (!down) + state.caps_lock_pressed = 0; + } + else if (sc == SC_LEFT_CTRL) + state.left_ctrl = down; + else if (sc == SC_LEFT_ALT) + state.left_alt = down; + else if (state.left_alt && down && IS_FUNC_KEY (sc)) + { + /* The virtual console to switch to. */ + int vc = 0; + + /* Check if a function key was pressed. + Choose the virtual console corresponding to that key. */ + switch (sc) + { + case SC_F1: + vc = 1; + break; + case SC_F2: + vc = 2; + break; + case SC_F3: + vc = 3; + break; + case SC_F4: + vc = 4; + break; + case SC_F5: + vc = 5; + break; + case SC_F6: + vc = 6; + break; + case SC_F7: + vc = 7; + break; + case SC_F8: + vc = 8; + break; + case SC_F9: + vc = 9; + break; + case SC_F10: + vc = 10; + break; + case SC_F11: + vc = 11; + break; + case SC_F12: + vc = 12; + break; + /* No function key was pressed, don't + switch to another vc. */ + default: + vc = 0; + } + + if (vc) + console_switch (vc, 0); + } + else if (state.left_alt && state.left_ctrl && down && sc == SC_BACKSPACE) + console_exit (); + else if (state.right_alt && down && sc == SC_PAD_0) /* XXX */ + state.direct = (state.direct << 4) | 0x0; + else if (state.right_alt && down && sc == SC_PAD_1) /* XXX */ + state.direct = (state.direct << 4) | 0x1; + else if (state.right_alt && down && sc == SC_PAD_2) /* XXX */ + state.direct = (state.direct << 4) | 0x2; + else if (state.right_alt && down && sc == SC_PAD_3) /* XXX */ + state.direct = (state.direct << 4) | 0x3; + else if (state.right_alt && down && sc == SC_PAD_4) /* XXX */ + state.direct = (state.direct << 4) | 0x4; + else if (state.right_alt && down && sc == SC_PAD_5) /* XXX */ + state.direct = (state.direct << 4) | 0x5; + else if (state.right_alt && down && sc == SC_PAD_6) /* XXX */ + state.direct = (state.direct << 4) | 0x6; + else if (state.right_alt && down && sc == SC_PAD_7) /* XXX */ + state.direct = (state.direct << 4) | 0x7; + else if (state.right_alt && down && sc == SC_PAD_8) /* XXX */ + state.direct = (state.direct << 4) | 0x8; + else if (state.right_alt && down && sc == SC_PAD_9) /* XXX */ + state.direct = (state.direct << 4) | 0x9; + else if (state.right_alt && down && sc == SC_NUMLOCK) /* XXX */ + state.direct = (state.direct << 4) | 0xa; + else if (state.right_alt && down && sc == SC_PAD_ASTERISK) /* XXX */ + state.direct = (state.direct << 4) | 0xc; + else if (state.right_alt && down && sc == SC_PAD_MINUS) /* XXX */ + state.direct = (state.direct << 4) | 0xd; + else if (state.right_alt && down && sc == SC_PAD_PLUS) /* XXX */ + state.direct = (state.direct << 4) | 0xe; + else if (sc == SC_NUMLOCK) + { + if (down && !state.num_lock_pressed) + { + state.num_lock = !state.num_lock; + state.num_lock_pressed = 1; + led_state.num_lock = state.num_lock; + update_leds (); + } + else if (!down) + state.num_lock_pressed = 0; + } + else if (down && sc < sizeof (sc_to_kc)/sizeof (sc_to_kc[0])) + { +#if QUAERENDO_INVENIETIS + if (state.left_alt && state.right_alt + && sc_to_kc[sc][0][0] >= '0' && sc_to_kc[sc][0][0] <= '9' + && sc_to_kc[sc][0][1] == '\0') + console_deprecated (sc_to_kc[sc][0][0] - '0'); + else +#endif + { + /* Special rule for caps lock. */ + if (modifier == 0 && state.caps_lock + && sc_to_kc[sc][modifier] + && sc_to_kc[sc][modifier][0] >= 'a' + && sc_to_kc[sc][modifier][0] <= 'z' + && sc_to_kc[sc][modifier][1] == '\0') + modifier = 1; + else if (state.num_lock && sc == SC_PAD_0) + { + modifier = 0; + sc = SC_0; + } + else if (state.num_lock && sc == SC_PAD_1) + { + modifier = 0; + sc = SC_1; + } + else if (state.num_lock && sc == SC_PAD_2) + { + modifier = 0; + sc = SC_2; + } + else if (state.num_lock && sc == SC_PAD_3) + { + modifier = 0; + sc = SC_3; + } + else if (state.num_lock && sc == SC_PAD_4) + { + modifier = 0; + sc = SC_4; + } + else if (state.num_lock && sc == SC_PAD_5) + { + modifier = 0; + sc = SC_5; + } + else if (state.num_lock && sc == SC_PAD_6) + { + modifier = 0; + sc = SC_6; + } + else if (state.num_lock && sc == SC_PAD_7) + { + modifier = 0; + sc = SC_7; + } + else if (state.num_lock && sc == SC_PAD_8) + { + modifier = 0; + sc = SC_8; + } + else if (state.num_lock && sc == SC_PAD_9) + { + modifier = 0; + sc = SC_9; + } + else if (state.num_lock && sc == SC_PAD_DECIMAL) + { + modifier = 0; + sc = SC_PERIOD; + } + + if (modifier >= 0 && sc_to_kc[sc][modifier]) + { + if (!sc_to_kc[sc][modifier][0]) + { + /* Special meaning, emit NUL. */ + assert (size < 100); + buf[size++] = '\0'; + } + else + { + assert (size + < 101 - strlen(sc_to_kc[sc][modifier])); + strcpy (&buf[size], sc_to_kc[sc][modifier]); + size += strlen (sc_to_kc[sc][modifier]); + } + } + } + } + } + else if (state.extended == 1) + { + state.extended = 0; + if (sc == SC_X1_RIGHT_CTRL) + state.right_ctrl = down; + else if (sc == SC_X1_RIGHT_ALT) + { + state.right_alt = down; + + /* Handle the AltGR+Keypad direct input. */ + if (down) + state.direct = (wchar_t) 0; + else + { + if (state.direct != (wchar_t) 0) + { + char *buffer = &buf[size]; + size_t left = sizeof (buf) - size; + char *inbuf = (char *) &state.direct; + size_t inbufsize = sizeof (wchar_t); + size_t nr; + + nr = iconv (cd, &inbuf, &inbufsize, &buffer, &left); + if (nr == (size_t) -1) + { + if (errno == E2BIG) + console_error (L"Input buffer overflow"); + else if (errno == EILSEQ) + console_error + (L"Input contained invalid byte sequence"); + else if (errno == EINVAL) + console_error + (L"Input contained incomplete byte sequence"); + else + console_error + (L"Input caused unexpected error"); + } + size = sizeof (buf) - left; + } + } + } + else if (state.right_alt && down && sc == SC_X1_PAD_SLASH) /* XXX */ + state.direct = (state.direct << 4) | 0xb; + else if (state.right_alt && down && sc == SC_X1_PAD_ENTER) /* XXX */ + state.direct = (state.direct << 4) | 0xf; + else if (state.left_alt && down && sc == SC_X1_RIGHT) /* XXX */ + console_switch (0, 1); + else if (state.left_alt && down && sc == SC_X1_LEFT) /* XXX */ + console_switch (0, -1); + else if (state.left_alt && down && sc == SC_X1_UP) /* XXX */ + console_scrollback (CONS_SCROLL_DELTA_LINES, 1); + else if (state.left_alt && down && sc == SC_X1_DOWN) /* XXX */ + console_scrollback (CONS_SCROLL_DELTA_LINES, -1); + else if ((state.right_shift || state.left_shift) + && down && sc == SC_X1_PGUP) /* XXX */ + console_scrollback (CONS_SCROLL_DELTA_SCREENS, 0.5); + else if ((state.right_shift || state.left_shift) + && down && sc == SC_X1_PGDN) /* XXX */ + console_scrollback (CONS_SCROLL_DELTA_SCREENS, -0.5); + else if (down && sc < sizeof (sc_x1_to_kc)/sizeof (sc_x1_to_kc[0])) + { + if (modifier >= 0 && sc_x1_to_kc[sc][modifier]) + { + assert (size < 101 - strlen(sc_x1_to_kc[sc][modifier])); + strcpy (&buf[size], sc_x1_to_kc[sc][modifier]); + size += strlen (sc_x1_to_kc[sc][modifier]); + } + } + } + else if (state.extended == 2) + state.extended = 3; + else if (state.extended == 3) + state.extended = 0; + + if (size) + console_input (buf, size); + } + return 0; +} + + + + +static const char doc[] = "PC Keyboard Driver"; + +static const struct argp_option options[] = + { + {"repeat", 'r', "NODE", OPTION_ARG_OPTIONAL, + "Set a repeater translator on NODE (default: " DEFAULT_REPEATER_NODE ")"}, + { 0 } + }; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + int *pos = (int *) state->input; + + switch (key) + { + case 'r': + repeater_node = arg ? arg: DEFAULT_REPEATER_NODE; + break; + + case ARGP_KEY_END: + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + *pos = state->next; + return 0; +} + +static struct argp argp = {options, parse_opt, 0, doc}; + +/* Initialize the PC keyboard driver. */ +static error_t +pc_kbd_init (void **handle, int no_exit, int argc, char *argv[], int *next) +{ + error_t err; + int pos = 1; + + /* Parse the arguments. */ + err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER | ARGP_NO_EXIT + | ARGP_SILENT, 0 , &pos); + *next += pos - 1; + + if (err && err != EINVAL) + return err; + + + return 0; +} + + +/* Start the PC keyboard driver. */ +static error_t +pc_kbd_start (void *handle) +{ + error_t err; + device_t device_master; + + cd = iconv_open ("UTF-8", "WCHAR_T"); + if (cd == (iconv_t) -1) + return errno; + + err = get_privileged_ports (0, &device_master); + if (err) + { + iconv_close (cd); + return err; + } + + err = device_open (device_master, D_READ | D_WRITE, "@>=kbd", &kbd_dev); + if (err == D_NO_SUCH_DEVICE) + { + /* GNU Mach v1 has a different device. */ + gnumach_v1_compat = 1; + err = device_open (device_master, D_READ, "kbd", &kbd_dev); + } + + mach_port_deallocate (mach_task_self (), device_master); + if (err) + { + iconv_close (cd); + return err; + } + + if (gnumach_v1_compat) + { + int data = KB_EVENT; + err = device_set_status (kbd_dev, KDSKBDMODE, &data, 1); + if (err) + { + device_close (kbd_dev); + mach_port_deallocate (mach_task_self (), kbd_dev); + iconv_close (cd); + return err; + } + } + update_leds (); + + err = driver_add_input (&pc_kbd_ops, NULL); + if (err) + { + if (gnumach_v1_compat) + { + int data = KB_ASCII; + device_set_status (kbd_dev, KDSKBDMODE, &data, 1); + } + device_close (kbd_dev); + mach_port_deallocate (mach_task_self (), kbd_dev); + iconv_close (cd); + return err; + } + + if (repeater_node) + kbd_setrepeater (repeater_node, &cnode); + + cthread_detach (cthread_fork (input_loop, NULL)); + + return 0; +} + +/* Deinitialize the PC keyboard driver. */ +static error_t +pc_kbd_fini (void *handle, int force) +{ + driver_remove_input (&pc_kbd_ops, NULL); + if (gnumach_v1_compat) + { + int data = KB_ASCII; + device_set_status (kbd_dev, KDSKBDMODE, &data, 1); + } + device_close (kbd_dev); + mach_port_deallocate (mach_task_self (), kbd_dev); + iconv_close (cd); + + console_unregister_consnode (cnode); + console_destroy_consnode (cnode); + + return 0; +} + + +/* Set the scroll lock status indication (Scroll LED) to ONOFF. */ +static error_t +pc_kbd_set_scroll_lock_status (void *handle, int onoff) +{ + led_state.scroll_lock = onoff; + update_leds (); + return 0; +} + + +struct driver_ops driver_pc_kbd_ops = + { + pc_kbd_init, + pc_kbd_start, + pc_kbd_fini + }; + +static struct input_ops pc_kbd_ops = + { + pc_kbd_set_scroll_lock_status, + NULL + }; |