/* pc-kbd.c - The PC Keyboard input driver. Copyright (C) 2002 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 #include #include #include #include #include #include #include #include #include "driver.h" /* The keyboard device in the kernel. */ static device_t kbd_dev; /* The converter. */ static iconv_t cd; /* Forward declaration. */ static struct input_ops pc_kbd_ops; /* A list of scan codes generated by the keyboard. */ enum scancode { SC_ESC = 0x01, SC_1 = 0x02, SC_2 = 0x03, SC_3 = 0x04, SC_4 = 0x05, SC_5 = 0x06, SC_6 = 0x07, SC_7 = 0x08, SC_8 = 0x09, SC_9 = 0x0A, SC_0 = 0x0B, SC_MINUS = 0x0C, /* - */ SC_EQUAL = 0x0D, /* = */ SC_BACKSPACE = 0x0E, SC_TAB = 0x0F, SC_Q = 0x10, SC_W = 0x11, SC_E = 0x12, SC_R = 0x13, SC_T = 0x14, SC_Y = 0x15, SC_U = 0x16, SC_I = 0x17, SC_O = 0x18, SC_P = 0x19, SC_LEFT_BRACKET = 0x1A, /* [ */ SC_RIGHT_BRACKET = 0x1B, /* ] */ SC_ENTER = 0x1C, SC_LEFT_CTRL = 0x1D, SC_A = 0x1E, SC_S = 0x1F, SC_D = 0x20, SC_F = 0x21, SC_G = 0x22, SC_H = 0x23, SC_J = 0x24, SC_K = 0x25, SC_L = 0x26, SC_SEMICOLON = 0x27, /* ; */ SC_APOSTROPHE = 0x28, /* ' */ SC_BACKQUOTE = 0x29, /* ` */ SC_LEFT_SHIFT = 0x2A, SC_BACKSLASH = 0x2B, /* \ */ SC_Z = 0x2C, SC_X = 0x2D, SC_C = 0x2E, SC_V = 0x2F, SC_B = 0x30, SC_N = 0x31, SC_M = 0x32, SC_COMMA = 0x33, /* , */ SC_PERIOD = 0x34, /* . */ SC_SLASH = 0x35, /* / */ SC_RIGHT_SHIFT = 0x36, SC_PAD_ASTERISK = 0x37, SC_LEFT_ALT = 0x38, SC_SPACE = 0x39, SC_CAPSLOCK = 0x3A, SC_F1 = 0x3B, SC_F2 = 0x3C, SC_F3 = 0x3D, SC_F4 = 0x3E, SC_F5 = 0x3F, SC_F6 = 0x40, SC_F7 = 0x41, SC_F8 = 0x42, SC_F9 = 0x43, SC_F10 = 0x44, SC_NUMLOCK = 0x45, SC_SCROLLLOCK = 0x46, SC_PAD_7 = 0x47, SC_PAD_8 = 0x48, SC_PAD_9 = 0x49, SC_PAD_MINUS = 0x4A, SC_PAD_4 = 0x4B, SC_PAD_5 = 0x4C, SC_PAD_6 = 0x4D, SC_PAD_PLUS = 0x4E, SC_PAD_1 = 0x4F, SC_PAD_2 = 0x50, SC_PAD_3 = 0x51, SC_PAD_0 = 0x52, SC_PAD_DECIMAL = 0x53, SC_SYSREQ = 0x54, SC_F11 = 0x57, SC_F12 = 0x58, SC_FLAG_UP = 0x80, /* ORed to basic scancode. */ SC_EXTENDED1 = 0xE0, /* One code follows. */ SC_EXTENDED2 = 0xE1, /* Two codes follow (only used for Pause). */ SC_ERROR = 0xFF /* Too many keys held down. */ }; /* Codes which can follow SC_EXTENDED1. */ enum scancode_x1 { SC_X1_PAD_ENTER = 0x1C, SC_X1_RIGHT_CTRL = 0x1D, SC_X1_PAD_SLASH = 0x35, SC_X1_PRTSC = 0x37, SC_X1_RIGHT_ALT = 0x38, SC_X1_BREAK = 0x46, SC_X1_HOME = 0x47, SC_X1_UP = 0x48, SC_X1_PGUP = 0x49, SC_X1_LEFT = 0x4B, SC_X1_RIGHT = 0x4D, SC_X1_END = 0x4F, SC_X1_DOWN = 0x50, SC_X1_PGDN = 0x51, SC_X1_INS = 0x52, SC_X1_DEL = 0x53 }; /* 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 }, { "\e", "\e", "\e", "\e\e", "\e\e", "\e\e", "\e" }, /* SC_ESC. */ { "1", "!", 0, "\e1", "\e!", 0, "1" }, /* SC_1. */ { "2", "@", "", "\e2", "\e@", 0, "2" }, /* SC_2. */ { "3", "#", "\e", "\e3", "\e#", 0, "3" }, /* SC_3. */ { "4", "$", "\x1c", "\e4", "\e$", "\e\x1c", "4" }, /* SC_4. */ { "5", "%", "\x1d", "\e5", "\e%", 0, "5" }, /* SC_5. */ { "6", "^", "\x1e", "\e6", "\e^", 0, "6" }, /* SC_6. */ { "7", "&", "\x1f", "\e7", "\e&", "\e\x1f", "7" }, /* SC_7. */ { "8", "*", "\x7f", "\e8", "\e*", 0, "8" }, /* SC_8. */ { "9", "(", 0, "\e9", "\e(", 0, "9" }, /* SC_9. */ { "0", ")", 0, "\e0", "\e)", 0, "0" }, /* SC_0. */ { "-", "_", "\x1f", "\e-", "\e_", "\e\x1f", "-" }, /* SC_MINUS. */ { "=", "+", 0, "\e=", "\e+", 0, "=" }, /* SC_EQUAL. */ { CONS_KEY_BACKSPACE, CONS_KEY_BACKSPACE, CONS_KEY_BACKSPACE, /* XXX */ 0, CONS_KEY_BACKSPACE, /*XXX*/ 0, CONS_KEY_BACKSPACE }, /* SC_BACKSPACE. */ /* XXX back tab? */ { "\t", "\t", "\t", "\e\t", "\e\t", "\e\t", "\t" }, /* SC_TAB. */ { "q", "Q", "\x11", "\eq", "\eQ", "\e\x11", "q" }, /* SC_Q. */ { "w", "W", "\x17", "\ew", "\eW", "\e\x17", "w" }, /* SC_W. */ { "e", "E", "\x05", "\ee", "\eE", "\e\x05","\xe2\x82\xac" }, /* SC_E. */ { "r", "R", "\x12", "\er", "\eR", "\e\x12", "r" }, /* SC_R. */ { "t", "T", "\x14", "\et", "\eT", "\e\x14", "t" }, /* SC_T. */ { "y", "Y", "\x19", "\ey", "\eY", "\e\x19", "y" }, /* SC_Y. */ { "u", "U", "\x15", "\eu", "\eU", "\e\x15", "u" }, /* SC_U. */ { "i", "I", "\x09", "\ei", "\eI", "\e\x09", "i" }, /* SC_I. */ { "o", "O", "\x0f", "\eo", "\eO", "\e\x0f", "o" }, /* SC_O. */ { "p", "P", "\x10", "\ep", "\eP", "\e\x10", "p" }, /* SC_P. */ { "[", "{", "\e", "\e[", "\e{", 0, 0 }, /* SC_LEFT_BRACKET. */ { "]", "}", "\x1d", "\e]", "\e}", "\e\x1d", "~" }, /* SC_RIGHT_BRACKET. */ {"\x0d","\x0d", "\x0d","\e\x0d","\e\x0d","\e\x0d","\x0d" }, /* SC_ENTER. */ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_LEFT_CTRL. XXX */ { "a", "A", "\x01", "\ea", "\eA", "\e\x01", "a" }, /* SC_A. */ { "s", "S", "\x13", "\es", "\eS", "\e\x13", "s" }, /* SC_S. */ { "d", "D", "\x04", "\ed", "\eD", "\e\x04", "d" }, /* SC_D. */ { "f", "F", "\x06", "\ef", "\eF", "\e\x06", "f" }, /* SC_F. */ { "g", "G", "\x07", "\eg", "\eG", "\e\x07", "g" }, /* SC_G. */ { "h", "H", "\x08", "\eh", "\eH", "\e\x08", "h" }, /* SC_H. */ { "j", "J", "\x0a", "\ej", "\eJ", "\e\x0a", "j" }, /* SC_J. */ { "k", "K", "\x0b", "\ek", "\eK", "\e\x0b", "k" }, /* SC_K. */ { "l", "L", "\x0c", "\el", "\eL", "\e\x0c", "l" }, /* SC_L. */ { ";", ":", 0, "\e;", "\e:", 0, 0 }, /* SC_SEMICOLON. */ { "'", "\"", "\x07", "\e'", "\e\"", 0, 0 }, /* SC_APOSTROPHE. */ { "`", "~", 0, "\e`", "\e~", 0, 0 }, /* SC_BACKQUOTE. */ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_LEFT_SHIFT. */ { "\\", "|", "\x1c", "\e\\", "\e|", 0, 0 }, /* SC_BACKSLASH. */ { "z", "Z", "\x1a", "\ez", "\eZ", "\e\x1a", "z" }, /* SC_Z. */ { "x", "X", "\x18", "\ex", "\eX", "\e\x18", "x" }, /* SC_X. */ { "c", "C", "\x03", "\ec", "\eC", "\e\x03", "\xc2\xa2" }, /* SC_C. */ { "v", "V", "\x16", "\ev", "\eV", "\e\x16", "v" }, /* SC_V. */ { "b", "B", "\x02", "\eb", "\eB", "\e\x02", "b" }, /* SC_B. */ { "n", "N", "\x0e", "\en", "\eN", "\e\x0e", "n" }, /* SC_N. */ { "m", "M", "\x0d", "\em", "\eM", "\e\x0d", "m" }, /* SC_M. */ { ",", "<", 0, "\e,", "\e<", 0, 0 }, /* SC_COMMA. */ { ".", ">", 0, "\e.", "\e>", 0, 0 }, /* SC_PERIOD. */ { "/", "?", "\x7f", "\e/", "\e?", 0, 0 }, /* SC_SLASH. */ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_RIGHT_SHIFT. */ { "*", "*", "*", "*", "*", "*", "*" }, /* SC_PAD_ASTERISK. XXX */ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_LEFT_ALT. XXX */ { " ", " ", "", "\e ", "\e ", /*XXX*/0, " " }, /* SC_SPACE. */ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_CAPSLOCK. */ { 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_F3, CONS_KEY_F15, 0, 0, 0, 0, 0 }, /* SC_F3. */ { CONS_KEY_F4, CONS_KEY_F16, 0, 0, 0, 0, 0 }, /* SC_F4. */ { CONS_KEY_F5, CONS_KEY_F17, 0, 0, 0, 0, 0 }, /* SC_F5. */ { CONS_KEY_F6, CONS_KEY_F18, 0, 0, 0, 0, 0 }, /* SC_F6. */ { CONS_KEY_F7, CONS_KEY_F19, 0, 0, 0, 0, 0 }, /* SC_F7. */ { CONS_KEY_F8, CONS_KEY_F20, 0, 0, 0, 0, 0 }, /* SC_F8. */ { CONS_KEY_F9, 0, 0, 0, 0, 0, 0 }, /* SC_F9. */ { CONS_KEY_F10, 0, 0, 0, 0, 0, 0 }, /* SC_F10. */ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_NUMLOCK. */ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_SCROLLLOCK. */ { CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME, 0, 0, 0, 0 }, /* SC_PAD_7. */ { CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP, 0, 0, 0, 0 }, /* SC_PAD_8. */ { CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE,0, 0, 0, 0 }, /* SC_PAD_9. */ { "-", "-", "-", "-", "-", "-", "-" }, /* SC_PAD_MINUS. */ { CONS_KEY_LEFT, CONS_KEY_LEFT, CONS_KEY_LEFT, 0, 0, 0, 0 }, /* SC_PAD_4. */ {/* 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. */ { "+", "+", "+", "+", "+", "+", "+" }, /* SC_PAD_MINUS. */ { CONS_KEY_END, CONS_KEY_END, CONS_KEY_END, 0, 0, 0, 0 }, /* SC_PAD_1. */ { CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN, 0, 0, 0, 0 }, /* SC_PAD_2. */ { CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE,0, 0, 0, 0 }, /* SC_PAD_3. */ { 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. */ { 0, 0, /*XX*/0, 0, 0, 0, 0 }, /* SC_SYSREQ. */ { 0, 0, 0, 0, 0, 0, 0 }, /* Unused. */ { 0, 0, 0, 0, 0, 0, 0 }, /* Unused. */ { CONS_KEY_F11, 0, 0, 0, 0, 0, 0 }, /* SC_F11. */ { CONS_KEY_F12, 0, 0, 0, 0, 0, 0 } /* SC_F12. */ }; 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 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 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 }, /* 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 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 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 }, /* SC_X1_PRTSC. */ { 0, 0, 0, 0, 0, 0, 0 }, /* SC_X1_RIGHT_ALT. */ { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0 }, { "\e[P", "\e[P", "\e[P", "\e[P", "\e[P", "\e[P","\e[P" }, /* SC_X1_BREAK. */ { CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME, CONS_KEY_HOME }, /* SC_X1_HOME. */ { CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP, CONS_KEY_UP }, /* SC_X1_UP. */ { CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE, CONS_KEY_PPAGE }, /* SC_X1_PGUP. */ { 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. */ { 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. */ { 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. */ { CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN, CONS_KEY_DOWN }, /* SC_X1_DOWN. */ { CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE, CONS_KEY_NPAGE }, /* SC_X1_PGDN. */ { 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. */ }; /* This gross stuff is cut & pasted from Mach sources, as Mach doesn't export the interface we are using here. */ /* * Mach Operating System * Copyright (c) 1991,1990,1989 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ typedef u_short kev_type; /* kd event type */ /* (used for event records) */ struct mouse_motion { short mm_deltaX; /* units? */ short mm_deltaY; }; typedef u_char Scancode; typedef struct { kev_type type; /* see below */ struct timeval time; /* timestamp */ union { /* value associated with event */ boolean_t up; /* MOUSE_LEFT .. MOUSE_RIGHT */ Scancode sc; /* KEYBD_EVENT */ struct mouse_motion mmotion; /* MOUSE_MOTION */ } value; } kd_event; #define m_deltaX mmotion.mm_deltaX #define m_deltaY mmotion.mm_deltaY /* * kd_event ID's. */ #define MOUSE_LEFT 1 /* mouse left button up/down */ #define MOUSE_MIDDLE 2 #define MOUSE_RIGHT 3 #define MOUSE_MOTION 4 /* mouse motion */ #define KEYBD_EVENT 5 /* key up/down */ #define IOCPARM_MASK 0x1fff /* parameter length, at most 13 bits */ #define IOC_OUT 0x40000000 /* copy out parameters */ #define IOC_IN 0x80000000U /* copy in parameters */ #define _IOC(inout,group,num,len) \ (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num)) #define _IOR(g,n,t) _IOC(IOC_OUT, (g), (n), sizeof(t)) #define _IOW(g,n,t) _IOC(IOC_IN, (g), (n), sizeof(t)) #define KDSKBDMODE _IOW('K', 1, int) /* set keyboard mode */ #define KB_EVENT 1 #define KB_ASCII 2 #define KDGKBDTYPE _IOR('K', 2, int) /* get keyboard type */ #define KB_VANILLAKB 0 /* End of Mach code. */ /* The input loop. */ static any_t input_loop (any_t unused) { while (1) { kd_event data_buf; /* io_buf_ptr_t is (char *), not (void *). So I have a few casts to quiet warnings. */ io_buf_ptr_t data_ptr = (void *) &data_buf; mach_msg_type_number_t data_cnt = sizeof data_buf; /* I suppose this could be sped up by reading multiple events per call. But that isn't important since this is called just a couple of times per keystroke. Things might be different if we supported a mouse. */ error_t err = device_read (kbd_dev, /* device */ 0, /* mode: could be D_NOWAIT */ -1, /* recnum: not used by kbdread */ sizeof (kd_event), /* bytes_wanted */ &data_ptr, &data_cnt); if (err) /* XXX The error occured likely because KBD_DEV was closed, so terminate. */ return 0; if ((void *) data_ptr != &data_buf) { /* Copy the structure to the expected place. */ data_buf = *(kd_event *) data_ptr; munmap (data_ptr, data_cnt); } if (data_buf.type == KEYBD_EVENT) { enum scancode fsc = data_buf.value.sc; enum scancode sc = fsc & 0x7f; 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; } 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 && sc >= SC_F1 && sc <= SC_F10) /* XXX */ console_switch (1 + (sc - SC_F1), 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; } 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); int 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 && down && sc == SC_X1_PGUP) /* XXX */ console_scrollback (CONS_SCROLL_DELTA_SCREENS, 0.5); else if (state.right_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; } /* Initialize the PC keyboard driver. */ static error_t pc_kbd_init (void **handle, int no_exit, int argc, char *argv[], int *next) { return 0; } /* Start the PC keyboard driver. */ static error_t pc_kbd_start (void *handle) { error_t err; device_t device_master; int data[1]; 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, "kbd", &kbd_dev); mach_port_deallocate (mach_task_self (), device_master); if (err) { iconv_close (cd); return err; } data[0] = 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; } err = driver_add_input (&pc_kbd_ops, NULL); if (err) { 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; } cthread_detach (cthread_fork (input_loop, NULL)); return 0; } /* Deinitialize the PC keyboard driver. */ static error_t pc_kbd_fini (void *handle, int force) { int data[1]; driver_remove_input (&pc_kbd_ops, NULL); data[0] = 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 0; } /* Set the scroll lock status indication (Scroll LED) to ONOFF. */ static error_t pc_kbd_set_scroll_lock_status (void *handle, int onoff) { /* XXX */ 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 };