summaryrefslogtreecommitdiff
path: root/drivers/input/keyboard
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-17 02:20:36 +0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-17 02:20:36 +0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/input/keyboard
downloadlinux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/input/keyboard')
-rw-r--r--drivers/input/keyboard/Kconfig185
-rw-r--r--drivers/input/keyboard/Makefile19
-rw-r--r--drivers/input/keyboard/amikbd.c241
-rw-r--r--drivers/input/keyboard/atkbd.c1148
-rw-r--r--drivers/input/keyboard/corgikbd.c361
-rw-r--r--drivers/input/keyboard/hil_kbd.c375
-rw-r--r--drivers/input/keyboard/hilkbd.c343
-rw-r--r--drivers/input/keyboard/hpps2atkbd.h110
-rw-r--r--drivers/input/keyboard/lkkbd.c752
-rw-r--r--drivers/input/keyboard/locomokbd.c309
-rw-r--r--drivers/input/keyboard/maple_keyb.c190
-rw-r--r--drivers/input/keyboard/newtonkbd.c184
-rw-r--r--drivers/input/keyboard/sunkbd.c353
-rw-r--r--drivers/input/keyboard/xtkbd.c188
14 files changed, 4758 insertions, 0 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
new file mode 100644
index 000000000000..e55dee390775
--- /dev/null
+++ b/drivers/input/keyboard/Kconfig
@@ -0,0 +1,185 @@
+#
+# Input core configuration
+#
+menuconfig INPUT_KEYBOARD
+ bool "Keyboards" if EMBEDDED || !X86
+ default y
+ help
+ Say Y here, and a list of supported keyboards will be displayed.
+ This option doesn't affect the kernel.
+
+ If unsure, say Y.
+
+if INPUT_KEYBOARD
+
+config KEYBOARD_ATKBD
+ tristate "AT keyboard" if !PC
+ default y
+ select SERIO
+ select SERIO_LIBPS2
+ select SERIO_I8042 if PC
+ select SERIO_GSCPS2 if GSC
+ help
+ Say Y here if you want to use a standard AT or PS/2 keyboard. Usually
+ you'll need this, unless you have a different type keyboard (USB, ADB
+ or other). This also works for AT and PS/2 keyboards connected over a
+ PS/2 to serial converter.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atkbd.
+
+config KEYBOARD_ATKBD_HP_KEYCODES
+ bool "Use HP keyboard scancodes"
+ depends on PARISC && KEYBOARD_ATKBD
+ default y
+ help
+ Say Y here if you have a PA-RISC machine and want to use an AT or
+ PS/2 keyboard, and your keyboard uses keycodes that are specific to
+ PA-RISC keyboards.
+
+ Say N if you use a standard keyboard.
+
+config KEYBOARD_ATKBD_RDI_KEYCODES
+ bool "Use PrecisionBook keyboard scancodes"
+ depends on KEYBOARD_ATKBD_HP_KEYCODES
+ default n
+ help
+ If you have an RDI PrecisionBook, say Y here if you want to use its
+ built-in keyboard (as opposed to an external keyboard).
+
+ The PrecisionBook has five keys that conflict with those used by most
+ AT and PS/2 keyboards. These are as follows:
+
+ PrecisionBook Standard AT or PS/2
+
+ F1 F12
+ Left Ctrl Left Alt
+ Caps Lock Left Ctrl
+ Right Ctrl Caps Lock
+ Left 102nd key (the key to the right of Left Shift)
+
+ If you say N here, and use the PrecisionBook keyboard, then each key
+ in the left-hand column will be interpreted as the corresponding key
+ in the right-hand column.
+
+ If you say Y here, and use an external keyboard, then each key in the
+ right-hand column will be interpreted as the key shown in the
+ left-hand column.
+
+config KEYBOARD_SUNKBD
+ tristate "Sun Type 4 and Type 5 keyboard"
+ select SERIO
+ help
+ Say Y here if you want to use a Sun Type 4 or Type 5 keyboard,
+ connected either to the Sun keyboard connector or to an serial
+ (RS-232) port via a simple adapter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sunkbd.
+
+config KEYBOARD_LKKBD
+ tristate "DECstation/VAXstation LK201/LK401 keyboard"
+ select SERIO
+ help
+ Say Y here if you want to use a LK201 or LK401 style serial
+ keyboard. This keyboard is also useable on PCs if you attach
+ it with the inputattach program. The connector pinout is
+ described within lkkbd.c.
+
+ To compile this driver as a module, choose M here: the
+ module will be called lkkbd.
+
+config KEYBOARD_LOCOMO
+ tristate "LoCoMo Keyboard Support"
+ depends on SHARP_LOCOMO
+ help
+ Say Y here if you are running Linux on a Sharp Zaurus Collie or Poodle based PDA
+
+ To compile this driver as a module, choose M here: the
+ module will be called locomokbd.
+
+config KEYBOARD_XTKBD
+ tristate "XT keyboard"
+ select SERIO
+ help
+ Say Y here if you want to use the old IBM PC/XT keyboard (or
+ compatible) on your system. This is only possible with a
+ parallel port keyboard adapter, you cannot connect it to the
+ keyboard port on a PC that runs Linux.
+
+ To compile this driver as a module, choose M here: the
+ module will be called xtkbd.
+
+config KEYBOARD_NEWTON
+ tristate "Newton keyboard"
+ select SERIO
+ help
+ Say Y here if you have a Newton keyboard on a serial port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called newtonkbd.
+
+config KEYBOARD_CORGI
+ tristate "Corgi keyboard"
+ depends on PXA_SHARPSL
+ default y
+ help
+ Say Y here to enable the keyboard on the Sharp Zaurus SL-C7xx
+ series of PDAs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called corgikbd.
+
+config KEYBOARD_MAPLE
+ tristate "Maple bus keyboard"
+ depends on SH_DREAMCAST && MAPLE
+ help
+ Say Y here if you have a DreamCast console running Linux and have
+ a keyboard attached to its Maple bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called maple_keyb.
+
+config KEYBOARD_AMIGA
+ tristate "Amiga keyboard"
+ depends on AMIGA
+ help
+ Say Y here if you are running Linux on any AMIGA and have a keyboard
+ attached.
+
+ To compile this driver as a module, choose M here: the
+ module will be called amikbd.
+
+config KEYBOARD_HIL_OLD
+ tristate "HP HIL keyboard support (simple driver)"
+ depends on GSC
+ default y
+ help
+ The "Human Interface Loop" is a older, 8-channel USB-like
+ controller used in several Hewlett Packard models. This driver
+ was adapted from the one written for m68k/hp300, and implements
+ support for a keyboard attached to the HIL port, but not for
+ any other types of HIL input devices like mice or tablets.
+ However, it has been thoroughly tested and is stable.
+
+ If you want full HIL support including support for multiple
+ keyboards, mices and tablets, you have to enable the
+ "HP System Device Controller i8042 Support" in the input/serio
+ submenu.
+
+config KEYBOARD_HIL
+ tristate "HP HIL keyboard support"
+ depends on GSC
+ default y
+ select HP_SDC
+ select HIL_MLC
+ select SERIO
+ help
+ The "Human Interface Loop" is a older, 8-channel USB-like
+ controller used in several Hewlett Packard models.
+ This driver implements support for HIL-keyboards attached
+ to your machine, so normally you should say Y here.
+
+endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
new file mode 100644
index 000000000000..b02eeceea3c3
--- /dev/null
+++ b/drivers/input/keyboard/Makefile
@@ -0,0 +1,19 @@
+#
+# Makefile for the input core drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
+obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
+obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
+obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o
+obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
+obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
+obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o
+obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
+obj-$(CONFIG_KEYBOARD_98KBD) += 98kbd.o
+obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
+obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
+obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
+
diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c
new file mode 100644
index 000000000000..4e8e8ea214ab
--- /dev/null
+++ b/drivers/input/keyboard/amikbd.c
@@ -0,0 +1,241 @@
+/*
+ * $Id: amikbd.c,v 1.13 2002/02/01 16:02:24 vojtech Exp $
+ *
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Hamish Macdonald
+ */
+
+/*
+ * Amiga keyboard driver for Linux/m68k
+ */
+
+/*
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Amiga keyboard driver");
+MODULE_LICENSE("GPL");
+
+static unsigned char amikbd_keycode[0x78] = {
+ [0] = KEY_GRAVE,
+ [1] = KEY_1,
+ [2] = KEY_2,
+ [3] = KEY_3,
+ [4] = KEY_4,
+ [5] = KEY_5,
+ [6] = KEY_6,
+ [7] = KEY_7,
+ [8] = KEY_8,
+ [9] = KEY_9,
+ [10] = KEY_0,
+ [11] = KEY_MINUS,
+ [12] = KEY_EQUAL,
+ [13] = KEY_BACKSLASH,
+ [15] = KEY_KP0,
+ [16] = KEY_Q,
+ [17] = KEY_W,
+ [18] = KEY_E,
+ [19] = KEY_R,
+ [20] = KEY_T,
+ [21] = KEY_Y,
+ [22] = KEY_U,
+ [23] = KEY_I,
+ [24] = KEY_O,
+ [25] = KEY_P,
+ [26] = KEY_LEFTBRACE,
+ [27] = KEY_RIGHTBRACE,
+ [29] = KEY_KP1,
+ [30] = KEY_KP2,
+ [31] = KEY_KP3,
+ [32] = KEY_A,
+ [33] = KEY_S,
+ [34] = KEY_D,
+ [35] = KEY_F,
+ [36] = KEY_G,
+ [37] = KEY_H,
+ [38] = KEY_J,
+ [39] = KEY_K,
+ [40] = KEY_L,
+ [41] = KEY_SEMICOLON,
+ [42] = KEY_APOSTROPHE,
+ [43] = KEY_BACKSLASH,
+ [45] = KEY_KP4,
+ [46] = KEY_KP5,
+ [47] = KEY_KP6,
+ [48] = KEY_102ND,
+ [49] = KEY_Z,
+ [50] = KEY_X,
+ [51] = KEY_C,
+ [52] = KEY_V,
+ [53] = KEY_B,
+ [54] = KEY_N,
+ [55] = KEY_M,
+ [56] = KEY_COMMA,
+ [57] = KEY_DOT,
+ [58] = KEY_SLASH,
+ [60] = KEY_KPDOT,
+ [61] = KEY_KP7,
+ [62] = KEY_KP8,
+ [63] = KEY_KP9,
+ [64] = KEY_SPACE,
+ [65] = KEY_BACKSPACE,
+ [66] = KEY_TAB,
+ [67] = KEY_KPENTER,
+ [68] = KEY_ENTER,
+ [69] = KEY_ESC,
+ [70] = KEY_DELETE,
+ [74] = KEY_KPMINUS,
+ [76] = KEY_UP,
+ [77] = KEY_DOWN,
+ [78] = KEY_RIGHT,
+ [79] = KEY_LEFT,
+ [80] = KEY_F1,
+ [81] = KEY_F2,
+ [82] = KEY_F3,
+ [83] = KEY_F4,
+ [84] = KEY_F5,
+ [85] = KEY_F6,
+ [86] = KEY_F7,
+ [87] = KEY_F8,
+ [88] = KEY_F9,
+ [89] = KEY_F10,
+ [90] = KEY_KPLEFTPAREN,
+ [91] = KEY_KPRIGHTPAREN,
+ [92] = KEY_KPSLASH,
+ [93] = KEY_KPASTERISK,
+ [94] = KEY_KPPLUS,
+ [95] = KEY_HELP,
+ [96] = KEY_LEFTSHIFT,
+ [97] = KEY_RIGHTSHIFT,
+ [98] = KEY_CAPSLOCK,
+ [99] = KEY_LEFTCTRL,
+ [100] = KEY_LEFTALT,
+ [101] = KEY_RIGHTALT,
+ [102] = KEY_LEFTMETA,
+ [103] = KEY_RIGHTMETA
+};
+
+static const char *amikbd_messages[8] = {
+ [0] = KERN_ALERT "amikbd: Ctrl-Amiga-Amiga reset warning!!\n",
+ [1] = KERN_WARNING "amikbd: keyboard lost sync\n",
+ [2] = KERN_WARNING "amikbd: keyboard buffer overflow\n",
+ [3] = KERN_WARNING "amikbd: keyboard controller failure\n",
+ [4] = KERN_ERR "amikbd: keyboard selftest failure\n",
+ [5] = KERN_INFO "amikbd: initiate power-up key stream\n",
+ [6] = KERN_INFO "amikbd: terminate power-up key stream\n",
+ [7] = KERN_WARNING "amikbd: keyboard interrupt\n"
+};
+
+static struct input_dev amikbd_dev;
+
+static char *amikbd_name = "Amiga keyboard";
+static char *amikbd_phys = "amikbd/input0";
+
+static irqreturn_t amikbd_interrupt(int irq, void *dummy, struct pt_regs *fp)
+{
+ unsigned char scancode, down;
+
+ scancode = ~ciaa.sdr; /* get and invert scancode (keyboard is active low) */
+ ciaa.cra |= 0x40; /* switch SP pin to output for handshake */
+ udelay(85); /* wait until 85 us have expired */
+ ciaa.cra &= ~0x40; /* switch CIA serial port to input mode */
+
+ down = !(scancode & 1); /* lowest bit is release bit */
+ scancode >>= 1;
+
+ if (scancode < 0x78) { /* scancodes < 0x78 are keys */
+
+ scancode = amikbd_keycode[scancode];
+
+ input_regs(&amikbd_dev, fp);
+
+ if (scancode == KEY_CAPSLOCK) { /* CapsLock is a toggle switch key on Amiga */
+ input_report_key(&amikbd_dev, scancode, 1);
+ input_report_key(&amikbd_dev, scancode, 0);
+ input_sync(&amikbd_dev);
+ } else {
+ input_report_key(&amikbd_dev, scancode, down);
+ input_sync(&amikbd_dev);
+ }
+ } else /* scancodes >= 0x78 are error codes */
+ printk(amikbd_messages[scancode - 0x78]);
+
+ return IRQ_HANDLED;
+}
+
+static int __init amikbd_init(void)
+{
+ int i;
+
+ if (!AMIGAHW_PRESENT(AMI_KEYBOARD))
+ return -EIO;
+
+ if (!request_mem_region(CIAA_PHYSADDR-1+0xb00, 0x100, "amikeyb"))
+ return -EBUSY;
+
+ init_input_dev(&amikbd_dev);
+
+ amikbd_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ amikbd_dev.keycode = amikbd_keycode;
+ amikbd_dev.keycodesize = sizeof(unsigned char);
+ amikbd_dev.keycodemax = ARRAY_SIZE(amikbd_keycode);
+
+ for (i = 0; i < 0x78; i++)
+ if (amikbd_keycode[i])
+ set_bit(amikbd_keycode[i], amikbd_dev.keybit);
+
+ ciaa.cra &= ~0x41; /* serial data in, turn off TA */
+ request_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt, 0, "amikbd", amikbd_interrupt);
+
+ amikbd_dev.name = amikbd_name;
+ amikbd_dev.phys = amikbd_phys;
+ amikbd_dev.id.bustype = BUS_AMIGA;
+ amikbd_dev.id.vendor = 0x0001;
+ amikbd_dev.id.product = 0x0001;
+ amikbd_dev.id.version = 0x0100;
+
+ input_register_device(&amikbd_dev);
+
+ printk(KERN_INFO "input: %s\n", amikbd_name);
+
+ return 0;
+}
+
+static void __exit amikbd_exit(void)
+{
+ input_unregister_device(&amikbd_dev);
+ free_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt);
+ release_mem_region(CIAA_PHYSADDR-1+0xb00, 0x100);
+}
+
+module_init(amikbd_init);
+module_exit(amikbd_exit);
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
new file mode 100644
index 000000000000..f7304f0ce542
--- /dev/null
+++ b/drivers/input/keyboard/atkbd.c
@@ -0,0 +1,1148 @@
+/*
+ * AT and PS/2 keyboard driver
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This driver can handle standard AT keyboards and PS/2 keyboards in
+ * Translated and Raw Set 2 and Set 3, as well as AT keyboards on dumb
+ * input-only controllers and AT keyboards connected over a one way RS232
+ * converter.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/workqueue.h>
+#include <linux/libps2.h>
+
+#define DRIVER_DESC "AT and PS/2 keyboard driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static int atkbd_set = 2;
+module_param_named(set, atkbd_set, int, 0);
+MODULE_PARM_DESC(set, "Select keyboard code set (2 = default, 3 = PS/2 native)");
+
+#if defined(__i386__) || defined(__x86_64__) || defined(__hppa__)
+static int atkbd_reset;
+#else
+static int atkbd_reset = 1;
+#endif
+module_param_named(reset, atkbd_reset, bool, 0);
+MODULE_PARM_DESC(reset, "Reset keyboard during initialization");
+
+static int atkbd_softrepeat;
+module_param_named(softrepeat, atkbd_softrepeat, bool, 0);
+MODULE_PARM_DESC(softrepeat, "Use software keyboard repeat");
+
+static int atkbd_softraw = 1;
+module_param_named(softraw, atkbd_softraw, bool, 0);
+MODULE_PARM_DESC(softraw, "Use software generated rawmode");
+
+static int atkbd_scroll = 1;
+module_param_named(scroll, atkbd_scroll, bool, 0);
+MODULE_PARM_DESC(scroll, "Enable scroll-wheel on MS Office and similar keyboards");
+
+static int atkbd_extra;
+module_param_named(extra, atkbd_extra, bool, 0);
+MODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and similar keyboards");
+
+__obsolete_setup("atkbd_set=");
+__obsolete_setup("atkbd_reset");
+__obsolete_setup("atkbd_softrepeat=");
+
+/*
+ * Scancode to keycode tables. These are just the default setting, and
+ * are loadable via an userland utility.
+ */
+
+static unsigned char atkbd_set2_keycode[512] = {
+
+#ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES
+
+/* XXX: need a more general approach */
+
+#include "hpps2atkbd.h" /* include the keyboard scancodes */
+
+#else
+ 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117,
+ 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0,
+ 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183,
+ 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185,
+ 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0,
+ 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85,
+ 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0,
+ 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125,
+ 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127,
+ 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142,
+ 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0,
+ 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112,
+ 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0,
+
+ 0, 0, 0, 65, 99,
+#endif
+};
+
+static unsigned char atkbd_set3_keycode[512] = {
+
+ 0, 0, 0, 0, 0, 0, 0, 59, 1,138,128,129,130, 15, 41, 60,
+ 131, 29, 42, 86, 58, 16, 2, 61,133, 56, 44, 31, 30, 17, 3, 62,
+ 134, 46, 45, 32, 18, 5, 4, 63,135, 57, 47, 33, 20, 19, 6, 64,
+ 136, 49, 48, 35, 34, 21, 7, 65,137,100, 50, 36, 22, 8, 9, 66,
+ 125, 51, 37, 23, 24, 11, 10, 67,126, 52, 53, 38, 39, 25, 12, 68,
+ 113,114, 40, 43, 26, 13, 87, 99, 97, 54, 28, 27, 43, 43, 88, 70,
+ 108,105,119,103,111,107, 14,110, 0, 79,106, 75, 71,109,102,104,
+ 82, 83, 80, 76, 77, 72, 69, 98, 0, 96, 81, 0, 78, 73, 55,183,
+
+ 184,185,186,187, 74, 94, 92, 93, 0, 0, 0,125,126,127,112, 0,
+ 0,139,150,163,165,115,152,150,166,140,160,154,113,114,167,168,
+ 148,149,147,140
+};
+
+static unsigned char atkbd_unxlate_table[128] = {
+ 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
+ 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
+ 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
+ 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3,
+ 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105,
+ 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63,
+ 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
+ 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
+};
+
+#define ATKBD_CMD_SETLEDS 0x10ed
+#define ATKBD_CMD_GSCANSET 0x11f0
+#define ATKBD_CMD_SSCANSET 0x10f0
+#define ATKBD_CMD_GETID 0x02f2
+#define ATKBD_CMD_SETREP 0x10f3
+#define ATKBD_CMD_ENABLE 0x00f4
+#define ATKBD_CMD_RESET_DIS 0x00f5
+#define ATKBD_CMD_SETALL_MBR 0x00fa
+#define ATKBD_CMD_RESET_BAT 0x02ff
+#define ATKBD_CMD_RESEND 0x00fe
+#define ATKBD_CMD_EX_ENABLE 0x10ea
+#define ATKBD_CMD_EX_SETLEDS 0x20eb
+#define ATKBD_CMD_OK_GETID 0x02e8
+
+#define ATKBD_RET_ACK 0xfa
+#define ATKBD_RET_NAK 0xfe
+#define ATKBD_RET_BAT 0xaa
+#define ATKBD_RET_EMUL0 0xe0
+#define ATKBD_RET_EMUL1 0xe1
+#define ATKBD_RET_RELEASE 0xf0
+#define ATKBD_RET_HANGUEL 0xf1
+#define ATKBD_RET_HANJA 0xf2
+#define ATKBD_RET_ERR 0xff
+
+#define ATKBD_KEY_UNKNOWN 0
+#define ATKBD_KEY_NULL 255
+
+#define ATKBD_SCR_1 254
+#define ATKBD_SCR_2 253
+#define ATKBD_SCR_4 252
+#define ATKBD_SCR_8 251
+#define ATKBD_SCR_CLICK 250
+#define ATKBD_SCR_LEFT 249
+#define ATKBD_SCR_RIGHT 248
+
+#define ATKBD_SPECIAL 248
+
+static struct {
+ unsigned char keycode;
+ unsigned char set2;
+} atkbd_scroll_keys[] = {
+ { ATKBD_SCR_1, 0xc5 },
+ { ATKBD_SCR_2, 0xa9 },
+ { ATKBD_SCR_4, 0xb6 },
+ { ATKBD_SCR_8, 0xa7 },
+ { ATKBD_SCR_CLICK, 0xe0 },
+ { ATKBD_SCR_LEFT, 0xcb },
+ { ATKBD_SCR_RIGHT, 0xd2 },
+};
+
+/*
+ * The atkbd control structure
+ */
+
+struct atkbd {
+
+ struct ps2dev ps2dev;
+
+ /* Written only during init */
+ char name[64];
+ char phys[32];
+ struct input_dev dev;
+
+ unsigned short id;
+ unsigned char keycode[512];
+ unsigned char set;
+ unsigned char translated;
+ unsigned char extra;
+ unsigned char write;
+ unsigned char softrepeat;
+ unsigned char softraw;
+ unsigned char scroll;
+ unsigned char enabled;
+
+ /* Accessed only from interrupt */
+ unsigned char emul;
+ unsigned char resend;
+ unsigned char release;
+ unsigned char bat_xl;
+ unsigned int last;
+ unsigned long time;
+};
+
+static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
+ ssize_t (*handler)(struct atkbd *, char *));
+static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count,
+ ssize_t (*handler)(struct atkbd *, const char *, size_t));
+#define ATKBD_DEFINE_ATTR(_name) \
+static ssize_t atkbd_show_##_name(struct atkbd *, char *); \
+static ssize_t atkbd_set_##_name(struct atkbd *, const char *, size_t); \
+static ssize_t atkbd_do_show_##_name(struct device *d, char *b) \
+{ \
+ return atkbd_attr_show_helper(d, b, atkbd_show_##_name); \
+} \
+static ssize_t atkbd_do_set_##_name(struct device *d, const char *b, size_t s) \
+{ \
+ return atkbd_attr_set_helper(d, b, s, atkbd_set_##_name); \
+} \
+static struct device_attribute atkbd_attr_##_name = \
+ __ATTR(_name, S_IWUSR | S_IRUGO, atkbd_do_show_##_name, atkbd_do_set_##_name);
+
+ATKBD_DEFINE_ATTR(extra);
+ATKBD_DEFINE_ATTR(scroll);
+ATKBD_DEFINE_ATTR(set);
+ATKBD_DEFINE_ATTR(softrepeat);
+ATKBD_DEFINE_ATTR(softraw);
+
+
+static void atkbd_report_key(struct input_dev *dev, struct pt_regs *regs, int code, int value)
+{
+ input_regs(dev, regs);
+ if (value == 3) {
+ input_report_key(dev, code, 1);
+ input_sync(dev);
+ input_report_key(dev, code, 0);
+ } else
+ input_event(dev, EV_KEY, code, value);
+ input_sync(dev);
+}
+
+/*
+ * atkbd_interrupt(). Here takes place processing of data received from
+ * the keyboard into events.
+ */
+
+static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
+ unsigned int flags, struct pt_regs *regs)
+{
+ struct atkbd *atkbd = serio_get_drvdata(serio);
+ unsigned int code = data;
+ int scroll = 0, hscroll = 0, click = -1;
+ int value;
+
+#ifdef ATKBD_DEBUG
+ printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags);
+#endif
+
+#if !defined(__i386__) && !defined (__x86_64__)
+ if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && !atkbd->resend && atkbd->write) {
+ printk(KERN_WARNING "atkbd.c: frame/parity error: %02x\n", flags);
+ serio_write(serio, ATKBD_CMD_RESEND);
+ atkbd->resend = 1;
+ goto out;
+ }
+
+ if (!flags && data == ATKBD_RET_ACK)
+ atkbd->resend = 0;
+#endif
+
+ if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_ACK))
+ if (ps2_handle_ack(&atkbd->ps2dev, data))
+ goto out;
+
+ if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_CMD))
+ if (ps2_handle_response(&atkbd->ps2dev, data))
+ goto out;
+
+ if (!atkbd->enabled)
+ goto out;
+
+ input_event(&atkbd->dev, EV_MSC, MSC_RAW, code);
+
+ if (atkbd->translated) {
+
+ if (atkbd->emul ||
+ !(code == ATKBD_RET_EMUL0 || code == ATKBD_RET_EMUL1 ||
+ code == ATKBD_RET_HANGUEL || code == ATKBD_RET_HANJA ||
+ code == ATKBD_RET_ERR ||
+ (code == ATKBD_RET_BAT && !atkbd->bat_xl))) {
+ atkbd->release = code >> 7;
+ code &= 0x7f;
+ }
+
+ if (!atkbd->emul &&
+ (code & 0x7f) == (ATKBD_RET_BAT & 0x7f))
+ atkbd->bat_xl = !atkbd->release;
+ }
+
+ switch (code) {
+ case ATKBD_RET_BAT:
+ atkbd->enabled = 0;
+ serio_rescan(atkbd->ps2dev.serio);
+ goto out;
+ case ATKBD_RET_EMUL0:
+ atkbd->emul = 1;
+ goto out;
+ case ATKBD_RET_EMUL1:
+ atkbd->emul = 2;
+ goto out;
+ case ATKBD_RET_RELEASE:
+ atkbd->release = 1;
+ goto out;
+ case ATKBD_RET_HANGUEL:
+ atkbd_report_key(&atkbd->dev, regs, KEY_HANGUEL, 3);
+ goto out;
+ case ATKBD_RET_HANJA:
+ atkbd_report_key(&atkbd->dev, regs, KEY_HANJA, 3);
+ goto out;
+ case ATKBD_RET_ERR:
+ printk(KERN_DEBUG "atkbd.c: Keyboard on %s reports too many keys pressed.\n", serio->phys);
+ goto out;
+ }
+
+ if (atkbd->set != 3)
+ code = (code & 0x7f) | ((code & 0x80) << 1);
+ if (atkbd->emul) {
+ if (--atkbd->emul)
+ goto out;
+ code |= (atkbd->set != 3) ? 0x80 : 0x100;
+ }
+
+ if (atkbd->keycode[code] != ATKBD_KEY_NULL)
+ input_event(&atkbd->dev, EV_MSC, MSC_SCAN, code);
+
+ switch (atkbd->keycode[code]) {
+ case ATKBD_KEY_NULL:
+ break;
+ case ATKBD_KEY_UNKNOWN:
+ if (data == ATKBD_RET_ACK || data == ATKBD_RET_NAK) {
+ printk(KERN_WARNING "atkbd.c: Spurious %s on %s. Some program, "
+ "like XFree86, might be trying access hardware directly.\n",
+ data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
+ } else {
+ printk(KERN_WARNING "atkbd.c: Unknown key %s "
+ "(%s set %d, code %#x on %s).\n",
+ atkbd->release ? "released" : "pressed",
+ atkbd->translated ? "translated" : "raw",
+ atkbd->set, code, serio->phys);
+ printk(KERN_WARNING "atkbd.c: Use 'setkeycodes %s%02x <keycode>' "
+ "to make it known.\n",
+ code & 0x80 ? "e0" : "", code & 0x7f);
+ }
+ input_sync(&atkbd->dev);
+ break;
+ case ATKBD_SCR_1:
+ scroll = 1 - atkbd->release * 2;
+ break;
+ case ATKBD_SCR_2:
+ scroll = 2 - atkbd->release * 4;
+ break;
+ case ATKBD_SCR_4:
+ scroll = 4 - atkbd->release * 8;
+ break;
+ case ATKBD_SCR_8:
+ scroll = 8 - atkbd->release * 16;
+ break;
+ case ATKBD_SCR_CLICK:
+ click = !atkbd->release;
+ break;
+ case ATKBD_SCR_LEFT:
+ hscroll = -1;
+ break;
+ case ATKBD_SCR_RIGHT:
+ hscroll = 1;
+ break;
+ default:
+ value = atkbd->release ? 0 :
+ (1 + (!atkbd->softrepeat && test_bit(atkbd->keycode[code], atkbd->dev.key)));
+
+ switch (value) { /* Workaround Toshiba laptop multiple keypress */
+ case 0:
+ atkbd->last = 0;
+ break;
+ case 1:
+ atkbd->last = code;
+ atkbd->time = jiffies + msecs_to_jiffies(atkbd->dev.rep[REP_DELAY]) / 2;
+ break;
+ case 2:
+ if (!time_after(jiffies, atkbd->time) && atkbd->last == code)
+ value = 1;
+ break;
+ }
+
+ atkbd_report_key(&atkbd->dev, regs, atkbd->keycode[code], value);
+ }
+
+ if (atkbd->scroll) {
+ input_regs(&atkbd->dev, regs);
+ if (click != -1)
+ input_report_key(&atkbd->dev, BTN_MIDDLE, click);
+ input_report_rel(&atkbd->dev, REL_WHEEL, scroll);
+ input_report_rel(&atkbd->dev, REL_HWHEEL, hscroll);
+ input_sync(&atkbd->dev);
+ }
+
+ atkbd->release = 0;
+out:
+ return IRQ_HANDLED;
+}
+
+/*
+ * Event callback from the input module. Events that change the state of
+ * the hardware are processed here.
+ */
+
+static int atkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct atkbd *atkbd = dev->private;
+ const short period[32] =
+ { 33, 37, 42, 46, 50, 54, 58, 63, 67, 75, 83, 92, 100, 109, 116, 125,
+ 133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 };
+ const short delay[4] =
+ { 250, 500, 750, 1000 };
+ unsigned char param[2];
+ int i, j;
+
+ if (!atkbd->write)
+ return -1;
+
+ switch (type) {
+
+ case EV_LED:
+
+ param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0)
+ | (test_bit(LED_NUML, dev->led) ? 2 : 0)
+ | (test_bit(LED_CAPSL, dev->led) ? 4 : 0);
+ ps2_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS);
+
+ if (atkbd->extra) {
+ param[0] = 0;
+ param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0)
+ | (test_bit(LED_SLEEP, dev->led) ? 0x02 : 0)
+ | (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0)
+ | (test_bit(LED_MISC, dev->led) ? 0x10 : 0)
+ | (test_bit(LED_MUTE, dev->led) ? 0x20 : 0);
+ ps2_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_EX_SETLEDS);
+ }
+
+ return 0;
+
+
+ case EV_REP:
+
+ if (atkbd->softrepeat) return 0;
+
+ i = j = 0;
+ while (i < 32 && period[i] < dev->rep[REP_PERIOD]) i++;
+ while (j < 4 && delay[j] < dev->rep[REP_DELAY]) j++;
+ dev->rep[REP_PERIOD] = period[i];
+ dev->rep[REP_DELAY] = delay[j];
+ param[0] = i | (j << 5);
+ ps2_schedule_command(&atkbd->ps2dev, param, ATKBD_CMD_SETREP);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * atkbd_enable() signals that interrupt handler is allowed to
+ * generate input events.
+ */
+
+static inline void atkbd_enable(struct atkbd *atkbd)
+{
+ serio_pause_rx(atkbd->ps2dev.serio);
+ atkbd->enabled = 1;
+ serio_continue_rx(atkbd->ps2dev.serio);
+}
+
+/*
+ * atkbd_disable() tells input handler that all incoming data except
+ * for ACKs and command response should be dropped.
+ */
+
+static inline void atkbd_disable(struct atkbd *atkbd)
+{
+ serio_pause_rx(atkbd->ps2dev.serio);
+ atkbd->enabled = 0;
+ serio_continue_rx(atkbd->ps2dev.serio);
+}
+
+/*
+ * atkbd_probe() probes for an AT keyboard on a serio port.
+ */
+
+static int atkbd_probe(struct atkbd *atkbd)
+{
+ struct ps2dev *ps2dev = &atkbd->ps2dev;
+ unsigned char param[2];
+
+/*
+ * Some systems, where the bit-twiddling when testing the io-lines of the
+ * controller may confuse the keyboard need a full reset of the keyboard. On
+ * these systems the BIOS also usually doesn't do it for us.
+ */
+
+ if (atkbd_reset)
+ if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_BAT))
+ printk(KERN_WARNING "atkbd.c: keyboard reset failed on %s\n", ps2dev->serio->phys);
+
+/*
+ * Then we check the keyboard ID. We should get 0xab83 under normal conditions.
+ * Some keyboards report different values, but the first byte is always 0xab or
+ * 0xac. Some old AT keyboards don't report anything. If a mouse is connected, this
+ * should make sure we don't try to set the LEDs on it.
+ */
+
+ param[0] = param[1] = 0xa5; /* initialize with invalid values */
+ if (ps2_command(ps2dev, param, ATKBD_CMD_GETID)) {
+
+/*
+ * If the get ID command failed, we check if we can at least set the LEDs on
+ * the keyboard. This should work on every keyboard out there. It also turns
+ * the LEDs off, which we want anyway.
+ */
+ param[0] = 0;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS))
+ return -1;
+ atkbd->id = 0xabba;
+ return 0;
+ }
+
+ if (param[0] != 0xab && param[0] != 0xac && /* Regular and NCD Sun keyboards */
+ param[0] != 0x2b && param[0] != 0x5d && /* Trust keyboard, raw and translated */
+ param[0] != 0x60 && param[0] != 0x47) /* NMB SGI keyboard, raw and translated */
+ return -1;
+
+ atkbd->id = (param[0] << 8) | param[1];
+
+ if (atkbd->id == 0xaca1 && atkbd->translated) {
+ printk(KERN_ERR "atkbd.c: NCD terminal keyboards are only supported on non-translating\n");
+ printk(KERN_ERR "atkbd.c: controllers. Use i8042.direct=1 to disable translation.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * atkbd_select_set checks if a keyboard has a working Set 3 support, and
+ * sets it into that. Unfortunately there are keyboards that can be switched
+ * to Set 3, but don't work well in that (BTC Multimedia ...)
+ */
+
+static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra)
+{
+ struct ps2dev *ps2dev = &atkbd->ps2dev;
+ unsigned char param[2];
+
+ atkbd->extra = 0;
+/*
+ * For known special keyboards we can go ahead and set the correct set.
+ * We check for NCD PS/2 Sun, NorthGate OmniKey 101 and
+ * IBM RapidAccess / IBM EzButton / Chicony KBP-8993 keyboards.
+ */
+
+ if (atkbd->translated)
+ return 2;
+
+ if (atkbd->id == 0xaca1) {
+ param[0] = 3;
+ ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET);
+ return 3;
+ }
+
+ if (allow_extra) {
+ param[0] = 0x71;
+ if (!ps2_command(ps2dev, param, ATKBD_CMD_EX_ENABLE)) {
+ atkbd->extra = 1;
+ return 2;
+ }
+ }
+
+ if (target_set != 3)
+ return 2;
+
+ if (!ps2_command(ps2dev, param, ATKBD_CMD_OK_GETID)) {
+ atkbd->id = param[0] << 8 | param[1];
+ return 2;
+ }
+
+ param[0] = 3;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET))
+ return 2;
+
+ param[0] = 0;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_GSCANSET))
+ return 2;
+
+ if (param[0] != 3) {
+ param[0] = 2;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET))
+ return 2;
+ }
+
+ ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MBR);
+
+ return 3;
+}
+
+static int atkbd_activate(struct atkbd *atkbd)
+{
+ struct ps2dev *ps2dev = &atkbd->ps2dev;
+ unsigned char param[1];
+
+/*
+ * Set the LEDs to a defined state.
+ */
+
+ param[0] = 0;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS))
+ return -1;
+
+/*
+ * Set autorepeat to fastest possible.
+ */
+
+ param[0] = 0;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_SETREP))
+ return -1;
+
+/*
+ * Enable the keyboard to receive keystrokes.
+ */
+
+ if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
+ printk(KERN_ERR "atkbd.c: Failed to enable keyboard on %s\n",
+ ps2dev->serio->phys);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a
+ * reboot.
+ */
+
+static void atkbd_cleanup(struct serio *serio)
+{
+ struct atkbd *atkbd = serio_get_drvdata(serio);
+ ps2_command(&atkbd->ps2dev, NULL, ATKBD_CMD_RESET_BAT);
+}
+
+
+/*
+ * atkbd_disconnect() closes and frees.
+ */
+
+static void atkbd_disconnect(struct serio *serio)
+{
+ struct atkbd *atkbd = serio_get_drvdata(serio);
+
+ atkbd_disable(atkbd);
+
+ /* make sure we don't have a command in flight */
+ synchronize_kernel();
+ flush_scheduled_work();
+
+ device_remove_file(&serio->dev, &atkbd_attr_extra);
+ device_remove_file(&serio->dev, &atkbd_attr_scroll);
+ device_remove_file(&serio->dev, &atkbd_attr_set);
+ device_remove_file(&serio->dev, &atkbd_attr_softrepeat);
+ device_remove_file(&serio->dev, &atkbd_attr_softraw);
+
+ input_unregister_device(&atkbd->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(atkbd);
+}
+
+
+/*
+ * atkbd_set_device_attrs() initializes keyboard's keycode table
+ * according to the selected scancode set
+ */
+
+static void atkbd_set_keycode_table(struct atkbd *atkbd)
+{
+ int i, j;
+
+ memset(atkbd->keycode, 0, sizeof(atkbd->keycode));
+
+ if (atkbd->translated) {
+ for (i = 0; i < 128; i++) {
+ atkbd->keycode[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]];
+ atkbd->keycode[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80];
+ if (atkbd->scroll)
+ for (j = 0; j < ARRAY_SIZE(atkbd_scroll_keys); j++)
+ if ((atkbd_unxlate_table[i] | 0x80) == atkbd_scroll_keys[j].set2)
+ atkbd->keycode[i | 0x80] = atkbd_scroll_keys[j].keycode;
+ }
+ } else if (atkbd->set == 3) {
+ memcpy(atkbd->keycode, atkbd_set3_keycode, sizeof(atkbd->keycode));
+ } else {
+ memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode));
+
+ if (atkbd->scroll)
+ for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++)
+ atkbd->keycode[atkbd_scroll_keys[i].set2] = atkbd_scroll_keys[i].keycode;
+ }
+}
+
+/*
+ * atkbd_set_device_attrs() sets up keyboard's input device structure
+ */
+
+static void atkbd_set_device_attrs(struct atkbd *atkbd)
+{
+ int i;
+
+ memset(&atkbd->dev, 0, sizeof(struct input_dev));
+
+ init_input_dev(&atkbd->dev);
+
+ atkbd->dev.name = atkbd->name;
+ atkbd->dev.phys = atkbd->phys;
+ atkbd->dev.id.bustype = BUS_I8042;
+ atkbd->dev.id.vendor = 0x0001;
+ atkbd->dev.id.product = atkbd->translated ? 1 : atkbd->set;
+ atkbd->dev.id.version = atkbd->id;
+ atkbd->dev.event = atkbd_event;
+ atkbd->dev.private = atkbd;
+ atkbd->dev.dev = &atkbd->ps2dev.serio->dev;
+
+ atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_MSC);
+
+ if (atkbd->write) {
+ atkbd->dev.evbit[0] |= BIT(EV_LED);
+ atkbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
+ }
+
+ if (atkbd->extra)
+ atkbd->dev.ledbit[0] |= BIT(LED_COMPOSE) | BIT(LED_SUSPEND) |
+ BIT(LED_SLEEP) | BIT(LED_MUTE) | BIT(LED_MISC);
+
+ if (!atkbd->softrepeat) {
+ atkbd->dev.rep[REP_DELAY] = 250;
+ atkbd->dev.rep[REP_PERIOD] = 33;
+ }
+
+ atkbd->dev.mscbit[0] = atkbd->softraw ? BIT(MSC_SCAN) : BIT(MSC_RAW) | BIT(MSC_SCAN);
+
+ if (atkbd->scroll) {
+ atkbd->dev.evbit[0] |= BIT(EV_REL);
+ atkbd->dev.relbit[0] = BIT(REL_WHEEL) | BIT(REL_HWHEEL);
+ set_bit(BTN_MIDDLE, atkbd->dev.keybit);
+ }
+
+ atkbd->dev.keycode = atkbd->keycode;
+ atkbd->dev.keycodesize = sizeof(unsigned char);
+ atkbd->dev.keycodemax = ARRAY_SIZE(atkbd_set2_keycode);
+
+ for (i = 0; i < 512; i++)
+ if (atkbd->keycode[i] && atkbd->keycode[i] < ATKBD_SPECIAL)
+ set_bit(atkbd->keycode[i], atkbd->dev.keybit);
+}
+
+/*
+ * atkbd_connect() is called when the serio module finds an interface
+ * that isn't handled yet by an appropriate device driver. We check if
+ * there is an AT keyboard out there and if yes, we register ourselves
+ * to the input module.
+ */
+
+static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct atkbd *atkbd;
+ int err;
+
+ if (!(atkbd = kmalloc(sizeof(struct atkbd), GFP_KERNEL)))
+ return - ENOMEM;
+
+ memset(atkbd, 0, sizeof(struct atkbd));
+
+ ps2_init(&atkbd->ps2dev, serio);
+
+ switch (serio->id.type) {
+
+ case SERIO_8042_XL:
+ atkbd->translated = 1;
+ case SERIO_8042:
+ if (serio->write)
+ atkbd->write = 1;
+ break;
+ }
+
+ atkbd->softraw = atkbd_softraw;
+ atkbd->softrepeat = atkbd_softrepeat;
+ atkbd->scroll = atkbd_scroll;
+
+ if (!atkbd->write)
+ atkbd->softrepeat = 1;
+
+ if (atkbd->softrepeat)
+ atkbd->softraw = 1;
+
+ serio_set_drvdata(serio, atkbd);
+
+ err = serio_open(serio, drv);
+ if (err) {
+ serio_set_drvdata(serio, NULL);
+ kfree(atkbd);
+ return err;
+ }
+
+ if (atkbd->write) {
+
+ if (atkbd_probe(atkbd)) {
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(atkbd);
+ return -ENODEV;
+ }
+
+ atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra);
+ atkbd_activate(atkbd);
+
+ } else {
+ atkbd->set = 2;
+ atkbd->id = 0xab00;
+ }
+
+ if (atkbd->extra)
+ sprintf(atkbd->name, "AT Set 2 Extra keyboard");
+ else
+ sprintf(atkbd->name, "AT %s Set %d keyboard",
+ atkbd->translated ? "Translated" : "Raw", atkbd->set);
+
+ sprintf(atkbd->phys, "%s/input0", serio->phys);
+
+ atkbd_set_keycode_table(atkbd);
+ atkbd_set_device_attrs(atkbd);
+
+ input_register_device(&atkbd->dev);
+
+ device_create_file(&serio->dev, &atkbd_attr_extra);
+ device_create_file(&serio->dev, &atkbd_attr_scroll);
+ device_create_file(&serio->dev, &atkbd_attr_set);
+ device_create_file(&serio->dev, &atkbd_attr_softrepeat);
+ device_create_file(&serio->dev, &atkbd_attr_softraw);
+
+ atkbd_enable(atkbd);
+
+ printk(KERN_INFO "input: %s on %s\n", atkbd->name, serio->phys);
+
+ return 0;
+}
+
+/*
+ * atkbd_reconnect() tries to restore keyboard into a sane state and is
+ * most likely called on resume.
+ */
+
+static int atkbd_reconnect(struct serio *serio)
+{
+ struct atkbd *atkbd = serio_get_drvdata(serio);
+ struct serio_driver *drv = serio->drv;
+ unsigned char param[1];
+
+ if (!atkbd || !drv) {
+ printk(KERN_DEBUG "atkbd: reconnect request, but serio is disconnected, ignoring...\n");
+ return -1;
+ }
+
+ atkbd_disable(atkbd);
+
+ if (atkbd->write) {
+ param[0] = (test_bit(LED_SCROLLL, atkbd->dev.led) ? 1 : 0)
+ | (test_bit(LED_NUML, atkbd->dev.led) ? 2 : 0)
+ | (test_bit(LED_CAPSL, atkbd->dev.led) ? 4 : 0);
+
+ if (atkbd_probe(atkbd))
+ return -1;
+ if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra))
+ return -1;
+
+ atkbd_activate(atkbd);
+
+ if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS))
+ return -1;
+ }
+
+ atkbd_enable(atkbd);
+
+ return 0;
+}
+
+static struct serio_device_id atkbd_serio_ids[] = {
+ {
+ .type = SERIO_8042,
+ .proto = SERIO_ANY,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ {
+ .type = SERIO_8042_XL,
+ .proto = SERIO_ANY,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_PS2SER,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, atkbd_serio_ids);
+
+static struct serio_driver atkbd_drv = {
+ .driver = {
+ .name = "atkbd",
+ },
+ .description = DRIVER_DESC,
+ .id_table = atkbd_serio_ids,
+ .interrupt = atkbd_interrupt,
+ .connect = atkbd_connect,
+ .reconnect = atkbd_reconnect,
+ .disconnect = atkbd_disconnect,
+ .cleanup = atkbd_cleanup,
+};
+
+static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
+ ssize_t (*handler)(struct atkbd *, char *))
+{
+ struct serio *serio = to_serio_port(dev);
+ int retval;
+
+ retval = serio_pin_driver(serio);
+ if (retval)
+ return retval;
+
+ if (serio->drv != &atkbd_drv) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ retval = handler((struct atkbd *)serio_get_drvdata(serio), buf);
+
+out:
+ serio_unpin_driver(serio);
+ return retval;
+}
+
+static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count,
+ ssize_t (*handler)(struct atkbd *, const char *, size_t))
+{
+ struct serio *serio = to_serio_port(dev);
+ struct atkbd *atkbd;
+ int retval;
+
+ retval = serio_pin_driver(serio);
+ if (retval)
+ return retval;
+
+ if (serio->drv != &atkbd_drv) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ atkbd = serio_get_drvdata(serio);
+ atkbd_disable(atkbd);
+ retval = handler(atkbd, buf, count);
+ atkbd_enable(atkbd);
+
+out:
+ serio_unpin_driver(serio);
+ return retval;
+}
+
+static ssize_t atkbd_show_extra(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%d\n", atkbd->extra ? 1 : 0);
+}
+
+static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t count)
+{
+ unsigned long value;
+ char *rest;
+
+ if (!atkbd->write)
+ return -EIO;
+
+ value = simple_strtoul(buf, &rest, 10);
+ if (*rest || value > 1)
+ return -EINVAL;
+
+ if (atkbd->extra != value) {
+ /* unregister device as it's properties will change */
+ input_unregister_device(&atkbd->dev);
+ atkbd->set = atkbd_select_set(atkbd, atkbd->set, value);
+ atkbd_activate(atkbd);
+ atkbd_set_device_attrs(atkbd);
+ input_register_device(&atkbd->dev);
+ }
+ return count;
+}
+
+static ssize_t atkbd_show_scroll(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%d\n", atkbd->scroll ? 1 : 0);
+}
+
+static ssize_t atkbd_set_scroll(struct atkbd *atkbd, const char *buf, size_t count)
+{
+ unsigned long value;
+ char *rest;
+
+ value = simple_strtoul(buf, &rest, 10);
+ if (*rest || value > 1)
+ return -EINVAL;
+
+ if (atkbd->scroll != value) {
+ /* unregister device as it's properties will change */
+ input_unregister_device(&atkbd->dev);
+ atkbd->scroll = value;
+ atkbd_set_keycode_table(atkbd);
+ atkbd_set_device_attrs(atkbd);
+ input_register_device(&atkbd->dev);
+ }
+ return count;
+}
+
+static ssize_t atkbd_show_set(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%d\n", atkbd->set);
+}
+
+static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count)
+{
+ unsigned long value;
+ char *rest;
+
+ if (!atkbd->write)
+ return -EIO;
+
+ value = simple_strtoul(buf, &rest, 10);
+ if (*rest || (value != 2 && value != 3))
+ return -EINVAL;
+
+ if (atkbd->set != value) {
+ /* unregister device as it's properties will change */
+ input_unregister_device(&atkbd->dev);
+ atkbd->set = atkbd_select_set(atkbd, value, atkbd->extra);
+ atkbd_activate(atkbd);
+ atkbd_set_keycode_table(atkbd);
+ atkbd_set_device_attrs(atkbd);
+ input_register_device(&atkbd->dev);
+ }
+ return count;
+}
+
+static ssize_t atkbd_show_softrepeat(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%d\n", atkbd->softrepeat ? 1 : 0);
+}
+
+static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t count)
+{
+ unsigned long value;
+ char *rest;
+
+ if (!atkbd->write)
+ return -EIO;
+
+ value = simple_strtoul(buf, &rest, 10);
+ if (*rest || value > 1)
+ return -EINVAL;
+
+ if (atkbd->softrepeat != value) {
+ /* unregister device as it's properties will change */
+ input_unregister_device(&atkbd->dev);
+ atkbd->softrepeat = value;
+ if (atkbd->softrepeat)
+ atkbd->softraw = 1;
+ atkbd_set_device_attrs(atkbd);
+ input_register_device(&atkbd->dev);
+ }
+
+ return count;
+}
+
+
+static ssize_t atkbd_show_softraw(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%d\n", atkbd->softraw ? 1 : 0);
+}
+
+static ssize_t atkbd_set_softraw(struct atkbd *atkbd, const char *buf, size_t count)
+{
+ unsigned long value;
+ char *rest;
+
+ value = simple_strtoul(buf, &rest, 10);
+ if (*rest || value > 1)
+ return -EINVAL;
+
+ if (atkbd->softraw != value) {
+ /* unregister device as it's properties will change */
+ input_unregister_device(&atkbd->dev);
+ atkbd->softraw = value;
+ atkbd_set_device_attrs(atkbd);
+ input_register_device(&atkbd->dev);
+ }
+ return count;
+}
+
+
+static int __init atkbd_init(void)
+{
+ serio_register_driver(&atkbd_drv);
+ return 0;
+}
+
+static void __exit atkbd_exit(void)
+{
+ serio_unregister_driver(&atkbd_drv);
+}
+
+module_init(atkbd_init);
+module_exit(atkbd_exit);
diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c
new file mode 100644
index 000000000000..0f1220a0ceb5
--- /dev/null
+++ b/drivers/input/keyboard/corgikbd.c
@@ -0,0 +1,361 @@
+/*
+ * Keyboard driver for Sharp Corgi models (SL-C7xx)
+ *
+ * Copyright (c) 2004-2005 Richard Purdie
+ *
+ * Based on xtkbd.c/locomkbd.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/irq.h>
+
+#include <asm/arch/corgi.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/hardware/scoop.h>
+
+#define KB_ROWS 8
+#define KB_COLS 12
+#define KB_ROWMASK(r) (1 << (r))
+#define SCANCODE(r,c) ( ((r)<<4) + (c) + 1 )
+/* zero code, 124 scancodes + 3 hinge combinations */
+#define NR_SCANCODES ( SCANCODE(KB_ROWS-1,KB_COLS-1) +1 +1 +3 )
+#define SCAN_INTERVAL (HZ/10)
+#define CORGIKBD_PRESSED 1
+
+#define HINGE_SCAN_INTERVAL (HZ/4)
+
+#define CORGI_KEY_CALENDER KEY_F1
+#define CORGI_KEY_ADDRESS KEY_F2
+#define CORGI_KEY_FN KEY_F3
+#define CORGI_KEY_OFF KEY_SUSPEND
+#define CORGI_KEY_EXOK KEY_F5
+#define CORGI_KEY_EXCANCEL KEY_F6
+#define CORGI_KEY_EXJOGDOWN KEY_F7
+#define CORGI_KEY_EXJOGUP KEY_F8
+#define CORGI_KEY_JAP1 KEY_LEFTCTRL
+#define CORGI_KEY_JAP2 KEY_LEFTALT
+#define CORGI_KEY_OK KEY_F11
+#define CORGI_KEY_MENU KEY_F12
+#define CORGI_HINGE_0 KEY_KP0
+#define CORGI_HINGE_1 KEY_KP1
+#define CORGI_HINGE_2 KEY_KP2
+
+static unsigned char corgikbd_keycode[NR_SCANCODES] = {
+ 0, /* 0 */
+ 0, KEY_1, KEY_3, KEY_5, KEY_6, KEY_7, KEY_9, KEY_0, KEY_BACKSPACE, 0, 0, 0, 0, 0, 0, 0, /* 1-16 */
+ 0, KEY_2, KEY_4, KEY_R, KEY_Y, KEY_8, KEY_I, KEY_O, KEY_P, 0, 0, 0, 0, 0, 0, 0, /* 17-32 */
+ KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0, /* 33-48 */
+ CORGI_KEY_CALENDER, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /* 49-64 */
+ CORGI_KEY_ADDRESS, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, 0, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, /* 65-80 */
+ KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, 0, CORGI_KEY_FN, 0, 0, 0, 0, /* 81-96 */
+ KEY_SYSRQ, CORGI_KEY_JAP1, CORGI_KEY_JAP2, KEY_CANCEL, CORGI_KEY_OK, CORGI_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0, /* 97-112 */
+ CORGI_KEY_OFF, CORGI_KEY_EXOK, CORGI_KEY_EXCANCEL, CORGI_KEY_EXJOGDOWN, CORGI_KEY_EXJOGUP, 0, 0, 0, 0, 0, 0, 0, /* 113-124 */
+ CORGI_HINGE_0, CORGI_HINGE_1, CORGI_HINGE_2 /* 125-127 */
+};
+
+
+struct corgikbd {
+ unsigned char keycode[ARRAY_SIZE(corgikbd_keycode)];
+ struct input_dev input;
+ char phys[32];
+
+ unsigned char state[ARRAY_SIZE(corgikbd_keycode)];
+ spinlock_t lock;
+
+ struct timer_list timer;
+ struct timer_list htimer;
+};
+
+static void handle_scancode(unsigned int pressed,unsigned int scancode, struct corgikbd *corgikbd_data)
+{
+ if (pressed && !(corgikbd_data->state[scancode] & CORGIKBD_PRESSED)) {
+ corgikbd_data->state[scancode] |= CORGIKBD_PRESSED;
+ input_report_key(&corgikbd_data->input, corgikbd_data->keycode[scancode], 1);
+ if (corgikbd_data->keycode[scancode] == CORGI_KEY_OFF)
+ input_event(&corgikbd_data->input, EV_PWR, CORGI_KEY_OFF, 1);
+ } else if (!pressed && corgikbd_data->state[scancode] & CORGIKBD_PRESSED) {
+ corgikbd_data->state[scancode] &= ~CORGIKBD_PRESSED;
+ input_report_key(&corgikbd_data->input, corgikbd_data->keycode[scancode], 0);
+ }
+}
+
+#define KB_DISCHARGE_DELAY 10
+#define KB_ACTIVATE_DELAY 10
+
+/* Helper functions for reading the keyboard matrix
+ * Note: We should really be using pxa_gpio_mode to alter GPDR but it
+ * requires a function call per GPIO bit which is excessive
+ * when we need to access 12 bits at once multiple times.
+ * These functions must be called within local_irq_save()/local_irq_restore()
+ * or similar.
+ */
+static inline void corgikbd_discharge_all(void)
+{
+ // STROBE All HiZ
+ GPCR2 = CORGI_GPIO_ALL_STROBE_BIT;
+ GPDR2 &= ~CORGI_GPIO_ALL_STROBE_BIT;
+}
+
+static inline void corgikbd_activate_all(void)
+{
+ // STROBE ALL -> High
+ GPSR2 = CORGI_GPIO_ALL_STROBE_BIT;
+ GPDR2 |= CORGI_GPIO_ALL_STROBE_BIT;
+
+ udelay(KB_DISCHARGE_DELAY);
+
+ // Clear any interrupts we may have triggered when altering the GPIO lines
+ GEDR1 = CORGI_GPIO_HIGH_SENSE_BIT;
+ GEDR2 = CORGI_GPIO_LOW_SENSE_BIT;
+}
+
+static inline void corgikbd_activate_col(int col)
+{
+ // STROBE col -> High, not col -> HiZ
+ GPSR2 = CORGI_GPIO_STROBE_BIT(col);
+ GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col);
+}
+
+static inline void corgikbd_reset_col(int col)
+{
+ // STROBE col -> Low
+ GPCR2 = CORGI_GPIO_STROBE_BIT(col);
+ // STROBE col -> out, not col -> HiZ
+ GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col);
+}
+
+#define GET_ROWS_STATUS(c) (((GPLR1 & CORGI_GPIO_HIGH_SENSE_BIT) >> CORGI_GPIO_HIGH_SENSE_RSHIFT) | ((GPLR2 & CORGI_GPIO_LOW_SENSE_BIT) << CORGI_GPIO_LOW_SENSE_LSHIFT))
+
+/*
+ * The corgi keyboard only generates interrupts when a key is pressed.
+ * When a key is pressed, we enable a timer which then scans the
+ * keyboard to detect when the key is released.
+ */
+
+/* Scan the hardware keyboard and push any changes up through the input layer */
+static void corgikbd_scankeyboard(struct corgikbd *corgikbd_data, struct pt_regs *regs)
+{
+ unsigned int row, col, rowd, scancode;
+ unsigned long flags;
+ unsigned int num_pressed;
+
+ spin_lock_irqsave(&corgikbd_data->lock, flags);
+
+ if (regs)
+ input_regs(&corgikbd_data->input, regs);
+
+ num_pressed = 0;
+ for (col = 0; col < KB_COLS; col++) {
+ /*
+ * Discharge the output driver capacitatance
+ * in the keyboard matrix. (Yes it is significant..)
+ */
+
+ corgikbd_discharge_all();
+ udelay(KB_DISCHARGE_DELAY);
+
+ corgikbd_activate_col(col);
+ udelay(KB_ACTIVATE_DELAY);
+
+ rowd = GET_ROWS_STATUS(col);
+ for (row = 0; row < KB_ROWS; row++) {
+ scancode = SCANCODE(row, col);
+ handle_scancode((rowd & KB_ROWMASK(row)), scancode, corgikbd_data);
+ if (rowd & KB_ROWMASK(row))
+ num_pressed++;
+ }
+ corgikbd_reset_col(col);
+ }
+
+ corgikbd_activate_all();
+
+ input_sync(&corgikbd_data->input);
+
+ /* if any keys are pressed, enable the timer */
+ if (num_pressed)
+ mod_timer(&corgikbd_data->timer, jiffies + SCAN_INTERVAL);
+
+ spin_unlock_irqrestore(&corgikbd_data->lock, flags);
+}
+
+/*
+ * corgi keyboard interrupt handler.
+ */
+static irqreturn_t corgikbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct corgikbd *corgikbd_data = dev_id;
+
+ if (!timer_pending(&corgikbd_data->timer)) {
+ /** wait chattering delay **/
+ udelay(20);
+ corgikbd_scankeyboard(corgikbd_data, regs);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * corgi timer checking for released keys
+ */
+static void corgikbd_timer_callback(unsigned long data)
+{
+ struct corgikbd *corgikbd_data = (struct corgikbd *) data;
+ corgikbd_scankeyboard(corgikbd_data, NULL);
+}
+
+/*
+ * The hinge switches generate no interrupt so they need to be
+ * monitored by a timer.
+ *
+ * When we detect changes, we debounce it and then pass the three
+ * positions the system can take as keypresses to the input system.
+ */
+
+#define HINGE_STABLE_COUNT 2
+static int sharpsl_hinge_state;
+static int hinge_count;
+
+static void corgikbd_hinge_timer(unsigned long data)
+{
+ struct corgikbd *corgikbd_data = (struct corgikbd *) data;
+ unsigned long gprr;
+ unsigned long flags;
+
+ gprr = read_scoop_reg(SCOOP_GPRR) & (CORGI_SCP_SWA | CORGI_SCP_SWB);
+ if (gprr != sharpsl_hinge_state) {
+ hinge_count = 0;
+ sharpsl_hinge_state = gprr;
+ } else if (hinge_count < HINGE_STABLE_COUNT) {
+ hinge_count++;
+ if (hinge_count >= HINGE_STABLE_COUNT) {
+ spin_lock_irqsave(&corgikbd_data->lock, flags);
+
+ handle_scancode((sharpsl_hinge_state == 0x00), 125, corgikbd_data); /* Keyboard with Landscape Screen */
+ handle_scancode((sharpsl_hinge_state == 0x08), 126, corgikbd_data); /* No Keyboard with Portrait Screen */
+ handle_scancode((sharpsl_hinge_state == 0x0c), 127, corgikbd_data); /* Keyboard and Screen Closed */
+ input_sync(&corgikbd_data->input);
+
+ spin_unlock_irqrestore(&corgikbd_data->lock, flags);
+ }
+ }
+ mod_timer(&corgikbd_data->htimer, jiffies + HINGE_SCAN_INTERVAL);
+}
+
+static int __init corgikbd_probe(struct device *dev)
+{
+ int i;
+ struct corgikbd *corgikbd;
+
+ corgikbd = kcalloc(1, sizeof(struct corgikbd), GFP_KERNEL);
+ if (!corgikbd)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev,corgikbd);
+ strcpy(corgikbd->phys, "corgikbd/input0");
+
+ spin_lock_init(corgikbd->lock);
+
+ /* Init Keyboard rescan timer */
+ init_timer(&corgikbd->timer);
+ corgikbd->timer.function = corgikbd_timer_callback;
+ corgikbd->timer.data = (unsigned long) corgikbd;
+
+ /* Init Hinge Timer */
+ init_timer(&corgikbd->htimer);
+ corgikbd->htimer.function = corgikbd_hinge_timer;
+ corgikbd->htimer.data = (unsigned long) corgikbd;
+
+ init_input_dev(&corgikbd->input);
+ corgikbd->input.private = corgikbd;
+ corgikbd->input.name = "Corgi Keyboard";
+ corgikbd->input.dev = dev;
+ corgikbd->input.phys = corgikbd->phys;
+ corgikbd->input.id.bustype = BUS_HOST;
+ corgikbd->input.id.vendor = 0x0001;
+ corgikbd->input.id.product = 0x0001;
+ corgikbd->input.id.version = 0x0100;
+ corgikbd->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR);
+ corgikbd->input.keycode = corgikbd->keycode;
+ corgikbd->input.keycodesize = sizeof(unsigned char);
+ corgikbd->input.keycodemax = ARRAY_SIZE(corgikbd_keycode);
+
+ memcpy(corgikbd->keycode, corgikbd_keycode, sizeof(corgikbd->keycode));
+ for (i = 0; i < ARRAY_SIZE(corgikbd_keycode); i++)
+ set_bit(corgikbd->keycode[i], corgikbd->input.keybit);
+ clear_bit(0, corgikbd->input.keybit);
+
+ input_register_device(&corgikbd->input);
+ mod_timer(&corgikbd->htimer, jiffies + HINGE_SCAN_INTERVAL);
+
+ /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
+ for (i = 0; i < CORGI_KEY_SENSE_NUM; i++) {
+ pxa_gpio_mode(CORGI_GPIO_KEY_SENSE(i) | GPIO_IN);
+ if (request_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd_interrupt,
+ SA_INTERRUPT, "corgikbd", corgikbd))
+ printk(KERN_WARNING "corgikbd: Can't get IRQ: %d!\n", i);
+ else
+ set_irq_type(CORGI_IRQ_GPIO_KEY_SENSE(i),IRQT_RISING);
+ }
+
+ /* Set Strobe lines as outputs - set high */
+ for (i = 0; i < CORGI_KEY_STROBE_NUM; i++)
+ pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
+
+ printk(KERN_INFO "input: Corgi Keyboard Registered\n");
+
+ return 0;
+}
+
+static int corgikbd_remove(struct device *dev)
+{
+ int i;
+ struct corgikbd *corgikbd = dev_get_drvdata(dev);
+
+ for (i = 0; i < CORGI_KEY_SENSE_NUM; i++)
+ free_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd);
+
+ del_timer_sync(&corgikbd->htimer);
+ del_timer_sync(&corgikbd->timer);
+
+ input_unregister_device(&corgikbd->input);
+
+ kfree(corgikbd);
+
+ return 0;
+}
+
+static struct device_driver corgikbd_driver = {
+ .name = "corgi-keyboard",
+ .bus = &platform_bus_type,
+ .probe = corgikbd_probe,
+ .remove = corgikbd_remove,
+};
+
+static int __devinit corgikbd_init(void)
+{
+ return driver_register(&corgikbd_driver);
+}
+
+static void __exit corgikbd_exit(void)
+{
+ driver_unregister(&corgikbd_driver);
+}
+
+module_init(corgikbd_init);
+module_exit(corgikbd_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
+MODULE_DESCRIPTION("Corgi Keyboard Driver");
+MODULE_LICENSE("GPLv2");
diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c
new file mode 100644
index 000000000000..ef78bffed5e7
--- /dev/null
+++ b/drivers/input/keyboard/hil_kbd.c
@@ -0,0 +1,375 @@
+/*
+ * Generic linux-input device driver for keyboard devices
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ *
+ * References:
+ * HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A
+ *
+ */
+
+#include <linux/hil.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci_ids.h>
+
+#define PREFIX "HIL KEYB: "
+#define HIL_GENERIC_NAME "HIL keyboard"
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define HIL_KBD_MAX_LENGTH 16
+
+#define HIL_KBD_SET1_UPBIT 0x01
+#define HIL_KBD_SET1_SHIFT 1
+static unsigned int hil_kbd_set1[HIL_KEYCODES_SET1_TBLSIZE] =
+ { HIL_KEYCODES_SET1 };
+
+#define HIL_KBD_SET2_UPBIT 0x01
+#define HIL_KBD_SET2_SHIFT 1
+/* Set2 is user defined */
+
+#define HIL_KBD_SET3_UPBIT 0x80
+#define HIL_KBD_SET3_SHIFT 0
+static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] =
+ { HIL_KEYCODES_SET3 };
+
+static char hil_language[][16] = { HIL_LOCALE_MAP };
+
+struct hil_kbd {
+ struct input_dev dev;
+ struct serio *serio;
+
+ /* Input buffer and index for packets from HIL bus. */
+ hil_packet data[HIL_KBD_MAX_LENGTH];
+ int idx4; /* four counts per packet */
+
+ /* Raw device info records from HIL bus, see hil.h for fields. */
+ char idd[HIL_KBD_MAX_LENGTH]; /* DID byte and IDD record */
+ char rsc[HIL_KBD_MAX_LENGTH]; /* RSC record */
+ char exd[HIL_KBD_MAX_LENGTH]; /* EXD record */
+ char rnm[HIL_KBD_MAX_LENGTH + 1]; /* RNM record + NULL term. */
+
+ /* Something to sleep around with. */
+ struct semaphore sem;
+};
+
+/* Process a complete packet after transfer from the HIL */
+static void hil_kbd_process_record(struct hil_kbd *kbd)
+{
+ struct input_dev *dev = &kbd->dev;
+ hil_packet *data = kbd->data;
+ hil_packet p;
+ int idx, i, cnt;
+
+ idx = kbd->idx4/4;
+ p = data[idx - 1];
+
+ if ((p & ~HIL_CMDCT_POL) ==
+ (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) goto report;
+ if ((p & ~HIL_CMDCT_RPL) ==
+ (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) goto report;
+
+ /* Not a poll response. See if we are loading config records. */
+ switch (p & HIL_PKT_DATA_MASK) {
+ case HIL_CMD_IDD:
+ for (i = 0; i < idx; i++)
+ kbd->idd[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_KBD_MAX_LENGTH; i++)
+ kbd->idd[i] = 0;
+ break;
+ case HIL_CMD_RSC:
+ for (i = 0; i < idx; i++)
+ kbd->rsc[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_KBD_MAX_LENGTH; i++)
+ kbd->rsc[i] = 0;
+ break;
+ case HIL_CMD_EXD:
+ for (i = 0; i < idx; i++)
+ kbd->exd[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_KBD_MAX_LENGTH; i++)
+ kbd->exd[i] = 0;
+ break;
+ case HIL_CMD_RNM:
+ for (i = 0; i < idx; i++)
+ kbd->rnm[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_KBD_MAX_LENGTH + 1; i++)
+ kbd->rnm[i] = '\0';
+ break;
+ default:
+ /* These occur when device isn't present */
+ if (p == (HIL_ERR_INT | HIL_PKT_CMD)) break;
+ /* Anything else we'd like to know about. */
+ printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p);
+ break;
+ }
+ goto out;
+
+ report:
+ cnt = 1;
+ switch (kbd->data[0] & HIL_POL_CHARTYPE_MASK) {
+ case HIL_POL_CHARTYPE_NONE:
+ break;
+ case HIL_POL_CHARTYPE_ASCII:
+ while (cnt < idx - 1)
+ input_report_key(dev, kbd->data[cnt++] & 0x7f, 1);
+ break;
+ case HIL_POL_CHARTYPE_RSVD1:
+ case HIL_POL_CHARTYPE_RSVD2:
+ case HIL_POL_CHARTYPE_BINARY:
+ while (cnt < idx - 1)
+ input_report_key(dev, kbd->data[cnt++], 1);
+ break;
+ case HIL_POL_CHARTYPE_SET1:
+ while (cnt < idx - 1) {
+ unsigned int key;
+ int up;
+ key = kbd->data[cnt++];
+ up = key & HIL_KBD_SET1_UPBIT;
+ key &= (~HIL_KBD_SET1_UPBIT & 0xff);
+ key = hil_kbd_set1[key >> HIL_KBD_SET1_SHIFT];
+ if (key != KEY_RESERVED)
+ input_report_key(dev, key, !up);
+ }
+ break;
+ case HIL_POL_CHARTYPE_SET2:
+ while (cnt < idx - 1) {
+ unsigned int key;
+ int up;
+ key = kbd->data[cnt++];
+ up = key & HIL_KBD_SET2_UPBIT;
+ key &= (~HIL_KBD_SET1_UPBIT & 0xff);
+ key = key >> HIL_KBD_SET2_SHIFT;
+ if (key != KEY_RESERVED)
+ input_report_key(dev, key, !up);
+ }
+ break;
+ case HIL_POL_CHARTYPE_SET3:
+ while (cnt < idx - 1) {
+ unsigned int key;
+ int up;
+ key = kbd->data[cnt++];
+ up = key & HIL_KBD_SET3_UPBIT;
+ key &= (~HIL_KBD_SET1_UPBIT & 0xff);
+ key = hil_kbd_set3[key >> HIL_KBD_SET3_SHIFT];
+ if (key != KEY_RESERVED)
+ input_report_key(dev, key, !up);
+ }
+ break;
+ }
+ out:
+ kbd->idx4 = 0;
+ up(&kbd->sem);
+}
+
+static void hil_kbd_process_err(struct hil_kbd *kbd) {
+ printk(KERN_WARNING PREFIX "errored HIL packet\n");
+ kbd->idx4 = 0;
+ up(&kbd->sem);
+}
+
+static irqreturn_t hil_kbd_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct hil_kbd *kbd;
+ hil_packet packet;
+ int idx;
+
+ kbd = (struct hil_kbd *)serio->private;
+ if (kbd == NULL) {
+ BUG();
+ return IRQ_HANDLED;
+ }
+
+ if (kbd->idx4 >= (HIL_KBD_MAX_LENGTH * sizeof(hil_packet))) {
+ hil_kbd_process_err(kbd);
+ return IRQ_HANDLED;
+ }
+ idx = kbd->idx4/4;
+ if (!(kbd->idx4 % 4)) kbd->data[idx] = 0;
+ packet = kbd->data[idx];
+ packet |= ((hil_packet)data) << ((3 - (kbd->idx4 % 4)) * 8);
+ kbd->data[idx] = packet;
+
+ /* Records of N 4-byte hil_packets must terminate with a command. */
+ if ((++(kbd->idx4)) % 4) return IRQ_HANDLED;
+ if ((packet & 0xffff0000) != HIL_ERR_INT) {
+ hil_kbd_process_err(kbd);
+ return IRQ_HANDLED;
+ }
+ if (packet & HIL_PKT_CMD) hil_kbd_process_record(kbd);
+ return IRQ_HANDLED;
+}
+
+static void hil_kbd_disconnect(struct serio *serio)
+{
+ struct hil_kbd *kbd;
+
+ kbd = (struct hil_kbd *)serio->private;
+ if (kbd == NULL) {
+ BUG();
+ return;
+ }
+
+ input_unregister_device(&kbd->dev);
+ serio_close(serio);
+ kfree(kbd);
+}
+
+static void hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct hil_kbd *kbd;
+ uint8_t did, *idd;
+ int i;
+
+ if (serio->type != (SERIO_HIL_MLC | SERIO_HIL)) return;
+
+ if (!(kbd = kmalloc(sizeof(struct hil_kbd), GFP_KERNEL))) return;
+ memset(kbd, 0, sizeof(struct hil_kbd));
+
+ if (serio_open(serio, drv)) goto bail0;
+
+ serio->private = kbd;
+ kbd->serio = serio;
+ kbd->dev.private = kbd;
+
+ init_MUTEX_LOCKED(&(kbd->sem));
+
+ /* Get device info. MLC driver supplies devid/status/etc. */
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_IDD);
+ down(&(kbd->sem));
+
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_RSC);
+ down(&(kbd->sem));
+
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_RNM);
+ down(&(kbd->sem));
+
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_EXD);
+ down(&(kbd->sem));
+
+ up(&(kbd->sem));
+
+ did = kbd->idd[0];
+ idd = kbd->idd + 1;
+ switch (did & HIL_IDD_DID_TYPE_MASK) {
+ case HIL_IDD_DID_TYPE_KB_INTEGRAL:
+ case HIL_IDD_DID_TYPE_KB_ITF:
+ case HIL_IDD_DID_TYPE_KB_RSVD:
+ case HIL_IDD_DID_TYPE_CHAR:
+ printk(KERN_INFO PREFIX "HIL keyboard found (did = 0x%02x, lang = %s)\n",
+ did, hil_language[did & HIL_IDD_DID_TYPE_KB_LANG_MASK]);
+ break;
+ default:
+ goto bail1;
+ }
+
+ if(HIL_IDD_NUM_BUTTONS(idd) || HIL_IDD_NUM_AXES_PER_SET(*idd)) {
+ printk(KERN_INFO PREFIX "keyboards only, no combo devices supported.\n");
+ goto bail1;
+ }
+
+
+ kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ kbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
+ kbd->dev.keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
+ kbd->dev.keycodesize = sizeof(hil_kbd_set1[0]);
+ kbd->dev.keycode = hil_kbd_set1;
+ kbd->dev.name = strlen(kbd->rnm) ? kbd->rnm : HIL_GENERIC_NAME;
+ kbd->dev.phys = "hpkbd/input0"; /* XXX */
+
+ kbd->dev.id.bustype = BUS_HIL;
+ kbd->dev.id.vendor = PCI_VENDOR_ID_HP;
+ kbd->dev.id.product = 0x0001; /* TODO: get from kbd->rsc */
+ kbd->dev.id.version = 0x0100; /* TODO: get from kbd->rsc */
+ kbd->dev.dev = &serio->dev;
+
+ for (i = 0; i < 128; i++) {
+ set_bit(hil_kbd_set1[i], kbd->dev.keybit);
+ set_bit(hil_kbd_set3[i], kbd->dev.keybit);
+ }
+ clear_bit(0, kbd->dev.keybit);
+
+ input_register_device(&kbd->dev);
+ printk(KERN_INFO "input: %s, ID: %d\n",
+ kbd->dev.name, did);
+
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_EK1); /* Enable Keyswitch Autorepeat 1 */
+ down(&(kbd->sem));
+ up(&(kbd->sem));
+
+ return;
+ bail1:
+ serio_close(serio);
+ bail0:
+ kfree(kbd);
+}
+
+
+struct serio_driver hil_kbd_serio_drv = {
+ .driver = {
+ .name = "hil_kbd",
+ },
+ .description = "HP HIL keyboard driver",
+ .connect = hil_kbd_connect,
+ .disconnect = hil_kbd_disconnect,
+ .interrupt = hil_kbd_interrupt
+};
+
+static int __init hil_kbd_init(void)
+{
+ serio_register_driver(&hil_kbd_serio_drv);
+ return 0;
+}
+
+static void __exit hil_kbd_exit(void)
+{
+ serio_unregister_driver(&hil_kbd_serio_drv);
+}
+
+module_init(hil_kbd_init);
+module_exit(hil_kbd_exit);
diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c
new file mode 100644
index 000000000000..eecb77db0847
--- /dev/null
+++ b/drivers/input/keyboard/hilkbd.c
@@ -0,0 +1,343 @@
+/*
+ * linux/drivers/hil/hilkbd.c
+ *
+ * Copyright (C) 1998 Philip Blundell <philb@gnu.org>
+ * Copyright (C) 1999 Matthew Wilcox <willy@bofh.ai>
+ * Copyright (C) 1999-2003 Helge Deller <deller@gmx.de>
+ *
+ * Very basic HP Human Interface Loop (HIL) driver.
+ * This driver handles the keyboard on HP300 (m68k) and on some
+ * HP700 (parisc) series machines.
+ *
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2. See the file COPYING in the main directory of this
+ * archive for more details.
+ */
+
+#include <linux/pci_ids.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/hil.h>
+#include <linux/spinlock.h>
+
+
+MODULE_AUTHOR("Philip Blundell, Matthew Wilcox, Helge Deller");
+MODULE_DESCRIPTION("HIL keyboard driver (basic functionality)");
+MODULE_LICENSE("GPL v2");
+
+
+#if defined(CONFIG_PARISC)
+
+ #include <asm/io.h>
+ #include <asm/hardware.h>
+ #include <asm/parisc-device.h>
+ static unsigned long hil_base; /* HPA for the HIL device */
+ static unsigned int hil_irq;
+ #define HILBASE hil_base /* HPPA (parisc) port address */
+ #define HIL_DATA 0x800
+ #define HIL_CMD 0x801
+ #define HIL_IRQ hil_irq
+ #define hil_readb(p) gsc_readb(p)
+ #define hil_writeb(v,p) gsc_writeb((v),(p))
+
+#elif defined(CONFIG_HP300)
+
+ #define HILBASE 0xf0428000 /* HP300 (m86k) port address */
+ #define HIL_DATA 0x1
+ #define HIL_CMD 0x3
+ #define HIL_IRQ 2
+ #define hil_readb(p) readb(p)
+ #define hil_writeb(v,p) writeb((v),(p))
+
+#else
+#error "HIL is not supported on this platform"
+#endif
+
+
+
+/* HIL helper functions */
+
+#define hil_busy() (hil_readb(HILBASE + HIL_CMD) & HIL_BUSY)
+#define hil_data_available() (hil_readb(HILBASE + HIL_CMD) & HIL_DATA_RDY)
+#define hil_status() (hil_readb(HILBASE + HIL_CMD))
+#define hil_command(x) do { hil_writeb((x), HILBASE + HIL_CMD); } while (0)
+#define hil_read_data() (hil_readb(HILBASE + HIL_DATA))
+#define hil_write_data(x) do { hil_writeb((x), HILBASE + HIL_DATA); } while (0)
+
+/* HIL constants */
+
+#define HIL_BUSY 0x02
+#define HIL_DATA_RDY 0x01
+
+#define HIL_SETARD 0xA0 /* set auto-repeat delay */
+#define HIL_SETARR 0xA2 /* set auto-repeat rate */
+#define HIL_SETTONE 0xA3 /* set tone generator */
+#define HIL_CNMT 0xB2 /* clear nmi */
+#define HIL_INTON 0x5C /* Turn on interrupts. */
+#define HIL_INTOFF 0x5D /* Turn off interrupts. */
+
+#define HIL_READKBDSADR 0xF9
+#define HIL_WRITEKBDSADR 0xE9
+
+static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] =
+ { HIL_KEYCODES_SET1 };
+
+/* HIL structure */
+static struct {
+ struct input_dev dev;
+
+ unsigned int curdev;
+
+ unsigned char s;
+ unsigned char c;
+ int valid;
+
+ unsigned char data[16];
+ unsigned int ptr;
+ spinlock_t lock;
+
+ void *dev_id; /* native bus device */
+} hil_dev;
+
+
+static void poll_finished(void)
+{
+ int down;
+ int key;
+ unsigned char scode;
+
+ switch (hil_dev.data[0]) {
+ case 0x40:
+ down = (hil_dev.data[1] & 1) == 0;
+ scode = hil_dev.data[1] >> 1;
+ key = hphilkeyb_keycode[scode];
+ input_report_key(&hil_dev.dev, key, down);
+ break;
+ }
+ hil_dev.curdev = 0;
+}
+
+static inline void handle_status(unsigned char s, unsigned char c)
+{
+ if (c & 0x8) {
+ /* End of block */
+ if (c & 0x10)
+ poll_finished();
+ } else {
+ if (c & 0x10) {
+ if (hil_dev.curdev)
+ poll_finished(); /* just in case */
+ hil_dev.curdev = c & 7;
+ hil_dev.ptr = 0;
+ }
+ }
+}
+
+static inline void handle_data(unsigned char s, unsigned char c)
+{
+ if (hil_dev.curdev) {
+ hil_dev.data[hil_dev.ptr++] = c;
+ hil_dev.ptr &= 15;
+ }
+}
+
+
+/*
+ * Handle HIL interrupts.
+ */
+static irqreturn_t hil_interrupt(int irq, void *handle, struct pt_regs *regs)
+{
+ unsigned char s, c;
+
+ s = hil_status();
+ c = hil_read_data();
+
+ switch (s >> 4) {
+ case 0x5:
+ handle_status(s, c);
+ break;
+ case 0x6:
+ handle_data(s, c);
+ break;
+ case 0x4:
+ hil_dev.s = s;
+ hil_dev.c = c;
+ mb();
+ hil_dev.valid = 1;
+ break;
+ }
+ return IRQ_HANDLED;
+}
+
+/*
+ * Send a command to the HIL
+ */
+
+static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hil_dev.lock, flags);
+ while (hil_busy())
+ /* wait */;
+ hil_command(cmd);
+ while (len--) {
+ while (hil_busy())
+ /* wait */;
+ hil_write_data(*(data++));
+ }
+ spin_unlock_irqrestore(&hil_dev.lock, flags);
+}
+
+
+/*
+ * Initialise HIL.
+ */
+
+static int __init
+hil_keyb_init(void)
+{
+ unsigned char c;
+ unsigned int i, kbid;
+ wait_queue_head_t hil_wait;
+
+ if (hil_dev.dev.id.bustype) {
+ return -ENODEV; /* already initialized */
+ }
+
+#if defined(CONFIG_HP300)
+ if (!hwreg_present((void *)(HILBASE + HIL_DATA)))
+ return -ENODEV;
+
+ request_region(HILBASE+HIL_DATA, 2, "hil");
+#endif
+
+ request_irq(HIL_IRQ, hil_interrupt, 0, "hil", hil_dev.dev_id);
+
+ /* Turn on interrupts */
+ hil_do(HIL_INTON, NULL, 0);
+
+ /* Look for keyboards */
+ hil_dev.valid = 0; /* clear any pending data */
+ hil_do(HIL_READKBDSADR, NULL, 0);
+
+ init_waitqueue_head(&hil_wait);
+ wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3*HZ);
+ if (!hil_dev.valid) {
+ printk(KERN_WARNING "HIL: timed out, assuming no keyboard present.\n");
+ }
+
+ c = hil_dev.c;
+ hil_dev.valid = 0;
+ if (c == 0) {
+ kbid = -1;
+ printk(KERN_WARNING "HIL: no keyboard present.\n");
+ } else {
+ kbid = ffz(~c);
+ /* printk(KERN_INFO "HIL: keyboard found at id %d\n", kbid); */
+ }
+
+ /* set it to raw mode */
+ c = 0;
+ hil_do(HIL_WRITEKBDSADR, &c, 1);
+
+ init_input_dev(&hil_dev.dev);
+
+ for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++)
+ if (hphilkeyb_keycode[i] != KEY_RESERVED)
+ set_bit(hphilkeyb_keycode[i], hil_dev.dev.keybit);
+
+ hil_dev.dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ hil_dev.dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
+ hil_dev.dev.keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
+ hil_dev.dev.keycodesize = sizeof(hphilkeyb_keycode[0]);
+ hil_dev.dev.keycode = hphilkeyb_keycode;
+ hil_dev.dev.name = "HIL keyboard";
+ hil_dev.dev.phys = "hpkbd/input0";
+
+ hil_dev.dev.id.bustype = BUS_HIL;
+ hil_dev.dev.id.vendor = PCI_VENDOR_ID_HP;
+ hil_dev.dev.id.product = 0x0001;
+ hil_dev.dev.id.version = 0x0010;
+
+ input_register_device(&hil_dev.dev);
+ printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n",
+ hil_dev.dev.name, kbid, HILBASE, HIL_IRQ);
+
+ return 0;
+}
+
+#if defined(CONFIG_PARISC)
+static int __init
+hil_init_chip(struct parisc_device *dev)
+{
+ if (!dev->irq) {
+ printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%08lx\n", dev->hpa);
+ return -ENODEV;
+ }
+
+ hil_base = dev->hpa;
+ hil_irq = dev->irq;
+ hil_dev.dev_id = dev;
+
+ printk(KERN_INFO "Found HIL bus at 0x%08lx, IRQ %d\n", hil_base, hil_irq);
+
+ return hil_keyb_init();
+}
+
+static struct parisc_device_id hil_tbl[] = {
+ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00073 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(parisc, hil_tbl);
+
+static struct parisc_driver hil_driver = {
+ .name = "HIL",
+ .id_table = hil_tbl,
+ .probe = hil_init_chip,
+};
+#endif /* CONFIG_PARISC */
+
+
+
+
+
+static int __init hil_init(void)
+{
+#if defined(CONFIG_PARISC)
+ return register_parisc_driver(&hil_driver);
+#else
+ return hil_keyb_init();
+#endif
+}
+
+
+static void __exit hil_exit(void)
+{
+ if (HIL_IRQ) {
+ disable_irq(HIL_IRQ);
+ free_irq(HIL_IRQ, hil_dev.dev_id);
+ }
+
+ /* Turn off interrupts */
+ hil_do(HIL_INTOFF, NULL, 0);
+
+ input_unregister_device(&hil_dev.dev);
+
+#if defined(CONFIG_PARISC)
+ unregister_parisc_driver(&hil_driver);
+#else
+ release_region(HILBASE+HIL_DATA, 2);
+#endif
+}
+
+module_init(hil_init);
+module_exit(hil_exit);
+
diff --git a/drivers/input/keyboard/hpps2atkbd.h b/drivers/input/keyboard/hpps2atkbd.h
new file mode 100644
index 000000000000..dc33f6945222
--- /dev/null
+++ b/drivers/input/keyboard/hpps2atkbd.h
@@ -0,0 +1,110 @@
+/*
+ * drivers/input/keyboard/hpps2atkbd.h
+ *
+ * Copyright (c) 2004 Helge Deller <deller@gmx.de>
+ * Copyright (c) 2002 Laurent Canet <canetl@esiee.fr>
+ * Copyright (c) 2002 Thibaut Varene <varenet@parisc-linux.org>
+ * Copyright (c) 2000 Xavier Debacker <debackex@esiee.fr>
+ *
+ * HP PS/2 AT-compatible Keyboard, found in PA/RISC Workstations & Laptops
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+
+/* Is the keyboard an RDI PrecisionBook? */
+#ifndef CONFIG_KEYBOARD_ATKBD_RDI_KEYCODES
+# define CONFLICT(x,y) x
+#else
+# define CONFLICT(x,y) y
+#endif
+
+/* sadly RDI (Tadpole) decided to ship a different keyboard layout
+ than HP for their PS/2 laptop keyboard which leads to conflicting
+ keycodes between a normal HP PS/2 keyboard and a RDI Precisionbook.
+ HP: RDI: */
+#define C_07 CONFLICT( KEY_F12, KEY_F1 )
+#define C_11 CONFLICT( KEY_LEFTALT, KEY_LEFTCTRL )
+#define C_14 CONFLICT( KEY_LEFTCTRL, KEY_CAPSLOCK )
+#define C_58 CONFLICT( KEY_CAPSLOCK, KEY_RIGHTCTRL )
+#define C_61 CONFLICT( KEY_102ND, KEY_LEFT )
+
+/* Raw SET 2 scancode table */
+
+/* 00 */ KEY_RESERVED, KEY_F9, KEY_RESERVED, KEY_F5, KEY_F3, KEY_F1, KEY_F2, C_07,
+/* 08 */ KEY_ESC, KEY_F10, KEY_F8, KEY_F6, KEY_F4, KEY_TAB, KEY_GRAVE, KEY_F2,
+/* 10 */ KEY_RESERVED, C_11, KEY_LEFTSHIFT, KEY_RESERVED, C_14, KEY_Q, KEY_1, KEY_F3,
+/* 18 */ KEY_RESERVED, KEY_LEFTALT, KEY_Z, KEY_S, KEY_A, KEY_W, KEY_2, KEY_F4,
+/* 20 */ KEY_RESERVED, KEY_C, KEY_X, KEY_D, KEY_E, KEY_4, KEY_3, KEY_F5,
+/* 28 */ KEY_RESERVED, KEY_SPACE, KEY_V, KEY_F, KEY_T, KEY_R, KEY_5, KEY_F6,
+/* 30 */ KEY_RESERVED, KEY_N, KEY_B, KEY_H, KEY_G, KEY_Y, KEY_6, KEY_F7,
+/* 38 */ KEY_RESERVED, KEY_RIGHTALT, KEY_M, KEY_J, KEY_U, KEY_7, KEY_8, KEY_F8,
+/* 40 */ KEY_RESERVED, KEY_COMMA, KEY_K, KEY_I, KEY_O, KEY_0, KEY_9, KEY_F9,
+/* 48 */ KEY_RESERVED, KEY_DOT, KEY_SLASH, KEY_L, KEY_SEMICOLON, KEY_P, KEY_MINUS, KEY_F10,
+/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_APOSTROPHE,KEY_RESERVED, KEY_LEFTBRACE, KEY_EQUAL, KEY_F11, KEY_SYSRQ,
+/* 58 */ C_58, KEY_RIGHTSHIFT,KEY_ENTER, KEY_RIGHTBRACE,KEY_BACKSLASH, KEY_BACKSLASH,KEY_F12, KEY_SCROLLLOCK,
+/* 60 */ KEY_DOWN, C_61, KEY_PAUSE, KEY_UP, KEY_DELETE, KEY_END, KEY_BACKSPACE, KEY_INSERT,
+/* 68 */ KEY_RESERVED, KEY_KP1, KEY_RIGHT, KEY_KP4, KEY_KP7, KEY_PAGEDOWN, KEY_HOME, KEY_PAGEUP,
+/* 70 */ KEY_KP0, KEY_KPDOT, KEY_KP2, KEY_KP5, KEY_KP6, KEY_KP8, KEY_ESC, KEY_NUMLOCK,
+/* 78 */ KEY_F11, KEY_KPPLUS, KEY_KP3, KEY_KPMINUS, KEY_KPASTERISK,KEY_KP9, KEY_SCROLLLOCK,KEY_102ND,
+/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 90 */ KEY_RESERVED, KEY_RIGHTALT, 255, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_CAPSLOCK, KEY_RESERVED, KEY_LEFTMETA,
+/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTMETA,
+/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_COMPOSE,
+/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPSLASH, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPENTER, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* e8 */ KEY_RESERVED, KEY_END, KEY_RESERVED, KEY_LEFT, KEY_HOME, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* f0 */ KEY_INSERT, KEY_DELETE, KEY_DOWN, KEY_RESERVED, KEY_RIGHT, KEY_UP, KEY_RESERVED, KEY_PAUSE,
+/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_PAGEDOWN, KEY_RESERVED, KEY_SYSRQ, KEY_PAGEUP, KEY_RESERVED, KEY_RESERVED,
+
+/* These are offset for escaped keycodes: */
+
+/* 00 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_F7, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 08 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 10 */ KEY_RESERVED, KEY_RIGHTALT, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 18 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 20 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 28 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 30 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 38 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 40 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 48 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 58 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 60 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 68 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 70 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 78 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 90 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* e8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* f0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED
+
+#undef CONFLICT
+#undef C_07
+#undef C_11
+#undef C_14
+#undef C_58
+#undef C_61
+
diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c
new file mode 100644
index 000000000000..2694ff2b5beb
--- /dev/null
+++ b/drivers/input/keyboard/lkkbd.c
@@ -0,0 +1,752 @@
+/*
+ * Copyright (C) 2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
+ */
+
+/*
+ * LK keyboard driver for Linux, based on sunkbd.c (C) by Vojtech Pavlik
+ */
+
+/*
+ * DEC LK201 and LK401 keyboard driver for Linux (primary for DECstations
+ * and VAXstations, but can also be used on any standard RS232 with an
+ * adaptor).
+ *
+ * DISCLAIMER: This works for _me_. If you break anything by using the
+ * information given below, I will _not_ be liable!
+ *
+ * RJ10 pinout: To DE9: Or DB25:
+ * 1 - RxD <----> Pin 3 (TxD) <-> Pin 2 (TxD)
+ * 2 - GND <----> Pin 5 (GND) <-> Pin 7 (GND)
+ * 4 - TxD <----> Pin 2 (RxD) <-> Pin 3 (RxD)
+ * 3 - +12V (from HDD drive connector), DON'T connect to DE9 or DB25!!!
+ *
+ * Pin numbers for DE9 and DB25 are noted on the plug (quite small:). For
+ * RJ10, it's like this:
+ *
+ * __=__ Hold the plug in front of you, cable downwards,
+ * /___/| nose is hidden behind the plug. Now, pin 1 is at
+ * |1234|| the left side, pin 4 at the right and 2 and 3 are
+ * |IIII|| in between, of course:)
+ * | ||
+ * |____|/
+ * || So the adaptor consists of three connected cables
+ * || for data transmission (RxD and TxD) and signal ground.
+ * Additionally, you have to get +12V from somewhere.
+ * Most easily, you'll get that from a floppy or HDD power connector.
+ * It's the yellow cable there (black is ground and red is +5V).
+ *
+ * The keyboard and all the commands it understands are documented in
+ * "VCB02 Video Subsystem - Technical Manual", EK-104AA-TM-001. This
+ * document is LK201 specific, but LK401 is mostly compatible. It comes
+ * up in LK201 mode and doesn't report any of the additional keys it
+ * has. These need to be switched on with the LK_CMD_ENABLE_LK401
+ * command. You'll find this document (scanned .pdf file) on MANX,
+ * a search engine specific to DEC documentation. Try
+ * http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
+ */
+
+/*
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * email or by paper mail:
+ * Jan-Benedict Glaw, Lilienstraße 16, 33790 Hörste (near Halle/Westf.),
+ * Germany.
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/workqueue.h>
+
+#define DRIVER_DESC "LK keyboard driver"
+
+MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_LICENSE ("GPL");
+
+/*
+ * Known parameters:
+ * bell_volume
+ * keyclick_volume
+ * ctrlclick_volume
+ *
+ * Please notice that there's not yet an API to set these at runtime.
+ */
+static int bell_volume = 100; /* % */
+module_param (bell_volume, int, 0);
+MODULE_PARM_DESC (bell_volume, "Bell volume (in %). default is 100%");
+
+static int keyclick_volume = 100; /* % */
+module_param (keyclick_volume, int, 0);
+MODULE_PARM_DESC (keyclick_volume, "Keyclick volume (in %), default is 100%");
+
+static int ctrlclick_volume = 100; /* % */
+module_param (ctrlclick_volume, int, 0);
+MODULE_PARM_DESC (ctrlclick_volume, "Ctrlclick volume (in %), default is 100%");
+
+static int lk201_compose_is_alt = 0;
+module_param (lk201_compose_is_alt, int, 0);
+MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key "
+ "will act as an Alt key");
+
+
+
+#undef LKKBD_DEBUG
+#ifdef LKKBD_DEBUG
+#define DBG(x...) printk (x)
+#else
+#define DBG(x...) do {} while (0)
+#endif
+
+/* LED control */
+#define LK_LED_WAIT 0x81
+#define LK_LED_COMPOSE 0x82
+#define LK_LED_SHIFTLOCK 0x84
+#define LK_LED_SCROLLLOCK 0x88
+#define LK_CMD_LED_ON 0x13
+#define LK_CMD_LED_OFF 0x11
+
+/* Mode control */
+#define LK_MODE_DOWN 0x80
+#define LK_MODE_AUTODOWN 0x82
+#define LK_MODE_UPDOWN 0x86
+#define LK_CMD_SET_MODE(mode,div) ((mode) | ((div) << 3))
+
+/* Misc commands */
+#define LK_CMD_ENABLE_KEYCLICK 0x1b
+#define LK_CMD_DISABLE_KEYCLICK 0x99
+#define LK_CMD_DISABLE_BELL 0xa1
+#define LK_CMD_SOUND_BELL 0xa7
+#define LK_CMD_ENABLE_BELL 0x23
+#define LK_CMD_DISABLE_CTRCLICK 0xb9
+#define LK_CMD_ENABLE_CTRCLICK 0xbb
+#define LK_CMD_SET_DEFAULTS 0xd3
+#define LK_CMD_POWERCYCLE_RESET 0xfd
+#define LK_CMD_ENABLE_LK401 0xe9
+#define LK_CMD_REQUEST_ID 0xab
+
+/* Misc responses from keyboard */
+#define LK_STUCK_KEY 0x3d
+#define LK_SELFTEST_FAILED 0x3e
+#define LK_ALL_KEYS_UP 0xb3
+#define LK_METRONOME 0xb4
+#define LK_OUTPUT_ERROR 0xb5
+#define LK_INPUT_ERROR 0xb6
+#define LK_KBD_LOCKED 0xb7
+#define LK_KBD_TEST_MODE_ACK 0xb8
+#define LK_PREFIX_KEY_DOWN 0xb9
+#define LK_MODE_CHANGE_ACK 0xba
+#define LK_RESPONSE_RESERVED 0xbb
+
+#define LK_NUM_KEYCODES 256
+#define LK_NUM_IGNORE_BYTES 6
+typedef u_int16_t lk_keycode_t;
+
+
+
+static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = {
+ [0x56] = KEY_F1,
+ [0x57] = KEY_F2,
+ [0x58] = KEY_F3,
+ [0x59] = KEY_F4,
+ [0x5a] = KEY_F5,
+ [0x64] = KEY_F6,
+ [0x65] = KEY_F7,
+ [0x66] = KEY_F8,
+ [0x67] = KEY_F9,
+ [0x68] = KEY_F10,
+ [0x71] = KEY_F11,
+ [0x72] = KEY_F12,
+ [0x73] = KEY_F13,
+ [0x74] = KEY_F14,
+ [0x7c] = KEY_F15,
+ [0x7d] = KEY_F16,
+ [0x80] = KEY_F17,
+ [0x81] = KEY_F18,
+ [0x82] = KEY_F19,
+ [0x83] = KEY_F20,
+ [0x8a] = KEY_FIND,
+ [0x8b] = KEY_INSERT,
+ [0x8c] = KEY_DELETE,
+ [0x8d] = KEY_SELECT,
+ [0x8e] = KEY_PAGEUP,
+ [0x8f] = KEY_PAGEDOWN,
+ [0x92] = KEY_KP0,
+ [0x94] = KEY_KPDOT,
+ [0x95] = KEY_KPENTER,
+ [0x96] = KEY_KP1,
+ [0x97] = KEY_KP2,
+ [0x98] = KEY_KP3,
+ [0x99] = KEY_KP4,
+ [0x9a] = KEY_KP5,
+ [0x9b] = KEY_KP6,
+ [0x9c] = KEY_KPCOMMA,
+ [0x9d] = KEY_KP7,
+ [0x9e] = KEY_KP8,
+ [0x9f] = KEY_KP9,
+ [0xa0] = KEY_KPMINUS,
+ [0xa1] = KEY_PROG1,
+ [0xa2] = KEY_PROG2,
+ [0xa3] = KEY_PROG3,
+ [0xa4] = KEY_PROG4,
+ [0xa7] = KEY_LEFT,
+ [0xa8] = KEY_RIGHT,
+ [0xa9] = KEY_DOWN,
+ [0xaa] = KEY_UP,
+ [0xab] = KEY_RIGHTSHIFT,
+ [0xac] = KEY_LEFTALT,
+ [0xad] = KEY_COMPOSE, /* Right Compose, that is. */
+ [0xae] = KEY_LEFTSHIFT, /* Same as KEY_RIGHTSHIFT on LK201 */
+ [0xaf] = KEY_LEFTCTRL,
+ [0xb0] = KEY_CAPSLOCK,
+ [0xb1] = KEY_COMPOSE, /* Left Compose, that is. */
+ [0xb2] = KEY_RIGHTALT,
+ [0xbc] = KEY_BACKSPACE,
+ [0xbd] = KEY_ENTER,
+ [0xbe] = KEY_TAB,
+ [0xbf] = KEY_ESC,
+ [0xc0] = KEY_1,
+ [0xc1] = KEY_Q,
+ [0xc2] = KEY_A,
+ [0xc3] = KEY_Z,
+ [0xc5] = KEY_2,
+ [0xc6] = KEY_W,
+ [0xc7] = KEY_S,
+ [0xc8] = KEY_X,
+ [0xc9] = KEY_102ND,
+ [0xcb] = KEY_3,
+ [0xcc] = KEY_E,
+ [0xcd] = KEY_D,
+ [0xce] = KEY_C,
+ [0xd0] = KEY_4,
+ [0xd1] = KEY_R,
+ [0xd2] = KEY_F,
+ [0xd3] = KEY_V,
+ [0xd4] = KEY_SPACE,
+ [0xd6] = KEY_5,
+ [0xd7] = KEY_T,
+ [0xd8] = KEY_G,
+ [0xd9] = KEY_B,
+ [0xdb] = KEY_6,
+ [0xdc] = KEY_Y,
+ [0xdd] = KEY_H,
+ [0xde] = KEY_N,
+ [0xe0] = KEY_7,
+ [0xe1] = KEY_U,
+ [0xe2] = KEY_J,
+ [0xe3] = KEY_M,
+ [0xe5] = KEY_8,
+ [0xe6] = KEY_I,
+ [0xe7] = KEY_K,
+ [0xe8] = KEY_COMMA,
+ [0xea] = KEY_9,
+ [0xeb] = KEY_O,
+ [0xec] = KEY_L,
+ [0xed] = KEY_DOT,
+ [0xef] = KEY_0,
+ [0xf0] = KEY_P,
+ [0xf2] = KEY_SEMICOLON,
+ [0xf3] = KEY_SLASH,
+ [0xf5] = KEY_EQUAL,
+ [0xf6] = KEY_RIGHTBRACE,
+ [0xf7] = KEY_BACKSLASH,
+ [0xf9] = KEY_MINUS,
+ [0xfa] = KEY_LEFTBRACE,
+ [0xfb] = KEY_APOSTROPHE,
+};
+
+#define CHECK_LED(LED, BITS) do { \
+ if (test_bit (LED, lk->dev.led)) \
+ leds_on |= BITS; \
+ else \
+ leds_off |= BITS; \
+ } while (0)
+
+/*
+ * Per-keyboard data
+ */
+struct lkkbd {
+ lk_keycode_t keycode[LK_NUM_KEYCODES];
+ int ignore_bytes;
+ unsigned char id[LK_NUM_IGNORE_BYTES];
+ struct input_dev dev;
+ struct serio *serio;
+ struct work_struct tq;
+ char name[64];
+ char phys[32];
+ char type;
+ int bell_volume;
+ int keyclick_volume;
+ int ctrlclick_volume;
+};
+
+/*
+ * Calculate volume parameter byte for a given volume.
+ */
+static unsigned char
+volume_to_hw (int volume_percent)
+{
+ unsigned char ret = 0;
+
+ if (volume_percent < 0)
+ volume_percent = 0;
+ if (volume_percent > 100)
+ volume_percent = 100;
+
+ if (volume_percent >= 0)
+ ret = 7;
+ if (volume_percent >= 13) /* 12.5 */
+ ret = 6;
+ if (volume_percent >= 25)
+ ret = 5;
+ if (volume_percent >= 38) /* 37.5 */
+ ret = 4;
+ if (volume_percent >= 50)
+ ret = 3;
+ if (volume_percent >= 63) /* 62.5 */
+ ret = 2; /* This is the default volume */
+ if (volume_percent >= 75)
+ ret = 1;
+ if (volume_percent >= 88) /* 87.5 */
+ ret = 0;
+
+ ret |= 0x80;
+
+ return ret;
+}
+
+static void
+lkkbd_detection_done (struct lkkbd *lk)
+{
+ int i;
+
+ /*
+ * Reset setting for Compose key. Let Compose be KEY_COMPOSE.
+ */
+ lk->keycode[0xb1] = KEY_COMPOSE;
+
+ /*
+ * Print keyboard name and modify Compose=Alt on user's request.
+ */
+ switch (lk->id[4]) {
+ case 1:
+ sprintf (lk->name, "DEC LK201 keyboard");
+
+ if (lk201_compose_is_alt)
+ lk->keycode[0xb1] = KEY_LEFTALT;
+ break;
+
+ case 2:
+ sprintf (lk->name, "DEC LK401 keyboard");
+ break;
+
+ default:
+ sprintf (lk->name, "Unknown DEC keyboard");
+ printk (KERN_ERR "lkkbd: keyboard on %s is unknown, "
+ "please report to Jan-Benedict Glaw "
+ "<jbglaw@lug-owl.de>\n", lk->phys);
+ printk (KERN_ERR "lkkbd: keyboard ID'ed as:");
+ for (i = 0; i < LK_NUM_IGNORE_BYTES; i++)
+ printk (" 0x%02x", lk->id[i]);
+ printk ("\n");
+ break;
+ }
+ printk (KERN_INFO "lkkbd: keyboard on %s identified as: %s\n",
+ lk->phys, lk->name);
+
+ /*
+ * Report errors during keyboard boot-up.
+ */
+ switch (lk->id[2]) {
+ case 0x00:
+ /* All okay */
+ break;
+
+ case LK_STUCK_KEY:
+ printk (KERN_ERR "lkkbd: Stuck key on keyboard at "
+ "%s\n", lk->phys);
+ break;
+
+ case LK_SELFTEST_FAILED:
+ printk (KERN_ERR "lkkbd: Selftest failed on keyboard "
+ "at %s, keyboard may not work "
+ "properly\n", lk->phys);
+ break;
+
+ default:
+ printk (KERN_ERR "lkkbd: Unknown error %02x on "
+ "keyboard at %s\n", lk->id[2],
+ lk->phys);
+ break;
+ }
+
+ /*
+ * Try to hint user if there's a stuck key.
+ */
+ if (lk->id[2] == LK_STUCK_KEY && lk->id[3] != 0)
+ printk (KERN_ERR "Scancode of stuck key is 0x%02x, keycode "
+ "is 0x%04x\n", lk->id[3],
+ lk->keycode[lk->id[3]]);
+
+ return;
+}
+
+/*
+ * lkkbd_interrupt() is called by the low level driver when a character
+ * is received.
+ */
+static irqreturn_t
+lkkbd_interrupt (struct serio *serio, unsigned char data, unsigned int flags,
+ struct pt_regs *regs)
+{
+ struct lkkbd *lk = serio_get_drvdata (serio);
+ int i;
+
+ DBG (KERN_INFO "Got byte 0x%02x\n", data);
+
+ if (lk->ignore_bytes > 0) {
+ DBG (KERN_INFO "Ignoring a byte on %s\n",
+ lk->name);
+ lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
+
+ if (lk->ignore_bytes == 0)
+ lkkbd_detection_done (lk);
+
+ return IRQ_HANDLED;
+ }
+
+ switch (data) {
+ case LK_ALL_KEYS_UP:
+ input_regs (&lk->dev, regs);
+ for (i = 0; i < ARRAY_SIZE (lkkbd_keycode); i++)
+ if (lk->keycode[i] != KEY_RESERVED)
+ input_report_key (&lk->dev, lk->keycode[i], 0);
+ input_sync (&lk->dev);
+ break;
+ case LK_METRONOME:
+ DBG (KERN_INFO "Got LK_METRONOME and don't "
+ "know how to handle...\n");
+ break;
+ case LK_OUTPUT_ERROR:
+ DBG (KERN_INFO "Got LK_OUTPUT_ERROR and don't "
+ "know how to handle...\n");
+ break;
+ case LK_INPUT_ERROR:
+ DBG (KERN_INFO "Got LK_INPUT_ERROR and don't "
+ "know how to handle...\n");
+ break;
+ case LK_KBD_LOCKED:
+ DBG (KERN_INFO "Got LK_KBD_LOCKED and don't "
+ "know how to handle...\n");
+ break;
+ case LK_KBD_TEST_MODE_ACK:
+ DBG (KERN_INFO "Got LK_KBD_TEST_MODE_ACK and don't "
+ "know how to handle...\n");
+ break;
+ case LK_PREFIX_KEY_DOWN:
+ DBG (KERN_INFO "Got LK_PREFIX_KEY_DOWN and don't "
+ "know how to handle...\n");
+ break;
+ case LK_MODE_CHANGE_ACK:
+ DBG (KERN_INFO "Got LK_MODE_CHANGE_ACK and ignored "
+ "it properly...\n");
+ break;
+ case LK_RESPONSE_RESERVED:
+ DBG (KERN_INFO "Got LK_RESPONSE_RESERVED and don't "
+ "know how to handle...\n");
+ break;
+ case 0x01:
+ DBG (KERN_INFO "Got 0x01, scheduling re-initialization\n");
+ lk->ignore_bytes = LK_NUM_IGNORE_BYTES;
+ lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
+ schedule_work (&lk->tq);
+ break;
+
+ default:
+ if (lk->keycode[data] != KEY_RESERVED) {
+ input_regs (&lk->dev, regs);
+ if (!test_bit (lk->keycode[data], lk->dev.key))
+ input_report_key (&lk->dev, lk->keycode[data], 1);
+ else
+ input_report_key (&lk->dev, lk->keycode[data], 0);
+ input_sync (&lk->dev);
+ } else
+ printk (KERN_WARNING "%s: Unknown key with "
+ "scancode 0x%02x on %s.\n",
+ __FILE__, data, lk->name);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * lkkbd_event() handles events from the input module.
+ */
+static int
+lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code,
+ int value)
+{
+ struct lkkbd *lk = dev->private;
+ unsigned char leds_on = 0;
+ unsigned char leds_off = 0;
+
+ switch (type) {
+ case EV_LED:
+ CHECK_LED (LED_CAPSL, LK_LED_SHIFTLOCK);
+ CHECK_LED (LED_COMPOSE, LK_LED_COMPOSE);
+ CHECK_LED (LED_SCROLLL, LK_LED_SCROLLLOCK);
+ CHECK_LED (LED_SLEEP, LK_LED_WAIT);
+ if (leds_on != 0) {
+ lk->serio->write (lk->serio, LK_CMD_LED_ON);
+ lk->serio->write (lk->serio, leds_on);
+ }
+ if (leds_off != 0) {
+ lk->serio->write (lk->serio, LK_CMD_LED_OFF);
+ lk->serio->write (lk->serio, leds_off);
+ }
+ return 0;
+
+ case EV_SND:
+ switch (code) {
+ case SND_CLICK:
+ if (value == 0) {
+ DBG ("%s: Deactivating key clicks\n", __FUNCTION__);
+ lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
+ lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
+ } else {
+ DBG ("%s: Activating key clicks\n", __FUNCTION__);
+ lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
+ lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume));
+ lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
+ lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
+ }
+ return 0;
+
+ case SND_BELL:
+ if (value != 0)
+ lk->serio->write (lk->serio, LK_CMD_SOUND_BELL);
+
+ return 0;
+ }
+ break;
+
+ default:
+ printk (KERN_ERR "%s (): Got unknown type %d, code %d, value %d\n",
+ __FUNCTION__, type, code, value);
+ }
+
+ return -1;
+}
+
+/*
+ * lkkbd_reinit() sets leds and beeps to a state the computer remembers they
+ * were in.
+ */
+static void
+lkkbd_reinit (void *data)
+{
+ struct lkkbd *lk = data;
+ int division;
+ unsigned char leds_on = 0;
+ unsigned char leds_off = 0;
+
+ /* Ask for ID */
+ lk->serio->write (lk->serio, LK_CMD_REQUEST_ID);
+
+ /* Reset parameters */
+ lk->serio->write (lk->serio, LK_CMD_SET_DEFAULTS);
+
+ /* Set LEDs */
+ CHECK_LED (LED_CAPSL, LK_LED_SHIFTLOCK);
+ CHECK_LED (LED_COMPOSE, LK_LED_COMPOSE);
+ CHECK_LED (LED_SCROLLL, LK_LED_SCROLLLOCK);
+ CHECK_LED (LED_SLEEP, LK_LED_WAIT);
+ if (leds_on != 0) {
+ lk->serio->write (lk->serio, LK_CMD_LED_ON);
+ lk->serio->write (lk->serio, leds_on);
+ }
+ if (leds_off != 0) {
+ lk->serio->write (lk->serio, LK_CMD_LED_OFF);
+ lk->serio->write (lk->serio, leds_off);
+ }
+
+ /*
+ * Try to activate extended LK401 mode. This command will
+ * only work with a LK401 keyboard and grants access to
+ * LAlt, RAlt, RCompose and RShift.
+ */
+ lk->serio->write (lk->serio, LK_CMD_ENABLE_LK401);
+
+ /* Set all keys to UPDOWN mode */
+ for (division = 1; division <= 14; division++)
+ lk->serio->write (lk->serio, LK_CMD_SET_MODE (LK_MODE_UPDOWN,
+ division));
+
+ /* Enable bell and set volume */
+ lk->serio->write (lk->serio, LK_CMD_ENABLE_BELL);
+ lk->serio->write (lk->serio, volume_to_hw (lk->bell_volume));
+
+ /* Enable/disable keyclick (and possibly set volume) */
+ if (test_bit (SND_CLICK, lk->dev.snd)) {
+ lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
+ lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume));
+ lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
+ lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
+ } else {
+ lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
+ lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
+ }
+
+ /* Sound the bell if needed */
+ if (test_bit (SND_BELL, lk->dev.snd))
+ lk->serio->write (lk->serio, LK_CMD_SOUND_BELL);
+}
+
+/*
+ * lkkbd_connect() probes for a LK keyboard and fills the necessary structures.
+ */
+static int
+lkkbd_connect (struct serio *serio, struct serio_driver *drv)
+{
+ struct lkkbd *lk;
+ int i;
+ int err;
+
+ if (!(lk = kmalloc (sizeof (struct lkkbd), GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset (lk, 0, sizeof (struct lkkbd));
+
+ init_input_dev (&lk->dev);
+ set_bit (EV_KEY, lk->dev.evbit);
+ set_bit (EV_LED, lk->dev.evbit);
+ set_bit (EV_SND, lk->dev.evbit);
+ set_bit (EV_REP, lk->dev.evbit);
+ set_bit (LED_CAPSL, lk->dev.ledbit);
+ set_bit (LED_SLEEP, lk->dev.ledbit);
+ set_bit (LED_COMPOSE, lk->dev.ledbit);
+ set_bit (LED_SCROLLL, lk->dev.ledbit);
+ set_bit (SND_BELL, lk->dev.sndbit);
+ set_bit (SND_CLICK, lk->dev.sndbit);
+
+ lk->serio = serio;
+
+ INIT_WORK (&lk->tq, lkkbd_reinit, lk);
+
+ lk->bell_volume = bell_volume;
+ lk->keyclick_volume = keyclick_volume;
+ lk->ctrlclick_volume = ctrlclick_volume;
+
+ lk->dev.keycode = lk->keycode;
+ lk->dev.keycodesize = sizeof (lk_keycode_t);
+ lk->dev.keycodemax = LK_NUM_KEYCODES;
+
+ lk->dev.event = lkkbd_event;
+ lk->dev.private = lk;
+
+ serio_set_drvdata (serio, lk);
+
+ err = serio_open (serio, drv);
+ if (err) {
+ serio_set_drvdata (serio, NULL);
+ kfree (lk);
+ return err;
+ }
+
+ sprintf (lk->name, "DEC LK keyboard");
+ sprintf (lk->phys, "%s/input0", serio->phys);
+
+ memcpy (lk->keycode, lkkbd_keycode, sizeof (lk_keycode_t) * LK_NUM_KEYCODES);
+ for (i = 0; i < LK_NUM_KEYCODES; i++)
+ set_bit (lk->keycode[i], lk->dev.keybit);
+
+ lk->dev.name = lk->name;
+ lk->dev.phys = lk->phys;
+ lk->dev.id.bustype = BUS_RS232;
+ lk->dev.id.vendor = SERIO_LKKBD;
+ lk->dev.id.product = 0;
+ lk->dev.id.version = 0x0100;
+ lk->dev.dev = &serio->dev;
+
+ input_register_device (&lk->dev);
+
+ printk (KERN_INFO "input: %s on %s, initiating reset\n", lk->name, serio->phys);
+ lk->serio->write (lk->serio, LK_CMD_POWERCYCLE_RESET);
+
+ return 0;
+}
+
+/*
+ * lkkbd_disconnect() unregisters and closes behind us.
+ */
+static void
+lkkbd_disconnect (struct serio *serio)
+{
+ struct lkkbd *lk = serio_get_drvdata (serio);
+
+ input_unregister_device (&lk->dev);
+ serio_close (serio);
+ serio_set_drvdata (serio, NULL);
+ kfree (lk);
+}
+
+static struct serio_device_id lkkbd_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_LKKBD,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, lkkbd_serio_ids);
+
+static struct serio_driver lkkbd_drv = {
+ .driver = {
+ .name = "lkkbd",
+ },
+ .description = DRIVER_DESC,
+ .id_table = lkkbd_serio_ids,
+ .connect = lkkbd_connect,
+ .disconnect = lkkbd_disconnect,
+ .interrupt = lkkbd_interrupt,
+};
+
+/*
+ * The functions for insering/removing us as a module.
+ */
+static int __init
+lkkbd_init (void)
+{
+ serio_register_driver(&lkkbd_drv);
+ return 0;
+}
+
+static void __exit
+lkkbd_exit (void)
+{
+ serio_unregister_driver(&lkkbd_drv);
+}
+
+module_init (lkkbd_init);
+module_exit (lkkbd_exit);
+
diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c
new file mode 100644
index 000000000000..d3e9dd6a13cd
--- /dev/null
+++ b/drivers/input/keyboard/locomokbd.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2005 John Lenz
+ *
+ * Based on from xtkbd.c
+ */
+
+/*
+ * LoCoMo keyboard driver for Linux/ARM
+ */
+
+/*
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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-1307 USA
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+
+#include <asm/hardware/locomo.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
+MODULE_DESCRIPTION("LoCoMo keyboard driver");
+MODULE_LICENSE("GPL");
+
+#define LOCOMOKBD_NUMKEYS 128
+
+#define KEY_ACTIVITY KEY_F16
+#define KEY_CONTACT KEY_F18
+#define KEY_CENTER KEY_F15
+
+static unsigned char locomokbd_keycode[LOCOMOKBD_NUMKEYS] = {
+ 0, KEY_ESC, KEY_ACTIVITY, 0, 0, 0, 0, 0, 0, 0, /* 0 - 9 */
+ 0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_HOME, KEY_CONTACT, /* 10 - 19 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 29 */
+ 0, 0, 0, KEY_CENTER, 0, KEY_MAIL, 0, 0, 0, 0, /* 30 - 39 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RIGHT, /* 40 - 49 */
+ KEY_UP, KEY_LEFT, 0, 0, KEY_P, 0, KEY_O, KEY_I, KEY_Y, KEY_T, /* 50 - 59 */
+ KEY_E, KEY_W, 0, 0, 0, 0, KEY_DOWN, KEY_ENTER, 0, 0, /* 60 - 69 */
+ KEY_BACKSPACE, 0, KEY_L, KEY_U, KEY_H, KEY_R, KEY_D, KEY_Q, 0, 0, /* 70 - 79 */
+ 0, 0, 0, 0, 0, 0, KEY_ENTER, KEY_RIGHTSHIFT, KEY_K, KEY_J, /* 80 - 89 */
+ KEY_G, KEY_F, KEY_X, KEY_S, 0, 0, 0, 0, 0, 0, /* 90 - 99 */
+ 0, 0, KEY_DOT, 0, KEY_COMMA, KEY_N, KEY_B, KEY_C, KEY_Z, KEY_A, /* 100 - 109 */
+ KEY_LEFTSHIFT, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0, 0, 0, /* 110 - 119 */
+ KEY_M, KEY_SPACE, KEY_V, KEY_APOSTROPHE, KEY_SLASH, 0, 0, 0 /* 120 - 128 */
+};
+
+#define KB_ROWS 16
+#define KB_COLS 8
+#define KB_ROWMASK(r) (1 << (r))
+#define SCANCODE(c,r) ( ((c)<<4) + (r) + 1 )
+#define NR_SCANCODES 128
+
+#define KB_DELAY 8
+#define SCAN_INTERVAL (HZ/10)
+#define LOCOMOKBD_PRESSED 1
+
+struct locomokbd {
+ unsigned char keycode[LOCOMOKBD_NUMKEYS];
+ struct input_dev input;
+ char phys[32];
+
+ struct locomo_dev *ldev;
+ unsigned long base;
+ spinlock_t lock;
+
+ struct timer_list timer;
+};
+
+/* helper functions for reading the keyboard matrix */
+static inline void locomokbd_charge_all(unsigned long membase)
+{
+ locomo_writel(0x00FF, membase + LOCOMO_KSC);
+}
+
+static inline void locomokbd_activate_all(unsigned long membase)
+{
+ unsigned long r;
+
+ locomo_writel(0, membase + LOCOMO_KSC);
+ r = locomo_readl(membase + LOCOMO_KIC);
+ r &= 0xFEFF;
+ locomo_writel(r, membase + LOCOMO_KIC);
+}
+
+static inline void locomokbd_activate_col(unsigned long membase, int col)
+{
+ unsigned short nset;
+ unsigned short nbset;
+
+ nset = 0xFF & ~(1 << col);
+ nbset = (nset << 8) + nset;
+ locomo_writel(nbset, membase + LOCOMO_KSC);
+}
+
+static inline void locomokbd_reset_col(unsigned long membase, int col)
+{
+ unsigned short nbset;
+
+ nbset = ((0xFF & ~(1 << col)) << 8) + 0xFF;
+ locomo_writel(nbset, membase + LOCOMO_KSC);
+}
+
+/*
+ * The LoCoMo keyboard only generates interrupts when a key is pressed.
+ * So when a key is pressed, we enable a timer. This timer scans the
+ * keyboard, and this is how we detect when the key is released.
+ */
+
+/* Scan the hardware keyboard and push any changes up through the input layer */
+static void locomokbd_scankeyboard(struct locomokbd *locomokbd, struct pt_regs *regs)
+{
+ unsigned int row, col, rowd, scancode;
+ unsigned long flags;
+ unsigned int num_pressed;
+ unsigned long membase = locomokbd->base;
+
+ spin_lock_irqsave(&locomokbd->lock, flags);
+
+ if (regs)
+ input_regs(&locomokbd->input, regs);
+
+ locomokbd_charge_all(membase);
+
+ num_pressed = 0;
+ for (col = 0; col < KB_COLS; col++) {
+
+ locomokbd_activate_col(membase, col);
+ udelay(KB_DELAY);
+
+ rowd = ~locomo_readl(membase + LOCOMO_KIB);
+ for (row = 0; row < KB_ROWS; row++ ) {
+ scancode = SCANCODE(col, row);
+ if (rowd & KB_ROWMASK(row)) {
+ num_pressed += 1;
+ input_report_key(&locomokbd->input, locomokbd->keycode[scancode], 1);
+ } else {
+ input_report_key(&locomokbd->input, locomokbd->keycode[scancode], 0);
+ }
+ }
+ locomokbd_reset_col(membase, col);
+ }
+ locomokbd_activate_all(membase);
+
+ input_sync(&locomokbd->input);
+
+ /* if any keys are pressed, enable the timer */
+ if (num_pressed)
+ mod_timer(&locomokbd->timer, jiffies + SCAN_INTERVAL);
+
+ spin_unlock_irqrestore(&locomokbd->lock, flags);
+}
+
+/*
+ * LoCoMo keyboard interrupt handler.
+ */
+static irqreturn_t locomokbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct locomokbd *locomokbd = dev_id;
+ /** wait chattering delay **/
+ udelay(100);
+
+ locomokbd_scankeyboard(locomokbd, regs);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * LoCoMo timer checking for released keys
+ */
+static void locomokbd_timer_callback(unsigned long data)
+{
+ struct locomokbd *locomokbd = (struct locomokbd *) data;
+ locomokbd_scankeyboard(locomokbd, NULL);
+}
+
+static int locomokbd_probe(struct locomo_dev *dev)
+{
+ struct locomokbd *locomokbd;
+ int i, ret;
+
+ locomokbd = kmalloc(sizeof(struct locomokbd), GFP_KERNEL);
+ if (!locomokbd)
+ return -ENOMEM;
+
+ memset(locomokbd, 0, sizeof(struct locomokbd));
+
+ /* try and claim memory region */
+ if (!request_mem_region((unsigned long) dev->mapbase,
+ dev->length,
+ LOCOMO_DRIVER_NAME(dev))) {
+ ret = -EBUSY;
+ printk(KERN_ERR "locomokbd: Can't acquire access to io memory for keyboard\n");
+ goto free;
+ }
+
+ locomokbd->ldev = dev;
+ locomo_set_drvdata(dev, locomokbd);
+
+ locomokbd->base = (unsigned long) dev->mapbase;
+
+ spin_lock_init(&locomokbd->lock);
+
+ init_timer(&locomokbd->timer);
+ locomokbd->timer.function = locomokbd_timer_callback;
+ locomokbd->timer.data = (unsigned long) locomokbd;
+
+ locomokbd->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+
+ init_input_dev(&locomokbd->input);
+ locomokbd->input.keycode = locomokbd->keycode;
+ locomokbd->input.keycodesize = sizeof(unsigned char);
+ locomokbd->input.keycodemax = ARRAY_SIZE(locomokbd_keycode);
+ locomokbd->input.private = locomokbd;
+
+ memcpy(locomokbd->keycode, locomokbd_keycode, sizeof(locomokbd->keycode));
+ for (i = 0; i < LOCOMOKBD_NUMKEYS; i++)
+ set_bit(locomokbd->keycode[i], locomokbd->input.keybit);
+ clear_bit(0, locomokbd->input.keybit);
+
+ strcpy(locomokbd->phys, "locomokbd/input0");
+
+ locomokbd->input.name = "LoCoMo keyboard";
+ locomokbd->input.phys = locomokbd->phys;
+ locomokbd->input.id.bustype = BUS_XTKBD;
+ locomokbd->input.id.vendor = 0x0001;
+ locomokbd->input.id.product = 0x0001;
+ locomokbd->input.id.version = 0x0100;
+
+ /* attempt to get the interrupt */
+ ret = request_irq(dev->irq[0], locomokbd_interrupt, 0, "locomokbd", locomokbd);
+ if (ret) {
+ printk(KERN_ERR "locomokbd: Can't get irq for keyboard\n");
+ goto out;
+ }
+
+ input_register_device(&locomokbd->input);
+
+ printk(KERN_INFO "input: LoCoMo keyboard on locomokbd\n");
+
+ return 0;
+
+out:
+ release_mem_region((unsigned long) dev->mapbase, dev->length);
+ locomo_set_drvdata(dev, NULL);
+free:
+ kfree(locomokbd);
+
+ return ret;
+}
+
+static int locomokbd_remove(struct locomo_dev *dev)
+{
+ struct locomokbd *locomokbd = locomo_get_drvdata(dev);
+
+ free_irq(dev->irq[0], locomokbd);
+
+ del_timer_sync(&locomokbd->timer);
+
+ input_unregister_device(&locomokbd->input);
+ locomo_set_drvdata(dev, NULL);
+
+ release_mem_region((unsigned long) dev->mapbase, dev->length);
+
+ kfree(locomokbd);
+
+ return 0;
+}
+
+static struct locomo_driver keyboard_driver = {
+ .drv = {
+ .name = "locomokbd"
+ },
+ .devid = LOCOMO_DEVID_KEYBOARD,
+ .probe = locomokbd_probe,
+ .remove = locomokbd_remove,
+};
+
+static int __init locomokbd_init(void)
+{
+ return locomo_driver_register(&keyboard_driver);
+}
+
+static void __exit locomokbd_exit(void)
+{
+ locomo_driver_unregister(&keyboard_driver);
+}
+
+module_init(locomokbd_init);
+module_exit(locomokbd_exit);
diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c
new file mode 100644
index 000000000000..859ed771ee0a
--- /dev/null
+++ b/drivers/input/keyboard/maple_keyb.c
@@ -0,0 +1,190 @@
+/*
+ * $Id: maple_keyb.c,v 1.4 2004/03/22 01:18:15 lethal Exp $
+ * SEGA Dreamcast keyboard driver
+ * Based on drivers/usb/usbkbd.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/maple.h>
+
+MODULE_AUTHOR("YAEGASHI Takeshi <t@keshi.org>");
+MODULE_DESCRIPTION("SEGA Dreamcast keyboard driver");
+MODULE_LICENSE("GPL");
+
+static unsigned char dc_kbd_keycode[256] = {
+ 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
+ 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
+ 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
+ 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
+ 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
+ 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
+ 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
+ 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
+ 150,158,159,128,136,177,178,176,142,152,173,140
+};
+
+
+struct dc_kbd {
+ struct input_dev dev;
+ unsigned char new[8];
+ unsigned char old[8];
+ int open;
+};
+
+
+static void dc_scan_kbd(struct dc_kbd *kbd)
+{
+ int i;
+ struct input_dev *dev = &kbd->dev;
+
+ for(i=0; i<8; i++)
+ input_report_key(dev,
+ dc_kbd_keycode[i+224],
+ (kbd->new[0]>>i)&1);
+
+ for(i=2; i<8; i++) {
+
+ if(kbd->old[i]>3&&memscan(kbd->new+2, kbd->old[i], 6)==NULL) {
+ if(dc_kbd_keycode[kbd->old[i]])
+ input_report_key(dev,
+ dc_kbd_keycode[kbd->old[i]],
+ 0);
+ else
+ printk("Unknown key (scancode %#x) released.",
+ kbd->old[i]);
+ }
+
+ if(kbd->new[i]>3&&memscan(kbd->old+2, kbd->new[i], 6)!=NULL) {
+ if(dc_kbd_keycode[kbd->new[i]])
+ input_report_key(dev,
+ dc_kbd_keycode[kbd->new[i]],
+ 1);
+ else
+ printk("Unknown key (scancode %#x) pressed.",
+ kbd->new[i]);
+ }
+ }
+
+ input_sync(dev);
+
+ memcpy(kbd->old, kbd->new, 8);
+}
+
+
+static void dc_kbd_callback(struct mapleq *mq)
+{
+ struct maple_device *mapledev = mq->dev;
+ struct dc_kbd *kbd = mapledev->private_data;
+ unsigned long *buf = mq->recvbuf;
+
+ if (buf[1] == mapledev->function) {
+ memcpy(kbd->new, buf+2, 8);
+ dc_scan_kbd(kbd);
+ }
+}
+
+
+static int dc_kbd_open(struct input_dev *dev)
+{
+ struct dc_kbd *kbd = dev->private;
+ kbd->open++;
+ return 0;
+}
+
+
+static void dc_kbd_close(struct input_dev *dev)
+{
+ struct dc_kbd *kbd = dev->private;
+ kbd->open--;
+}
+
+
+static int dc_kbd_connect(struct maple_device *dev)
+{
+ int i;
+ unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]);
+ struct dc_kbd *kbd;
+
+ if (!(kbd = kmalloc(sizeof(struct dc_kbd), GFP_KERNEL)))
+ return -1;
+ memset(kbd, 0, sizeof(struct dc_kbd));
+
+ dev->private_data = kbd;
+
+ kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+
+ init_input_dev(&kbd->dev);
+
+ for (i=0; i<255; i++)
+ set_bit(dc_kbd_keycode[i], kbd->dev.keybit);
+
+ clear_bit(0, kbd->dev.keybit);
+
+ kbd->dev.private = kbd;
+ kbd->dev.open = dc_kbd_open;
+ kbd->dev.close = dc_kbd_close;
+ kbd->dev.event = NULL;
+
+ kbd->dev.name = dev->product_name;
+ kbd->dev.id.bustype = BUS_MAPLE;
+
+ input_register_device(&kbd->dev);
+
+ maple_getcond_callback(dev, dc_kbd_callback, 1, MAPLE_FUNC_KEYBOARD);
+
+ printk(KERN_INFO "input: keyboard(0x%lx): %s\n", data, kbd->dev.name);
+
+ return 0;
+}
+
+
+static void dc_kbd_disconnect(struct maple_device *dev)
+{
+ struct dc_kbd *kbd = dev->private_data;
+
+ input_unregister_device(&kbd->dev);
+ kfree(kbd);
+}
+
+
+static struct maple_driver dc_kbd_driver = {
+ .function = MAPLE_FUNC_KEYBOARD,
+ .name = "Dreamcast keyboard",
+ .connect = dc_kbd_connect,
+ .disconnect = dc_kbd_disconnect,
+};
+
+
+static int __init dc_kbd_init(void)
+{
+ maple_register_driver(&dc_kbd_driver);
+ return 0;
+}
+
+
+static void __exit dc_kbd_exit(void)
+{
+ maple_unregister_driver(&dc_kbd_driver);
+}
+
+
+module_init(dc_kbd_init);
+module_exit(dc_kbd_exit);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/input/keyboard/newtonkbd.c b/drivers/input/keyboard/newtonkbd.c
new file mode 100644
index 000000000000..2e8ce1613eec
--- /dev/null
+++ b/drivers/input/keyboard/newtonkbd.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2000 Justin Cormack
+ */
+
+/*
+ * Newton keyboard driver for Linux
+ */
+
+/*
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <j.cormack@doc.ic.ac.uk>, or by paper mail:
+ * Justin Cormack, 68 Dartmouth Park Road, London NW5 1SN, UK.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "Newton keyboard driver"
+
+MODULE_AUTHOR("Justin Cormack <j.cormack@doc.ic.ac.uk>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define NKBD_KEY 0x7f
+#define NKBD_PRESS 0x80
+
+static unsigned char nkbd_keycode[128] = {
+ KEY_A, KEY_S, KEY_D, KEY_F, KEY_H, KEY_G, KEY_Z, KEY_X,
+ KEY_C, KEY_V, 0, KEY_B, KEY_Q, KEY_W, KEY_E, KEY_R,
+ KEY_Y, KEY_T, KEY_1, KEY_2, KEY_3, KEY_4, KEY_6, KEY_5,
+ KEY_EQUAL, KEY_9, KEY_7, KEY_MINUS, KEY_8, KEY_0, KEY_RIGHTBRACE, KEY_O,
+ KEY_U, KEY_LEFTBRACE, KEY_I, KEY_P, KEY_ENTER, KEY_L, KEY_J, KEY_APOSTROPHE,
+ KEY_K, KEY_SEMICOLON, KEY_BACKSLASH, KEY_COMMA, KEY_SLASH, KEY_N, KEY_M, KEY_DOT,
+ KEY_TAB, KEY_SPACE, KEY_GRAVE, KEY_DELETE, 0, 0, 0, KEY_LEFTMETA,
+ KEY_LEFTSHIFT, KEY_CAPSLOCK, KEY_LEFTALT, KEY_LEFTCTRL, KEY_RIGHTSHIFT, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, 0
+};
+
+static char *nkbd_name = "Newton Keyboard";
+
+struct nkbd {
+ unsigned char keycode[128];
+ struct input_dev dev;
+ struct serio *serio;
+ char phys[32];
+};
+
+static irqreturn_t nkbd_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct nkbd *nkbd = serio_get_drvdata(serio);
+
+ /* invalid scan codes are probably the init sequence, so we ignore them */
+ if (nkbd->keycode[data & NKBD_KEY]) {
+ input_regs(&nkbd->dev, regs);
+ input_report_key(&nkbd->dev, nkbd->keycode[data & NKBD_KEY], data & NKBD_PRESS);
+ input_sync(&nkbd->dev);
+ }
+
+ else if (data == 0xe7) /* end of init sequence */
+ printk(KERN_INFO "input: %s on %s\n", nkbd_name, serio->phys);
+ return IRQ_HANDLED;
+
+}
+
+static int nkbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct nkbd *nkbd;
+ int i;
+ int err;
+
+ if (!(nkbd = kmalloc(sizeof(struct nkbd), GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset(nkbd, 0, sizeof(struct nkbd));
+
+ nkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+
+ nkbd->serio = serio;
+
+ init_input_dev(&nkbd->dev);
+ nkbd->dev.keycode = nkbd->keycode;
+ nkbd->dev.keycodesize = sizeof(unsigned char);
+ nkbd->dev.keycodemax = ARRAY_SIZE(nkbd_keycode);
+ nkbd->dev.private = nkbd;
+
+ serio_set_drvdata(serio, nkbd);
+
+ err = serio_open(serio, drv);
+ if (err) {
+ serio_set_drvdata(serio, NULL);
+ kfree(nkbd);
+ return err;
+ }
+
+ memcpy(nkbd->keycode, nkbd_keycode, sizeof(nkbd->keycode));
+ for (i = 0; i < 128; i++)
+ set_bit(nkbd->keycode[i], nkbd->dev.keybit);
+ clear_bit(0, nkbd->dev.keybit);
+
+ sprintf(nkbd->phys, "%s/input0", serio->phys);
+
+ nkbd->dev.name = nkbd_name;
+ nkbd->dev.phys = nkbd->phys;
+ nkbd->dev.id.bustype = BUS_RS232;
+ nkbd->dev.id.vendor = SERIO_NEWTON;
+ nkbd->dev.id.product = 0x0001;
+ nkbd->dev.id.version = 0x0100;
+ nkbd->dev.dev = &serio->dev;
+
+ input_register_device(&nkbd->dev);
+
+ printk(KERN_INFO "input: %s on %s\n", nkbd_name, serio->phys);
+
+ return 0;
+}
+
+static void nkbd_disconnect(struct serio *serio)
+{
+ struct nkbd *nkbd = serio_get_drvdata(serio);
+
+ input_unregister_device(&nkbd->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(nkbd);
+}
+
+static struct serio_device_id nkbd_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_NEWTON,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, nkbd_serio_ids);
+
+static struct serio_driver nkbd_drv = {
+ .driver = {
+ .name = "newtonkbd",
+ },
+ .description = DRIVER_DESC,
+ .id_table = nkbd_serio_ids,
+ .interrupt = nkbd_interrupt,
+ .connect = nkbd_connect,
+ .disconnect = nkbd_disconnect,
+};
+
+static int __init nkbd_init(void)
+{
+ serio_register_driver(&nkbd_drv);
+ return 0;
+}
+
+static void __exit nkbd_exit(void)
+{
+ serio_unregister_driver(&nkbd_drv);
+}
+
+module_init(nkbd_init);
+module_exit(nkbd_exit);
diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c
new file mode 100644
index 000000000000..596964ceb96d
--- /dev/null
+++ b/drivers/input/keyboard/sunkbd.c
@@ -0,0 +1,353 @@
+/*
+ * $Id: sunkbd.c,v 1.14 2001/09/25 10:12:07 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Sun keyboard driver for Linux
+ */
+
+/*
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/workqueue.h>
+
+#define DRIVER_DESC "Sun keyboard driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static unsigned char sunkbd_keycode[128] = {
+ 0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64, 0,
+ 65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106, 1, 2, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 41, 14,110,113, 98, 55,
+ 116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27,111,127, 71, 72, 73, 74,134,135,107, 0, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136,
+ 104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101,
+ 79, 80, 81, 0, 0, 0,138, 58,125, 57,126,109, 86, 78
+};
+
+#define SUNKBD_CMD_RESET 0x1
+#define SUNKBD_CMD_BELLON 0x2
+#define SUNKBD_CMD_BELLOFF 0x3
+#define SUNKBD_CMD_CLICK 0xa
+#define SUNKBD_CMD_NOCLICK 0xb
+#define SUNKBD_CMD_SETLED 0xe
+#define SUNKBD_CMD_LAYOUT 0xf
+
+#define SUNKBD_RET_RESET 0xff
+#define SUNKBD_RET_ALLUP 0x7f
+#define SUNKBD_RET_LAYOUT 0xfe
+
+#define SUNKBD_LAYOUT_5_MASK 0x20
+#define SUNKBD_RELEASE 0x80
+#define SUNKBD_KEY 0x7f
+
+/*
+ * Per-keyboard data.
+ */
+
+struct sunkbd {
+ unsigned char keycode[128];
+ struct input_dev dev;
+ struct serio *serio;
+ struct work_struct tq;
+ wait_queue_head_t wait;
+ char name[64];
+ char phys[32];
+ char type;
+ volatile s8 reset;
+ volatile s8 layout;
+};
+
+/*
+ * sunkbd_interrupt() is called by the low level driver when a character
+ * is received.
+ */
+
+static irqreturn_t sunkbd_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct sunkbd* sunkbd = serio_get_drvdata(serio);
+
+ if (sunkbd->reset <= -1) { /* If cp[i] is 0xff, sunkbd->reset will stay -1. */
+ sunkbd->reset = data; /* The keyboard sends 0xff 0xff 0xID on powerup */
+ wake_up_interruptible(&sunkbd->wait);
+ goto out;
+ }
+
+ if (sunkbd->layout == -1) {
+ sunkbd->layout = data;
+ wake_up_interruptible(&sunkbd->wait);
+ goto out;
+ }
+
+ switch (data) {
+
+ case SUNKBD_RET_RESET:
+ schedule_work(&sunkbd->tq);
+ sunkbd->reset = -1;
+ break;
+
+ case SUNKBD_RET_LAYOUT:
+ sunkbd->layout = -1;
+ break;
+
+ case SUNKBD_RET_ALLUP: /* All keys released */
+ break;
+
+ default:
+ if (sunkbd->keycode[data & SUNKBD_KEY]) {
+ input_regs(&sunkbd->dev, regs);
+ input_report_key(&sunkbd->dev, sunkbd->keycode[data & SUNKBD_KEY], !(data & SUNKBD_RELEASE));
+ input_sync(&sunkbd->dev);
+ } else {
+ printk(KERN_WARNING "sunkbd.c: Unknown key (scancode %#x) %s.\n",
+ data & SUNKBD_KEY, data & SUNKBD_RELEASE ? "released" : "pressed");
+ }
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+/*
+ * sunkbd_event() handles events from the input module.
+ */
+
+static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct sunkbd *sunkbd = dev->private;
+
+ switch (type) {
+
+ case EV_LED:
+
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
+ sunkbd->serio->write(sunkbd->serio,
+ (!!test_bit(LED_CAPSL, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) |
+ (!!test_bit(LED_COMPOSE, dev->led) << 1) | !!test_bit(LED_NUML, dev->led));
+ return 0;
+
+ case EV_SND:
+
+ switch (code) {
+
+ case SND_CLICK:
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
+ return 0;
+
+ case SND_BELL:
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
+ return 0;
+ }
+
+ break;
+ }
+
+ return -1;
+}
+
+/*
+ * sunkbd_initialize() checks for a Sun keyboard attached, and determines
+ * its type.
+ */
+
+static int sunkbd_initialize(struct sunkbd *sunkbd)
+{
+ sunkbd->reset = -2;
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_RESET);
+ wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
+ if (sunkbd->reset <0)
+ return -1;
+
+ sunkbd->type = sunkbd->reset;
+
+ if (sunkbd->type == 4) { /* Type 4 keyboard */
+ sunkbd->layout = -2;
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
+ wait_event_interruptible_timeout(sunkbd->wait, sunkbd->layout >= 0, HZ/4);
+ if (sunkbd->layout < 0) return -1;
+ if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK) sunkbd->type = 5;
+ }
+
+ return 0;
+}
+
+/*
+ * sunkbd_reinit() sets leds and beeps to a state the computer remembers they
+ * were in.
+ */
+
+static void sunkbd_reinit(void *data)
+{
+ struct sunkbd *sunkbd = data;
+
+ wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
+
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
+ sunkbd->serio->write(sunkbd->serio,
+ (!!test_bit(LED_CAPSL, sunkbd->dev.led) << 3) | (!!test_bit(LED_SCROLLL, sunkbd->dev.led) << 2) |
+ (!!test_bit(LED_COMPOSE, sunkbd->dev.led) << 1) | !!test_bit(LED_NUML, sunkbd->dev.led));
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev.snd));
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev.snd));
+}
+
+/*
+ * sunkbd_connect() probes for a Sun keyboard and fills the necessary structures.
+ */
+
+static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct sunkbd *sunkbd;
+ int i;
+ int err;
+
+ if (!(sunkbd = kmalloc(sizeof(struct sunkbd), GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset(sunkbd, 0, sizeof(struct sunkbd));
+
+ init_input_dev(&sunkbd->dev);
+ init_waitqueue_head(&sunkbd->wait);
+
+ sunkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_SND) | BIT(EV_REP);
+ sunkbd->dev.ledbit[0] = BIT(LED_CAPSL) | BIT(LED_COMPOSE) | BIT(LED_SCROLLL) | BIT(LED_NUML);
+ sunkbd->dev.sndbit[0] = BIT(SND_CLICK) | BIT(SND_BELL);
+
+ sunkbd->serio = serio;
+
+ INIT_WORK(&sunkbd->tq, sunkbd_reinit, sunkbd);
+
+ sunkbd->dev.keycode = sunkbd->keycode;
+ sunkbd->dev.keycodesize = sizeof(unsigned char);
+ sunkbd->dev.keycodemax = ARRAY_SIZE(sunkbd_keycode);
+
+ sunkbd->dev.event = sunkbd_event;
+ sunkbd->dev.private = sunkbd;
+
+ serio_set_drvdata(serio, sunkbd);
+
+ err = serio_open(serio, drv);
+ if (err) {
+ serio_set_drvdata(serio, NULL);
+ kfree(sunkbd);
+ return err;
+ }
+
+ if (sunkbd_initialize(sunkbd) < 0) {
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(sunkbd);
+ return -ENODEV;
+ }
+
+ sprintf(sunkbd->name, "Sun Type %d keyboard", sunkbd->type);
+
+ memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
+ for (i = 0; i < 128; i++)
+ set_bit(sunkbd->keycode[i], sunkbd->dev.keybit);
+ clear_bit(0, sunkbd->dev.keybit);
+
+ sprintf(sunkbd->phys, "%s/input0", serio->phys);
+
+ sunkbd->dev.name = sunkbd->name;
+ sunkbd->dev.phys = sunkbd->phys;
+ sunkbd->dev.id.bustype = BUS_RS232;
+ sunkbd->dev.id.vendor = SERIO_SUNKBD;
+ sunkbd->dev.id.product = sunkbd->type;
+ sunkbd->dev.id.version = 0x0100;
+ sunkbd->dev.dev = &serio->dev;
+
+ input_register_device(&sunkbd->dev);
+
+ printk(KERN_INFO "input: %s on %s\n", sunkbd->name, serio->phys);
+
+ return 0;
+}
+
+/*
+ * sunkbd_disconnect() unregisters and closes behind us.
+ */
+
+static void sunkbd_disconnect(struct serio *serio)
+{
+ struct sunkbd *sunkbd = serio_get_drvdata(serio);
+ input_unregister_device(&sunkbd->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(sunkbd);
+}
+
+static struct serio_device_id sunkbd_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_SUNKBD,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_UNKNOWN, /* sunkbd does probe */
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
+
+static struct serio_driver sunkbd_drv = {
+ .driver = {
+ .name = "sunkbd",
+ },
+ .description = DRIVER_DESC,
+ .id_table = sunkbd_serio_ids,
+ .interrupt = sunkbd_interrupt,
+ .connect = sunkbd_connect,
+ .disconnect = sunkbd_disconnect,
+};
+
+/*
+ * The functions for insering/removing us as a module.
+ */
+
+static int __init sunkbd_init(void)
+{
+ serio_register_driver(&sunkbd_drv);
+ return 0;
+}
+
+static void __exit sunkbd_exit(void)
+{
+ serio_unregister_driver(&sunkbd_drv);
+}
+
+module_init(sunkbd_init);
+module_exit(sunkbd_exit);
diff --git a/drivers/input/keyboard/xtkbd.c b/drivers/input/keyboard/xtkbd.c
new file mode 100644
index 000000000000..19eaec7789d1
--- /dev/null
+++ b/drivers/input/keyboard/xtkbd.c
@@ -0,0 +1,188 @@
+/*
+ * $Id: xtkbd.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $
+ *
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * XT keyboard driver for Linux
+ */
+
+/*
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "XT keyboard driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define XTKBD_EMUL0 0xe0
+#define XTKBD_EMUL1 0xe1
+#define XTKBD_KEY 0x7f
+#define XTKBD_RELEASE 0x80
+
+static unsigned char xtkbd_keycode[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 0, 0, 0, 87, 88, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 87, 88, 0, 0, 0, 0,110,111,103,108,105,
+ 106
+};
+
+static char *xtkbd_name = "XT Keyboard";
+
+struct xtkbd {
+ unsigned char keycode[256];
+ struct input_dev dev;
+ struct serio *serio;
+ char phys[32];
+};
+
+static irqreturn_t xtkbd_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct xtkbd *xtkbd = serio_get_drvdata(serio);
+
+ switch (data) {
+ case XTKBD_EMUL0:
+ case XTKBD_EMUL1:
+ break;
+ default:
+
+ if (xtkbd->keycode[data & XTKBD_KEY]) {
+ input_regs(&xtkbd->dev, regs);
+ input_report_key(&xtkbd->dev, xtkbd->keycode[data & XTKBD_KEY], !(data & XTKBD_RELEASE));
+ input_sync(&xtkbd->dev);
+ } else {
+ printk(KERN_WARNING "xtkbd.c: Unknown key (scancode %#x) %s.\n",
+ data & XTKBD_KEY, data & XTKBD_RELEASE ? "released" : "pressed");
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+static int xtkbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct xtkbd *xtkbd;
+ int i;
+ int err;
+
+ if (!(xtkbd = kmalloc(sizeof(struct xtkbd), GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset(xtkbd, 0, sizeof(struct xtkbd));
+
+ xtkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+
+ xtkbd->serio = serio;
+
+ init_input_dev(&xtkbd->dev);
+ xtkbd->dev.keycode = xtkbd->keycode;
+ xtkbd->dev.keycodesize = sizeof(unsigned char);
+ xtkbd->dev.keycodemax = ARRAY_SIZE(xtkbd_keycode);
+ xtkbd->dev.private = xtkbd;
+
+ serio_set_drvdata(serio, xtkbd);
+
+ err = serio_open(serio, drv);
+ if (err) {
+ serio_set_drvdata(serio, NULL);
+ kfree(xtkbd);
+ return err;
+ }
+
+ memcpy(xtkbd->keycode, xtkbd_keycode, sizeof(xtkbd->keycode));
+ for (i = 0; i < 255; i++)
+ set_bit(xtkbd->keycode[i], xtkbd->dev.keybit);
+ clear_bit(0, xtkbd->dev.keybit);
+
+ sprintf(xtkbd->phys, "%s/input0", serio->phys);
+
+ xtkbd->dev.name = xtkbd_name;
+ xtkbd->dev.phys = xtkbd->phys;
+ xtkbd->dev.id.bustype = BUS_XTKBD;
+ xtkbd->dev.id.vendor = 0x0001;
+ xtkbd->dev.id.product = 0x0001;
+ xtkbd->dev.id.version = 0x0100;
+ xtkbd->dev.dev = &serio->dev;
+
+ input_register_device(&xtkbd->dev);
+
+ printk(KERN_INFO "input: %s on %s\n", xtkbd_name, serio->phys);
+
+ return 0;
+}
+
+static void xtkbd_disconnect(struct serio *serio)
+{
+ struct xtkbd *xtkbd = serio_get_drvdata(serio);
+
+ input_unregister_device(&xtkbd->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(xtkbd);
+}
+
+static struct serio_device_id xtkbd_serio_ids[] = {
+ {
+ .type = SERIO_XT,
+ .proto = SERIO_ANY,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, xtkbd_serio_ids);
+
+static struct serio_driver xtkbd_drv = {
+ .driver = {
+ .name = "xtkbd",
+ },
+ .description = DRIVER_DESC,
+ .id_table = xtkbd_serio_ids,
+ .interrupt = xtkbd_interrupt,
+ .connect = xtkbd_connect,
+ .disconnect = xtkbd_disconnect,
+};
+
+static int __init xtkbd_init(void)
+{
+ serio_register_driver(&xtkbd_drv);
+ return 0;
+}
+
+static void __exit xtkbd_exit(void)
+{
+ serio_unregister_driver(&xtkbd_drv);
+}
+
+module_init(xtkbd_init);
+module_exit(xtkbd_exit);