Linux-2.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!
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
new file mode 100644
index 0000000..58223b5
--- /dev/null
+++ b/drivers/input/Kconfig
@@ -0,0 +1,157 @@
+#
+# Input device configuration
+#
+
+menu "Input device support"
+
+config INPUT
+	tristate "Generic input layer (needed for keyboard, mouse, ...)" if EMBEDDED
+	default y
+	---help---
+	  Say Y here if you have any input device (mouse, keyboard, tablet,
+	  joystick, steering wheel ...) connected to your system and want
+	  it to be available to applications. This includes standard PS/2
+	  keyboard and mouse.
+
+	  Say N here if you have a headless (no monitor, no keyboard) system.
+
+	  More information is available: <file:Documentation/input/input.txt>
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called input.
+
+if INPUT
+
+comment "Userland interfaces"
+
+config INPUT_MOUSEDEV
+	tristate "Mouse interface" if EMBEDDED
+	default y
+	---help---
+	  Say Y here if you want your mouse to be accessible as char devices
+	  13:32+ - /dev/input/mouseX and 13:63 - /dev/input/mice as an
+	  emulated IntelliMouse Explorer PS/2 mouse. That way, all user space
+	  programs (including SVGAlib, GPM and X) will be able to use your
+	  mouse.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mousedev.
+
+config INPUT_MOUSEDEV_PSAUX
+	bool "Provide legacy /dev/psaux device"
+	default y
+	depends on INPUT_MOUSEDEV
+	---help---
+	  Say Y here if you want your mouse also be accessible as char device
+	  10:1 - /dev/psaux. The data available through /dev/psaux is exactly
+	  the same as the data from /dev/input/mice.
+
+	  If unsure, say Y.
+
+
+config INPUT_MOUSEDEV_SCREEN_X
+	int "Horizontal screen resolution"
+	depends on INPUT_MOUSEDEV
+	default "1024"
+	help
+	  If you're using a digitizer, or a graphic tablet, and want to use
+	  it as a mouse then the mousedev driver needs to know the X window
+	  screen resolution you are using to correctly scale the data. If
+	  you're not using a digitizer, this value is ignored.
+
+config INPUT_MOUSEDEV_SCREEN_Y
+	int "Vertical screen resolution"
+	depends on INPUT_MOUSEDEV
+	default "768"
+	help
+	  If you're using a digitizer, or a graphic tablet, and want to use
+	  it as a mouse then the mousedev driver needs to know the X window
+	  screen resolution you are using to correctly scale the data. If
+	  you're not using a digitizer, this value is ignored.
+
+config INPUT_JOYDEV
+	tristate "Joystick interface"
+	---help---
+	  Say Y here if you want your joystick or gamepad to be
+	  accessible as char device 13:0+ - /dev/input/jsX device.
+
+	  If unsure, say Y.
+
+	  More information is available: <file:Documentation/input/joystick.txt>
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called joydev.
+
+config INPUT_TSDEV
+	tristate "Touchscreen interface"
+	---help---
+	  Say Y here if you have an application that only can understand the
+	  Compaq touchscreen protocol for absolute pointer data. This is
+	  useful namely for embedded configurations.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tsdev.
+
+config INPUT_TSDEV_SCREEN_X
+	int "Horizontal screen resolution"
+	depends on INPUT_TSDEV
+	default "240"
+
+config INPUT_TSDEV_SCREEN_Y
+	int "Vertical screen resolution"
+	depends on INPUT_TSDEV
+	default "320"
+
+config INPUT_EVDEV
+	tristate "Event interface"
+	help
+	  Say Y here if you want your input device events be accessible
+	  under char device 13:64+ - /dev/input/eventX in a generic way.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called evdev.
+
+config INPUT_EVBUG
+	tristate "Event debugging"
+	---help---
+	  Say Y here if you have a problem with the input subsystem and
+	  want all events (keypresses, mouse movements), to be output to
+	  the system log. While this is useful for debugging, it's also
+	  a security threat - your keypresses include your passwords, of
+	  course.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called evbug.
+
+comment "Input Device Drivers"
+
+source "drivers/input/keyboard/Kconfig"
+
+source "drivers/input/mouse/Kconfig"
+
+source "drivers/input/joystick/Kconfig"
+
+source "drivers/input/touchscreen/Kconfig"
+
+source "drivers/input/misc/Kconfig"
+
+endif
+
+menu "Hardware I/O ports"
+
+source "drivers/input/serio/Kconfig"
+
+source "drivers/input/gameport/Kconfig"
+
+endmenu
+
+endmenu
+
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
new file mode 100644
index 0000000..1a6ff49
--- /dev/null
+++ b/drivers/input/Makefile
@@ -0,0 +1,19 @@
+#
+# Makefile for the input core drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_INPUT)		+= input.o
+obj-$(CONFIG_INPUT_MOUSEDEV)	+= mousedev.o
+obj-$(CONFIG_INPUT_JOYDEV)	+= joydev.o
+obj-$(CONFIG_INPUT_EVDEV)	+= evdev.o
+obj-$(CONFIG_INPUT_TSDEV)	+= tsdev.o
+obj-$(CONFIG_INPUT_POWER)	+= power.o
+obj-$(CONFIG_INPUT_EVBUG)	+= evbug.o
+
+obj-$(CONFIG_INPUT_KEYBOARD)	+= keyboard/
+obj-$(CONFIG_INPUT_MOUSE)	+= mouse/
+obj-$(CONFIG_INPUT_JOYSTICK)	+= joystick/
+obj-$(CONFIG_INPUT_TOUCHSCREEN)	+= touchscreen/
+obj-$(CONFIG_INPUT_MISC)	+= misc/
diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c
new file mode 100644
index 0000000..d782893
--- /dev/null
+++ b/drivers/input/evbug.c
@@ -0,0 +1,103 @@
+/*
+ * $Id: evbug.c,v 1.10 2001/09/25 10:12:07 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ *  Input driver event debug module - dumps all events into syslog
+ */
+
+/*
+ * 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/device.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Input driver event debug module");
+MODULE_LICENSE("GPL");
+
+static char evbug_name[] = "evbug";
+
+static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+	printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n", handle->dev->phys, type, code, value);
+}
+
+static struct input_handle *evbug_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
+{
+	struct input_handle *handle;
+
+	if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL)))
+		return NULL;
+	memset(handle, 0, sizeof(struct input_handle));
+
+	handle->dev = dev;
+	handle->handler = handler;
+	handle->name = evbug_name;
+
+	input_open_device(handle);
+
+	printk(KERN_DEBUG "evbug.c: Connected device: \"%s\", %s\n", dev->name, dev->phys);
+
+	return handle;
+}
+
+static void evbug_disconnect(struct input_handle *handle)
+{
+	printk(KERN_DEBUG "evbug.c: Disconnected device: %s\n", handle->dev->phys);
+
+	input_close_device(handle);
+
+	kfree(handle);
+}
+
+static struct input_device_id evbug_ids[] = {
+	{ .driver_info = 1 },	/* Matches all devices */
+	{ },			/* Terminating zero entry */
+};
+
+MODULE_DEVICE_TABLE(input, evbug_ids);
+
+static struct input_handler evbug_handler = {
+	.event =	evbug_event,
+	.connect =	evbug_connect,
+	.disconnect =	evbug_disconnect,
+	.name =		"evbug",
+	.id_table =	evbug_ids,
+};
+
+static int __init evbug_init(void)
+{
+	input_register_handler(&evbug_handler);
+	return 0;
+}
+
+static void __exit evbug_exit(void)
+{
+	input_unregister_handler(&evbug_handler);
+}
+
+module_init(evbug_init);
+module_exit(evbug_exit);
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
new file mode 100644
index 0000000..17552a2
--- /dev/null
+++ b/drivers/input/evdev.c
@@ -0,0 +1,492 @@
+/*
+ * Event char devices, giving access to raw input device events.
+ *
+ * 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.
+ */
+
+#define EVDEV_MINOR_BASE	64
+#define EVDEV_MINORS		32
+#define EVDEV_BUFFER_SIZE	64
+
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/major.h>
+#include <linux/smp_lock.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+
+struct evdev {
+	int exist;
+	int open;
+	int minor;
+	char name[16];
+	struct input_handle handle;
+	wait_queue_head_t wait;
+	struct evdev_list *grab;
+	struct list_head list;
+};
+
+struct evdev_list {
+	struct input_event buffer[EVDEV_BUFFER_SIZE];
+	int head;
+	int tail;
+	struct fasync_struct *fasync;
+	struct evdev *evdev;
+	struct list_head node;
+};
+
+static struct evdev *evdev_table[EVDEV_MINORS];
+
+static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+	struct evdev *evdev = handle->private;
+	struct evdev_list *list;
+
+	if (evdev->grab) {
+		list = evdev->grab;
+
+		do_gettimeofday(&list->buffer[list->head].time);
+		list->buffer[list->head].type = type;
+		list->buffer[list->head].code = code;
+		list->buffer[list->head].value = value;
+		list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
+
+		kill_fasync(&list->fasync, SIGIO, POLL_IN);
+	} else
+		list_for_each_entry(list, &evdev->list, node) {
+
+			do_gettimeofday(&list->buffer[list->head].time);
+			list->buffer[list->head].type = type;
+			list->buffer[list->head].code = code;
+			list->buffer[list->head].value = value;
+			list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
+
+			kill_fasync(&list->fasync, SIGIO, POLL_IN);
+		}
+
+	wake_up_interruptible(&evdev->wait);
+}
+
+static int evdev_fasync(int fd, struct file *file, int on)
+{
+	int retval;
+	struct evdev_list *list = file->private_data;
+	retval = fasync_helper(fd, file, on, &list->fasync);
+	return retval < 0 ? retval : 0;
+}
+
+static int evdev_flush(struct file * file)
+{
+	struct evdev_list *list = file->private_data;
+	if (!list->evdev->exist) return -ENODEV;
+	return input_flush_device(&list->evdev->handle, file);
+}
+
+static void evdev_free(struct evdev *evdev)
+{
+	evdev_table[evdev->minor] = NULL;
+	kfree(evdev);
+}
+
+static int evdev_release(struct inode * inode, struct file * file)
+{
+	struct evdev_list *list = file->private_data;
+
+	if (list->evdev->grab == list) {
+		input_release_device(&list->evdev->handle);
+		list->evdev->grab = NULL;
+	}
+
+	evdev_fasync(-1, file, 0);
+	list_del(&list->node);
+
+	if (!--list->evdev->open) {
+		if (list->evdev->exist)
+			input_close_device(&list->evdev->handle);
+		else
+			evdev_free(list->evdev);
+	}
+
+	kfree(list);
+	return 0;
+}
+
+static int evdev_open(struct inode * inode, struct file * file)
+{
+	struct evdev_list *list;
+	int i = iminor(inode) - EVDEV_MINOR_BASE;
+	int accept_err;
+
+	if (i >= EVDEV_MINORS || !evdev_table[i] || !evdev_table[i]->exist)
+		return -ENODEV;
+
+	if ((accept_err = input_accept_process(&(evdev_table[i]->handle), file)))
+		return accept_err;
+
+	if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL)))
+		return -ENOMEM;
+	memset(list, 0, sizeof(struct evdev_list));
+
+	list->evdev = evdev_table[i];
+	list_add_tail(&list->node, &evdev_table[i]->list);
+	file->private_data = list;
+
+	if (!list->evdev->open++)
+		if (list->evdev->exist)
+			input_open_device(&list->evdev->handle);
+
+	return 0;
+}
+
+static ssize_t evdev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
+{
+	struct evdev_list *list = file->private_data;
+	struct input_event event;
+	int retval = 0;
+
+	if (!list->evdev->exist) return -ENODEV;
+
+	while (retval < count) {
+
+		if (copy_from_user(&event, buffer + retval, sizeof(struct input_event)))
+			return -EFAULT;
+		input_event(list->evdev->handle.dev, event.type, event.code, event.value);
+		retval += sizeof(struct input_event);
+	}
+
+	return retval;
+}
+
+static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
+{
+	struct evdev_list *list = file->private_data;
+	int retval;
+
+	if (count < sizeof(struct input_event))
+		return -EINVAL;
+
+	if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK))
+		return -EAGAIN;
+
+	retval = wait_event_interruptible(list->evdev->wait,
+		list->head != list->tail || (!list->evdev->exist));
+
+	if (retval)
+		return retval;
+
+	if (!list->evdev->exist)
+		return -ENODEV;
+
+	while (list->head != list->tail && retval + sizeof(struct input_event) <= count) {
+		if (copy_to_user(buffer + retval, list->buffer + list->tail,
+			 sizeof(struct input_event))) return -EFAULT;
+		list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
+		retval += sizeof(struct input_event);
+	}
+
+	return retval;
+}
+
+/* No kernel lock - fine */
+static unsigned int evdev_poll(struct file *file, poll_table *wait)
+{
+	struct evdev_list *list = file->private_data;
+	poll_wait(file, &list->evdev->wait, wait);
+	return ((list->head == list->tail) ? 0 : (POLLIN | POLLRDNORM)) |
+		(list->evdev->exist ? 0 : (POLLHUP | POLLERR));
+}
+
+static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct evdev_list *list = file->private_data;
+	struct evdev *evdev = list->evdev;
+	struct input_dev *dev = evdev->handle.dev;
+	struct input_absinfo abs;
+	void __user *p = (void __user *)arg;
+	int __user *ip = (int __user *)arg;
+	int i, t, u, v;
+
+	if (!evdev->exist) return -ENODEV;
+
+	switch (cmd) {
+
+		case EVIOCGVERSION:
+			return put_user(EV_VERSION, ip);
+
+		case EVIOCGID:
+			return copy_to_user(p, &dev->id, sizeof(struct input_id)) ? -EFAULT : 0;
+
+		case EVIOCGKEYCODE:
+			if (get_user(t, ip)) return -EFAULT;
+			if (t < 0 || t >= dev->keycodemax || !dev->keycodesize) return -EINVAL;
+			if (put_user(INPUT_KEYCODE(dev, t), ip + 1)) return -EFAULT;
+			return 0;
+
+		case EVIOCSKEYCODE:
+			if (get_user(t, ip)) return -EFAULT;
+			if (t < 0 || t >= dev->keycodemax || !dev->keycodesize) return -EINVAL;
+			if (get_user(v, ip + 1)) return -EFAULT;
+			if (v < 0 || v > KEY_MAX) return -EINVAL;
+			u = SET_INPUT_KEYCODE(dev, t, v);
+			clear_bit(u, dev->keybit);
+			set_bit(v, dev->keybit);
+			for (i = 0; i < dev->keycodemax; i++)
+				if (INPUT_KEYCODE(dev,i) == u)
+					set_bit(u, dev->keybit);
+			return 0;
+
+		case EVIOCSFF:
+			if (dev->upload_effect) {
+				struct ff_effect effect;
+				int err;
+
+				if (copy_from_user(&effect, p, sizeof(effect)))
+					return -EFAULT;
+				err = dev->upload_effect(dev, &effect);
+				if (put_user(effect.id, &(((struct ff_effect __user *)arg)->id)))
+					return -EFAULT;
+				return err;
+			}
+			else return -ENOSYS;
+
+		case EVIOCRMFF:
+			if (dev->erase_effect) {
+				return dev->erase_effect(dev, (int)arg);
+			}
+			else return -ENOSYS;
+
+		case EVIOCGEFFECTS:
+			if (put_user(dev->ff_effects_max, ip))
+				return -EFAULT;
+			return 0;
+
+		case EVIOCGRAB:
+			if (arg) {
+				if (evdev->grab)
+					return -EBUSY;
+				if (input_grab_device(&evdev->handle))
+					return -EBUSY;
+				evdev->grab = list;
+				return 0;
+			} else {
+				if (evdev->grab != list)
+					return -EINVAL;
+				input_release_device(&evdev->handle);
+				evdev->grab = NULL;
+				return 0;
+			}
+
+		default:
+
+			if (_IOC_TYPE(cmd) != 'E' || _IOC_DIR(cmd) != _IOC_READ)
+				return -EINVAL;
+
+			if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
+
+				long *bits;
+				int len;
+
+				switch (_IOC_NR(cmd) & EV_MAX) {
+					case      0: bits = dev->evbit;  len = EV_MAX;  break;
+					case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
+					case EV_REL: bits = dev->relbit; len = REL_MAX; break;
+					case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
+					case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
+					case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
+					case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
+					case EV_FF:  bits = dev->ffbit;  len = FF_MAX;  break;
+					default: return -EINVAL;
+				}
+				len = NBITS(len) * sizeof(long);
+				if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+				return copy_to_user(p, bits, len) ? -EFAULT : len;
+			}
+
+			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) {
+				int len;
+				len = NBITS(KEY_MAX) * sizeof(long);
+				if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+				return copy_to_user(p, dev->key, len) ? -EFAULT : len;
+			}
+
+			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) {
+				int len;
+				len = NBITS(LED_MAX) * sizeof(long);
+				if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+				return copy_to_user(p, dev->led, len) ? -EFAULT : len;
+			}
+
+			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0))) {
+				int len;
+				len = NBITS(SND_MAX) * sizeof(long);
+				if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+				return copy_to_user(p, dev->snd, len) ? -EFAULT : len;
+			}
+
+			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) {
+				int len;
+				if (!dev->name) return -ENOENT;
+				len = strlen(dev->name) + 1;
+				if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+				return copy_to_user(p, dev->name, len) ? -EFAULT : len;
+			}
+
+			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) {
+				int len;
+				if (!dev->phys) return -ENOENT;
+				len = strlen(dev->phys) + 1;
+				if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+				return copy_to_user(p, dev->phys, len) ? -EFAULT : len;
+			}
+
+			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) {
+				int len;
+				if (!dev->uniq) return -ENOENT;
+				len = strlen(dev->uniq) + 1;
+				if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+				return copy_to_user(p, dev->uniq, len) ? -EFAULT : len;
+			}
+
+			if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
+
+				int t = _IOC_NR(cmd) & ABS_MAX;
+
+				abs.value = dev->abs[t];
+				abs.minimum = dev->absmin[t];
+				abs.maximum = dev->absmax[t];
+				abs.fuzz = dev->absfuzz[t];
+				abs.flat = dev->absflat[t];
+
+				if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
+					return -EFAULT;
+
+				return 0;
+			}
+
+			if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
+
+				int t = _IOC_NR(cmd) & ABS_MAX;
+
+				if (copy_from_user(&abs, p, sizeof(struct input_absinfo)))
+					return -EFAULT;
+
+				dev->abs[t] = abs.value;
+				dev->absmin[t] = abs.minimum;
+				dev->absmax[t] = abs.maximum;
+				dev->absfuzz[t] = abs.fuzz;
+				dev->absflat[t] = abs.flat;
+
+				return 0;
+			}
+	}
+	return -EINVAL;
+}
+
+static struct file_operations evdev_fops = {
+	.owner =	THIS_MODULE,
+	.read =		evdev_read,
+	.write =	evdev_write,
+	.poll =		evdev_poll,
+	.open =		evdev_open,
+	.release =	evdev_release,
+	.ioctl =	evdev_ioctl,
+	.fasync =	evdev_fasync,
+	.flush =	evdev_flush
+};
+
+static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
+{
+	struct evdev *evdev;
+	int minor;
+
+	for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
+	if (minor == EVDEV_MINORS) {
+		printk(KERN_ERR "evdev: no more free evdev devices\n");
+		return NULL;
+	}
+
+	if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL)))
+		return NULL;
+	memset(evdev, 0, sizeof(struct evdev));
+
+	INIT_LIST_HEAD(&evdev->list);
+	init_waitqueue_head(&evdev->wait);
+
+	evdev->exist = 1;
+	evdev->minor = minor;
+	evdev->handle.dev = dev;
+	evdev->handle.name = evdev->name;
+	evdev->handle.handler = handler;
+	evdev->handle.private = evdev;
+	sprintf(evdev->name, "event%d", minor);
+
+	evdev_table[minor] = evdev;
+
+	devfs_mk_cdev(MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
+			S_IFCHR|S_IRUGO|S_IWUSR, "input/event%d", minor);
+	class_simple_device_add(input_class,
+				MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
+				dev->dev, "event%d", minor);
+
+	return &evdev->handle;
+}
+
+static void evdev_disconnect(struct input_handle *handle)
+{
+	struct evdev *evdev = handle->private;
+	struct evdev_list *list;
+
+	class_simple_device_remove(MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + evdev->minor));
+	devfs_remove("input/event%d", evdev->minor);
+	evdev->exist = 0;
+
+	if (evdev->open) {
+		input_close_device(handle);
+		wake_up_interruptible(&evdev->wait);
+		list_for_each_entry(list, &evdev->list, node)
+			kill_fasync(&list->fasync, SIGIO, POLL_HUP);
+	} else
+		evdev_free(evdev);
+}
+
+static struct input_device_id evdev_ids[] = {
+	{ .driver_info = 1 },	/* Matches all devices */
+	{ },			/* Terminating zero entry */
+};
+
+MODULE_DEVICE_TABLE(input, evdev_ids);
+
+static struct input_handler evdev_handler = {
+	.event =	evdev_event,
+	.connect =	evdev_connect,
+	.disconnect =	evdev_disconnect,
+	.fops =		&evdev_fops,
+	.minor =	EVDEV_MINOR_BASE,
+	.name =		"evdev",
+	.id_table =	evdev_ids,
+};
+
+static int __init evdev_init(void)
+{
+	input_register_handler(&evdev_handler);
+	return 0;
+}
+
+static void __exit evdev_exit(void)
+{
+	input_unregister_handler(&evdev_handler);
+}
+
+module_init(evdev_init);
+module_exit(evdev_exit);
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Input driver event char devices");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/gameport/Kconfig b/drivers/input/gameport/Kconfig
new file mode 100644
index 0000000..6282f46
--- /dev/null
+++ b/drivers/input/gameport/Kconfig
@@ -0,0 +1,90 @@
+#
+# Gameport configuration
+#
+config GAMEPORT
+	tristate "Gameport support"
+	---help---
+	  Gameport support is for the standard 15-pin PC gameport. If you
+	  have a joystick, gamepad, gameport card, a soundcard with a gameport
+	  or anything else that uses the gameport, say Y or M here and also to
+	  at least one of the hardware specific drivers.
+
+	  For Ensoniq AudioPCI (ES1370), AudioPCI 97 (ES1371), ESS Solo1,
+	  S3 SonicVibes, Trident 4DWave, SiS7018, and ALi 5451 gameport
+	  support is provided by the sound drivers, so you won't need any
+	  from the below listed modules. You still need to say Y here.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gameport.
+
+if GAMEPORT
+
+config GAMEPORT_NS558
+	tristate "Classic ISA and PnP gameport support"
+	help
+	  Say Y here if you have an ISA or PnP gameport.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ns558.
+
+config GAMEPORT_L4
+	tristate "PDPI Lightning 4 gamecard support"
+	help
+	  Say Y here if you have a PDPI Lightning 4 gamecard.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called lightning.
+
+config GAMEPORT_EMU10K1
+	tristate "SB Live and Audigy gameport support"
+	depends on PCI
+	help
+	  Say Y here if you have a SoundBlaster Live! or SoundBlaster
+	  Audigy card and want to use its gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called emu10k1-gp.
+
+config GAMEPORT_VORTEX
+	tristate "Aureal Vortex, Vortex 2 gameport support"
+	depends on PCI
+	help
+	  Say Y here if you have an Aureal Vortex 1 or 2  card and want
+	  to use its gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called vortex.
+
+config GAMEPORT_FM801
+	tristate "ForteMedia FM801 gameport support"
+	depends on PCI
+
+config GAMEPORT_CS461X
+	tristate "Crystal SoundFusion gameport support"
+	depends on PCI
+
+endif
+
+# Yes, SOUND_GAMEPORT looks a bit odd. Yes, it ends up being turned on
+# in every .config. Please don't touch it. It is here to handle an
+# unusual dependency between GAMEPORT and sound drivers.
+#
+# Some sound drivers call gameport functions. If GAMEPORT is
+# not selected, empty stubs are provided for the functions and all is
+# well.
+# If GAMEPORT is built in, everything is fine.
+# If GAMEPORT is a module, however, it would need to be loaded for the
+# sound driver to be able to link properly. Therefore, the sound
+# driver must be a module as well in that case. Since there's no way
+# to express that directly in Kconfig, we use SOUND_GAMEPORT to
+# express it. SOUND_GAMEPORT boils down to "if GAMEPORT is 'm',
+# anything that depends on SOUND_GAMEPORT must be 'm' as well. if
+# GAMEPORT is 'y' or 'n', it can be anything".
+config SOUND_GAMEPORT
+	tristate
+	default m if GAMEPORT=m
+	default y
diff --git a/drivers/input/gameport/Makefile b/drivers/input/gameport/Makefile
new file mode 100644
index 0000000..5367b42
--- /dev/null
+++ b/drivers/input/gameport/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the gameport drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_GAMEPORT)		+= gameport.o
+obj-$(CONFIG_GAMEPORT_CS461X)	+= cs461x.o
+obj-$(CONFIG_GAMEPORT_EMU10K1)	+= emu10k1-gp.o
+obj-$(CONFIG_GAMEPORT_FM801)	+= fm801-gp.o
+obj-$(CONFIG_GAMEPORT_L4)	+= lightning.o
+obj-$(CONFIG_GAMEPORT_NS558)	+= ns558.o
+obj-$(CONFIG_GAMEPORT_VORTEX)	+= vortex.o
diff --git a/drivers/input/gameport/cs461x.c b/drivers/input/gameport/cs461x.c
new file mode 100644
index 0000000..d4013ff
--- /dev/null
+++ b/drivers/input/gameport/cs461x.c
@@ -0,0 +1,322 @@
+/*
+	The all defines and part of code (such as cs461x_*) are
+	contributed from ALSA 0.5.8 sources.
+	See http://www.alsa-project.org/ for sources
+
+	Tested on Linux 686 2.4.0-test9, ALSA 0.5.8a and CS4610
+*/
+
+#include <asm/io.h>
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+
+MODULE_AUTHOR("Victor Krapivin");
+MODULE_LICENSE("GPL");
+
+/*
+	These options are experimental
+
+#define CS461X_FULL_MAP
+*/
+
+
+#ifndef PCI_VENDOR_ID_CIRRUS
+#define PCI_VENDOR_ID_CIRRUS            0x1013
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_4610
+#define PCI_DEVICE_ID_CIRRUS_4610       0x6001
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_4612
+#define PCI_DEVICE_ID_CIRRUS_4612       0x6003
+#endif
+#ifndef PCI_DEVICE_ID_CIRRUS_4615
+#define PCI_DEVICE_ID_CIRRUS_4615       0x6004
+#endif
+
+/* Registers */
+
+#define BA0_JSPT                                0x00000480
+#define BA0_JSCTL                               0x00000484
+#define BA0_JSC1                                0x00000488
+#define BA0_JSC2                                0x0000048C
+#define BA0_JSIO                                0x000004A0
+
+/* Bits for JSPT */
+
+#define JSPT_CAX                                0x00000001
+#define JSPT_CAY                                0x00000002
+#define JSPT_CBX                                0x00000004
+#define JSPT_CBY                                0x00000008
+#define JSPT_BA1                                0x00000010
+#define JSPT_BA2                                0x00000020
+#define JSPT_BB1                                0x00000040
+#define JSPT_BB2                                0x00000080
+
+/* Bits for JSCTL */
+
+#define JSCTL_SP_MASK                           0x00000003
+#define JSCTL_SP_SLOW                           0x00000000
+#define JSCTL_SP_MEDIUM_SLOW                    0x00000001
+#define JSCTL_SP_MEDIUM_FAST                    0x00000002
+#define JSCTL_SP_FAST                           0x00000003
+#define JSCTL_ARE                               0x00000004
+
+/* Data register pairs masks */
+
+#define JSC1_Y1V_MASK                           0x0000FFFF
+#define JSC1_X1V_MASK                           0xFFFF0000
+#define JSC1_Y1V_SHIFT                          0
+#define JSC1_X1V_SHIFT                          16
+#define JSC2_Y2V_MASK                           0x0000FFFF
+#define JSC2_X2V_MASK                           0xFFFF0000
+#define JSC2_Y2V_SHIFT                          0
+#define JSC2_X2V_SHIFT                          16
+
+/* JS GPIO */
+
+#define JSIO_DAX                                0x00000001
+#define JSIO_DAY                                0x00000002
+#define JSIO_DBX                                0x00000004
+#define JSIO_DBY                                0x00000008
+#define JSIO_AXOE                               0x00000010
+#define JSIO_AYOE                               0x00000020
+#define JSIO_BXOE                               0x00000040
+#define JSIO_BYOE                               0x00000080
+
+/*
+   The card initialization code is obfuscated; the module cs461x
+   need to be loaded after ALSA modules initialized and something
+   played on the CS 4610 chip (see sources for details of CS4610
+   initialization code from ALSA)
+*/
+
+/* Card specific definitions */
+
+#define CS461X_BA0_SIZE         0x2000
+#define CS461X_BA1_DATA0_SIZE   0x3000
+#define CS461X_BA1_DATA1_SIZE   0x3800
+#define CS461X_BA1_PRG_SIZE     0x7000
+#define CS461X_BA1_REG_SIZE     0x0100
+
+#define BA1_SP_DMEM0                            0x00000000
+#define BA1_SP_DMEM1                            0x00010000
+#define BA1_SP_PMEM                             0x00020000
+#define BA1_SP_REG                              0x00030000
+
+#define BA1_DWORD_SIZE          (13 * 1024 + 512)
+#define BA1_MEMORY_COUNT        3
+
+/*
+   Only one CS461x card is still suppoted; the code requires
+   redesign to avoid this limitatuion.
+*/
+
+static unsigned long ba0_addr;
+static unsigned int __iomem *ba0;
+
+#ifdef CS461X_FULL_MAP
+static unsigned long ba1_addr;
+static union ba1_t {
+        struct {
+                unsigned int __iomem *data0;
+                unsigned int __iomem *data1;
+                unsigned int __iomem *pmem;
+                unsigned int __iomem *reg;
+        } name;
+        unsigned int __iomem *idx[4];
+} ba1;
+
+static void cs461x_poke(unsigned long reg, unsigned int val)
+{
+        writel(val, &ba1.idx[(reg >> 16) & 3][(reg >> 2) & 0x3fff]);
+}
+
+static unsigned int cs461x_peek(unsigned long reg)
+{
+        return readl(&ba1.idx[(reg >> 16) & 3][(reg >> 2) & 0x3fff]);
+}
+
+#endif
+
+static void cs461x_pokeBA0(unsigned long reg, unsigned int val)
+{
+        writel(val, &ba0[reg >> 2]);
+}
+
+static unsigned int cs461x_peekBA0(unsigned long reg)
+{
+        return readl(&ba0[reg >> 2]);
+}
+
+static int cs461x_free(struct pci_dev *pdev)
+{
+	struct gameport *port = pci_get_drvdata(pdev);
+
+	if (port)
+	    gameport_unregister_port(port);
+
+	if (ba0) iounmap(ba0);
+#ifdef CS461X_FULL_MAP
+	if (ba1.name.data0) iounmap(ba1.name.data0);
+	if (ba1.name.data1) iounmap(ba1.name.data1);
+	if (ba1.name.pmem)  iounmap(ba1.name.pmem);
+	if (ba1.name.reg)   iounmap(ba1.name.reg);
+#endif
+	return 0;
+}
+
+static void cs461x_gameport_trigger(struct gameport *gameport)
+{
+	cs461x_pokeBA0(BA0_JSPT, 0xFF);  //outb(gameport->io, 0xFF);
+}
+
+static unsigned char cs461x_gameport_read(struct gameport *gameport)
+{
+	return cs461x_peekBA0(BA0_JSPT); //inb(gameport->io);
+}
+
+static int cs461x_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+	unsigned js1, js2, jst;
+
+	js1 = cs461x_peekBA0(BA0_JSC1);
+	js2 = cs461x_peekBA0(BA0_JSC2);
+	jst = cs461x_peekBA0(BA0_JSPT);
+
+	*buttons = (~jst >> 4) & 0x0F;
+
+	axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF;
+	axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF;
+	axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF;
+	axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF;
+
+	for(jst=0;jst<4;++jst)
+		if(axes[jst]==0xFFFF) axes[jst] = -1;
+	return 0;
+}
+
+static int cs461x_gameport_open(struct gameport *gameport, int mode)
+{
+	switch (mode) {
+		case GAMEPORT_MODE_COOKED:
+		case GAMEPORT_MODE_RAW:
+			return 0;
+		default:
+			return -1;
+	}
+	return 0;
+}
+
+static struct pci_device_id cs461x_pci_tbl[] = {
+	{ PCI_VENDOR_ID_CIRRUS, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4610 */
+	{ PCI_VENDOR_ID_CIRRUS, 0x6003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4612 */
+	{ PCI_VENDOR_ID_CIRRUS, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Cirrus CS4615 */
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, cs461x_pci_tbl);
+
+static int __devinit cs461x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int rc;
+	struct gameport* port;
+
+	rc = pci_enable_device(pdev);
+	if (rc) {
+		printk(KERN_ERR "cs461x: Cannot enable PCI gameport (bus %d, devfn %d) error=%d\n",
+			pdev->bus->number, pdev->devfn, rc);
+		return rc;
+	}
+
+	ba0_addr = pci_resource_start(pdev, 0);
+#ifdef CS461X_FULL_MAP
+	ba1_addr = pci_resource_start(pdev, 1);
+#endif
+	if (ba0_addr == 0 || ba0_addr == ~0
+#ifdef CS461X_FULL_MAP
+            || ba1_addr == 0 || ba1_addr == ~0
+#endif
+	    ) {
+                printk(KERN_ERR "cs461x: wrong address - ba0 = 0x%lx\n", ba0_addr);
+#ifdef CS461X_FULL_MAP
+                printk(KERN_ERR "cs461x: wrong address - ba1 = 0x%lx\n", ba1_addr);
+#endif
+		cs461x_free(pdev);
+                return -ENOMEM;
+        }
+
+	ba0 = ioremap(ba0_addr, CS461X_BA0_SIZE);
+#ifdef CS461X_FULL_MAP
+	ba1.name.data0 = ioremap(ba1_addr + BA1_SP_DMEM0, CS461X_BA1_DATA0_SIZE);
+	ba1.name.data1 = ioremap(ba1_addr + BA1_SP_DMEM1, CS461X_BA1_DATA1_SIZE);
+	ba1.name.pmem  = ioremap(ba1_addr + BA1_SP_PMEM, CS461X_BA1_PRG_SIZE);
+	ba1.name.reg   = ioremap(ba1_addr + BA1_SP_REG, CS461X_BA1_REG_SIZE);
+
+	if (ba0 == NULL || ba1.name.data0 == NULL ||
+            ba1.name.data1 == NULL || ba1.name.pmem == NULL ||
+            ba1.name.reg == NULL) {
+		cs461x_free(pdev);
+                return -ENOMEM;
+        }
+#else
+	if (ba0 == NULL) {
+		cs461x_free(pdev);
+		return -ENOMEM;
+	}
+#endif
+
+	if (!(port = gameport_allocate_port())) {
+		printk(KERN_ERR "cs461x: Memory allocation failed\n");
+		cs461x_free(pdev);
+		return -ENOMEM;
+	}
+
+	pci_set_drvdata(pdev, port);
+
+	port->open = cs461x_gameport_open;
+	port->trigger = cs461x_gameport_trigger;
+	port->read = cs461x_gameport_read;
+	port->cooked_read = cs461x_gameport_cooked_read;
+
+	gameport_set_name(port, "CS416x");
+	gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev));
+	port->dev.parent = &pdev->dev;
+
+	cs461x_pokeBA0(BA0_JSIO, 0xFF); // ?
+	cs461x_pokeBA0(BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW);
+
+	gameport_register_port(port);
+
+	return 0;
+}
+
+static void __devexit cs461x_pci_remove(struct pci_dev *pdev)
+{
+	cs461x_free(pdev);
+}
+
+static struct pci_driver cs461x_pci_driver = {
+        .name =         "CS461x_gameport",
+        .id_table =     cs461x_pci_tbl,
+        .probe =        cs461x_pci_probe,
+        .remove =       __devexit_p(cs461x_pci_remove),
+};
+
+static int __init cs461x_init(void)
+{
+        return pci_register_driver(&cs461x_pci_driver);
+}
+
+static void __exit cs461x_exit(void)
+{
+        pci_unregister_driver(&cs461x_pci_driver);
+}
+
+module_init(cs461x_init);
+module_exit(cs461x_exit);
+
diff --git a/drivers/input/gameport/emu10k1-gp.c b/drivers/input/gameport/emu10k1-gp.c
new file mode 100644
index 0000000..a011803
--- /dev/null
+++ b/drivers/input/gameport/emu10k1-gp.c
@@ -0,0 +1,132 @@
+/*
+ * $Id: emu10k1-gp.c,v 1.8 2002/01/22 20:40:46 vojtech Exp $
+ *
+ *  Copyright (c) 2001 Vojtech Pavlik
+ */
+
+/*
+ * EMU10k1 - SB Live / Audigy - gameport 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 <asm/io.h>
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("EMU10k1 gameport driver");
+MODULE_LICENSE("GPL");
+
+struct emu {
+	struct pci_dev *dev;
+	struct gameport *gameport;
+	int io;
+	int size;
+};
+
+static struct pci_device_id emu_tbl[] = {
+
+	{ 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID }, /* SB Live gameport */
+	{ 0x1102, 0x7003, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy gameport */
+	{ 0x1102, 0x7004, PCI_ANY_ID, PCI_ANY_ID }, /* Dell SB Live */
+	{ 0x1102, 0x7005, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy LS gameport */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, emu_tbl);
+
+static int __devinit emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int ioport, iolen;
+	struct emu *emu;
+	struct gameport *port;
+
+	if (pci_enable_device(pdev))
+		return -EBUSY;
+
+	ioport = pci_resource_start(pdev, 0);
+	iolen = pci_resource_len(pdev, 0);
+
+	if (!request_region(ioport, iolen, "emu10k1-gp"))
+		return -EBUSY;
+
+	emu = kcalloc(1, sizeof(struct emu), GFP_KERNEL);
+	port = gameport_allocate_port();
+	if (!emu || !port) {
+		printk(KERN_ERR "emu10k1-gp: Memory allocation failed\n");
+		release_region(ioport, iolen);
+		kfree(emu);
+		gameport_free_port(port);
+		return -ENOMEM;
+	}
+
+	emu->io = ioport;
+	emu->size = iolen;
+	emu->dev = pdev;
+	emu->gameport = port;
+
+	gameport_set_name(port, "EMU10K1");
+	gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev));
+	port->dev.parent = &pdev->dev;
+	port->io = ioport;
+
+	pci_set_drvdata(pdev, emu);
+
+	gameport_register_port(port);
+
+	return 0;
+}
+
+static void __devexit emu_remove(struct pci_dev *pdev)
+{
+	struct emu *emu = pci_get_drvdata(pdev);
+
+	gameport_unregister_port(emu->gameport);
+	release_region(emu->io, emu->size);
+	kfree(emu);
+}
+
+static struct pci_driver emu_driver = {
+        .name =         "Emu10k1_gameport",
+        .id_table =     emu_tbl,
+        .probe =        emu_probe,
+        .remove =       __devexit_p(emu_remove),
+};
+
+static int __init emu_init(void)
+{
+	return pci_register_driver(&emu_driver);
+}
+
+static void __exit emu_exit(void)
+{
+	pci_unregister_driver(&emu_driver);
+}
+
+module_init(emu_init);
+module_exit(emu_exit);
diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c
new file mode 100644
index 0000000..57615bc
--- /dev/null
+++ b/drivers/input/gameport/fm801-gp.c
@@ -0,0 +1,163 @@
+/*
+ *  FM801 gameport driver for Linux
+ *
+ *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *   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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+
+#define PCI_VENDOR_ID_FORTEMEDIA	0x1319
+#define PCI_DEVICE_ID_FM801_GP	0x0802
+
+#define HAVE_COOKED
+
+struct fm801_gp {
+	struct gameport *gameport;
+	struct resource *res_port;
+};
+
+#ifdef HAVE_COOKED
+static int fm801_gp_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+	unsigned short w;
+
+	w = inw(gameport->io + 2);
+	*buttons = (~w >> 14) & 0x03;
+	axes[0] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
+	w = inw(gameport->io + 4);
+	axes[1] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
+	w = inw(gameport->io + 6);
+	*buttons |= ((~w >> 14) & 0x03) << 2;
+	axes[2] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
+	w = inw(gameport->io + 8);
+	axes[3] = (w == 0xffff) ? -1 : ((w & 0x1fff) << 5);
+	outw(0xff, gameport->io); /* reset */
+
+        return 0;
+}
+#endif
+
+static int fm801_gp_open(struct gameport *gameport, int mode)
+{
+	switch (mode) {
+#ifdef HAVE_COOKED
+	case GAMEPORT_MODE_COOKED:
+		return 0;
+#endif
+	case GAMEPORT_MODE_RAW:
+		return 0;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+static int __devinit fm801_gp_probe(struct pci_dev *pci, const struct pci_device_id *id)
+{
+	struct fm801_gp *gp;
+	struct gameport *port;
+
+	gp = kcalloc(1, sizeof(struct fm801_gp), GFP_KERNEL);
+	port = gameport_allocate_port();
+	if (!gp || !port) {
+		printk(KERN_ERR "fm801-gp: Memory allocation failed\n");
+		kfree(gp);
+		gameport_free_port(port);
+		return -ENOMEM;
+	}
+
+	pci_enable_device(pci);
+
+	port->open = fm801_gp_open;
+#ifdef HAVE_COOKED
+	port->cooked_read = fm801_gp_cooked_read;
+#endif
+	gameport_set_name(port, "FM801");
+	gameport_set_phys(port, "pci%s/gameport0", pci_name(pci));
+	port->dev.parent = &pci->dev;
+	port->io = pci_resource_start(pci, 0);
+
+	gp->gameport = port;
+	gp->res_port = request_region(port->io, 0x10, "FM801 GP");
+	if (!gp->res_port) {
+		kfree(gp);
+		gameport_free_port(port);
+		printk(KERN_DEBUG "fm801-gp: unable to grab region 0x%x-0x%x\n",
+			port->io, port->io + 0x0f);
+		return -EBUSY;
+	}
+
+	pci_set_drvdata(pci, gp);
+
+	outb(0x60, port->io + 0x0d); /* enable joystick 1 and 2 */
+	gameport_register_port(port);
+
+	return 0;
+}
+
+static void __devexit fm801_gp_remove(struct pci_dev *pci)
+{
+	struct fm801_gp *gp = pci_get_drvdata(pci);
+
+	if (gp) {
+		gameport_unregister_port(gp->gameport);
+		release_resource(gp->res_port);
+		kfree(gp);
+	}
+}
+
+static struct pci_device_id fm801_gp_id_table[] = {
+	{ PCI_VENDOR_ID_FORTEMEDIA, PCI_DEVICE_ID_FM801_GP, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0  },
+	{ 0 }
+};
+
+static struct pci_driver fm801_gp_driver = {
+	.name =		"FM801_gameport",
+	.id_table =	fm801_gp_id_table,
+	.probe =	fm801_gp_probe,
+	.remove =	__devexit_p(fm801_gp_remove),
+};
+
+static int __init fm801_gp_init(void)
+{
+	return pci_register_driver(&fm801_gp_driver);
+}
+
+static void __exit fm801_gp_exit(void)
+{
+	pci_unregister_driver(&fm801_gp_driver);
+}
+
+module_init(fm801_gp_init);
+module_exit(fm801_gp_exit);
+
+MODULE_DEVICE_TABLE(pci, fm801_gp_id_table);
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c
new file mode 100644
index 0000000..f20c3f2
--- /dev/null
+++ b/drivers/input/gameport/gameport.c
@@ -0,0 +1,797 @@
+/*
+ * Generic gameport layer
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ * Copyright (c) 2005 Dmitry Torokhov
+ */
+
+/*
+ * 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/stddef.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/wait.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+/*#include <asm/io.h>*/
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Generic gameport layer");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(__gameport_register_port);
+EXPORT_SYMBOL(gameport_unregister_port);
+EXPORT_SYMBOL(__gameport_register_driver);
+EXPORT_SYMBOL(gameport_unregister_driver);
+EXPORT_SYMBOL(gameport_open);
+EXPORT_SYMBOL(gameport_close);
+EXPORT_SYMBOL(gameport_rescan);
+EXPORT_SYMBOL(gameport_cooked_read);
+EXPORT_SYMBOL(gameport_set_name);
+EXPORT_SYMBOL(gameport_set_phys);
+EXPORT_SYMBOL(gameport_start_polling);
+EXPORT_SYMBOL(gameport_stop_polling);
+
+/*
+ * gameport_sem protects entire gameport subsystem and is taken
+ * every time gameport port or driver registrered or unregistered.
+ */
+static DECLARE_MUTEX(gameport_sem);
+
+static LIST_HEAD(gameport_list);
+
+static struct bus_type gameport_bus = {
+	.name =	"gameport",
+};
+
+static void gameport_add_port(struct gameport *gameport);
+static void gameport_destroy_port(struct gameport *gameport);
+static void gameport_reconnect_port(struct gameport *gameport);
+static void gameport_disconnect_port(struct gameport *gameport);
+
+#if defined(__i386__)
+
+#define DELTA(x,y)      ((y)-(x)+((y)<(x)?1193182/HZ:0))
+#define GET_TIME(x)     do { x = get_time_pit(); } while (0)
+
+static unsigned int get_time_pit(void)
+{
+	extern spinlock_t i8253_lock;
+	unsigned long flags;
+	unsigned int count;
+
+	spin_lock_irqsave(&i8253_lock, flags);
+	outb_p(0x00, 0x43);
+	count = inb_p(0x40);
+	count |= inb_p(0x40) << 8;
+	spin_unlock_irqrestore(&i8253_lock, flags);
+
+	return count;
+}
+
+#endif
+
+
+
+/*
+ * gameport_measure_speed() measures the gameport i/o speed.
+ */
+
+static int gameport_measure_speed(struct gameport *gameport)
+{
+#if defined(__i386__)
+
+	unsigned int i, t, t1, t2, t3, tx;
+	unsigned long flags;
+
+	if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
+		return 0;
+
+	tx = 1 << 30;
+
+	for(i = 0; i < 50; i++) {
+		local_irq_save(flags);
+		GET_TIME(t1);
+		for (t = 0; t < 50; t++) gameport_read(gameport);
+		GET_TIME(t2);
+		GET_TIME(t3);
+		local_irq_restore(flags);
+		udelay(i * 10);
+		if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t;
+	}
+
+	gameport_close(gameport);
+	return 59659 / (tx < 1 ? 1 : tx);
+
+#elif defined (__x86_64__)
+
+	unsigned int i, t;
+	unsigned long tx, t1, t2, flags;
+
+	if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
+		return 0;
+
+	tx = 1 << 30;
+
+	for(i = 0; i < 50; i++) {
+		local_irq_save(flags);
+		rdtscl(t1);
+		for (t = 0; t < 50; t++) gameport_read(gameport);
+		rdtscl(t2);
+		local_irq_restore(flags);
+		udelay(i * 10);
+		if (t2 - t1 < tx) tx = t2 - t1;
+	}
+
+	gameport_close(gameport);
+	return (cpu_data[_smp_processor_id()].loops_per_jiffy * (unsigned long)HZ / (1000 / 50)) / (tx < 1 ? 1 : tx);
+
+#else
+
+	unsigned int j, t = 0;
+
+	if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW))
+		return 0;
+
+	j = jiffies; while (j == jiffies);
+	j = jiffies; while (j == jiffies) { t++; gameport_read(gameport); }
+
+	gameport_close(gameport);
+	return t * HZ / 1000;
+
+#endif
+}
+
+void gameport_start_polling(struct gameport *gameport)
+{
+	spin_lock(&gameport->timer_lock);
+
+	if (!gameport->poll_cnt++) {
+		BUG_ON(!gameport->poll_handler);
+		BUG_ON(!gameport->poll_interval);
+		mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval));
+	}
+
+	spin_unlock(&gameport->timer_lock);
+}
+
+void gameport_stop_polling(struct gameport *gameport)
+{
+	spin_lock(&gameport->timer_lock);
+
+	if (!--gameport->poll_cnt)
+		del_timer(&gameport->poll_timer);
+
+	spin_unlock(&gameport->timer_lock);
+}
+
+static void gameport_run_poll_handler(unsigned long d)
+{
+	struct gameport *gameport = (struct gameport *)d;
+
+	gameport->poll_handler(gameport);
+	if (gameport->poll_cnt)
+		mod_timer(&gameport->poll_timer, jiffies + msecs_to_jiffies(gameport->poll_interval));
+}
+
+/*
+ * Basic gameport -> driver core mappings
+ */
+
+static void gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv)
+{
+	down_write(&gameport_bus.subsys.rwsem);
+
+	gameport->dev.driver = &drv->driver;
+	if (drv->connect(gameport, drv)) {
+		gameport->dev.driver = NULL;
+		goto out;
+	}
+	device_bind_driver(&gameport->dev);
+out:
+	up_write(&gameport_bus.subsys.rwsem);
+}
+
+static void gameport_release_driver(struct gameport *gameport)
+{
+	down_write(&gameport_bus.subsys.rwsem);
+	device_release_driver(&gameport->dev);
+	up_write(&gameport_bus.subsys.rwsem);
+}
+
+static void gameport_find_driver(struct gameport *gameport)
+{
+	down_write(&gameport_bus.subsys.rwsem);
+	device_attach(&gameport->dev);
+	up_write(&gameport_bus.subsys.rwsem);
+}
+
+
+/*
+ * Gameport event processing.
+ */
+
+enum gameport_event_type {
+	GAMEPORT_RESCAN,
+	GAMEPORT_RECONNECT,
+	GAMEPORT_REGISTER_PORT,
+	GAMEPORT_REGISTER_DRIVER,
+};
+
+struct gameport_event {
+	enum gameport_event_type type;
+	void *object;
+	struct module *owner;
+	struct list_head node;
+};
+
+static DEFINE_SPINLOCK(gameport_event_lock);	/* protects gameport_event_list */
+static LIST_HEAD(gameport_event_list);
+static DECLARE_WAIT_QUEUE_HEAD(gameport_wait);
+static DECLARE_COMPLETION(gameport_exited);
+static int gameport_pid;
+
+static void gameport_queue_event(void *object, struct module *owner,
+			      enum gameport_event_type event_type)
+{
+	unsigned long flags;
+	struct gameport_event *event;
+
+	spin_lock_irqsave(&gameport_event_lock, flags);
+
+	/*
+ 	 * Scan event list for the other events for the same gameport port,
+	 * starting with the most recent one. If event is the same we
+	 * do not need add new one. If event is of different type we
+	 * need to add this event and should not look further because
+	 * we need to preseve sequence of distinct events.
+ 	 */
+	list_for_each_entry_reverse(event, &gameport_event_list, node) {
+		if (event->object == object) {
+			if (event->type == event_type)
+				goto out;
+			break;
+		}
+	}
+
+	if ((event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC))) {
+		if (!try_module_get(owner)) {
+			printk(KERN_WARNING "gameport: Can't get module reference, dropping event %d\n", event_type);
+			goto out;
+		}
+
+		event->type = event_type;
+		event->object = object;
+		event->owner = owner;
+
+		list_add_tail(&event->node, &gameport_event_list);
+		wake_up(&gameport_wait);
+	} else {
+		printk(KERN_ERR "gameport: Not enough memory to queue event %d\n", event_type);
+	}
+out:
+	spin_unlock_irqrestore(&gameport_event_lock, flags);
+}
+
+static void gameport_free_event(struct gameport_event *event)
+{
+	module_put(event->owner);
+	kfree(event);
+}
+
+static void gameport_remove_duplicate_events(struct gameport_event *event)
+{
+	struct list_head *node, *next;
+	struct gameport_event *e;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gameport_event_lock, flags);
+
+	list_for_each_safe(node, next, &gameport_event_list) {
+		e = list_entry(node, struct gameport_event, node);
+		if (event->object == e->object) {
+			/*
+			 * If this event is of different type we should not
+			 * look further - we only suppress duplicate events
+			 * that were sent back-to-back.
+			 */
+			if (event->type != e->type)
+				break;
+
+			list_del_init(node);
+			gameport_free_event(e);
+		}
+	}
+
+	spin_unlock_irqrestore(&gameport_event_lock, flags);
+}
+
+
+static struct gameport_event *gameport_get_event(void)
+{
+	struct gameport_event *event;
+	struct list_head *node;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gameport_event_lock, flags);
+
+	if (list_empty(&gameport_event_list)) {
+		spin_unlock_irqrestore(&gameport_event_lock, flags);
+		return NULL;
+	}
+
+	node = gameport_event_list.next;
+	event = list_entry(node, struct gameport_event, node);
+	list_del_init(node);
+
+	spin_unlock_irqrestore(&gameport_event_lock, flags);
+
+	return event;
+}
+
+static void gameport_handle_events(void)
+{
+	struct gameport_event *event;
+	struct gameport_driver *gameport_drv;
+
+	down(&gameport_sem);
+
+	while ((event = gameport_get_event())) {
+
+		switch (event->type) {
+			case GAMEPORT_REGISTER_PORT:
+				gameport_add_port(event->object);
+				break;
+
+			case GAMEPORT_RECONNECT:
+				gameport_reconnect_port(event->object);
+				break;
+
+			case GAMEPORT_RESCAN:
+				gameport_disconnect_port(event->object);
+				gameport_find_driver(event->object);
+				break;
+
+			case GAMEPORT_REGISTER_DRIVER:
+				gameport_drv = event->object;
+				driver_register(&gameport_drv->driver);
+				break;
+
+			default:
+				break;
+		}
+
+		gameport_remove_duplicate_events(event);
+		gameport_free_event(event);
+	}
+
+	up(&gameport_sem);
+}
+
+/*
+ * Remove all events that have been submitted for a given gameport port.
+ */
+static void gameport_remove_pending_events(struct gameport *gameport)
+{
+	struct list_head *node, *next;
+	struct gameport_event *event;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gameport_event_lock, flags);
+
+	list_for_each_safe(node, next, &gameport_event_list) {
+		event = list_entry(node, struct gameport_event, node);
+		if (event->object == gameport) {
+			list_del_init(node);
+			gameport_free_event(event);
+		}
+	}
+
+	spin_unlock_irqrestore(&gameport_event_lock, flags);
+}
+
+/*
+ * Destroy child gameport port (if any) that has not been fully registered yet.
+ *
+ * Note that we rely on the fact that port can have only one child and therefore
+ * only one child registration request can be pending. Additionally, children
+ * are registered by driver's connect() handler so there can't be a grandchild
+ * pending registration together with a child.
+ */
+static struct gameport *gameport_get_pending_child(struct gameport *parent)
+{
+	struct gameport_event *event;
+	struct gameport *gameport, *child = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gameport_event_lock, flags);
+
+	list_for_each_entry(event, &gameport_event_list, node) {
+		if (event->type == GAMEPORT_REGISTER_PORT) {
+			gameport = event->object;
+			if (gameport->parent == parent) {
+				child = gameport;
+				break;
+			}
+		}
+	}
+
+	spin_unlock_irqrestore(&gameport_event_lock, flags);
+	return child;
+}
+
+static int gameport_thread(void *nothing)
+{
+	lock_kernel();
+	daemonize("kgameportd");
+	allow_signal(SIGTERM);
+
+	do {
+		gameport_handle_events();
+		wait_event_interruptible(gameport_wait, !list_empty(&gameport_event_list));
+		try_to_freeze(PF_FREEZE);
+	} while (!signal_pending(current));
+
+	printk(KERN_DEBUG "gameport: kgameportd exiting\n");
+
+	unlock_kernel();
+	complete_and_exit(&gameport_exited, 0);
+}
+
+
+/*
+ * Gameport port operations
+ */
+
+static ssize_t gameport_show_description(struct device *dev, char *buf)
+{
+	struct gameport *gameport = to_gameport_port(dev);
+	return sprintf(buf, "%s\n", gameport->name);
+}
+
+static ssize_t gameport_rebind_driver(struct device *dev, const char *buf, size_t count)
+{
+	struct gameport *gameport = to_gameport_port(dev);
+	struct device_driver *drv;
+	int retval;
+
+	retval = down_interruptible(&gameport_sem);
+	if (retval)
+		return retval;
+
+	retval = count;
+	if (!strncmp(buf, "none", count)) {
+		gameport_disconnect_port(gameport);
+	} else if (!strncmp(buf, "reconnect", count)) {
+		gameport_reconnect_port(gameport);
+	} else if (!strncmp(buf, "rescan", count)) {
+		gameport_disconnect_port(gameport);
+		gameport_find_driver(gameport);
+	} else if ((drv = driver_find(buf, &gameport_bus)) != NULL) {
+		gameport_disconnect_port(gameport);
+		gameport_bind_driver(gameport, to_gameport_driver(drv));
+		put_driver(drv);
+	} else {
+		retval = -EINVAL;
+	}
+
+	up(&gameport_sem);
+
+	return retval;
+}
+
+static struct device_attribute gameport_device_attrs[] = {
+	__ATTR(description, S_IRUGO, gameport_show_description, NULL),
+	__ATTR(drvctl, S_IWUSR, NULL, gameport_rebind_driver),
+	__ATTR_NULL
+};
+
+static void gameport_release_port(struct device *dev)
+{
+	struct gameport *gameport = to_gameport_port(dev);
+
+	kfree(gameport);
+	module_put(THIS_MODULE);
+}
+
+void gameport_set_phys(struct gameport *gameport, const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	vsnprintf(gameport->phys, sizeof(gameport->phys), fmt, args);
+	va_end(args);
+}
+
+/*
+ * Prepare gameport port for registration.
+ */
+static void gameport_init_port(struct gameport *gameport)
+{
+	static atomic_t gameport_no = ATOMIC_INIT(0);
+
+	__module_get(THIS_MODULE);
+
+	init_MUTEX(&gameport->drv_sem);
+	device_initialize(&gameport->dev);
+	snprintf(gameport->dev.bus_id, sizeof(gameport->dev.bus_id),
+		 "gameport%lu", (unsigned long)atomic_inc_return(&gameport_no) - 1);
+	gameport->dev.bus = &gameport_bus;
+	gameport->dev.release = gameport_release_port;
+	if (gameport->parent)
+		gameport->dev.parent = &gameport->parent->dev;
+
+	spin_lock_init(&gameport->timer_lock);
+	init_timer(&gameport->poll_timer);
+	gameport->poll_timer.function = gameport_run_poll_handler;
+	gameport->poll_timer.data = (unsigned long)gameport;
+}
+
+/*
+ * Complete gameport port registration.
+ * Driver core will attempt to find appropriate driver for the port.
+ */
+static void gameport_add_port(struct gameport *gameport)
+{
+	if (gameport->parent)
+		gameport->parent->child = gameport;
+
+	gameport->speed = gameport_measure_speed(gameport);
+
+	list_add_tail(&gameport->node, &gameport_list);
+
+	if (gameport->io)
+		printk(KERN_INFO "gameport: %s is %s, io %#x, speed %dkHz\n",
+			gameport->name, gameport->phys, gameport->io, gameport->speed);
+	else
+		printk(KERN_INFO "gameport: %s is %s, speed %dkHz\n",
+			gameport->name, gameport->phys, gameport->speed);
+
+	device_add(&gameport->dev);
+	gameport->registered = 1;
+}
+
+/*
+ * gameport_destroy_port() completes deregistration process and removes
+ * port from the system
+ */
+static void gameport_destroy_port(struct gameport *gameport)
+{
+	struct gameport *child;
+
+	child = gameport_get_pending_child(gameport);
+	if (child) {
+		gameport_remove_pending_events(child);
+		put_device(&child->dev);
+	}
+
+	if (gameport->parent) {
+		gameport->parent->child = NULL;
+		gameport->parent = NULL;
+	}
+
+	if (gameport->registered) {
+		device_del(&gameport->dev);
+		list_del_init(&gameport->node);
+		gameport->registered = 0;
+	}
+
+	gameport_remove_pending_events(gameport);
+	put_device(&gameport->dev);
+}
+
+/*
+ * Reconnect gameport port and all its children (re-initialize attached devices)
+ */
+static void gameport_reconnect_port(struct gameport *gameport)
+{
+	do {
+		if (!gameport->drv || !gameport->drv->reconnect || gameport->drv->reconnect(gameport)) {
+			gameport_disconnect_port(gameport);
+			gameport_find_driver(gameport);
+			/* Ok, old children are now gone, we are done */
+			break;
+		}
+		gameport = gameport->child;
+	} while (gameport);
+}
+
+/*
+ * gameport_disconnect_port() unbinds a port from its driver. As a side effect
+ * all child ports are unbound and destroyed.
+ */
+static void gameport_disconnect_port(struct gameport *gameport)
+{
+	struct gameport *s, *parent;
+
+	if (gameport->child) {
+		/*
+		 * Children ports should be disconnected and destroyed
+		 * first, staring with the leaf one, since we don't want
+		 * to do recursion
+		 */
+		for (s = gameport; s->child; s = s->child)
+			/* empty */;
+
+		do {
+			parent = s->parent;
+
+			gameport_release_driver(s);
+			gameport_destroy_port(s);
+		} while ((s = parent) != gameport);
+	}
+
+	/*
+	 * Ok, no children left, now disconnect this port
+	 */
+	gameport_release_driver(gameport);
+}
+
+void gameport_rescan(struct gameport *gameport)
+{
+	gameport_queue_event(gameport, NULL, GAMEPORT_RESCAN);
+}
+
+void gameport_reconnect(struct gameport *gameport)
+{
+	gameport_queue_event(gameport, NULL, GAMEPORT_RECONNECT);
+}
+
+/*
+ * Submits register request to kgameportd for subsequent execution.
+ * Note that port registration is always asynchronous.
+ */
+void __gameport_register_port(struct gameport *gameport, struct module *owner)
+{
+	gameport_init_port(gameport);
+	gameport_queue_event(gameport, owner, GAMEPORT_REGISTER_PORT);
+}
+
+/*
+ * Synchronously unregisters gameport port.
+ */
+void gameport_unregister_port(struct gameport *gameport)
+{
+	down(&gameport_sem);
+	gameport_disconnect_port(gameport);
+	gameport_destroy_port(gameport);
+	up(&gameport_sem);
+}
+
+
+/*
+ * Gameport driver operations
+ */
+
+static ssize_t gameport_driver_show_description(struct device_driver *drv, char *buf)
+{
+	struct gameport_driver *driver = to_gameport_driver(drv);
+	return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)");
+}
+
+static struct driver_attribute gameport_driver_attrs[] = {
+	__ATTR(description, S_IRUGO, gameport_driver_show_description, NULL),
+	__ATTR_NULL
+};
+
+static int gameport_driver_probe(struct device *dev)
+{
+	struct gameport *gameport = to_gameport_port(dev);
+	struct gameport_driver *drv = to_gameport_driver(dev->driver);
+
+	drv->connect(gameport, drv);
+	return gameport->drv ? 0 : -ENODEV;
+}
+
+static int gameport_driver_remove(struct device *dev)
+{
+	struct gameport *gameport = to_gameport_port(dev);
+	struct gameport_driver *drv = to_gameport_driver(dev->driver);
+
+	drv->disconnect(gameport);
+	return 0;
+}
+
+void __gameport_register_driver(struct gameport_driver *drv, struct module *owner)
+{
+	drv->driver.bus = &gameport_bus;
+	drv->driver.probe = gameport_driver_probe;
+	drv->driver.remove = gameport_driver_remove;
+	gameport_queue_event(drv, owner, GAMEPORT_REGISTER_DRIVER);
+}
+
+void gameport_unregister_driver(struct gameport_driver *drv)
+{
+	struct gameport *gameport;
+
+	down(&gameport_sem);
+	drv->ignore = 1;	/* so gameport_find_driver ignores it */
+
+start_over:
+	list_for_each_entry(gameport, &gameport_list, node) {
+		if (gameport->drv == drv) {
+			gameport_disconnect_port(gameport);
+			gameport_find_driver(gameport);
+			/* we could've deleted some ports, restart */
+			goto start_over;
+		}
+	}
+
+	driver_unregister(&drv->driver);
+	up(&gameport_sem);
+}
+
+static int gameport_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct gameport_driver *gameport_drv = to_gameport_driver(drv);
+
+	return !gameport_drv->ignore;
+}
+
+static void gameport_set_drv(struct gameport *gameport, struct gameport_driver *drv)
+{
+	down(&gameport->drv_sem);
+	gameport->drv = drv;
+	up(&gameport->drv_sem);
+}
+
+int gameport_open(struct gameport *gameport, struct gameport_driver *drv, int mode)
+{
+
+	if (gameport->open) {
+		if (gameport->open(gameport, mode)) {
+			return -1;
+		}
+	} else {
+		if (mode != GAMEPORT_MODE_RAW)
+			return -1;
+	}
+
+	gameport_set_drv(gameport, drv);
+	return 0;
+}
+
+void gameport_close(struct gameport *gameport)
+{
+	del_timer_sync(&gameport->poll_timer);
+	gameport->poll_handler = NULL;
+	gameport->poll_interval = 0;
+	gameport_set_drv(gameport, NULL);
+	if (gameport->close)
+		gameport->close(gameport);
+}
+
+static int __init gameport_init(void)
+{
+	if (!(gameport_pid = kernel_thread(gameport_thread, NULL, CLONE_KERNEL))) {
+		printk(KERN_ERR "gameport: Failed to start kgameportd\n");
+		return -1;
+	}
+
+	gameport_bus.dev_attrs = gameport_device_attrs;
+	gameport_bus.drv_attrs = gameport_driver_attrs;
+	gameport_bus.match = gameport_bus_match;
+	bus_register(&gameport_bus);
+
+	return 0;
+}
+
+static void __exit gameport_exit(void)
+{
+	bus_unregister(&gameport_bus);
+	kill_proc(gameport_pid, SIGTERM, 1);
+	wait_for_completion(&gameport_exited);
+}
+
+module_init(gameport_init);
+module_exit(gameport_exit);
diff --git a/drivers/input/gameport/lightning.c b/drivers/input/gameport/lightning.c
new file mode 100644
index 0000000..d65d810
--- /dev/null
+++ b/drivers/input/gameport/lightning.c
@@ -0,0 +1,344 @@
+/*
+ * $Id: lightning.c,v 1.20 2002/01/22 20:41:31 vojtech Exp $
+ *
+ *  Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * PDPI Lightning 4 gamecard 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 <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/slab.h>
+
+#define L4_PORT			0x201
+#define L4_SELECT_ANALOG	0xa4
+#define L4_SELECT_DIGITAL	0xa5
+#define L4_SELECT_SECONDARY	0xa6
+#define L4_CMD_ID		0x80
+#define L4_CMD_GETCAL		0x92
+#define L4_CMD_SETCAL		0x93
+#define L4_ID			0x04
+#define L4_BUSY			0x01
+#define L4_TIMEOUT		80	/* 80 us */
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("PDPI Lightning 4 gamecard driver");
+MODULE_LICENSE("GPL");
+
+struct l4 {
+	struct gameport *gameport;
+	unsigned char port;
+};
+
+static struct l4 l4_ports[8];
+
+/*
+ * l4_wait_ready() waits for the L4 to become ready.
+ */
+
+static int l4_wait_ready(void)
+{
+	unsigned int t = L4_TIMEOUT;
+
+	while ((inb(L4_PORT) & L4_BUSY) && t > 0) t--;
+	return -(t <= 0);
+}
+
+/*
+ * l4_cooked_read() reads data from the Lightning 4.
+ */
+
+static int l4_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+	struct l4 *l4 = gameport->port_data;
+	unsigned char status;
+	int i, result = -1;
+
+	outb(L4_SELECT_ANALOG, L4_PORT);
+	outb(L4_SELECT_DIGITAL + (l4->port >> 2), L4_PORT);
+
+	if (inb(L4_PORT) & L4_BUSY) goto fail;
+	outb(l4->port & 3, L4_PORT);
+
+	if (l4_wait_ready()) goto fail;
+	status = inb(L4_PORT);
+
+	for (i = 0; i < 4; i++)
+		if (status & (1 << i)) {
+			if (l4_wait_ready()) goto fail;
+			axes[i] = inb(L4_PORT);
+			if (axes[i] > 252) axes[i] = -1;
+		}
+
+	if (status & 0x10) {
+		if (l4_wait_ready()) goto fail;
+		*buttons = inb(L4_PORT) & 0x0f;
+	}
+
+	result = 0;
+
+fail:	outb(L4_SELECT_ANALOG, L4_PORT);
+	return result;
+}
+
+static int l4_open(struct gameport *gameport, int mode)
+{
+	struct l4 *l4 = gameport->port_data;
+
+        if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED)
+		return -1;
+	outb(L4_SELECT_ANALOG, L4_PORT);
+	return 0;
+}
+
+/*
+ * l4_getcal() reads the L4 with calibration values.
+ */
+
+static int l4_getcal(int port, int *cal)
+{
+	int i, result = -1;
+
+	outb(L4_SELECT_ANALOG, L4_PORT);
+	outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
+	if (inb(L4_PORT) & L4_BUSY)
+		goto out;
+
+	outb(L4_CMD_GETCAL, L4_PORT);
+	if (l4_wait_ready())
+		goto out;
+
+	if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))
+		goto out;
+
+	if (l4_wait_ready())
+		goto out;
+        outb(port & 3, L4_PORT);
+
+	for (i = 0; i < 4; i++) {
+		if (l4_wait_ready())
+			goto out;
+		cal[i] = inb(L4_PORT);
+	}
+
+	result = 0;
+
+out:	outb(L4_SELECT_ANALOG, L4_PORT);
+	return result;
+}
+
+/*
+ * l4_setcal() programs the L4 with calibration values.
+ */
+
+static int l4_setcal(int port, int *cal)
+{
+	int i, result = -1;
+
+	outb(L4_SELECT_ANALOG, L4_PORT);
+	outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT);
+	if (inb(L4_PORT) & L4_BUSY)
+		goto out;
+
+	outb(L4_CMD_SETCAL, L4_PORT);
+	if (l4_wait_ready())
+		goto out;
+
+	if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2))
+		goto out;
+
+	if (l4_wait_ready())
+		goto out;
+        outb(port & 3, L4_PORT);
+
+	for (i = 0; i < 4; i++) {
+		if (l4_wait_ready())
+			goto out;
+		outb(cal[i], L4_PORT);
+	}
+
+	result = 0;
+
+out:	outb(L4_SELECT_ANALOG, L4_PORT);
+	return result;
+}
+
+/*
+ * l4_calibrate() calibrates the L4 for the attached device, so
+ * that the device's resistance fits into the L4's 8-bit range.
+ */
+
+static int l4_calibrate(struct gameport *gameport, int *axes, int *max)
+{
+	int i, t;
+	int cal[4];
+	struct l4 *l4 = gameport->port_data;
+
+	if (l4_getcal(l4->port, cal))
+		return -1;
+
+	for (i = 0; i < 4; i++) {
+		t = (max[i] * cal[i]) / 200;
+		t = (t < 1) ? 1 : ((t > 255) ? 255 : t);
+		axes[i] = (axes[i] < 0) ? -1 : (axes[i] * cal[i]) / t;
+		axes[i] = (axes[i] > 252) ? 252 : axes[i];
+		cal[i] = t;
+	}
+
+	if (l4_setcal(l4->port, cal))
+		return -1;
+
+	return 0;
+}
+
+static int __init l4_create_ports(int card_no)
+{
+	struct l4 *l4;
+	struct gameport *port;
+	int i, idx;
+
+	for (i = 0; i < 4; i++) {
+
+		idx = card_no * 4 + i;
+		l4 = &l4_ports[idx];
+
+		if (!(l4->gameport = port = gameport_allocate_port())) {
+			printk(KERN_ERR "lightning: Memory allocation failed\n");
+			while (--i >= 0) {
+				gameport_free_port(l4->gameport);
+				l4->gameport = NULL;
+			}
+			return -ENOMEM;
+		}
+		l4->port = idx;
+
+		port->port_data = l4;
+		port->open = l4_open;
+		port->cooked_read = l4_cooked_read;
+		port->calibrate = l4_calibrate;
+
+		gameport_set_name(port, "PDPI Lightning 4");
+		gameport_set_phys(port, "isa%04x/gameport%d", L4_PORT, idx);
+
+		if (idx == 0)
+			port->io = L4_PORT;
+	}
+
+	return 0;
+}
+
+static int __init l4_add_card(int card_no)
+{
+	int cal[4] = { 255, 255, 255, 255 };
+	int i, rev, result;
+	struct l4 *l4;
+
+	outb(L4_SELECT_ANALOG, L4_PORT);
+	outb(L4_SELECT_DIGITAL + card_no, L4_PORT);
+
+	if (inb(L4_PORT) & L4_BUSY)
+		return -1;
+	outb(L4_CMD_ID, L4_PORT);
+
+	if (l4_wait_ready())
+		return -1;
+
+	if (inb(L4_PORT) != L4_SELECT_DIGITAL + card_no)
+		return -1;
+
+	if (l4_wait_ready())
+		return -1;
+	if (inb(L4_PORT) != L4_ID)
+		return -1;
+
+	if (l4_wait_ready())
+		return -1;
+	rev = inb(L4_PORT);
+
+	if (!rev)
+		return -1;
+
+	result = l4_create_ports(card_no);
+	if (result)
+		return result;
+
+	printk(KERN_INFO "gameport: PDPI Lightning 4 %s card v%d.%d at %#x\n",
+		card_no ? "secondary" : "primary", rev >> 4, rev, L4_PORT);
+
+	for (i = 0; i < 4; i++) {
+		l4 = &l4_ports[card_no * 4 + i];
+
+		if (rev > 0x28)		/* on 2.9+ the setcal command works correctly */
+			l4_setcal(l4->port, cal);
+		gameport_register_port(l4->gameport);
+	}
+
+	return 0;
+}
+
+static int __init l4_init(void)
+{
+	int i, cards = 0;
+
+	if (!request_region(L4_PORT, 1, "lightning"))
+		return -1;
+
+	for (i = 0; i < 2; i++)
+		if (l4_add_card(i) == 0)
+			cards++;
+
+	outb(L4_SELECT_ANALOG, L4_PORT);
+
+	if (!cards) {
+		release_region(L4_PORT, 1);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void __exit l4_exit(void)
+{
+	int i;
+	int cal[4] = { 59, 59, 59, 59 };
+
+	for (i = 0; i < 8; i++)
+		if (l4_ports[i].gameport) {
+			l4_setcal(l4_ports[i].port, cal);
+			gameport_unregister_port(l4_ports[i].gameport);
+		}
+
+	outb(L4_SELECT_ANALOG, L4_PORT);
+	release_region(L4_PORT, 1);
+}
+
+module_init(l4_init);
+module_exit(l4_exit);
diff --git a/drivers/input/gameport/ns558.c b/drivers/input/gameport/ns558.c
new file mode 100644
index 0000000..7c5c631
--- /dev/null
+++ b/drivers/input/gameport/ns558.c
@@ -0,0 +1,291 @@
+/*
+ * $Id: ns558.c,v 1.43 2002/01/24 19:23:21 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ *  Copyright (c) 1999 Brian Gerst
+ */
+
+/*
+ * NS558 based standard IBM game port 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 <asm/io.h>
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gameport.h>
+#include <linux/slab.h>
+#include <linux/pnp.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Classic gameport (ISA/PnP) driver");
+MODULE_LICENSE("GPL");
+
+static int ns558_isa_portlist[] = { 0x201, 0x200, 0x202, 0x203, 0x204, 0x205, 0x207, 0x209,
+				    0x20b, 0x20c, 0x20e, 0x20f, 0x211, 0x219, 0x101, 0 };
+
+struct ns558 {
+	int type;
+	int io;
+	int size;
+	struct pnp_dev *dev;
+	struct gameport *gameport;
+	struct list_head node;
+};
+
+static LIST_HEAD(ns558_list);
+
+/*
+ * ns558_isa_probe() tries to find an isa gameport at the
+ * specified address, and also checks for mirrors.
+ * A joystick must be attached for this to work.
+ */
+
+static int ns558_isa_probe(int io)
+{
+	int i, j, b;
+	unsigned char c, u, v;
+	struct ns558 *ns558;
+	struct gameport *port;
+
+/*
+ * No one should be using this address.
+ */
+
+	if (!request_region(io, 1, "ns558-isa"))
+		return -EBUSY;
+
+/*
+ * We must not be able to write arbitrary values to the port.
+ * The lower two axis bits must be 1 after a write.
+ */
+
+	c = inb(io);
+	outb(~c & ~3, io);
+	if (~(u = v = inb(io)) & 3) {
+		outb(c, io);
+		release_region(io, 1);
+		return -ENODEV;
+	}
+/*
+ * After a trigger, there must be at least some bits changing.
+ */
+
+	for (i = 0; i < 1000; i++) v &= inb(io);
+
+	if (u == v) {
+		outb(c, io);
+		release_region(io, 1);
+		return -ENODEV;
+	}
+	msleep(3);
+/*
+ * After some time (4ms) the axes shouldn't change anymore.
+ */
+
+	u = inb(io);
+	for (i = 0; i < 1000; i++)
+		if ((u ^ inb(io)) & 0xf) {
+			outb(c, io);
+			release_region(io, 1);
+			return -ENODEV;
+		}
+/*
+ * And now find the number of mirrors of the port.
+ */
+
+	for (i = 1; i < 5; i++) {
+
+		release_region(io & (-1 << (i - 1)), (1 << (i - 1)));
+
+		if (!request_region(io & (-1 << i), (1 << i), "ns558-isa"))
+			break;				/* Don't disturb anyone */
+
+		outb(0xff, io & (-1 << i));
+		for (j = b = 0; j < 1000; j++)
+			if (inb(io & (-1 << i)) != inb((io & (-1 << i)) + (1 << i) - 1)) b++;
+		msleep(3);
+
+		if (b > 300) {				/* We allow 30% difference */
+			release_region(io & (-1 << i), (1 << i));
+			break;
+		}
+	}
+
+	i--;
+
+	if (i != 4) {
+		if (!request_region(io & (-1 << i), (1 << i), "ns558-isa"))
+			return -EBUSY;
+	}
+
+	ns558 = kcalloc(1, sizeof(struct ns558), GFP_KERNEL);
+	port = gameport_allocate_port();
+	if (!ns558 || !port) {
+		printk(KERN_ERR "ns558: Memory allocation failed.\n");
+		release_region(io & (-1 << i), (1 << i));
+		kfree(ns558);
+		gameport_free_port(port);
+		return -ENOMEM;
+	}
+
+	memset(ns558, 0, sizeof(struct ns558));
+	ns558->io = io;
+	ns558->size = 1 << i;
+	ns558->gameport = port;
+
+	port->io = io;
+	gameport_set_name(port, "NS558 ISA Gameport");
+	gameport_set_phys(port, "isa%04x/gameport0", io & (-1 << i));
+
+	gameport_register_port(port);
+
+	list_add(&ns558->node, &ns558_list);
+
+	return 0;
+}
+
+#ifdef CONFIG_PNP
+
+static struct pnp_device_id pnp_devids[] = {
+	{ .id = "@P@0001", .driver_data = 0 }, /* ALS 100 */
+	{ .id = "@P@0020", .driver_data = 0 }, /* ALS 200 */
+	{ .id = "@P@1001", .driver_data = 0 }, /* ALS 100+ */
+	{ .id = "@P@2001", .driver_data = 0 }, /* ALS 120 */
+	{ .id = "ASB16fd", .driver_data = 0 }, /* AdLib NSC16 */
+	{ .id = "AZT3001", .driver_data = 0 }, /* AZT1008 */
+	{ .id = "CDC0001", .driver_data = 0 }, /* Opl3-SAx */
+	{ .id = "CSC0001", .driver_data = 0 }, /* CS4232 */
+	{ .id = "CSC000f", .driver_data = 0 }, /* CS4236 */
+	{ .id = "CSC0101", .driver_data = 0 }, /* CS4327 */
+	{ .id = "CTL7001", .driver_data = 0 }, /* SB16 */
+	{ .id = "CTL7002", .driver_data = 0 }, /* AWE64 */
+	{ .id = "CTL7005", .driver_data = 0 }, /* Vibra16 */
+	{ .id = "ENS2020", .driver_data = 0 }, /* SoundscapeVIVO */
+	{ .id = "ESS0001", .driver_data = 0 }, /* ES1869 */
+	{ .id = "ESS0005", .driver_data = 0 }, /* ES1878 */
+	{ .id = "ESS6880", .driver_data = 0 }, /* ES688 */
+	{ .id = "IBM0012", .driver_data = 0 }, /* CS4232 */
+	{ .id = "OPT0001", .driver_data = 0 }, /* OPTi Audio16 */
+	{ .id = "YMH0006", .driver_data = 0 }, /* Opl3-SA */
+	{ .id = "YMH0022", .driver_data = 0 }, /* Opl3-SAx */
+	{ .id = "PNPb02f", .driver_data = 0 }, /* Generic */
+	{ .id = "", },
+};
+
+MODULE_DEVICE_TABLE(pnp, pnp_devids);
+
+static int ns558_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *did)
+{
+	int ioport, iolen;
+	struct ns558 *ns558;
+	struct gameport *port;
+
+	if (!pnp_port_valid(dev, 0)) {
+		printk(KERN_WARNING "ns558: No i/o ports on a gameport? Weird\n");
+		return -ENODEV;
+	}
+
+	ioport = pnp_port_start(dev, 0);
+	iolen = pnp_port_len(dev, 0);
+
+	if (!request_region(ioport, iolen, "ns558-pnp"))
+		return -EBUSY;
+
+	ns558 = kcalloc(1, sizeof(struct ns558), GFP_KERNEL);
+	port = gameport_allocate_port();
+	if (!ns558 || !port) {
+		printk(KERN_ERR "ns558: Memory allocation failed\n");
+		kfree(ns558);
+		gameport_free_port(port);
+		return -ENOMEM;
+	}
+
+	ns558->io = ioport;
+	ns558->size = iolen;
+	ns558->dev = dev;
+	ns558->gameport = port;
+
+	gameport_set_name(port, "NS558 PnP Gameport");
+	gameport_set_phys(port, "pnp%s/gameport0", dev->dev.bus_id);
+	port->dev.parent = &dev->dev;
+	port->io = ioport;
+
+	gameport_register_port(port);
+
+	list_add_tail(&ns558->node, &ns558_list);
+	return 0;
+}
+
+static struct pnp_driver ns558_pnp_driver = {
+	.name		= "ns558",
+	.id_table	= pnp_devids,
+	.probe		= ns558_pnp_probe,
+};
+
+#else
+
+static struct pnp_driver ns558_pnp_driver;
+
+#endif
+
+static int pnp_registered = 0;
+
+static int __init ns558_init(void)
+{
+	int i = 0;
+
+/*
+ * Probe ISA ports first so that PnP gets to choose free port addresses
+ * not occupied by the ISA ports.
+ */
+
+	while (ns558_isa_portlist[i])
+		ns558_isa_probe(ns558_isa_portlist[i++]);
+
+	if (pnp_register_driver(&ns558_pnp_driver) >= 0)
+		pnp_registered = 1;
+
+
+	return (list_empty(&ns558_list) && !pnp_registered) ? -ENODEV : 0;
+}
+
+static void __exit ns558_exit(void)
+{
+	struct ns558 *ns558;
+
+	list_for_each_entry(ns558, &ns558_list, node) {
+		gameport_unregister_port(ns558->gameport);
+		release_region(ns558->io & ~(ns558->size - 1), ns558->size);
+		kfree(ns558);
+	}
+
+	if (pnp_registered)
+		pnp_unregister_driver(&ns558_pnp_driver);
+}
+
+module_init(ns558_init);
+module_exit(ns558_exit);
diff --git a/drivers/input/gameport/vortex.c b/drivers/input/gameport/vortex.c
new file mode 100644
index 0000000..36b0309
--- /dev/null
+++ b/drivers/input/gameport/vortex.c
@@ -0,0 +1,186 @@
+/*
+ * $Id: vortex.c,v 1.5 2002/07/01 15:39:30 vojtech Exp $
+ *
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Raymond Ingles
+ */
+
+/*
+ * Trident 4DWave and Aureal Vortex gameport 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 <asm/io.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/gameport.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Aureal Vortex and Vortex2 gameport driver");
+MODULE_LICENSE("GPL");
+
+#define VORTEX_GCR		0x0c	/* Gameport control register */
+#define VORTEX_LEG		0x08	/* Legacy port location */
+#define VORTEX_AXD		0x10	/* Axes start */
+#define VORTEX_DATA_WAIT	20	/* 20 ms */
+
+struct vortex {
+	struct gameport *gameport;
+	struct pci_dev *dev;
+	unsigned char __iomem *base;
+	unsigned char __iomem *io;
+};
+
+static unsigned char vortex_read(struct gameport *gameport)
+{
+	struct vortex *vortex = gameport->port_data;
+	return readb(vortex->io + VORTEX_LEG);
+}
+
+static void vortex_trigger(struct gameport *gameport)
+{
+	struct vortex *vortex = gameport->port_data;
+	writeb(0xff, vortex->io + VORTEX_LEG);
+}
+
+static int vortex_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+	struct vortex *vortex = gameport->port_data;
+	int i;
+
+	*buttons = (~readb(vortex->base + VORTEX_LEG) >> 4) & 0xf;
+
+	for (i = 0; i < 4; i++) {
+		axes[i] = readw(vortex->io + VORTEX_AXD + i * sizeof(u32));
+		if (axes[i] == 0x1fff) axes[i] = -1;
+	}
+
+        return 0;
+}
+
+static int vortex_open(struct gameport *gameport, int mode)
+{
+	struct vortex *vortex = gameport->port_data;
+
+	switch (mode) {
+		case GAMEPORT_MODE_COOKED:
+			writeb(0x40, vortex->io + VORTEX_GCR);
+			msleep(VORTEX_DATA_WAIT);
+			return 0;
+		case GAMEPORT_MODE_RAW:
+			writeb(0x00, vortex->io + VORTEX_GCR);
+			return 0;
+		default:
+			return -1;
+	}
+
+	return 0;
+}
+
+static int __devinit vortex_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct vortex *vortex;
+	struct gameport *port;
+	int i;
+
+	vortex = kcalloc(1, sizeof(struct vortex), GFP_KERNEL);
+	port = gameport_allocate_port();
+	if (!vortex || !port) {
+		printk(KERN_ERR "vortex: Memory allocation failed.\n");
+		kfree(vortex);
+		gameport_free_port(port);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < 6; i++)
+		if (~pci_resource_flags(dev, i) & IORESOURCE_IO)
+			break;
+
+	pci_enable_device(dev);
+
+	vortex->dev = dev;
+	vortex->gameport = port;
+	vortex->base = ioremap(pci_resource_start(vortex->dev, i),
+				pci_resource_len(vortex->dev, i));
+	vortex->io = vortex->base + id->driver_data;
+
+	pci_set_drvdata(dev, vortex);
+
+	port->port_data = vortex;
+	port->fuzz = 64;
+
+	gameport_set_name(port, "AU88x0");
+	gameport_set_phys(port, "pci%s/gameport0", pci_name(dev));
+	port->dev.parent = &dev->dev;
+	port->read = vortex_read;
+	port->trigger = vortex_trigger;
+	port->cooked_read = vortex_cooked_read;
+	port->open = vortex_open;
+
+	gameport_register_port(port);
+
+	return 0;
+}
+
+static void __devexit vortex_remove(struct pci_dev *dev)
+{
+	struct vortex *vortex = pci_get_drvdata(dev);
+
+	gameport_unregister_port(vortex->gameport);
+	iounmap(vortex->base);
+	kfree(vortex);
+}
+
+static struct pci_device_id vortex_id_table[] = {
+	{ 0x12eb, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x11000 },
+	{ 0x12eb, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x28800 },
+	{ 0 }
+};
+
+static struct pci_driver vortex_driver = {
+	.name =		"vortex_gameport",
+	.id_table =	vortex_id_table,
+	.probe =	vortex_probe,
+	.remove =	__devexit_p(vortex_remove),
+};
+
+static int __init vortex_init(void)
+{
+	return pci_register_driver(&vortex_driver);
+}
+
+static void __exit vortex_exit(void)
+{
+	pci_unregister_driver(&vortex_driver);
+}
+
+module_init(vortex_init);
+module_exit(vortex_exit);
diff --git a/drivers/input/input.c b/drivers/input/input.c
new file mode 100644
index 0000000..3385dd0
--- /dev/null
+++ b/drivers/input/input.c
@@ -0,0 +1,748 @@
+/*
+ * The input core
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/major.h>
+#include <linux/proc_fs.h>
+#include <linux/kobject_uevent.h>
+#include <linux/interrupt.h>
+#include <linux/poll.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("Input core");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(input_register_device);
+EXPORT_SYMBOL(input_unregister_device);
+EXPORT_SYMBOL(input_register_handler);
+EXPORT_SYMBOL(input_unregister_handler);
+EXPORT_SYMBOL(input_grab_device);
+EXPORT_SYMBOL(input_release_device);
+EXPORT_SYMBOL(input_open_device);
+EXPORT_SYMBOL(input_close_device);
+EXPORT_SYMBOL(input_accept_process);
+EXPORT_SYMBOL(input_flush_device);
+EXPORT_SYMBOL(input_event);
+EXPORT_SYMBOL(input_class);
+
+#define INPUT_DEVICES	256
+
+static LIST_HEAD(input_dev_list);
+static LIST_HEAD(input_handler_list);
+
+static struct input_handler *input_table[8];
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *proc_bus_input_dir;
+static DECLARE_WAIT_QUEUE_HEAD(input_devices_poll_wait);
+static int input_devices_state;
+#endif
+
+void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	struct input_handle *handle;
+
+	if (type > EV_MAX || !test_bit(type, dev->evbit))
+		return;
+
+	add_input_randomness(type, code, value);
+
+	switch (type) {
+
+		case EV_SYN:
+			switch (code) {
+				case SYN_CONFIG:
+					if (dev->event) dev->event(dev, type, code, value);
+					break;
+
+				case SYN_REPORT:
+					if (dev->sync) return;
+					dev->sync = 1;
+					break;
+			}
+			break;
+
+		case EV_KEY:
+
+			if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
+				return;
+
+			if (value == 2)
+				break;
+
+			change_bit(code, dev->key);
+
+			if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
+				dev->repeat_key = code;
+				mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
+			}
+
+			break;
+
+		case EV_ABS:
+
+			if (code > ABS_MAX || !test_bit(code, dev->absbit))
+				return;
+
+			if (dev->absfuzz[code]) {
+				if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
+				    (value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
+					return;
+
+				if ((value > dev->abs[code] - dev->absfuzz[code]) &&
+				    (value < dev->abs[code] + dev->absfuzz[code]))
+					value = (dev->abs[code] * 3 + value) >> 2;
+
+				if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
+				    (value < dev->abs[code] + (dev->absfuzz[code] << 1)))
+					value = (dev->abs[code] + value) >> 1;
+			}
+
+			if (dev->abs[code] == value)
+				return;
+
+			dev->abs[code] = value;
+			break;
+
+		case EV_REL:
+
+			if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0))
+				return;
+
+			break;
+
+		case EV_MSC:
+
+			if (code > MSC_MAX || !test_bit(code, dev->mscbit))
+				return;
+
+			if (dev->event) dev->event(dev, type, code, value);
+
+			break;
+
+		case EV_LED:
+
+			if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
+				return;
+
+			change_bit(code, dev->led);
+			if (dev->event) dev->event(dev, type, code, value);
+
+			break;
+
+		case EV_SND:
+
+			if (code > SND_MAX || !test_bit(code, dev->sndbit))
+				return;
+
+			if (dev->event) dev->event(dev, type, code, value);
+
+			break;
+
+		case EV_REP:
+
+			if (code > REP_MAX || value < 0 || dev->rep[code] == value) return;
+
+			dev->rep[code] = value;
+			if (dev->event) dev->event(dev, type, code, value);
+
+			break;
+
+		case EV_FF:
+			if (dev->event) dev->event(dev, type, code, value);
+			break;
+	}
+
+	if (type != EV_SYN)
+		dev->sync = 0;
+
+	if (dev->grab)
+		dev->grab->handler->event(dev->grab, type, code, value);
+	else
+		list_for_each_entry(handle, &dev->h_list, d_node)
+			if (handle->open)
+				handle->handler->event(handle, type, code, value);
+}
+
+static void input_repeat_key(unsigned long data)
+{
+	struct input_dev *dev = (void *) data;
+
+	if (!test_bit(dev->repeat_key, dev->key))
+		return;
+
+	input_event(dev, EV_KEY, dev->repeat_key, 2);
+	input_sync(dev);
+
+	if (dev->rep[REP_PERIOD])
+		mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD]));
+}
+
+int input_accept_process(struct input_handle *handle, struct file *file)
+{
+	if (handle->dev->accept)
+		return handle->dev->accept(handle->dev, file);
+
+	return 0;
+}
+
+int input_grab_device(struct input_handle *handle)
+{
+	if (handle->dev->grab)
+		return -EBUSY;
+
+	handle->dev->grab = handle;
+	return 0;
+}
+
+void input_release_device(struct input_handle *handle)
+{
+	if (handle->dev->grab == handle)
+		handle->dev->grab = NULL;
+}
+
+int input_open_device(struct input_handle *handle)
+{
+	handle->open++;
+	if (handle->dev->open)
+		return handle->dev->open(handle->dev);
+	return 0;
+}
+
+int input_flush_device(struct input_handle* handle, struct file* file)
+{
+	if (handle->dev->flush)
+		return handle->dev->flush(handle->dev, file);
+
+	return 0;
+}
+
+void input_close_device(struct input_handle *handle)
+{
+	input_release_device(handle);
+	if (handle->dev->close)
+		handle->dev->close(handle->dev);
+	handle->open--;
+}
+
+static void input_link_handle(struct input_handle *handle)
+{
+	list_add_tail(&handle->d_node, &handle->dev->h_list);
+	list_add_tail(&handle->h_node, &handle->handler->h_list);
+}
+
+#define MATCH_BIT(bit, max) \
+		for (i = 0; i < NBITS(max); i++) \
+			if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
+				break; \
+		if (i != NBITS(max)) \
+			continue;
+
+static struct input_device_id *input_match_device(struct input_device_id *id, struct input_dev *dev)
+{
+	int i;
+
+	for (; id->flags || id->driver_info; id++) {
+
+		if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
+			if (id->id.bustype != dev->id.bustype)
+				continue;
+
+		if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
+			if (id->id.vendor != dev->id.vendor)
+				continue;
+
+		if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
+			if (id->id.product != dev->id.product)
+				continue;
+
+		if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
+			if (id->id.version != dev->id.version)
+				continue;
+
+		MATCH_BIT(evbit,  EV_MAX);
+		MATCH_BIT(keybit, KEY_MAX);
+		MATCH_BIT(relbit, REL_MAX);
+		MATCH_BIT(absbit, ABS_MAX);
+		MATCH_BIT(mscbit, MSC_MAX);
+		MATCH_BIT(ledbit, LED_MAX);
+		MATCH_BIT(sndbit, SND_MAX);
+		MATCH_BIT(ffbit,  FF_MAX);
+
+		return id;
+	}
+
+	return NULL;
+}
+
+/*
+ * Input hotplugging interface - loading event handlers based on
+ * device bitfields.
+ */
+
+#ifdef CONFIG_HOTPLUG
+
+/*
+ * Input hotplugging invokes what /proc/sys/kernel/hotplug says
+ * (normally /sbin/hotplug) when input devices get added or removed.
+ *
+ * This invokes a user mode policy agent, typically helping to load driver
+ * or other modules, configure the device, and more.  Drivers can provide
+ * a MODULE_DEVICE_TABLE to help with module loading subtasks.
+ *
+ */
+
+#define SPRINTF_BIT_A(bit, name, max) \
+	do { \
+		envp[i++] = scratch; \
+		scratch += sprintf(scratch, name); \
+		for (j = NBITS(max) - 1; j >= 0; j--) \
+			if (dev->bit[j]) break; \
+		for (; j >= 0; j--) \
+			scratch += sprintf(scratch, "%lx ", dev->bit[j]); \
+		scratch++; \
+	} while (0)
+
+#define SPRINTF_BIT_A2(bit, name, max, ev) \
+	do { \
+		if (test_bit(ev, dev->evbit)) \
+			SPRINTF_BIT_A(bit, name, max); \
+	} while (0)
+
+static void input_call_hotplug(char *verb, struct input_dev *dev)
+{
+	char *argv[3], **envp, *buf, *scratch;
+	int i = 0, j, value;
+
+	if (!hotplug_path[0]) {
+		printk(KERN_ERR "input.c: calling hotplug without a hotplug agent defined\n");
+		return;
+	}
+	if (in_interrupt()) {
+		printk(KERN_ERR "input.c: calling hotplug from interrupt\n");
+		return;
+	}
+	if (!current->fs->root) {
+		printk(KERN_WARNING "input.c: calling hotplug without valid filesystem\n");
+		return;
+	}
+	if (!(envp = (char **) kmalloc(20 * sizeof(char *), GFP_KERNEL))) {
+		printk(KERN_ERR "input.c: not enough memory allocating hotplug environment\n");
+		return;
+	}
+	if (!(buf = kmalloc(1024, GFP_KERNEL))) {
+		kfree (envp);
+		printk(KERN_ERR "input.c: not enough memory allocating hotplug environment\n");
+		return;
+	}
+
+	argv[0] = hotplug_path;
+	argv[1] = "input";
+	argv[2] = NULL;
+
+	envp[i++] = "HOME=/";
+	envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+
+	scratch = buf;
+
+	envp[i++] = scratch;
+	scratch += sprintf(scratch, "ACTION=%s", verb) + 1;
+
+	envp[i++] = scratch;
+	scratch += sprintf(scratch, "PRODUCT=%x/%x/%x/%x",
+		dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version) + 1;
+
+	if (dev->name) {
+		envp[i++] = scratch;
+		scratch += sprintf(scratch, "NAME=%s", dev->name) + 1;
+	}
+
+	if (dev->phys) {
+		envp[i++] = scratch;
+		scratch += sprintf(scratch, "PHYS=%s", dev->phys) + 1;
+	}
+
+	SPRINTF_BIT_A(evbit, "EV=", EV_MAX);
+	SPRINTF_BIT_A2(keybit, "KEY=", KEY_MAX, EV_KEY);
+	SPRINTF_BIT_A2(relbit, "REL=", REL_MAX, EV_REL);
+	SPRINTF_BIT_A2(absbit, "ABS=", ABS_MAX, EV_ABS);
+	SPRINTF_BIT_A2(mscbit, "MSC=", MSC_MAX, EV_MSC);
+	SPRINTF_BIT_A2(ledbit, "LED=", LED_MAX, EV_LED);
+	SPRINTF_BIT_A2(sndbit, "SND=", SND_MAX, EV_SND);
+	SPRINTF_BIT_A2(ffbit,  "FF=",  FF_MAX, EV_FF);
+
+	envp[i++] = NULL;
+
+#ifdef INPUT_DEBUG
+	printk(KERN_DEBUG "input.c: calling %s %s [%s %s %s %s %s]\n",
+		argv[0], argv[1], envp[0], envp[1], envp[2], envp[3], envp[4]);
+#endif
+
+	value = call_usermodehelper(argv [0], argv, envp, 0);
+
+	kfree(buf);
+	kfree(envp);
+
+#ifdef INPUT_DEBUG
+	if (value != 0)
+		printk(KERN_DEBUG "input.c: hotplug returned %d\n", value);
+#endif
+}
+
+#endif
+
+void input_register_device(struct input_dev *dev)
+{
+	struct input_handle *handle;
+	struct input_handler *handler;
+	struct input_device_id *id;
+
+	set_bit(EV_SYN, dev->evbit);
+
+	/*
+	 * If delay and period are pre-set by the driver, then autorepeating
+	 * is handled by the driver itself and we don't do it in input.c.
+	 */
+
+	init_timer(&dev->timer);
+	if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
+		dev->timer.data = (long) dev;
+		dev->timer.function = input_repeat_key;
+		dev->rep[REP_DELAY] = 250;
+		dev->rep[REP_PERIOD] = 33;
+	}
+
+	INIT_LIST_HEAD(&dev->h_list);
+	list_add_tail(&dev->node, &input_dev_list);
+
+	list_for_each_entry(handler, &input_handler_list, node)
+		if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
+			if ((id = input_match_device(handler->id_table, dev)))
+				if ((handle = handler->connect(handler, dev, id)))
+					input_link_handle(handle);
+
+#ifdef CONFIG_HOTPLUG
+	input_call_hotplug("add", dev);
+#endif
+
+#ifdef CONFIG_PROC_FS
+	input_devices_state++;
+	wake_up(&input_devices_poll_wait);
+#endif
+}
+
+void input_unregister_device(struct input_dev *dev)
+{
+	struct list_head * node, * next;
+
+	if (!dev) return;
+
+	del_timer_sync(&dev->timer);
+
+	list_for_each_safe(node, next, &dev->h_list) {
+		struct input_handle * handle = to_handle(node);
+		list_del_init(&handle->d_node);
+		list_del_init(&handle->h_node);
+		handle->handler->disconnect(handle);
+	}
+
+#ifdef CONFIG_HOTPLUG
+	input_call_hotplug("remove", dev);
+#endif
+
+	list_del_init(&dev->node);
+
+#ifdef CONFIG_PROC_FS
+	input_devices_state++;
+	wake_up(&input_devices_poll_wait);
+#endif
+}
+
+void input_register_handler(struct input_handler *handler)
+{
+	struct input_dev *dev;
+	struct input_handle *handle;
+	struct input_device_id *id;
+
+	if (!handler) return;
+
+	INIT_LIST_HEAD(&handler->h_list);
+
+	if (handler->fops != NULL)
+		input_table[handler->minor >> 5] = handler;
+
+	list_add_tail(&handler->node, &input_handler_list);
+
+	list_for_each_entry(dev, &input_dev_list, node)
+		if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
+			if ((id = input_match_device(handler->id_table, dev)))
+				if ((handle = handler->connect(handler, dev, id)))
+					input_link_handle(handle);
+
+#ifdef CONFIG_PROC_FS
+	input_devices_state++;
+	wake_up(&input_devices_poll_wait);
+#endif
+}
+
+void input_unregister_handler(struct input_handler *handler)
+{
+	struct list_head * node, * next;
+
+	list_for_each_safe(node, next, &handler->h_list) {
+		struct input_handle * handle = to_handle_h(node);
+		list_del_init(&handle->h_node);
+		list_del_init(&handle->d_node);
+		handler->disconnect(handle);
+	}
+
+	list_del_init(&handler->node);
+
+	if (handler->fops != NULL)
+		input_table[handler->minor >> 5] = NULL;
+
+#ifdef CONFIG_PROC_FS
+	input_devices_state++;
+	wake_up(&input_devices_poll_wait);
+#endif
+}
+
+static int input_open_file(struct inode *inode, struct file *file)
+{
+	struct input_handler *handler = input_table[iminor(inode) >> 5];
+	struct file_operations *old_fops, *new_fops = NULL;
+	int err;
+
+	/* No load-on-demand here? */
+	if (!handler || !(new_fops = fops_get(handler->fops)))
+		return -ENODEV;
+
+	/*
+	 * That's _really_ odd. Usually NULL ->open means "nothing special",
+	 * not "no device". Oh, well...
+	 */
+	if (!new_fops->open) {
+		fops_put(new_fops);
+		return -ENODEV;
+	}
+	old_fops = file->f_op;
+	file->f_op = new_fops;
+
+	err = new_fops->open(inode, file);
+
+	if (err) {
+		fops_put(file->f_op);
+		file->f_op = fops_get(old_fops);
+	}
+	fops_put(old_fops);
+	return err;
+}
+
+static struct file_operations input_fops = {
+	.owner = THIS_MODULE,
+	.open = input_open_file,
+};
+
+#ifdef CONFIG_PROC_FS
+
+#define SPRINTF_BIT_B(bit, name, max) \
+	do { \
+		len += sprintf(buf + len, "B: %s", name); \
+		for (i = NBITS(max) - 1; i >= 0; i--) \
+			if (dev->bit[i]) break; \
+		for (; i >= 0; i--) \
+			len += sprintf(buf + len, "%lx ", dev->bit[i]); \
+		len += sprintf(buf + len, "\n"); \
+	} while (0)
+
+#define SPRINTF_BIT_B2(bit, name, max, ev) \
+	do { \
+		if (test_bit(ev, dev->evbit)) \
+			SPRINTF_BIT_B(bit, name, max); \
+	} while (0)
+
+
+static unsigned int input_devices_poll(struct file *file, poll_table *wait)
+{
+	int state = input_devices_state;
+	poll_wait(file, &input_devices_poll_wait, wait);
+	if (state != input_devices_state)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int input_devices_read(char *buf, char **start, off_t pos, int count, int *eof, void *data)
+{
+	struct input_dev *dev;
+	struct input_handle *handle;
+
+	off_t at = 0;
+	int i, len, cnt = 0;
+
+	list_for_each_entry(dev, &input_dev_list, node) {
+
+		len = sprintf(buf, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n",
+			dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version);
+
+		len += sprintf(buf + len, "N: Name=\"%s\"\n", dev->name ? dev->name : "");
+		len += sprintf(buf + len, "P: Phys=%s\n", dev->phys ? dev->phys : "");
+		len += sprintf(buf + len, "H: Handlers=");
+
+		list_for_each_entry(handle, &dev->h_list, d_node)
+			len += sprintf(buf + len, "%s ", handle->name);
+
+		len += sprintf(buf + len, "\n");
+
+		SPRINTF_BIT_B(evbit, "EV=", EV_MAX);
+		SPRINTF_BIT_B2(keybit, "KEY=", KEY_MAX, EV_KEY);
+		SPRINTF_BIT_B2(relbit, "REL=", REL_MAX, EV_REL);
+		SPRINTF_BIT_B2(absbit, "ABS=", ABS_MAX, EV_ABS);
+		SPRINTF_BIT_B2(mscbit, "MSC=", MSC_MAX, EV_MSC);
+		SPRINTF_BIT_B2(ledbit, "LED=", LED_MAX, EV_LED);
+		SPRINTF_BIT_B2(sndbit, "SND=", SND_MAX, EV_SND);
+		SPRINTF_BIT_B2(ffbit,  "FF=",  FF_MAX, EV_FF);
+
+		len += sprintf(buf + len, "\n");
+
+		at += len;
+
+		if (at >= pos) {
+			if (!*start) {
+				*start = buf + (pos - (at - len));
+				cnt = at - pos;
+			} else  cnt += len;
+			buf += len;
+			if (cnt >= count)
+				break;
+		}
+	}
+
+	if (&dev->node == &input_dev_list)
+		*eof = 1;
+
+	return (count > cnt) ? cnt : count;
+}
+
+static int input_handlers_read(char *buf, char **start, off_t pos, int count, int *eof, void *data)
+{
+	struct input_handler *handler;
+
+	off_t at = 0;
+	int len = 0, cnt = 0;
+	int i = 0;
+
+	list_for_each_entry(handler, &input_handler_list, node) {
+
+		if (handler->fops)
+			len = sprintf(buf, "N: Number=%d Name=%s Minor=%d\n",
+				i++, handler->name, handler->minor);
+		else
+			len = sprintf(buf, "N: Number=%d Name=%s\n",
+				i++, handler->name);
+
+		at += len;
+
+		if (at >= pos) {
+			if (!*start) {
+				*start = buf + (pos - (at - len));
+				cnt = at - pos;
+			} else  cnt += len;
+			buf += len;
+			if (cnt >= count)
+				break;
+		}
+	}
+	if (&handler->node == &input_handler_list)
+		*eof = 1;
+
+	return (count > cnt) ? cnt : count;
+}
+
+static int __init input_proc_init(void)
+{
+	struct proc_dir_entry *entry;
+
+	proc_bus_input_dir = proc_mkdir("input", proc_bus);
+	if (proc_bus_input_dir == NULL)
+		return -ENOMEM;
+	proc_bus_input_dir->owner = THIS_MODULE;
+	entry = create_proc_read_entry("devices", 0, proc_bus_input_dir, input_devices_read, NULL);
+	if (entry == NULL) {
+		remove_proc_entry("input", proc_bus);
+		return -ENOMEM;
+	}
+	entry->owner = THIS_MODULE;
+	entry->proc_fops->poll = input_devices_poll;
+	entry = create_proc_read_entry("handlers", 0, proc_bus_input_dir, input_handlers_read, NULL);
+	if (entry == NULL) {
+		remove_proc_entry("devices", proc_bus_input_dir);
+		remove_proc_entry("input", proc_bus);
+		return -ENOMEM;
+	}
+	entry->owner = THIS_MODULE;
+	return 0;
+}
+#else /* !CONFIG_PROC_FS */
+static inline int input_proc_init(void) { return 0; }
+#endif
+
+struct class_simple *input_class;
+
+static int __init input_init(void)
+{
+	int retval = -ENOMEM;
+
+	input_class = class_simple_create(THIS_MODULE, "input");
+	if (IS_ERR(input_class))
+		return PTR_ERR(input_class);
+	input_proc_init();
+	retval = register_chrdev(INPUT_MAJOR, "input", &input_fops);
+	if (retval) {
+		printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
+		remove_proc_entry("devices", proc_bus_input_dir);
+		remove_proc_entry("handlers", proc_bus_input_dir);
+		remove_proc_entry("input", proc_bus);
+		class_simple_destroy(input_class);
+		return retval;
+	}
+
+	retval = devfs_mk_dir("input");
+	if (retval) {
+		remove_proc_entry("devices", proc_bus_input_dir);
+		remove_proc_entry("handlers", proc_bus_input_dir);
+		remove_proc_entry("input", proc_bus);
+		unregister_chrdev(INPUT_MAJOR, "input");
+		class_simple_destroy(input_class);
+	}
+	return retval;
+}
+
+static void __exit input_exit(void)
+{
+	remove_proc_entry("devices", proc_bus_input_dir);
+	remove_proc_entry("handlers", proc_bus_input_dir);
+	remove_proc_entry("input", proc_bus);
+
+	devfs_remove("input");
+	unregister_chrdev(INPUT_MAJOR, "input");
+	class_simple_destroy(input_class);
+}
+
+subsys_initcall(input_init);
+module_exit(input_exit);
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
new file mode 100644
index 0000000..7d7527f
--- /dev/null
+++ b/drivers/input/joydev.c
@@ -0,0 +1,533 @@
+/*
+ * Joystick device driver for the input driver suite.
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ * Copyright (c) 1999 Colin Van Dyke
+ *
+ * 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.
+ */
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/joystick.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Joystick device interfaces");
+MODULE_SUPPORTED_DEVICE("input/js");
+MODULE_LICENSE("GPL");
+
+#define JOYDEV_MINOR_BASE	0
+#define JOYDEV_MINORS		16
+#define JOYDEV_BUFFER_SIZE	64
+
+#define MSECS(t)	(1000 * ((t) / HZ) + 1000 * ((t) % HZ) / HZ)
+
+struct joydev {
+	int exist;
+	int open;
+	int minor;
+	char name[16];
+	struct input_handle handle;
+	wait_queue_head_t wait;
+	struct list_head list;
+	struct js_corr corr[ABS_MAX + 1];
+	struct JS_DATA_SAVE_TYPE glue;
+	int nabs;
+	int nkey;
+	__u16 keymap[KEY_MAX - BTN_MISC + 1];
+	__u16 keypam[KEY_MAX - BTN_MISC + 1];
+	__u8 absmap[ABS_MAX + 1];
+	__u8 abspam[ABS_MAX + 1];
+	__s16 abs[ABS_MAX + 1];
+};
+
+struct joydev_list {
+	struct js_event buffer[JOYDEV_BUFFER_SIZE];
+	int head;
+	int tail;
+	int startup;
+	struct fasync_struct *fasync;
+	struct joydev *joydev;
+	struct list_head node;
+};
+
+static struct joydev *joydev_table[JOYDEV_MINORS];
+
+static int joydev_correct(int value, struct js_corr *corr)
+{
+	switch (corr->type) {
+		case JS_CORR_NONE:
+			break;
+		case JS_CORR_BROKEN:
+			value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
+				((corr->coef[3] * (value - corr->coef[1])) >> 14)) :
+				((corr->coef[2] * (value - corr->coef[0])) >> 14);
+			break;
+		default:
+			return 0;
+	}
+
+	if (value < -32767) return -32767;
+	if (value >  32767) return  32767;
+
+	return value;
+}
+
+static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+	struct joydev *joydev = handle->private;
+	struct joydev_list *list;
+	struct js_event event;
+
+	switch (type) {
+
+		case EV_KEY:
+			if (code < BTN_MISC || value == 2) return;
+			event.type = JS_EVENT_BUTTON;
+			event.number = joydev->keymap[code - BTN_MISC];
+			event.value = value;
+			break;
+
+		case EV_ABS:
+			event.type = JS_EVENT_AXIS;
+			event.number = joydev->absmap[code];
+			event.value = joydev_correct(value, joydev->corr + event.number);
+			if (event.value == joydev->abs[event.number]) return;
+			joydev->abs[event.number] = event.value;
+			break;
+
+		default:
+			return;
+	}
+
+	event.time = MSECS(jiffies);
+
+	list_for_each_entry(list, &joydev->list, node) {
+
+		memcpy(list->buffer + list->head, &event, sizeof(struct js_event));
+
+		if (list->startup == joydev->nabs + joydev->nkey)
+			if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1)))
+				list->startup = 0;
+
+		kill_fasync(&list->fasync, SIGIO, POLL_IN);
+	}
+
+	wake_up_interruptible(&joydev->wait);
+}
+
+static int joydev_fasync(int fd, struct file *file, int on)
+{
+	int retval;
+	struct joydev_list *list = file->private_data;
+	retval = fasync_helper(fd, file, on, &list->fasync);
+	return retval < 0 ? retval : 0;
+}
+
+static void joydev_free(struct joydev *joydev)
+{
+	joydev_table[joydev->minor] = NULL;
+	kfree(joydev);
+}
+
+static int joydev_release(struct inode * inode, struct file * file)
+{
+	struct joydev_list *list = file->private_data;
+
+	joydev_fasync(-1, file, 0);
+
+	list_del(&list->node);
+
+	if (!--list->joydev->open) {
+		if (list->joydev->exist)
+			input_close_device(&list->joydev->handle);
+		else
+			joydev_free(list->joydev);
+	}
+
+	kfree(list);
+	return 0;
+}
+
+static int joydev_open(struct inode *inode, struct file *file)
+{
+	struct joydev_list *list;
+	int i = iminor(inode) - JOYDEV_MINOR_BASE;
+
+	if (i >= JOYDEV_MINORS || !joydev_table[i])
+		return -ENODEV;
+
+	if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL)))
+		return -ENOMEM;
+	memset(list, 0, sizeof(struct joydev_list));
+
+	list->joydev = joydev_table[i];
+	list_add_tail(&list->node, &joydev_table[i]->list);
+	file->private_data = list;
+
+	if (!list->joydev->open++)
+		if (list->joydev->exist)
+			input_open_device(&list->joydev->handle);
+
+	return 0;
+}
+
+static ssize_t joydev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
+{
+	return -EINVAL;
+}
+
+static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+	struct joydev_list *list = file->private_data;
+	struct joydev *joydev = list->joydev;
+	struct input_dev *input = joydev->handle.dev;
+	int retval = 0;
+
+	if (!list->joydev->exist)
+		return -ENODEV;
+
+	if (count < sizeof(struct js_event))
+		return -EINVAL;
+
+	if (count == sizeof(struct JS_DATA_TYPE)) {
+
+		struct JS_DATA_TYPE data;
+		int i;
+
+		for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++)
+			data.buttons |= test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0;
+		data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x;
+		data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y;
+
+		if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)))
+			return -EFAULT;
+
+		list->startup = 0;
+		list->tail = list->head;
+
+		return sizeof(struct JS_DATA_TYPE);
+	}
+
+	if (list->startup == joydev->nabs + joydev->nkey
+		&& list->head == list->tail && (file->f_flags & O_NONBLOCK))
+			return -EAGAIN;
+
+	retval = wait_event_interruptible(list->joydev->wait,
+		 			  !list->joydev->exist ||
+					  list->startup < joydev->nabs + joydev->nkey ||
+					  list->head != list->tail);
+
+	if (retval)
+		return retval;
+
+	if (!list->joydev->exist)
+		return -ENODEV;
+
+	while (list->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) {
+
+		struct js_event event;
+
+		event.time = MSECS(jiffies);
+
+		if (list->startup < joydev->nkey) {
+			event.type = JS_EVENT_BUTTON | JS_EVENT_INIT;
+			event.number = list->startup;
+			event.value = !!test_bit(joydev->keypam[event.number], input->key);
+		} else {
+			event.type = JS_EVENT_AXIS | JS_EVENT_INIT;
+			event.number = list->startup - joydev->nkey;
+			event.value = joydev->abs[event.number];
+		}
+
+		if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
+			return -EFAULT;
+
+		list->startup++;
+		retval += sizeof(struct js_event);
+	}
+
+	while (list->head != list->tail && retval + sizeof(struct js_event) <= count) {
+
+		if (copy_to_user(buf + retval, list->buffer + list->tail, sizeof(struct js_event)))
+			return -EFAULT;
+
+		list->tail = (list->tail + 1) & (JOYDEV_BUFFER_SIZE - 1);
+		retval += sizeof(struct js_event);
+	}
+
+	return retval;
+}
+
+/* No kernel lock - fine */
+static unsigned int joydev_poll(struct file *file, poll_table *wait)
+{
+	struct joydev_list *list = file->private_data;
+	poll_wait(file, &list->joydev->wait, wait);
+	return ((list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey) ? 
+		(POLLIN | POLLRDNORM) : 0) | (list->joydev->exist ? 0 : (POLLHUP | POLLERR));
+}
+
+static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct joydev_list *list = file->private_data;
+	struct joydev *joydev = list->joydev;
+	struct input_dev *dev = joydev->handle.dev;
+	void __user *argp = (void __user *)arg;
+	int i, j;
+
+	if (!joydev->exist) return -ENODEV;
+
+	switch (cmd) {
+
+		case JS_SET_CAL:
+			return copy_from_user(&joydev->glue.JS_CORR, argp,
+				sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
+		case JS_GET_CAL:
+			return copy_to_user(argp, &joydev->glue.JS_CORR,
+				sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0;
+		case JS_SET_TIMEOUT:
+			return get_user(joydev->glue.JS_TIMEOUT, (int __user *) arg);
+		case JS_GET_TIMEOUT:
+			return put_user(joydev->glue.JS_TIMEOUT, (int __user *) arg);
+		case JS_SET_TIMELIMIT:
+			return get_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg);
+		case JS_GET_TIMELIMIT:
+			return put_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg);
+		case JS_SET_ALL:
+			return copy_from_user(&joydev->glue, argp,
+						sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0;
+		case JS_GET_ALL:
+			return copy_to_user(argp, &joydev->glue,
+						sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0;
+
+		case JSIOCGVERSION:
+			return put_user(JS_VERSION, (__u32 __user *) arg);
+		case JSIOCGAXES:
+			return put_user(joydev->nabs, (__u8 __user *) arg);
+		case JSIOCGBUTTONS:
+			return put_user(joydev->nkey, (__u8 __user *) arg);
+		case JSIOCSCORR:
+			if (copy_from_user(joydev->corr, argp,
+				      sizeof(struct js_corr) * joydev->nabs))
+			    return -EFAULT;
+			for (i = 0; i < joydev->nabs; i++) {
+				j = joydev->abspam[i];
+			        joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i);
+			}
+			return 0;
+		case JSIOCGCORR:
+			return copy_to_user(argp, joydev->corr,
+						sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0;
+		case JSIOCSAXMAP:
+			if (copy_from_user(joydev->abspam, argp, sizeof(__u8) * (ABS_MAX + 1)))
+				return -EFAULT;
+			for (i = 0; i < joydev->nabs; i++) {
+				if (joydev->abspam[i] > ABS_MAX) return -EINVAL;
+				joydev->absmap[joydev->abspam[i]] = i;
+			}
+			return 0;
+		case JSIOCGAXMAP:
+			return copy_to_user(argp, joydev->abspam,
+						sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0;
+		case JSIOCSBTNMAP:
+			if (copy_from_user(joydev->keypam, argp, sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)))
+				return -EFAULT;
+			for (i = 0; i < joydev->nkey; i++) {
+				if (joydev->keypam[i] > KEY_MAX || joydev->keypam[i] < BTN_MISC) return -EINVAL;
+				joydev->keymap[joydev->keypam[i] - BTN_MISC] = i;
+			}
+			return 0;
+		case JSIOCGBTNMAP:
+			return copy_to_user(argp, joydev->keypam,
+						sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0;
+		default:
+			if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
+				int len;
+				if (!dev->name) return 0;
+				len = strlen(dev->name) + 1;
+				if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
+				if (copy_to_user(argp, dev->name, len)) return -EFAULT;
+				return len;
+			}
+	}
+	return -EINVAL;
+}
+
+static struct file_operations joydev_fops = {
+	.owner =	THIS_MODULE,
+	.read =		joydev_read,
+	.write =	joydev_write,
+	.poll =		joydev_poll,
+	.open =		joydev_open,
+	.release =	joydev_release,
+	.ioctl =	joydev_ioctl,
+	.fasync =	joydev_fasync,
+};
+
+static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
+{
+	struct joydev *joydev;
+	int i, j, t, minor;
+
+	for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++);
+	if (minor == JOYDEV_MINORS) {
+		printk(KERN_ERR "joydev: no more free joydev devices\n");
+		return NULL;
+	}
+
+	if (!(joydev = kmalloc(sizeof(struct joydev), GFP_KERNEL)))
+		return NULL;
+	memset(joydev, 0, sizeof(struct joydev));
+
+	INIT_LIST_HEAD(&joydev->list);
+	init_waitqueue_head(&joydev->wait);
+
+	joydev->minor = minor;
+	joydev->exist = 1;
+	joydev->handle.dev = dev;
+	joydev->handle.name = joydev->name;
+	joydev->handle.handler = handler;
+	joydev->handle.private = joydev;
+	sprintf(joydev->name, "js%d", minor);
+
+	for (i = 0; i < ABS_MAX + 1; i++)
+		if (test_bit(i, dev->absbit)) {
+			joydev->absmap[i] = joydev->nabs;
+			joydev->abspam[joydev->nabs] = i;
+			joydev->nabs++;
+		}
+
+	for (i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC + 1; i++)
+		if (test_bit(i + BTN_MISC, dev->keybit)) {
+			joydev->keymap[i] = joydev->nkey;
+			joydev->keypam[joydev->nkey] = i + BTN_MISC;
+			joydev->nkey++;
+		}
+
+	for (i = 0; i < BTN_JOYSTICK - BTN_MISC + 1; i++)
+		if (test_bit(i + BTN_MISC, dev->keybit)) {
+			joydev->keymap[i] = joydev->nkey;
+			joydev->keypam[joydev->nkey] = i + BTN_MISC;
+			joydev->nkey++;
+		}
+
+	for (i = 0; i < joydev->nabs; i++) {
+		j = joydev->abspam[i];
+		if (dev->absmax[j] == dev->absmin[j]) {
+			joydev->corr[i].type = JS_CORR_NONE;
+			joydev->abs[i] = dev->abs[j];
+			continue;
+		}
+		joydev->corr[i].type = JS_CORR_BROKEN;
+		joydev->corr[i].prec = dev->absfuzz[j];
+		joydev->corr[i].coef[0] = (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j];
+		joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j];
+		if (!(t = ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j])))
+			continue;
+		joydev->corr[i].coef[2] = (1 << 29) / t;
+		joydev->corr[i].coef[3] = (1 << 29) / t;
+
+		joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i);
+	}
+
+	joydev_table[minor] = joydev;
+
+	devfs_mk_cdev(MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor),
+			S_IFCHR|S_IRUGO|S_IWUSR, "input/js%d", minor);
+	class_simple_device_add(input_class,
+				MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor),
+				dev->dev, "js%d", minor);
+
+	return &joydev->handle;
+}
+
+static void joydev_disconnect(struct input_handle *handle)
+{
+	struct joydev *joydev = handle->private;
+	struct joydev_list *list;
+
+	class_simple_device_remove(MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + joydev->minor));
+	devfs_remove("input/js%d", joydev->minor);
+	joydev->exist = 0;
+
+	if (joydev->open) {
+		input_close_device(handle);
+		wake_up_interruptible(&joydev->wait);
+		list_for_each_entry(list, &joydev->list, node)
+			kill_fasync(&list->fasync, SIGIO, POLL_HUP);
+	} else
+		joydev_free(joydev);
+}
+
+static struct input_device_id joydev_blacklist[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
+		.evbit = { BIT(EV_KEY) },
+		.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
+	}, 	/* Avoid itouchpads, touchscreens and tablets */
+	{ }, 	/* Terminating entry */
+};
+
+static struct input_device_id joydev_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
+		.evbit = { BIT(EV_ABS) },
+		.absbit = { BIT(ABS_X) },
+	},
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
+		.evbit = { BIT(EV_ABS) },
+		.absbit = { BIT(ABS_WHEEL) },
+	},
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
+		.evbit = { BIT(EV_ABS) },
+		.absbit = { BIT(ABS_THROTTLE) },
+	},
+	{ }, 	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(input, joydev_ids);
+
+static struct input_handler joydev_handler = {
+	.event =	joydev_event,
+	.connect =	joydev_connect,
+	.disconnect =	joydev_disconnect,
+	.fops =		&joydev_fops,
+	.minor =	JOYDEV_MINOR_BASE,
+	.name =		"joydev",
+	.id_table =	joydev_ids,
+	.blacklist = 	joydev_blacklist,
+};
+
+static int __init joydev_init(void)
+{
+	input_register_handler(&joydev_handler);
+	return 0;
+}
+
+static void __exit joydev_exit(void)
+{
+	input_unregister_handler(&joydev_handler);
+}
+
+module_init(joydev_init);
+module_exit(joydev_exit);
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
new file mode 100644
index 0000000..67519ef
--- /dev/null
+++ b/drivers/input/joystick/Kconfig
@@ -0,0 +1,256 @@
+#
+# Joystick driver configuration
+#
+menuconfig INPUT_JOYSTICK
+	bool "Joysticks"
+	help
+	  If you have a joystick, 6dof controller, gamepad, steering wheel,
+	  weapon control system or something like that you can say Y here
+	  and the list of supported devices will be displayed. This option
+	  doesn't affect the kernel.
+
+	  Please read the file <file:Documentation/input/joystick.txt> which
+	  contains more information.
+
+if INPUT_JOYSTICK
+
+config JOYSTICK_ANALOG
+	tristate "Classic PC analog joysticks and gamepads"
+	select GAMEPORT
+	---help---
+	  Say Y here if you have a joystick that connects to the PC
+	  gameport. In addition to the usual PC analog joystick, this driver
+	  supports many extensions, including joysticks with throttle control,
+	  with rudders, additional hats and buttons compatible with CH
+	  Flightstick Pro, ThrustMaster FCS, 6 and 8 button gamepads, or
+	  Saitek Cyborg joysticks.
+
+	  Please read the file <file:Documentation/input/joystick.txt> which
+	  contains more information.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called analog.
+
+config JOYSTICK_A3D
+	tristate "Assasin 3D and MadCatz Panther devices"
+	select GAMEPORT
+	help
+	  Say Y here if you have an FPGaming or MadCatz controller using the
+	  A3D protocol over the PC gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called a3d.
+
+config JOYSTICK_ADI
+	tristate "Logitech ADI digital joysticks and gamepads"
+	select GAMEPORT
+	help
+	  Say Y here if you have a Logitech controller using the ADI
+	  protocol over the PC gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called adi.
+
+config JOYSTICK_COBRA
+	tristate "Creative Labs Blaster Cobra gamepad"
+	select GAMEPORT
+	help
+	  Say Y here if you have a Creative Labs Blaster Cobra gamepad.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called cobra.
+
+config JOYSTICK_GF2K
+	tristate "Genius Flight2000 Digital joysticks and gamepads"
+	select GAMEPORT
+	help
+	  Say Y here if you have a Genius Flight2000 or MaxFighter digitally
+	  communicating joystick or gamepad.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gf2k.
+
+config JOYSTICK_GRIP
+	tristate "Gravis GrIP joysticks and gamepads"
+	select GAMEPORT
+	help
+	  Say Y here if you have a Gravis controller using the GrIP protocol
+	  over the PC gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called grip.
+
+config JOYSTICK_GRIP_MP
+	tristate "Gravis GrIP MultiPort"
+	select GAMEPORT
+	help
+	  Say Y here if you have the original Gravis GrIP MultiPort, a hub
+	  that connects to the gameport and you connect gamepads to it.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called grip_mp.
+
+config JOYSTICK_GUILLEMOT
+	tristate "Guillemot joysticks and gamepads"
+	select GAMEPORT
+	help
+	  Say Y here if you have a Guillemot joystick using a digital
+	  protocol over the PC gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called guillemot.
+
+config JOYSTICK_INTERACT
+	tristate "InterAct digital joysticks and gamepads"
+	select GAMEPORT
+	help
+	  Say Y here if you have an InterAct gameport or joystick
+	  communicating digitally over the gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called interact.
+
+config JOYSTICK_SIDEWINDER
+	tristate "Microsoft SideWinder digital joysticks and gamepads"
+	select GAMEPORT
+	help
+	  Say Y here if you have a Microsoft controller using the Digital
+	  Overdrive protocol over PC gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sidewinder.
+
+config JOYSTICK_TMDC
+	tristate "ThrustMaster DirectConnect joysticks and gamepads"
+	select GAMEPORT
+	help
+	  Say Y here if you have a ThrustMaster controller using the
+	  DirectConnect (BSP) protocol over the PC gameport.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called tmdc.
+
+source "drivers/input/joystick/iforce/Kconfig"
+
+config JOYSTICK_WARRIOR
+	tristate "Logitech WingMan Warrior joystick"
+	select SERIO
+	help
+	  Say Y here if you have a Logitech WingMan Warrior joystick connected
+	  to your computer's serial port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called warrior.
+
+config JOYSTICK_MAGELLAN
+	tristate "LogiCad3d Magellan/SpaceMouse 6dof controllers"
+	select SERIO
+	help
+	  Say Y here if you have a Magellan or Space Mouse 6DOF controller
+	  connected to your computer's serial port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called magellan.
+
+config JOYSTICK_SPACEORB
+	tristate "SpaceTec SpaceOrb/Avenger 6dof controllers"
+	select SERIO
+	help
+	  Say Y here if you have a SpaceOrb 360 or SpaceBall Avenger 6DOF
+	  controller connected to your computer's serial port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called spaceorb.
+
+config JOYSTICK_SPACEBALL
+	tristate "SpaceTec SpaceBall 6dof controllers"
+	select SERIO
+	help
+	  Say Y here if you have a SpaceTec SpaceBall 2003/3003/4000 FLX
+	  controller connected to your computer's serial port. For the
+	  SpaceBall 4000 USB model, use the USB HID driver.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called spaceball.
+
+config JOYSTICK_STINGER
+	tristate "Gravis Stinger gamepad"
+	select SERIO
+	help
+	  Say Y here if you have a Gravis Stinger connected to one of your
+	  serial ports.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called stinger.
+
+config JOYSTICK_TWIDJOY
+	tristate "Twiddler as a joystick"
+	select SERIO
+	help
+	  Say Y here if you have a Handykey Twiddler connected to your
+	  computer's serial port and want to use it as a joystick.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called twidjoy.
+
+config JOYSTICK_DB9
+	tristate "Multisystem, Sega Genesis, Saturn joysticks and gamepads"
+	depends on PARPORT
+	---help---
+	  Say Y here if you have a Sega Master System gamepad, Sega Genesis
+	  gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga,
+	  Commodore, Amstrad CPC joystick connected to your parallel port.
+	  For more information on how to use the driver please read
+	  <file:Documentation/input/joystick-parport.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called db9.
+
+config JOYSTICK_GAMECON
+	tristate "Multisystem, NES, SNES, N64, PSX joysticks and gamepads"
+	depends on PARPORT
+	---help---
+	  Say Y here if you have a Nintendo Entertainment System gamepad,
+	  Super Nintendo Entertainment System gamepad, Nintendo 64 gamepad,
+	  Sony PlayStation gamepad or a Multisystem -- Atari, Amiga,
+	  Commodore, Amstrad CPC joystick connected to your parallel port.
+	  For more information on how to use the driver please read
+	  <file:Documentation/input/joystick-parport.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gamecon.
+
+config JOYSTICK_TURBOGRAFX
+	tristate "Multisystem joysticks via TurboGraFX device"
+	depends on PARPORT
+	help
+	  Say Y here if you have the TurboGraFX interface by Steffen Schwenke,
+	  and want to use it with Multisystem -- Atari, Amiga, Commodore,
+	  Amstrad CPC joystick. For more information on how to use the driver
+	  please read <file:Documentation/input/joystick-parport.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called turbografx.
+
+config JOYSTICK_AMIGA
+	tristate "Amiga joysticks"
+	depends on AMIGA
+	help
+	  Say Y here if you have an Amiga with a digital joystick connected
+	  to it.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called amijoy.
+
+config JOYSTICK_JOYDUMP
+	tristate "Gameport data dumper"
+	select GAMEPORT
+	help
+	  Say Y here if you want to dump data from your joystick into the system
+	  log for debugging purposes. Say N if you are making a production
+	  configuration or aren't sure.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called joydump.
+
+endif
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
new file mode 100644
index 0000000..5231f6f
--- /dev/null
+++ b/drivers/input/joystick/Makefile
@@ -0,0 +1,30 @@
+#
+# Makefile for the input core drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_JOYSTICK_A3D)		+= a3d.o
+obj-$(CONFIG_JOYSTICK_ADI)		+= adi.o
+obj-$(CONFIG_JOYSTICK_AMIGA)		+= amijoy.o
+obj-$(CONFIG_JOYSTICK_ANALOG)		+= analog.o
+obj-$(CONFIG_JOYSTICK_COBRA)		+= cobra.o
+obj-$(CONFIG_JOYSTICK_DB9)		+= db9.o
+obj-$(CONFIG_JOYSTICK_GAMECON)		+= gamecon.o
+obj-$(CONFIG_JOYSTICK_GF2K)		+= gf2k.o
+obj-$(CONFIG_JOYSTICK_GRIP)		+= grip.o
+obj-$(CONFIG_JOYSTICK_GRIP_MP)		+= grip_mp.o
+obj-$(CONFIG_JOYSTICK_GUILLEMOT)	+= guillemot.o
+obj-$(CONFIG_JOYSTICK_INTERACT)		+= interact.o
+obj-$(CONFIG_JOYSTICK_JOYDUMP)		+= joydump.o
+obj-$(CONFIG_JOYSTICK_MAGELLAN)		+= magellan.o
+obj-$(CONFIG_JOYSTICK_SIDEWINDER)	+= sidewinder.o
+obj-$(CONFIG_JOYSTICK_SPACEBALL)	+= spaceball.o
+obj-$(CONFIG_JOYSTICK_SPACEORB)		+= spaceorb.o
+obj-$(CONFIG_JOYSTICK_STINGER)		+= stinger.o
+obj-$(CONFIG_JOYSTICK_TMDC)		+= tmdc.o
+obj-$(CONFIG_JOYSTICK_TURBOGRAFX)	+= turbografx.o
+obj-$(CONFIG_JOYSTICK_TWIDJOY)		+= twidjoy.o
+obj-$(CONFIG_JOYSTICK_WARRIOR)		+= warrior.o
+
+obj-$(CONFIG_JOYSTICK_IFORCE)		+= iforce/
diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c
new file mode 100644
index 0000000..ad39fe4
--- /dev/null
+++ b/drivers/input/joystick/a3d.c
@@ -0,0 +1,417 @@
+/*
+ * $Id: a3d.c,v 1.21 2002/01/22 20:11:50 vojtech Exp $
+ *
+ *  Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * FP-Gaming Assasin 3D joystick 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/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+
+#define DRIVER_DESC	"FP-Gaming Assasin 3D joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define A3D_MAX_START		600	/* 600 us */
+#define A3D_MAX_STROBE		80	/* 80 us */
+#define A3D_MAX_LENGTH		40	/* 40*3 bits */
+
+#define A3D_MODE_A3D		1	/* Assassin 3D */
+#define A3D_MODE_PAN		2	/* Panther */
+#define A3D_MODE_OEM		3	/* Panther OEM version */
+#define A3D_MODE_PXL		4	/* Panther XL */
+
+static char *a3d_names[] = { NULL, "FP-Gaming Assassin 3D", "MadCatz Panther", "OEM Panther",
+			"MadCatz Panther XL", "MadCatz Panther XL w/ rudder" };
+
+struct a3d {
+	struct gameport *gameport;
+	struct gameport *adc;
+	struct input_dev dev;
+	int axes[4];
+	int buttons;
+	int mode;
+	int length;
+	int reads;
+	int bads;
+	char phys[32];
+};
+
+/*
+ * a3d_read_packet() reads an Assassin 3D packet.
+ */
+
+static int a3d_read_packet(struct gameport *gameport, int length, char *data)
+{
+	unsigned long flags;
+	unsigned char u, v;
+	unsigned int t, s;
+	int i;
+
+	i = 0;
+	t = gameport_time(gameport, A3D_MAX_START);
+	s = gameport_time(gameport, A3D_MAX_STROBE);
+
+	local_irq_save(flags);
+	gameport_trigger(gameport);
+	v = gameport_read(gameport);
+
+	while (t > 0 && i < length) {
+		t--;
+		u = v; v = gameport_read(gameport);
+		if (~v & u & 0x10) {
+			data[i++] = v >> 5;
+			t = s;
+		}
+	}
+
+	local_irq_restore(flags);
+
+	return i;
+}
+
+/*
+ * a3d_csum() computes checksum of triplet packet
+ */
+
+static int a3d_csum(char *data, int count)
+{
+	int i, csum = 0;
+
+	for (i = 0; i < count - 2; i++)
+		csum += data[i];
+	return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]);
+}
+
+static void a3d_read(struct a3d *a3d, unsigned char *data)
+{
+	struct input_dev *dev = &a3d->dev;
+
+	switch (a3d->mode) {
+
+		case A3D_MODE_A3D:
+		case A3D_MODE_OEM:
+		case A3D_MODE_PAN:
+
+			input_report_rel(dev, REL_X, ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7));
+			input_report_rel(dev, REL_Y, ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7));
+
+			input_report_key(dev, BTN_RIGHT,  data[2] & 1);
+			input_report_key(dev, BTN_LEFT,   data[3] & 2);
+			input_report_key(dev, BTN_MIDDLE, data[3] & 4);
+
+			input_sync(dev);
+
+			a3d->axes[0] = ((signed char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128;
+			a3d->axes[1] = ((signed char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128;
+			a3d->axes[2] = ((signed char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128;
+			a3d->axes[3] = ((signed char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128;
+
+			a3d->buttons = ((data[3] << 3) | data[4]) & 0xf;
+
+			break;
+
+		case A3D_MODE_PXL:
+
+			input_report_rel(dev, REL_X, ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7));
+			input_report_rel(dev, REL_Y, ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7));
+
+			input_report_key(dev, BTN_RIGHT,  data[2] & 1);
+			input_report_key(dev, BTN_LEFT,   data[3] & 2);
+			input_report_key(dev, BTN_MIDDLE, data[3] & 4);
+			input_report_key(dev, BTN_SIDE,   data[7] & 2);
+			input_report_key(dev, BTN_EXTRA,  data[7] & 4);
+
+			input_report_abs(dev, ABS_X,        ((signed char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128);
+			input_report_abs(dev, ABS_Y,        ((signed char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128);
+			input_report_abs(dev, ABS_RUDDER,   ((signed char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128);
+			input_report_abs(dev, ABS_THROTTLE, ((signed char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128);
+
+			input_report_abs(dev, ABS_HAT0X, ( data[5]       & 1) - ((data[5] >> 2) & 1));
+			input_report_abs(dev, ABS_HAT0Y, ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1));
+			input_report_abs(dev, ABS_HAT1X, ((data[4] >> 1) & 1) - ( data[3]       & 1));
+			input_report_abs(dev, ABS_HAT1Y, ((data[4] >> 2) & 1) - ( data[4]       & 1));
+
+			input_report_key(dev, BTN_TRIGGER, data[8] & 1);
+			input_report_key(dev, BTN_THUMB,   data[8] & 2);
+			input_report_key(dev, BTN_TOP,     data[8] & 4);
+			input_report_key(dev, BTN_PINKIE,  data[7] & 1);
+
+			input_sync(dev);
+
+			break;
+	}
+}
+
+
+/*
+ * a3d_poll() reads and analyzes A3D joystick data.
+ */
+
+static void a3d_poll(struct gameport *gameport)
+{
+	struct a3d *a3d = gameport_get_drvdata(gameport);
+	unsigned char data[A3D_MAX_LENGTH];
+
+	a3d->reads++;
+	if (a3d_read_packet(a3d->gameport, a3d->length, data) != a3d->length ||
+	    data[0] != a3d->mode || a3d_csum(data, a3d->length))
+	 	a3d->bads++;
+	else
+		a3d_read(a3d, data);
+}
+
+/*
+ * a3d_adc_cooked_read() copies the acis and button data to the
+ * callers arrays. It could do the read itself, but the caller could
+ * call this more than 50 times a second, which would use too much CPU.
+ */
+
+static int a3d_adc_cooked_read(struct gameport *gameport, int *axes, int *buttons)
+{
+	struct a3d *a3d = gameport->port_data;
+	int i;
+
+	for (i = 0; i < 4; i++)
+		axes[i] = (a3d->axes[i] < 254) ? a3d->axes[i] : -1;
+	*buttons = a3d->buttons;
+	return 0;
+}
+
+/*
+ * a3d_adc_open() is the gameport open routine. It refuses to serve
+ * any but cooked data.
+ */
+
+static int a3d_adc_open(struct gameport *gameport, int mode)
+{
+	struct a3d *a3d = gameport->port_data;
+
+	if (mode != GAMEPORT_MODE_COOKED)
+		return -1;
+
+	gameport_start_polling(a3d->gameport);
+	return 0;
+}
+
+/*
+ * a3d_adc_close() is a callback from the input close routine.
+ */
+
+static void a3d_adc_close(struct gameport *gameport)
+{
+	struct a3d *a3d = gameport->port_data;
+
+	gameport_stop_polling(a3d->gameport);
+}
+
+/*
+ * a3d_open() is a callback from the input open routine.
+ */
+
+static int a3d_open(struct input_dev *dev)
+{
+	struct a3d *a3d = dev->private;
+
+	gameport_start_polling(a3d->gameport);
+	return 0;
+}
+
+/*
+ * a3d_close() is a callback from the input close routine.
+ */
+
+static void a3d_close(struct input_dev *dev)
+{
+	struct a3d *a3d = dev->private;
+
+	gameport_stop_polling(a3d->gameport);
+}
+
+/*
+ * a3d_connect() probes for A3D joysticks.
+ */
+
+static int a3d_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct a3d *a3d;
+	struct gameport *adc;
+	unsigned char data[A3D_MAX_LENGTH];
+	int i;
+	int err;
+
+	if (!(a3d = kcalloc(1, sizeof(struct a3d), GFP_KERNEL)))
+		return -ENOMEM;
+
+	a3d->gameport = gameport;
+
+	gameport_set_drvdata(gameport, a3d);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	i = a3d_read_packet(gameport, A3D_MAX_LENGTH, data);
+
+	if (!i || a3d_csum(data, i)) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	a3d->mode = data[0];
+
+	if (!a3d->mode || a3d->mode > 5) {
+		printk(KERN_WARNING "a3d.c: Unknown A3D device detected "
+			"(%s, id=%d), contact <vojtech@ucw.cz>\n", gameport->phys, a3d->mode);
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	gameport_set_poll_handler(gameport, a3d_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	sprintf(a3d->phys, "%s/input0", gameport->phys);
+
+	if (a3d->mode == A3D_MODE_PXL) {
+
+		int axes[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER };
+
+		a3d->length = 33;
+
+		init_input_dev(&a3d->dev);
+
+		a3d->dev.evbit[0] |= BIT(EV_ABS) | BIT(EV_KEY) | BIT(EV_REL);
+		a3d->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y);
+		a3d->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_RUDDER)
+				   | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y) | BIT(ABS_HAT1X) | BIT(ABS_HAT1Y);
+
+		a3d->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_RIGHT) | BIT(BTN_LEFT) | BIT(BTN_MIDDLE)
+						 | BIT(BTN_SIDE) | BIT(BTN_EXTRA);
+
+		a3d->dev.keybit[LONG(BTN_JOYSTICK)] |= BIT(BTN_TRIGGER) | BIT(BTN_THUMB) | BIT(BTN_TOP) | BIT(BTN_PINKIE);
+
+		a3d_read(a3d, data);
+
+		for (i = 0; i < 4; i++) {
+			if (i < 2)
+				input_set_abs_params(&a3d->dev, axes[i], 48, a3d->dev.abs[axes[i]] * 2 - 48, 0, 8);
+			else
+				input_set_abs_params(&a3d->dev, axes[i], 2, 253, 0, 0);
+			input_set_abs_params(&a3d->dev, ABS_HAT0X + i, -1, 1, 0, 0);
+		}
+
+	} else {
+		a3d->length = 29;
+
+		init_input_dev(&a3d->dev);
+
+		a3d->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_REL);
+		a3d->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y);
+		a3d->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_RIGHT) | BIT(BTN_LEFT) | BIT(BTN_MIDDLE);
+
+		a3d_read(a3d, data);
+
+		if (!(a3d->adc = adc = gameport_allocate_port()))
+			printk(KERN_ERR "a3d: Not enough memory for ADC port\n");
+		else {
+			adc->port_data = a3d;
+			adc->open = a3d_adc_open;
+			adc->close = a3d_adc_close;
+			adc->cooked_read = a3d_adc_cooked_read;
+			adc->fuzz = 1;
+
+			gameport_set_name(adc, a3d_names[a3d->mode]);
+			gameport_set_phys(adc, "%s/gameport0", gameport->phys);
+			adc->dev.parent = &gameport->dev;
+
+			gameport_register_port(adc);
+		}
+	}
+
+	a3d->dev.private = a3d;
+	a3d->dev.open = a3d_open;
+	a3d->dev.close = a3d_close;
+
+	a3d->dev.name = a3d_names[a3d->mode];
+	a3d->dev.phys = a3d->phys;
+	a3d->dev.id.bustype = BUS_GAMEPORT;
+	a3d->dev.id.vendor = GAMEPORT_ID_VENDOR_MADCATZ;
+	a3d->dev.id.product = a3d->mode;
+	a3d->dev.id.version = 0x0100;
+
+	input_register_device(&a3d->dev);
+	printk(KERN_INFO "input: %s on %s\n", a3d_names[a3d->mode], a3d->phys);
+
+	return 0;
+
+fail2:	gameport_close(gameport);
+fail1:  gameport_set_drvdata(gameport, NULL);
+	kfree(a3d);
+	return err;
+}
+
+static void a3d_disconnect(struct gameport *gameport)
+{
+	struct a3d *a3d = gameport_get_drvdata(gameport);
+
+	input_unregister_device(&a3d->dev);
+	if (a3d->adc) {
+		gameport_unregister_port(a3d->adc);
+		a3d->adc = NULL;
+	}
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(a3d);
+}
+
+static struct gameport_driver a3d_drv = {
+	.driver		= {
+		.name	= "adc",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= a3d_connect,
+	.disconnect	= a3d_disconnect,
+};
+
+static int __init a3d_init(void)
+{
+	gameport_register_driver(&a3d_drv);
+	return 0;
+}
+
+static void __exit a3d_exit(void)
+{
+	gameport_unregister_driver(&a3d_drv);
+}
+
+module_init(a3d_init);
+module_exit(a3d_exit);
diff --git a/drivers/input/joystick/adi.c b/drivers/input/joystick/adi.c
new file mode 100644
index 0000000..83f6daf
--- /dev/null
+++ b/drivers/input/joystick/adi.c
@@ -0,0 +1,560 @@
+/*
+ *  Copyright (c) 1998-2005 Vojtech Pavlik
+ */
+
+/*
+ * Logitech ADI joystick family 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/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/gameport.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC	"Logitech ADI joystick family driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Times, array sizes, flags, ids.
+ */
+
+#define ADI_MAX_START		200	/* Trigger to packet timeout [200us] */
+#define ADI_MAX_STROBE		40	/* Single bit timeout [40us] */
+#define ADI_INIT_DELAY		10	/* Delay after init packet [10ms] */
+#define ADI_DATA_DELAY		4	/* Delay after data packet [4ms] */
+
+#define ADI_MAX_LENGTH		256
+#define ADI_MIN_LENGTH		8
+#define ADI_MIN_LEN_LENGTH	10
+#define ADI_MIN_ID_LENGTH	66
+#define ADI_MAX_NAME_LENGTH	48
+#define ADI_MAX_CNAME_LENGTH	16
+#define ADI_MAX_PHYS_LENGTH	64
+
+#define ADI_FLAG_HAT		0x04
+#define ADI_FLAG_10BIT		0x08
+
+#define ADI_ID_TPD		0x01
+#define ADI_ID_WGP		0x06
+#define ADI_ID_WGPE		0x08
+#define ADI_ID_MAX		0x0a
+
+/*
+ * Names, buttons, axes ...
+ */
+
+static char *adi_names[] = {	"WingMan Extreme Digital", "ThunderPad Digital", "SideCar", "CyberMan 2",
+				"WingMan Interceptor", "WingMan Formula", "WingMan GamePad",
+				"WingMan Extreme Digital 3D", "WingMan GamePad Extreme",
+				"WingMan GamePad USB", "Unknown Device %#x" };
+
+static char adi_wmgpe_abs[] =	{ ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y };
+static char adi_wmi_abs[] =	{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y };
+static char adi_wmed3d_abs[] =	{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_RZ, ABS_HAT0X, ABS_HAT0Y };
+static char adi_cm2_abs[] =	{ ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ };
+static char adi_wmf_abs[] =	{ ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y };
+
+static short adi_wmgpe_key[] =	{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z,  BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT };
+static short adi_wmi_key[] = 	{ BTN_TRIGGER,  BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_EXTRA };
+static short adi_wmed3d_key[] =	{ BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2 };
+static short adi_cm2_key[] =	{ BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 };
+
+static char* adi_abs[] = { adi_wmi_abs, adi_wmgpe_abs, adi_wmf_abs, adi_cm2_abs, adi_wmi_abs, adi_wmf_abs,
+			   adi_wmgpe_abs, adi_wmed3d_abs, adi_wmgpe_abs, adi_wmgpe_abs, adi_wmi_abs };
+
+static short* adi_key[] = { adi_wmi_key, adi_wmgpe_key, adi_cm2_key, adi_cm2_key, adi_wmi_key, adi_cm2_key,
+			    adi_wmgpe_key, adi_wmed3d_key, adi_wmgpe_key, adi_wmgpe_key, adi_wmi_key };
+
+/*
+ * Hat to axis conversion arrays.
+ */
+
+static struct {
+	int x;
+	int y;
+} adi_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+/*
+ * Per-port information.
+ */
+
+struct adi {
+	struct input_dev dev;
+	int length;
+	int ret;
+	int idx;
+	unsigned char id;
+	char buttons;
+	char axes10;
+	char axes8;
+	signed char pad;
+	char hats;
+	char *abs;
+	short *key;
+	char name[ADI_MAX_NAME_LENGTH];
+	char cname[ADI_MAX_CNAME_LENGTH];
+	char phys[ADI_MAX_PHYS_LENGTH];
+	unsigned char data[ADI_MAX_LENGTH];
+};
+
+struct adi_port {
+	struct gameport *gameport;
+	struct adi adi[2];
+	int bad;
+	int reads;
+};
+
+/*
+ * adi_read_packet() reads a Logitech ADI packet.
+ */
+
+static void adi_read_packet(struct adi_port *port)
+{
+	struct adi *adi = port->adi;
+	struct gameport *gameport = port->gameport;
+	unsigned char u, v, w, x, z;
+	int t[2], s[2], i;
+	unsigned long flags;
+
+	for (i = 0; i < 2; i++) {
+		adi[i].ret = -1;
+		t[i] = gameport_time(gameport, ADI_MAX_START);
+		s[i] = 0;
+	}
+
+	local_irq_save(flags);
+
+	gameport_trigger(gameport);
+	v = z = gameport_read(gameport);
+
+	do {
+		u = v;
+		w = u ^ (v = x = gameport_read(gameport));
+		for (i = 0; i < 2; i++, w >>= 2, x >>= 2) {
+			t[i]--;
+			if ((w & 0x30) && s[i]) {
+				if ((w & 0x30) < 0x30 && adi[i].ret < ADI_MAX_LENGTH && t[i] > 0) {
+					adi[i].data[++adi[i].ret] = w;
+					t[i] = gameport_time(gameport, ADI_MAX_STROBE);
+				} else t[i] = 0;
+			} else if (!(x & 0x30)) s[i] = 1;
+		}
+	} while (t[0] > 0 || t[1] > 0);
+
+	local_irq_restore(flags);
+
+	return;
+}
+
+/*
+ * adi_move_bits() detects a possible 2-stream mode, and moves
+ * the bits accordingly.
+ */
+
+static void adi_move_bits(struct adi_port *port, int length)
+{
+	int i;
+	struct adi *adi = port->adi;
+
+ 	adi[0].idx = adi[1].idx = 0;
+
+	if (adi[0].ret <= 0 || adi[1].ret <= 0) return;
+	if (adi[0].data[0] & 0x20 || ~adi[1].data[0] & 0x20) return;
+
+	for (i = 1; i <= adi[1].ret; i++)
+		adi[0].data[((length - 1) >> 1) + i + 1] = adi[1].data[i];
+
+	adi[0].ret += adi[1].ret;
+	adi[1].ret = -1;
+}
+
+/*
+ * adi_get_bits() gathers bits from the data packet.
+ */
+
+static inline int adi_get_bits(struct adi *adi, int count)
+{
+	int bits = 0;
+	int i;
+	if ((adi->idx += count) > adi->ret) return 0;
+	for (i = 0; i < count; i++)
+		bits |= ((adi->data[adi->idx - i] >> 5) & 1) << i;
+	return bits;
+}
+
+/*
+ * adi_decode() decodes Logitech joystick data into input events.
+ */
+
+static int adi_decode(struct adi *adi)
+{
+	struct input_dev *dev = &adi->dev;
+	char *abs = adi->abs;
+	short *key = adi->key;
+	int i, t;
+
+	if (adi->ret < adi->length || adi->id != (adi_get_bits(adi, 4) | (adi_get_bits(adi, 4) << 4)))
+		return -1;
+
+	for (i = 0; i < adi->axes10; i++)
+		input_report_abs(dev, *abs++, adi_get_bits(adi, 10));
+
+	for (i = 0; i < adi->axes8; i++)
+		input_report_abs(dev, *abs++, adi_get_bits(adi, 8));
+
+	for (i = 0; i < adi->buttons && i < 63; i++) {
+		if (i == adi->pad) {
+			t = adi_get_bits(adi, 4);
+			input_report_abs(dev, *abs++, ((t >> 2) & 1) - ( t       & 1));
+			input_report_abs(dev, *abs++, ((t >> 1) & 1) - ((t >> 3) & 1));
+		}
+		input_report_key(dev, *key++, adi_get_bits(adi, 1));
+	}
+
+	for (i = 0; i < adi->hats; i++) {
+		if ((t = adi_get_bits(adi, 4)) > 8) t = 0;
+		input_report_abs(dev, *abs++, adi_hat_to_axis[t].x);
+		input_report_abs(dev, *abs++, adi_hat_to_axis[t].y);
+	}
+
+	for (i = 63; i < adi->buttons; i++)
+		input_report_key(dev, *key++, adi_get_bits(adi, 1));
+
+	input_sync(dev);
+
+	return 0;
+}
+
+/*
+ * adi_read() reads the data packet and decodes it.
+ */
+
+static int adi_read(struct adi_port *port)
+{
+	int i;
+	int result = 0;
+
+	adi_read_packet(port);
+	adi_move_bits(port, port->adi[0].length);
+
+	for (i = 0; i < 2; i++)
+		if (port->adi[i].length)
+			 result |= adi_decode(port->adi + i);
+
+	return result;
+}
+
+/*
+ * adi_poll() repeatedly polls the Logitech joysticks.
+ */
+
+static void adi_poll(struct gameport *gameport)
+{
+	struct adi_port *port = gameport_get_drvdata(gameport);
+
+	port->bad -= adi_read(port);
+	port->reads++;
+}
+
+/*
+ * adi_open() is a callback from the input open routine.
+ */
+
+static int adi_open(struct input_dev *dev)
+{
+	struct adi_port *port = dev->private;
+
+	gameport_start_polling(port->gameport);
+	return 0;
+}
+
+/*
+ * adi_close() is a callback from the input close routine.
+ */
+
+static void adi_close(struct input_dev *dev)
+{
+	struct adi_port *port = dev->private;
+
+	gameport_stop_polling(port->gameport);
+}
+
+/*
+ * adi_init_digital() sends a trigger & delay sequence
+ * to reset and initialize a Logitech joystick into digital mode.
+ */
+
+static void adi_init_digital(struct gameport *gameport)
+{
+	int seq[] = { 4, -2, -3, 10, -6, -11, -7, -9, 11, 0 };
+	int i;
+
+	for (i = 0; seq[i]; i++) {
+		gameport_trigger(gameport);
+		if (seq[i] > 0) msleep(seq[i]);
+		if (seq[i] < 0) {
+			mdelay(-seq[i]);
+			udelay(-seq[i]*14);	/* It looks like mdelay() is off by approx 1.4% */
+		}
+	}
+}
+
+static void adi_id_decode(struct adi *adi, struct adi_port *port)
+{
+	int i, t;
+
+	if (adi->ret < ADI_MIN_ID_LENGTH) /* Minimum ID packet length */
+		return;
+
+	if (adi->ret < (t = adi_get_bits(adi, 10))) {
+		printk(KERN_WARNING "adi: Short ID packet: reported: %d != read: %d\n", t, adi->ret);
+		return;
+	}
+
+	adi->id = adi_get_bits(adi, 4) | (adi_get_bits(adi, 4) << 4);
+
+	if ((t = adi_get_bits(adi, 4)) & ADI_FLAG_HAT) adi->hats++;
+
+	adi->length = adi_get_bits(adi, 10);
+
+	if (adi->length >= ADI_MAX_LENGTH || adi->length < ADI_MIN_LENGTH) {
+		printk(KERN_WARNING "adi: Bad data packet length (%d).\n", adi->length);
+		adi->length = 0;
+		return;
+	}
+
+	adi->axes8 = adi_get_bits(adi, 4);
+	adi->buttons = adi_get_bits(adi, 6);
+
+	if (adi_get_bits(adi, 6) != 8 && adi->hats) {
+		printk(KERN_WARNING "adi: Other than 8-dir POVs not supported yet.\n");
+		adi->length = 0;
+		return;
+	}
+
+	adi->buttons += adi_get_bits(adi, 6);
+	adi->hats += adi_get_bits(adi, 4);
+
+	i = adi_get_bits(adi, 4);
+
+	if (t & ADI_FLAG_10BIT) {
+		adi->axes10 = adi->axes8 - i;
+		adi->axes8 = i;
+	}
+
+	t = adi_get_bits(adi, 4);
+
+	for (i = 0; i < t; i++)
+		adi->cname[i] = adi_get_bits(adi, 8);
+	adi->cname[i] = 0;
+
+	t = 8 + adi->buttons + adi->axes10 * 10 + adi->axes8 * 8 + adi->hats * 4;
+	if (adi->length != t && adi->length != t + (t & 1)) {
+		printk(KERN_WARNING "adi: Expected length %d != data length %d\n", t, adi->length);
+		adi->length = 0;
+		return;
+	}
+
+	switch (adi->id) {
+		case ADI_ID_TPD:
+			adi->pad = 4;
+			adi->buttons -= 4;
+			break;
+		case ADI_ID_WGP:
+			adi->pad = 0;
+			adi->buttons -= 4;
+			break;
+		default:
+			adi->pad = -1;
+			break;
+	}
+}
+
+static void adi_init_input(struct adi *adi, struct adi_port *port, int half)
+{
+	int i, t;
+	char buf[ADI_MAX_NAME_LENGTH];
+
+	if (!adi->length) return;
+
+	init_input_dev(&adi->dev);
+
+	t = adi->id < ADI_ID_MAX ? adi->id : ADI_ID_MAX;
+
+	snprintf(buf, ADI_MAX_PHYS_LENGTH, adi_names[t], adi->id);
+	snprintf(adi->name, ADI_MAX_NAME_LENGTH, "Logitech %s", buf);
+	snprintf(adi->phys, ADI_MAX_PHYS_LENGTH, "%s/input%d", port->gameport->phys, half);
+
+	adi->abs = adi_abs[t];
+	adi->key = adi_key[t];
+
+	adi->dev.open = adi_open;
+	adi->dev.close = adi_close;
+
+	adi->dev.name = adi->name;
+	adi->dev.phys = adi->phys;
+	adi->dev.id.bustype = BUS_GAMEPORT;
+	adi->dev.id.vendor = GAMEPORT_ID_VENDOR_LOGITECH;
+	adi->dev.id.product = adi->id;
+	adi->dev.id.version = 0x0100;
+
+	adi->dev.private = port;
+	adi->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+	for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad != -1)) * 2; i++)
+		set_bit(adi->abs[i], adi->dev.absbit);
+
+	for (i = 0; i < adi->buttons; i++)
+		set_bit(adi->key[i], adi->dev.keybit);
+}
+
+static void adi_init_center(struct adi *adi)
+{
+	int i, t, x;
+
+	if (!adi->length)
+		return;
+
+	for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad != -1)) * 2; i++) {
+
+		t = adi->abs[i];
+		x = adi->dev.abs[t];
+
+		if (t == ABS_THROTTLE || t == ABS_RUDDER || adi->id == ADI_ID_WGPE)
+			x = i < adi->axes10 ? 512 : 128;
+
+		if (i < adi->axes10)
+			input_set_abs_params(&adi->dev, t, 64, x * 2 - 64, 2, 16);
+		else if (i < adi->axes10 + adi->axes8)
+			input_set_abs_params(&adi->dev, t, 48, x * 2 - 48, 1, 16);
+		else
+			input_set_abs_params(&adi->dev, t, -1, 1, 0, 0);
+	}
+}
+
+/*
+ * adi_connect() probes for Logitech ADI joysticks.
+ */
+
+static int adi_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct adi_port *port;
+	int i;
+	int err;
+
+	if (!(port = kcalloc(1, sizeof(struct adi_port), GFP_KERNEL)))
+		return -ENOMEM;
+
+	port->gameport = gameport;
+
+	gameport_set_drvdata(gameport, port);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err) {
+		kfree(port);
+		return err;
+	}
+
+	adi_init_digital(gameport);
+	adi_read_packet(port);
+
+	if (port->adi[0].ret >= ADI_MIN_LEN_LENGTH)
+		adi_move_bits(port, adi_get_bits(port->adi, 10));
+
+	for (i = 0; i < 2; i++) {
+		adi_id_decode(port->adi + i, port);
+		adi_init_input(port->adi + i, port, i);
+	}
+
+	if (!port->adi[0].length && !port->adi[1].length) {
+		gameport_close(gameport);
+		kfree(port);
+		return -ENODEV;
+	}
+
+	gameport_set_poll_handler(gameport, adi_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	msleep(ADI_INIT_DELAY);
+	if (adi_read(port)) {
+		msleep(ADI_DATA_DELAY);
+		adi_read(port);
+	}
+
+	for (i = 0; i < 2; i++)
+		if (port->adi[i].length > 0) {
+			adi_init_center(port->adi + i);
+			input_register_device(&port->adi[i].dev);
+			printk(KERN_INFO "input: %s [%s] on %s\n",
+				port->adi[i].name, port->adi[i].cname, gameport->phys);
+		}
+
+	return 0;
+}
+
+static void adi_disconnect(struct gameport *gameport)
+{
+	int i;
+	struct adi_port *port = gameport_get_drvdata(gameport);
+
+	for (i = 0; i < 2; i++)
+		if (port->adi[i].length > 0)
+			input_unregister_device(&port->adi[i].dev);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(port);
+}
+
+/*
+ * The gameport device structure.
+ */
+
+static struct gameport_driver adi_drv = {
+	.driver		= {
+		.name	= "adi",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= adi_connect,
+	.disconnect	= adi_disconnect,
+};
+
+static int __init adi_init(void)
+{
+	gameport_register_driver(&adi_drv);
+	return 0;
+}
+
+static void __exit adi_exit(void)
+{
+	gameport_unregister_driver(&adi_drv);
+}
+
+module_init(adi_init);
+module_exit(adi_exit);
diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c
new file mode 100644
index 0000000..cf36ca9
--- /dev/null
+++ b/drivers/input/joystick/amijoy.c
@@ -0,0 +1,161 @@
+/*
+ * $Id: amijoy.c,v 1.13 2002/01/22 20:26:32 vojtech Exp $
+ *
+ *  Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * Driver for Amiga joysticks 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/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <asm/system.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Driver for Amiga joysticks");
+MODULE_LICENSE("GPL");
+
+static int amijoy[2] = { 0, 1 };
+module_param_array_named(map, amijoy, uint, NULL, 0);
+MODULE_PARM_DESC(map, "Map of attached joysticks in form of <a>,<b> (default is 0,1)");
+
+__obsolete_setup("amijoy=");
+
+static int amijoy_used[2] = { 0, 0 };
+static struct input_dev amijoy_dev[2];
+static char *amijoy_phys[2] = { "amijoy/input0", "amijoy/input1" };
+
+static char *amijoy_name = "Amiga joystick";
+
+static irqreturn_t amijoy_interrupt(int irq, void *dummy, struct pt_regs *fp)
+{
+	int i, data = 0, button = 0;
+
+	for (i = 0; i < 2; i++)
+		if (amijoy[i]) {
+
+			switch (i) {
+				case 0: data = ~custom.joy0dat; button = (~ciaa.pra >> 6) & 1; break;
+				case 1: data = ~custom.joy1dat; button = (~ciaa.pra >> 7) & 1; break;
+			}
+
+			input_regs(amijoy_dev + i, fp);
+
+			input_report_key(amijoy_dev + i, BTN_TRIGGER, button);
+
+			input_report_abs(amijoy_dev + i, ABS_X, ((data >> 1) & 1) - ((data >> 9) & 1));
+			data = ~(data ^ (data << 1));
+			input_report_abs(amijoy_dev + i, ABS_Y, ((data >> 1) & 1) - ((data >> 9) & 1));
+
+			input_sync(amijoy_dev + i);
+		}
+	return IRQ_HANDLED;
+}
+
+static int amijoy_open(struct input_dev *dev)
+{
+	int *used = dev->private;
+
+	if ((*used)++)
+		return 0;
+
+	if (request_irq(IRQ_AMIGA_VERTB, amijoy_interrupt, 0, "amijoy", amijoy_interrupt)) {
+		(*used)--;
+		printk(KERN_ERR "amijoy.c: Can't allocate irq %d\n", IRQ_AMIGA_VERTB);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static void amijoy_close(struct input_dev *dev)
+{
+	int *used = dev->private;
+
+	if (!--(*used))
+		free_irq(IRQ_AMIGA_VERTB, amijoy_interrupt);
+}
+
+static int __init amijoy_init(void)
+{
+	int i, j;
+
+	for (i = 0; i < 2; i++)
+		if (amijoy[i]) {
+			if (!request_mem_region(CUSTOM_PHYSADDR+10+i*2, 2,
+						"amijoy [Denise]")) {
+				if (i == 1 && amijoy[0]) {
+					input_unregister_device(amijoy_dev);
+					release_mem_region(CUSTOM_PHYSADDR+10, 2);
+				}
+				return -EBUSY;
+			}
+
+			amijoy_dev[i].open = amijoy_open;
+			amijoy_dev[i].close = amijoy_close;
+			amijoy_dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+			amijoy_dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+			amijoy_dev[i].keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
+			for (j = 0; j < 2; j++) {
+				amijoy_dev[i].absmin[ABS_X + j] = -1;
+				amijoy_dev[i].absmax[ABS_X + j] = 1;
+			}
+
+			amijoy_dev[i].name = amijoy_name;
+			amijoy_dev[i].phys = amijoy_phys[i];
+			amijoy_dev[i].id.bustype = BUS_AMIGA;
+			amijoy_dev[i].id.vendor = 0x0001;
+			amijoy_dev[i].id.product = 0x0003;
+			amijoy_dev[i].id.version = 0x0100;
+
+			amijoy_dev[i].private = amijoy_used + i;
+
+			input_register_device(amijoy_dev + i);
+			printk(KERN_INFO "input: %s at joy%ddat\n", amijoy_name, i);
+		}
+	return 0;
+}
+
+static void __exit amijoy_exit(void)
+{
+	int i;
+
+	for (i = 0; i < 2; i++)
+		if (amijoy[i]) {
+			input_unregister_device(amijoy_dev + i);
+			release_mem_region(CUSTOM_PHYSADDR+10+i*2, 2);
+		}
+}
+
+module_init(amijoy_init);
+module_exit(amijoy_exit);
diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c
new file mode 100644
index 0000000..504b7d5
--- /dev/null
+++ b/drivers/input/joystick/analog.c
@@ -0,0 +1,772 @@
+/*
+ * $Id: analog.c,v 1.68 2002/01/22 20:18:32 vojtech Exp $
+ *
+ *  Copyright (c) 1996-2001 Vojtech Pavlik
+ */
+
+/*
+ * Analog joystick and gamepad 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/config.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/gameport.h>
+#include <asm/timex.h>
+
+#define DRIVER_DESC	"Analog joystick and gamepad driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Option parsing.
+ */
+
+#define ANALOG_PORTS		16
+
+static char *js[ANALOG_PORTS];
+static int js_nargs;
+static int analog_options[ANALOG_PORTS];
+module_param_array_named(map, js, charp, &js_nargs, 0);
+MODULE_PARM_DESC(map, "Describes analog joysticks type/capabilities");
+
+__obsolete_setup("js=");
+
+/*
+ * Times, feature definitions.
+ */
+
+#define ANALOG_RUDDER		0x00004
+#define ANALOG_THROTTLE		0x00008
+#define ANALOG_AXES_STD		0x0000f
+#define ANALOG_BTNS_STD		0x000f0
+
+#define ANALOG_BTNS_CHF		0x00100
+#define ANALOG_HAT1_CHF		0x00200
+#define ANALOG_HAT2_CHF		0x00400
+#define ANALOG_HAT_FCS		0x00800
+#define ANALOG_HATS_ALL		0x00e00
+#define ANALOG_BTN_TL		0x01000
+#define ANALOG_BTN_TR		0x02000
+#define ANALOG_BTN_TL2		0x04000
+#define ANALOG_BTN_TR2		0x08000
+#define ANALOG_BTNS_TLR		0x03000
+#define ANALOG_BTNS_TLR2	0x0c000
+#define ANALOG_BTNS_GAMEPAD	0x0f000
+
+#define ANALOG_HBTN_CHF		0x10000
+#define ANALOG_ANY_CHF		0x10700
+#define ANALOG_SAITEK		0x20000
+#define ANALOG_EXTENSIONS	0x7ff00
+#define ANALOG_GAMEPAD		0x80000
+
+#define ANALOG_MAX_TIME		3	/* 3 ms */
+#define ANALOG_LOOP_TIME	2000	/* 2 * loop */
+#define ANALOG_SAITEK_DELAY	200	/* 200 us */
+#define ANALOG_SAITEK_TIME	2000	/* 2000 us */
+#define ANALOG_AXIS_TIME	2	/* 2 * refresh */
+#define ANALOG_INIT_RETRIES	8	/* 8 times */
+#define ANALOG_FUZZ_BITS	2	/* 2 bit more */
+#define ANALOG_FUZZ_MAGIC	36	/* 36 u*ms/loop */
+
+#define ANALOG_MAX_NAME_LENGTH  128
+#define ANALOG_MAX_PHYS_LENGTH	32
+
+static short analog_axes[] = { ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE };
+static short analog_hats[] = { ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y };
+static short analog_pads[] = { BTN_Y, BTN_Z, BTN_TL, BTN_TR };
+static short analog_exts[] = { ANALOG_HAT1_CHF, ANALOG_HAT2_CHF, ANALOG_HAT_FCS };
+static short analog_pad_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_TL2, BTN_TR2, BTN_SELECT, BTN_START, BTN_MODE, BTN_BASE };
+static short analog_joy_btn[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2,
+				  BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_BASE6 };
+
+static unsigned char analog_chf[] = { 0xf, 0x0, 0x1, 0x9, 0x2, 0x4, 0xc, 0x8, 0x3, 0x5, 0xb, 0x7, 0xd, 0xe, 0xa, 0x6 };
+
+struct analog {
+	struct input_dev dev;
+	int mask;
+	short *buttons;
+	char name[ANALOG_MAX_NAME_LENGTH];
+	char phys[ANALOG_MAX_PHYS_LENGTH];
+};
+
+struct analog_port {
+	struct gameport *gameport;
+	struct analog analog[2];
+	unsigned char mask;
+	char saitek;
+	char cooked;
+	int bads;
+	int reads;
+	int speed;
+	int loop;
+	int fuzz;
+	int axes[4];
+	int buttons;
+	int initial[4];
+	int axtime;
+};
+
+/*
+ * Time macros.
+ */
+
+#ifdef __i386__
+#define GET_TIME(x)	do { if (cpu_has_tsc) rdtscl(x); else x = get_time_pit(); } while (0)
+#define DELTA(x,y)	(cpu_has_tsc ? ((y) - (x)) : ((x) - (y) + ((x) < (y) ? CLOCK_TICK_RATE / HZ : 0)))
+#define TIME_NAME	(cpu_has_tsc?"TSC":"PIT")
+static unsigned int get_time_pit(void)
+{
+        extern spinlock_t i8253_lock;
+        unsigned long flags;
+        unsigned int count;
+
+        spin_lock_irqsave(&i8253_lock, flags);
+        outb_p(0x00, 0x43);
+        count = inb_p(0x40);
+        count |= inb_p(0x40) << 8;
+        spin_unlock_irqrestore(&i8253_lock, flags);
+
+        return count;
+}
+#elif defined(__x86_64__)
+#define GET_TIME(x)	rdtscl(x)
+#define DELTA(x,y)	((y)-(x))
+#define TIME_NAME	"TSC"
+#elif defined(__alpha__)
+#define GET_TIME(x)	do { x = get_cycles(); } while (0)
+#define DELTA(x,y)	((y)-(x))
+#define TIME_NAME	"PCC"
+#else
+#define FAKE_TIME
+static unsigned long analog_faketime = 0;
+#define GET_TIME(x)     do { x = analog_faketime++; } while(0)
+#define DELTA(x,y)	((y)-(x))
+#define TIME_NAME	"Unreliable"
+#warning Precise timer not defined for this architecture.
+#endif
+
+/*
+ * analog_decode() decodes analog joystick data and reports input events.
+ */
+
+static void analog_decode(struct analog *analog, int *axes, int *initial, int buttons)
+{
+	struct input_dev *dev = &analog->dev;
+	int i, j;
+
+	if (analog->mask & ANALOG_HAT_FCS)
+		for (i = 0; i < 4; i++)
+			if (axes[3] < ((initial[3] * ((i << 1) + 1)) >> 3)) {
+				buttons |= 1 << (i + 14);
+				break;
+			}
+
+	for (i = j = 0; i < 6; i++)
+		if (analog->mask & (0x10 << i))
+			input_report_key(dev, analog->buttons[j++], (buttons >> i) & 1);
+
+	if (analog->mask & ANALOG_HBTN_CHF)
+		for (i = 0; i < 4; i++)
+			input_report_key(dev, analog->buttons[j++], (buttons >> (i + 10)) & 1);
+
+	if (analog->mask & ANALOG_BTN_TL)
+		input_report_key(dev, analog_pads[0], axes[2] < (initial[2] >> 1));
+	if (analog->mask & ANALOG_BTN_TR)
+		input_report_key(dev, analog_pads[1], axes[3] < (initial[3] >> 1));
+	if (analog->mask & ANALOG_BTN_TL2)
+		input_report_key(dev, analog_pads[2], axes[2] > (initial[2] + (initial[2] >> 1)));
+	if (analog->mask & ANALOG_BTN_TR2)
+		input_report_key(dev, analog_pads[3], axes[3] > (initial[3] + (initial[3] >> 1)));
+
+	for (i = j = 0; i < 4; i++)
+		if (analog->mask & (1 << i))
+			input_report_abs(dev, analog_axes[j++], axes[i]);
+
+	for (i = j = 0; i < 3; i++)
+		if (analog->mask & analog_exts[i]) {
+			input_report_abs(dev, analog_hats[j++],
+				((buttons >> ((i << 2) + 7)) & 1) - ((buttons >> ((i << 2) + 9)) & 1));
+			input_report_abs(dev, analog_hats[j++],
+				((buttons >> ((i << 2) + 8)) & 1) - ((buttons >> ((i << 2) + 6)) & 1));
+		}
+
+	input_sync(dev);
+}
+
+/*
+ * analog_cooked_read() reads analog joystick data.
+ */
+
+static int analog_cooked_read(struct analog_port *port)
+{
+	struct gameport *gameport = port->gameport;
+	unsigned int time[4], start, loop, now, loopout, timeout;
+	unsigned char data[4], this, last;
+	unsigned long flags;
+	int i, j;
+
+	loopout = (ANALOG_LOOP_TIME * port->loop) / 1000;
+	timeout = ANALOG_MAX_TIME * port->speed;
+
+	local_irq_save(flags);
+	gameport_trigger(gameport);
+	GET_TIME(now);
+	local_irq_restore(flags);
+
+	start = now;
+	this = port->mask;
+	i = 0;
+
+	do {
+		loop = now;
+		last = this;
+
+		local_irq_disable();
+		this = gameport_read(gameport) & port->mask;
+		GET_TIME(now);
+		local_irq_restore(flags);
+
+		if ((last ^ this) && (DELTA(loop, now) < loopout)) {
+			data[i] = last ^ this;
+			time[i] = now;
+			i++;
+		}
+
+	} while (this && (i < 4) && (DELTA(start, now) < timeout));
+
+	this <<= 4;
+
+	for (--i; i >= 0; i--) {
+		this |= data[i];
+		for (j = 0; j < 4; j++)
+			if (data[i] & (1 << j))
+				port->axes[j] = (DELTA(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop;
+	}
+
+	return -(this != port->mask);
+}
+
+static int analog_button_read(struct analog_port *port, char saitek, char chf)
+{
+	unsigned char u;
+	int t = 1, i = 0;
+	int strobe = gameport_time(port->gameport, ANALOG_SAITEK_TIME);
+
+	u = gameport_read(port->gameport);
+
+	if (!chf) {
+		port->buttons = (~u >> 4) & 0xf;
+		return 0;
+	}
+
+	port->buttons = 0;
+
+	while ((~u & 0xf0) && (i < 16) && t) {
+		port->buttons |= 1 << analog_chf[(~u >> 4) & 0xf];
+		if (!saitek) return 0;
+		udelay(ANALOG_SAITEK_DELAY);
+		t = strobe;
+		gameport_trigger(port->gameport);
+		while (((u = gameport_read(port->gameport)) & port->mask) && t) t--;
+		i++;
+	}
+
+	return -(!t || (i == 16));
+}
+
+/*
+ * analog_poll() repeatedly polls the Analog joysticks.
+ */
+
+static void analog_poll(struct gameport *gameport)
+{
+	struct analog_port *port = gameport_get_drvdata(gameport);
+	int i;
+
+	char saitek = !!(port->analog[0].mask & ANALOG_SAITEK);
+	char chf = !!(port->analog[0].mask & ANALOG_ANY_CHF);
+
+	if (port->cooked) {
+		port->bads -= gameport_cooked_read(port->gameport, port->axes, &port->buttons);
+		if (chf)
+			port->buttons = port->buttons ? (1 << analog_chf[port->buttons]) : 0;
+		port->reads++;
+	} else {
+		if (!port->axtime--) {
+			port->bads -= analog_cooked_read(port);
+			port->bads -= analog_button_read(port, saitek, chf);
+			port->reads++;
+			port->axtime = ANALOG_AXIS_TIME - 1;
+		} else {
+			if (!saitek)
+				analog_button_read(port, saitek, chf);
+		}
+	}
+
+	for (i = 0; i < 2; i++)
+		if (port->analog[i].mask)
+			analog_decode(port->analog + i, port->axes, port->initial, port->buttons);
+}
+
+/*
+ * analog_open() is a callback from the input open routine.
+ */
+
+static int analog_open(struct input_dev *dev)
+{
+	struct analog_port *port = dev->private;
+
+	gameport_start_polling(port->gameport);
+	return 0;
+}
+
+/*
+ * analog_close() is a callback from the input close routine.
+ */
+
+static void analog_close(struct input_dev *dev)
+{
+	struct analog_port *port = dev->private;
+
+	gameport_stop_polling(port->gameport);
+}
+
+/*
+ * analog_calibrate_timer() calibrates the timer and computes loop
+ * and timeout values for a joystick port.
+ */
+
+static void analog_calibrate_timer(struct analog_port *port)
+{
+	struct gameport *gameport = port->gameport;
+	unsigned int i, t, tx, t1, t2, t3;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	GET_TIME(t1);
+#ifdef FAKE_TIME
+	analog_faketime += 830;
+#endif
+	mdelay(1);
+	GET_TIME(t2);
+	GET_TIME(t3);
+	local_irq_restore(flags);
+
+	port->speed = DELTA(t1, t2) - DELTA(t2, t3);
+
+	tx = ~0;
+
+	for (i = 0; i < 50; i++) {
+		local_irq_save(flags);
+		GET_TIME(t1);
+		for (t = 0; t < 50; t++) { gameport_read(gameport); GET_TIME(t2); }
+		GET_TIME(t3);
+		local_irq_restore(flags);
+		udelay(i);
+		t = DELTA(t1, t2) - DELTA(t2, t3);
+		if (t < tx) tx = t;
+	}
+
+        port->loop = tx / 50;
+}
+
+/*
+ * analog_name() constructs a name for an analog joystick.
+ */
+
+static void analog_name(struct analog *analog)
+{
+	sprintf(analog->name, "Analog %d-axis %d-button",
+		hweight8(analog->mask & ANALOG_AXES_STD),
+		hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 +
+		hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4);
+
+	if (analog->mask & ANALOG_HATS_ALL)
+		sprintf(analog->name, "%s %d-hat",
+			analog->name, hweight16(analog->mask & ANALOG_HATS_ALL));
+
+	if (analog->mask & ANALOG_HAT_FCS)
+			strcat(analog->name, " FCS");
+	if (analog->mask & ANALOG_ANY_CHF)
+			strcat(analog->name, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF");
+
+	strcat(analog->name, (analog->mask & ANALOG_GAMEPAD) ? " gamepad": " joystick");
+}
+
+/*
+ * analog_init_device()
+ */
+
+static void analog_init_device(struct analog_port *port, struct analog *analog, int index)
+{
+	int i, j, t, v, w, x, y, z;
+
+	analog_name(analog);
+	sprintf(analog->phys, "%s/input%d", port->gameport->phys, index);
+	analog->buttons = (analog->mask & ANALOG_GAMEPAD) ? analog_pad_btn : analog_joy_btn;
+
+	init_input_dev(&analog->dev);
+
+	analog->dev.name = analog->name;
+	analog->dev.phys = analog->phys;
+	analog->dev.id.bustype = BUS_GAMEPORT;
+	analog->dev.id.vendor = GAMEPORT_ID_VENDOR_ANALOG;
+	analog->dev.id.product = analog->mask >> 4;
+	analog->dev.id.version = 0x0100;
+
+	analog->dev.open = analog_open;
+	analog->dev.close = analog_close;
+	analog->dev.private = port;
+	analog->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+	for (i = j = 0; i < 4; i++)
+		if (analog->mask & (1 << i)) {
+
+			t = analog_axes[j];
+			x = port->axes[i];
+			y = (port->axes[0] + port->axes[1]) >> 1;
+			z = y - port->axes[i];
+			z = z > 0 ? z : -z;
+			v = (x >> 3);
+			w = (x >> 3);
+
+			set_bit(t, analog->dev.absbit);
+
+			if ((i == 2 || i == 3) && (j == 2 || j == 3) && (z > (y >> 3)))
+				x = y;
+
+			if (analog->mask & ANALOG_SAITEK) {
+				if (i == 2) x = port->axes[i];
+				v = x - (x >> 2);
+				w = (x >> 4);
+			}
+
+			analog->dev.absmax[t] = (x << 1) - v;
+			analog->dev.absmin[t] = v;
+			analog->dev.absfuzz[t] = port->fuzz;
+			analog->dev.absflat[t] = w;
+
+			j++;
+		}
+
+	for (i = j = 0; i < 3; i++)
+		if (analog->mask & analog_exts[i])
+			for (x = 0; x < 2; x++) {
+				t = analog_hats[j++];
+				set_bit(t, analog->dev.absbit);
+				analog->dev.absmax[t] = 1;
+				analog->dev.absmin[t] = -1;
+			}
+
+	for (i = j = 0; i < 4; i++)
+		if (analog->mask & (0x10 << i))
+			set_bit(analog->buttons[j++], analog->dev.keybit);
+
+	if (analog->mask & ANALOG_BTNS_CHF)
+		for (i = 0; i < 2; i++)
+			set_bit(analog->buttons[j++], analog->dev.keybit);
+
+	if (analog->mask & ANALOG_HBTN_CHF)
+		for (i = 0; i < 4; i++)
+			set_bit(analog->buttons[j++], analog->dev.keybit);
+
+	for (i = 0; i < 4; i++)
+		if (analog->mask & (ANALOG_BTN_TL << i))
+			set_bit(analog_pads[i], analog->dev.keybit);
+
+	analog_decode(analog, port->axes, port->initial, port->buttons);
+
+	input_register_device(&analog->dev);
+
+	printk(KERN_INFO "input: %s at %s", analog->name, port->gameport->phys);
+
+	if (port->cooked)
+		printk(" [ADC port]\n");
+	else
+		printk(" [%s timer, %d %sHz clock, %d ns res]\n", TIME_NAME,
+		port->speed > 10000 ? (port->speed + 800) / 1000 : port->speed,
+		port->speed > 10000 ? "M" : "k",
+		port->speed > 10000 ? (port->loop * 1000) / (port->speed / 1000)
+				    : (port->loop * 1000000) / port->speed);
+}
+
+/*
+ * analog_init_devices() sets up device-specific values and registers the input devices.
+ */
+
+static int analog_init_masks(struct analog_port *port)
+{
+	int i;
+	struct analog *analog = port->analog;
+	int max[4];
+
+	if (!port->mask)
+		return -1;
+
+	if ((port->mask & 3) != 3 && port->mask != 0xc) {
+		printk(KERN_WARNING "analog.c: Unknown joystick device found  "
+			"(data=%#x, %s), probably not analog joystick.\n",
+			port->mask, port->gameport->phys);
+		return -1;
+	}
+
+
+	i = analog_options[0]; /* FIXME !!! - need to specify options for different ports */
+
+	analog[0].mask = i & 0xfffff;
+
+	analog[0].mask &= ~(ANALOG_AXES_STD | ANALOG_HAT_FCS | ANALOG_BTNS_GAMEPAD)
+			| port->mask | ((port->mask << 8) & ANALOG_HAT_FCS)
+			| ((port->mask << 10) & ANALOG_BTNS_TLR) | ((port->mask << 12) & ANALOG_BTNS_TLR2);
+
+	analog[0].mask &= ~(ANALOG_HAT2_CHF)
+			| ((analog[0].mask & ANALOG_HBTN_CHF) ? 0 : ANALOG_HAT2_CHF);
+
+	analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_BTN_TR | ANALOG_BTN_TR2)
+			| ((~analog[0].mask & ANALOG_HAT_FCS) >> 8)
+			| ((~analog[0].mask & ANALOG_HAT_FCS) << 2)
+			| ((~analog[0].mask & ANALOG_HAT_FCS) << 4);
+
+	analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_RUDDER)
+			| (((~analog[0].mask & ANALOG_BTNS_TLR ) >> 10)
+			&  ((~analog[0].mask & ANALOG_BTNS_TLR2) >> 12));
+
+	analog[1].mask = ((i >> 20) & 0xff) | ((i >> 12) & 0xf0000);
+
+	analog[1].mask &= (analog[0].mask & ANALOG_EXTENSIONS) ? ANALOG_GAMEPAD
+			: (((ANALOG_BTNS_STD | port->mask) & ~analog[0].mask) | ANALOG_GAMEPAD);
+
+	if (port->cooked) {
+
+		for (i = 0; i < 4; i++) max[i] = port->axes[i] << 1;
+
+		if ((analog[0].mask & 0x7) == 0x7) max[2] = (max[0] + max[1]) >> 1;
+		if ((analog[0].mask & 0xb) == 0xb) max[3] = (max[0] + max[1]) >> 1;
+		if ((analog[0].mask & ANALOG_BTN_TL) && !(analog[0].mask & ANALOG_BTN_TL2)) max[2] >>= 1;
+		if ((analog[0].mask & ANALOG_BTN_TR) && !(analog[0].mask & ANALOG_BTN_TR2)) max[3] >>= 1;
+		if ((analog[0].mask & ANALOG_HAT_FCS)) max[3] >>= 1;
+
+		gameport_calibrate(port->gameport, port->axes, max);
+	}
+
+	for (i = 0; i < 4; i++)
+		port->initial[i] = port->axes[i];
+
+	return -!(analog[0].mask || analog[1].mask);
+}
+
+static int analog_init_port(struct gameport *gameport, struct gameport_driver *drv, struct analog_port *port)
+{
+	int i, t, u, v;
+
+	port->gameport = gameport;
+
+	gameport_set_drvdata(gameport, port);
+
+	if (!gameport_open(gameport, drv, GAMEPORT_MODE_RAW)) {
+
+		analog_calibrate_timer(port);
+
+		gameport_trigger(gameport);
+		t = gameport_read(gameport);
+		msleep(ANALOG_MAX_TIME);
+		port->mask = (gameport_read(gameport) ^ t) & t & 0xf;
+		port->fuzz = (port->speed * ANALOG_FUZZ_MAGIC) / port->loop / 1000 + ANALOG_FUZZ_BITS;
+
+		for (i = 0; i < ANALOG_INIT_RETRIES; i++) {
+			if (!analog_cooked_read(port))
+				break;
+			msleep(ANALOG_MAX_TIME);
+		}
+
+		u = v = 0;
+
+		msleep(ANALOG_MAX_TIME);
+		t = gameport_time(gameport, ANALOG_MAX_TIME * 1000);
+		gameport_trigger(gameport);
+		while ((gameport_read(port->gameport) & port->mask) && (u < t))
+			u++;
+		udelay(ANALOG_SAITEK_DELAY);
+		t = gameport_time(gameport, ANALOG_SAITEK_TIME);
+		gameport_trigger(gameport);
+		while ((gameport_read(port->gameport) & port->mask) && (v < t))
+			v++;
+
+		if (v < (u >> 1)) { /* FIXME - more than one port */
+			analog_options[0] |= /* FIXME - more than one port */
+				ANALOG_SAITEK | ANALOG_BTNS_CHF | ANALOG_HBTN_CHF | ANALOG_HAT1_CHF;
+			return 0;
+		}
+
+		gameport_close(gameport);
+	}
+
+	if (!gameport_open(gameport, drv, GAMEPORT_MODE_COOKED)) {
+
+		for (i = 0; i < ANALOG_INIT_RETRIES; i++)
+			if (!gameport_cooked_read(gameport, port->axes, &port->buttons))
+				break;
+		for (i = 0; i < 4; i++)
+			if (port->axes[i] != -1)
+				port->mask |= 1 << i;
+
+		port->fuzz = gameport->fuzz;
+		port->cooked = 1;
+		return 0;
+	}
+
+	return gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+}
+
+static int analog_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct analog_port *port;
+	int i;
+	int err;
+
+	if (!(port = kcalloc(1, sizeof(struct analog_port), GFP_KERNEL)))
+		return - ENOMEM;
+
+	err = analog_init_port(gameport, drv, port);
+	if (err) {
+		kfree(port);
+		return err;
+	}
+
+	err = analog_init_masks(port);
+	if (err) {
+		gameport_close(gameport);
+		gameport_set_drvdata(gameport, NULL);
+		kfree(port);
+		return err;
+	}
+
+	gameport_set_poll_handler(gameport, analog_poll);
+	gameport_set_poll_interval(gameport, 10);
+
+	for (i = 0; i < 2; i++)
+		if (port->analog[i].mask)
+			analog_init_device(port, port->analog + i, i);
+
+	return 0;
+}
+
+static void analog_disconnect(struct gameport *gameport)
+{
+	int i;
+	struct analog_port *port = gameport_get_drvdata(gameport);
+
+	for (i = 0; i < 2; i++)
+		if (port->analog[i].mask)
+			input_unregister_device(&port->analog[i].dev);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	printk(KERN_INFO "analog.c: %d out of %d reads (%d%%) on %s failed\n",
+		port->bads, port->reads, port->reads ? (port->bads * 100 / port->reads) : 0,
+		port->gameport->phys);
+	kfree(port);
+}
+
+struct analog_types {
+	char *name;
+	int value;
+};
+
+static struct analog_types analog_types[] = {
+	{ "none",	0x00000000 },
+	{ "auto",	0x000000ff },
+	{ "2btn",	0x0000003f },
+	{ "y-joy",	0x0cc00033 },
+	{ "y-pad",	0x8cc80033 },
+	{ "fcs",	0x000008f7 },
+	{ "chf",	0x000002ff },
+	{ "fullchf",	0x000007ff },
+	{ "gamepad",	0x000830f3 },
+	{ "gamepad8",	0x0008f0f3 },
+	{ NULL, 0 }
+};
+
+static void analog_parse_options(void)
+{
+	int i, j;
+	char *end;
+
+	for (i = 0; i < js_nargs; i++) {
+
+		for (j = 0; analog_types[j].name; j++)
+			if (!strcmp(analog_types[j].name, js[i])) {
+				analog_options[i] = analog_types[j].value;
+				break;
+			}
+		if (analog_types[j].name) continue;
+
+		analog_options[i] = simple_strtoul(js[i], &end, 0);
+		if (end != js[i]) continue;
+
+		analog_options[i] = 0xff;
+		if (!strlen(js[i])) continue;
+
+		printk(KERN_WARNING "analog.c: Bad config for port %d - \"%s\"\n", i, js[i]);
+	}
+
+	for (; i < ANALOG_PORTS; i++)
+		analog_options[i] = 0xff;
+}
+
+/*
+ * The gameport device structure.
+ */
+
+static struct gameport_driver analog_drv = {
+	.driver		= {
+		.name	= "analog",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= analog_connect,
+	.disconnect	= analog_disconnect,
+};
+
+static int __init analog_init(void)
+{
+	analog_parse_options();
+	gameport_register_driver(&analog_drv);
+
+	return 0;
+}
+
+static void __exit analog_exit(void)
+{
+	gameport_unregister_driver(&analog_drv);
+}
+
+module_init(analog_init);
+module_exit(analog_exit);
diff --git a/drivers/input/joystick/cobra.c b/drivers/input/joystick/cobra.c
new file mode 100644
index 0000000..a600220
--- /dev/null
+++ b/drivers/input/joystick/cobra.c
@@ -0,0 +1,264 @@
+/*
+ * $Id: cobra.c,v 1.19 2002/01/22 20:26:52 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Creative Labs Blaster GamePad Cobra 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/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+
+#define DRIVER_DESC	"Creative Labs Blaster GamePad Cobra driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define COBRA_MAX_STROBE	45	/* 45 us max wait for first strobe */
+#define COBRA_LENGTH		36
+
+static char* cobra_name = "Creative Labs Blaster GamePad Cobra";
+
+static int cobra_btn[] = { BTN_START, BTN_SELECT, BTN_TL, BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL2, BTN_TR2, 0 };
+
+struct cobra {
+	struct gameport *gameport;
+	struct input_dev dev[2];
+	int reads;
+	int bads;
+	unsigned char exists;
+	char phys[2][32];
+};
+
+static unsigned char cobra_read_packet(struct gameport *gameport, unsigned int *data)
+{
+	unsigned long flags;
+	unsigned char u, v, w;
+	__u64 buf[2];
+	int r[2], t[2];
+	int i, j, ret;
+
+	int strobe = gameport_time(gameport, COBRA_MAX_STROBE);
+
+	for (i = 0; i < 2; i++) {
+		r[i] = buf[i] = 0;
+		t[i] = COBRA_MAX_STROBE;
+	}
+
+	local_irq_save(flags);
+
+	u = gameport_read(gameport);
+
+	do {
+		t[0]--; t[1]--;
+		v = gameport_read(gameport);
+		for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2)
+			if (w & 0x30) {
+				if ((w & 0x30) < 0x30 && r[i] < COBRA_LENGTH && t[i] > 0) {
+					buf[i] |= (__u64)((w >> 5) & 1) << r[i]++;
+					t[i] = strobe;
+					u = v;
+				} else t[i] = 0;
+			}
+	} while (t[0] > 0 || t[1] > 0);
+
+	local_irq_restore(flags);
+
+	ret = 0;
+
+	for (i = 0; i < 2; i++) {
+
+		if (r[i] != COBRA_LENGTH) continue;
+
+		for (j = 0; j < COBRA_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++)
+			buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (COBRA_LENGTH - 1));
+
+		if (j < COBRA_LENGTH) ret |= (1 << i);
+
+		data[i] = ((buf[i] >>  7) & 0x000001f) | ((buf[i] >>  8) & 0x00003e0)
+			| ((buf[i] >>  9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000)
+			| ((buf[i] >> 11) & 0x1f00000);
+
+	}
+
+	return ret;
+}
+
+static void cobra_poll(struct gameport *gameport)
+{
+	struct cobra *cobra = gameport_get_drvdata(gameport);
+	struct input_dev *dev;
+	unsigned int data[2];
+	int i, j, r;
+
+	cobra->reads++;
+
+	if ((r = cobra_read_packet(gameport, data)) != cobra->exists) {
+		cobra->bads++;
+		return;
+	}
+
+	for (i = 0; i < 2; i++)
+		if (cobra->exists & r & (1 << i)) {
+
+			dev = cobra->dev + i;
+
+			input_report_abs(dev, ABS_X, ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1));
+			input_report_abs(dev, ABS_Y, ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1));
+
+			for (j = 0; cobra_btn[j]; j++)
+				input_report_key(dev, cobra_btn[j], data[i] & (0x20 << j));
+
+			input_sync(dev);
+
+		}
+}
+
+static int cobra_open(struct input_dev *dev)
+{
+	struct cobra *cobra = dev->private;
+
+	gameport_start_polling(cobra->gameport);
+	return 0;
+}
+
+static void cobra_close(struct input_dev *dev)
+{
+	struct cobra *cobra = dev->private;
+
+	gameport_stop_polling(cobra->gameport);
+}
+
+static int cobra_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct cobra *cobra;
+	unsigned int data[2];
+	int i, j;
+	int err;
+
+	if (!(cobra = kcalloc(1, sizeof(struct cobra), GFP_KERNEL)))
+		return -ENOMEM;
+
+	cobra->gameport = gameport;
+
+	gameport_set_drvdata(gameport, cobra);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	cobra->exists = cobra_read_packet(gameport, data);
+
+	for (i = 0; i < 2; i++)
+		if ((cobra->exists >> i) & data[i] & 1) {
+			printk(KERN_WARNING "cobra.c: Device %d on %s has the Ext bit set. ID is: %d"
+				" Contact vojtech@ucw.cz\n", i, gameport->phys, (data[i] >> 2) & 7);
+			cobra->exists &= ~(1 << i);
+		}
+
+	if (!cobra->exists) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	gameport_set_poll_handler(gameport, cobra_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	for (i = 0; i < 2; i++)
+		if ((cobra->exists >> i) & 1) {
+
+			sprintf(cobra->phys[i], "%s/input%d", gameport->phys, i);
+
+			cobra->dev[i].private = cobra;
+			cobra->dev[i].open = cobra_open;
+			cobra->dev[i].close = cobra_close;
+
+			cobra->dev[i].name = cobra_name;
+			cobra->dev[i].phys = cobra->phys[i];
+			cobra->dev[i].id.bustype = BUS_GAMEPORT;
+			cobra->dev[i].id.vendor = GAMEPORT_ID_VENDOR_CREATIVE;
+			cobra->dev[i].id.product = 0x0008;
+			cobra->dev[i].id.version = 0x0100;
+
+			cobra->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+			input_set_abs_params(&cobra->dev[i], ABS_X, -1, 1, 0, 0);
+			input_set_abs_params(&cobra->dev[i], ABS_Y, -1, 1, 0, 0);
+
+			for (j = 0; cobra_btn[j]; j++)
+				set_bit(cobra_btn[j], cobra->dev[i].keybit);
+
+			input_register_device(&cobra->dev[i]);
+			printk(KERN_INFO "input: %s on %s\n", cobra_name, gameport->phys);
+		}
+
+	return 0;
+
+fail2:	gameport_close(gameport);
+fail1:	gameport_set_drvdata(gameport, NULL);
+	kfree(cobra);
+	return err;
+}
+
+static void cobra_disconnect(struct gameport *gameport)
+{
+	struct cobra *cobra = gameport_get_drvdata(gameport);
+	int i;
+
+	for (i = 0; i < 2; i++)
+		if ((cobra->exists >> i) & 1)
+			input_unregister_device(cobra->dev + i);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(cobra);
+}
+
+static struct gameport_driver cobra_drv = {
+	.driver		= {
+		.name	= "cobra",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= cobra_connect,
+	.disconnect	= cobra_disconnect,
+};
+
+static int __init cobra_init(void)
+{
+	gameport_register_driver(&cobra_drv);
+	return 0;
+}
+
+static void __exit cobra_exit(void)
+{
+	gameport_unregister_driver(&cobra_drv);
+}
+
+module_init(cobra_init);
+module_exit(cobra_exit);
diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c
new file mode 100644
index 0000000..cfdd3ac
--- /dev/null
+++ b/drivers/input/joystick/db9.c
@@ -0,0 +1,647 @@
+/*
+ * $Id: db9.c,v 1.13 2002/04/07 20:13:37 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Andree Borrmann		Mats Sjövall
+ */
+
+/*
+ * Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick 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/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/parport.h>
+#include <linux/input.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver");
+MODULE_LICENSE("GPL");
+
+static int db9[] __initdata = { -1, 0 };
+static int db9_nargs __initdata = 0;
+module_param_array_named(dev, db9, int, &db9_nargs, 0);
+MODULE_PARM_DESC(dev, "Describes first attached device (<parport#>,<type>)");
+
+static int db9_2[] __initdata = { -1, 0 };
+static int db9_nargs_2 __initdata = 0;
+module_param_array_named(dev2, db9_2, int, &db9_nargs_2, 0);
+MODULE_PARM_DESC(dev2, "Describes second attached device (<parport#>,<type>)");
+
+static int db9_3[] __initdata = { -1, 0 };
+static int db9_nargs_3 __initdata = 0;
+module_param_array_named(dev3, db9_3, int, &db9_nargs_3, 0);
+MODULE_PARM_DESC(dev3, "Describes third attached device (<parport#>,<type>)");
+
+__obsolete_setup("db9=");
+__obsolete_setup("db9_2=");
+__obsolete_setup("db9_3=");
+
+#define DB9_MULTI_STICK		0x01
+#define DB9_MULTI2_STICK	0x02
+#define DB9_GENESIS_PAD		0x03
+#define DB9_GENESIS5_PAD	0x05
+#define DB9_GENESIS6_PAD	0x06
+#define DB9_SATURN_PAD		0x07
+#define DB9_MULTI_0802		0x08
+#define DB9_MULTI_0802_2	0x09
+#define DB9_CD32_PAD		0x0A
+#define DB9_SATURN_DPP		0x0B
+#define DB9_SATURN_DPP_2	0x0C
+#define DB9_MAX_PAD		0x0D
+
+#define DB9_UP			0x01
+#define DB9_DOWN		0x02
+#define DB9_LEFT		0x04
+#define DB9_RIGHT		0x08
+#define DB9_FIRE1		0x10
+#define DB9_FIRE2		0x20
+#define DB9_FIRE3		0x40
+#define DB9_FIRE4		0x80
+
+#define DB9_NORMAL		0x0a
+#define DB9_NOSELECT		0x08
+
+#define DB9_MAX_DEVICES 2
+
+#define DB9_GENESIS6_DELAY	14
+#define DB9_REFRESH_TIME	HZ/100
+
+struct db9 {
+	struct input_dev dev[DB9_MAX_DEVICES];
+	struct timer_list timer;
+	struct pardevice *pd;
+	int mode;
+	int used;
+	char phys[2][32];
+};
+
+static struct db9 *db9_base[3];
+
+static short db9_multi_btn[] = { BTN_TRIGGER, BTN_THUMB };
+static short db9_genesis_btn[] = { BTN_START, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_MODE };
+static short db9_cd32_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START };
+
+static char db9_buttons[DB9_MAX_PAD] = { 0, 1, 2, 4, 0, 6, 8, 9, 1, 1, 7, 9, 9 };
+static short *db9_btn[DB9_MAX_PAD] = { NULL, db9_multi_btn, db9_multi_btn, db9_genesis_btn, NULL, db9_genesis_btn,
+					db9_genesis_btn, db9_cd32_btn, db9_multi_btn, db9_multi_btn, db9_cd32_btn,
+					db9_cd32_btn, db9_cd32_btn };
+static char *db9_name[DB9_MAX_PAD] = { NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad",
+				      NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick",
+				     "Multisystem (0.8.0.2-dual) joystick", "Amiga CD-32 pad", "Saturn dpp", "Saturn dpp dual" };
+
+static const int db9_max_pads[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 6, 1, 2, 1, 6, 12 };
+static const int db9_num_axis[DB9_MAX_PAD] = { 0, 2, 2, 2, 0, 2, 2, 7, 2, 2, 2 ,7, 7 };
+static const short db9_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_RZ, ABS_Z, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y };
+static const int db9_bidirectional[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0 };
+static const int db9_reverse[DB9_MAX_PAD] = { 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0 };
+
+/*
+ * Saturn controllers
+ */
+#define DB9_SATURN_DELAY 300
+static const int db9_saturn_byte[] = { 1, 1, 1, 2, 2, 2, 2, 2, 1 };
+static const unsigned char db9_saturn_mask[] = { 0x04, 0x01, 0x02, 0x40, 0x20, 0x10, 0x08, 0x80, 0x08 };
+
+/*
+ * db9_saturn_write_sub() writes 2 bit data.
+ */
+static void db9_saturn_write_sub(struct parport *port, int type, unsigned char data, int powered, int pwr_sub)
+{
+	unsigned char c;
+
+	switch (type) {
+	case 1: /* DPP1 */
+		c = 0x80 | 0x30 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | data;
+		parport_write_data(port, c);
+		break;
+	case 2: /* DPP2 */
+		c = 0x40 | data << 4 | (powered ? 0x08 : 0) | (pwr_sub ? 0x04 : 0) | 0x03;
+		parport_write_data(port, c);
+		break;
+	case 0:	/* DB9 */
+		c = ((((data & 2) ? 2 : 0) | ((data & 1) ? 4 : 0)) ^ 0x02) | !powered;
+		parport_write_control(port, c);
+		break;
+	}
+}
+
+/*
+ * gc_saturn_read_sub() reads 4 bit data.
+ */
+static unsigned char db9_saturn_read_sub(struct parport *port, int type)
+{
+	unsigned char data;
+
+	if (type) {
+		/* DPP */
+		data = parport_read_status(port) ^ 0x80;
+		return (data & 0x80 ? 1 : 0) | (data & 0x40 ? 2 : 0)
+		     | (data & 0x20 ? 4 : 0) | (data & 0x10 ? 8 : 0);
+	} else {
+		/* DB9 */
+		data = parport_read_data(port) & 0x0f;
+		return (data & 0x8 ? 1 : 0) | (data & 0x4 ? 2 : 0)
+		     | (data & 0x2 ? 4 : 0) | (data & 0x1 ? 8 : 0);
+	}
+}
+
+/*
+ * db9_saturn_read_analog() sends clock and reads 8 bit data.
+ */
+static unsigned char db9_saturn_read_analog(struct parport *port, int type, int powered)
+{
+	unsigned char data;
+
+	db9_saturn_write_sub(port, type, 0, powered, 0);
+	udelay(DB9_SATURN_DELAY);
+	data = db9_saturn_read_sub(port, type) << 4;
+	db9_saturn_write_sub(port, type, 2, powered, 0);
+	udelay(DB9_SATURN_DELAY);
+	data |= db9_saturn_read_sub(port, type);
+	return data;
+}
+
+/*
+ * db9_saturn_read_packet() reads whole saturn packet at connector
+ * and returns device identifier code.
+ */
+static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char *data, int type, int powered)
+{
+	int i, j;
+	unsigned char tmp;
+
+	db9_saturn_write_sub(port, type, 3, powered, 0);
+	data[0] = db9_saturn_read_sub(port, type);
+	switch (data[0] & 0x0f) {
+	case 0xf:
+		/* 1111  no pad */
+		return data[0] = 0xff;
+	case 0x4: case 0x4 | 0x8:
+		/* ?100 : digital controller */
+		db9_saturn_write_sub(port, type, 0, powered, 1);
+		data[2] = db9_saturn_read_sub(port, type) << 4;
+		db9_saturn_write_sub(port, type, 2, powered, 1);
+		data[1] = db9_saturn_read_sub(port, type) << 4;
+		db9_saturn_write_sub(port, type, 1, powered, 1);
+		data[1] |= db9_saturn_read_sub(port, type);
+		db9_saturn_write_sub(port, type, 3, powered, 1);
+		/* data[2] |= db9_saturn_read_sub(port, type); */
+		data[2] |= data[0];
+		return data[0] = 0x02;
+	case 0x1:
+		/* 0001 : analog controller or multitap */
+		db9_saturn_write_sub(port, type, 2, powered, 0);
+		udelay(DB9_SATURN_DELAY);
+		data[0] = db9_saturn_read_analog(port, type, powered);
+		if (data[0] != 0x41) {
+			/* read analog controller */
+			for (i = 0; i < (data[0] & 0x0f); i++)
+				data[i + 1] = db9_saturn_read_analog(port, type, powered);
+			db9_saturn_write_sub(port, type, 3, powered, 0);
+			return data[0];
+		} else {
+			/* read multitap */
+			if (db9_saturn_read_analog(port, type, powered) != 0x60)
+				return data[0] = 0xff;
+			for (i = 0; i < 60; i += 10) {
+				data[i] = db9_saturn_read_analog(port, type, powered);
+				if (data[i] != 0xff)
+					/* read each pad */
+					for (j = 0; j < (data[i] & 0x0f); j++)
+						data[i + j + 1] = db9_saturn_read_analog(port, type, powered);
+			}
+			db9_saturn_write_sub(port, type, 3, powered, 0);
+			return 0x41;
+		}
+	case 0x0:
+		/* 0000 : mouse */
+		db9_saturn_write_sub(port, type, 2, powered, 0);
+		udelay(DB9_SATURN_DELAY);
+		tmp = db9_saturn_read_analog(port, type, powered);
+		if (tmp == 0xff) {
+			for (i = 0; i < 3; i++)
+				data[i + 1] = db9_saturn_read_analog(port, type, powered);
+			db9_saturn_write_sub(port, type, 3, powered, 0);
+			return data[0] = 0xe3;
+		}
+	default:
+		return data[0];
+	}
+}
+
+/*
+ * db9_saturn_report() analyzes packet and reports.
+ */
+static int db9_saturn_report(unsigned char id, unsigned char data[60], struct input_dev *dev, int n, int max_pads)
+{
+	int tmp, i, j;
+
+	tmp = (id == 0x41) ? 60 : 10;
+	for (j = 0; (j < tmp) && (n < max_pads); j += 10, n++) {
+		switch (data[j]) {
+		case 0x16: /* multi controller (analog 4 axis) */
+			input_report_abs(dev + n, db9_abs[5], data[j + 6]);
+		case 0x15: /* mission stick (analog 3 axis) */
+			input_report_abs(dev + n, db9_abs[3], data[j + 4]);
+			input_report_abs(dev + n, db9_abs[4], data[j + 5]);
+		case 0x13: /* racing controller (analog 1 axis) */
+			input_report_abs(dev + n, db9_abs[2], data[j + 3]);
+		case 0x34: /* saturn keyboard (udlr ZXC ASD QE Esc) */
+		case 0x02: /* digital pad (digital 2 axis + buttons) */
+			input_report_abs(dev + n, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
+			input_report_abs(dev + n, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
+			for (i = 0; i < 9; i++)
+				input_report_key(dev + n, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
+			break;
+		case 0x19: /* mission stick x2 (analog 6 axis + buttons) */
+			input_report_abs(dev + n, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
+			input_report_abs(dev + n, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
+			for (i = 0; i < 9; i++)
+				input_report_key(dev + n, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
+			input_report_abs(dev + n, db9_abs[2], data[j + 3]);
+			input_report_abs(dev + n, db9_abs[3], data[j + 4]);
+			input_report_abs(dev + n, db9_abs[4], data[j + 5]);
+			/*
+			input_report_abs(dev + n, db9_abs[8], (data[j + 6] & 128 ? 0 : 1) - (data[j + 6] & 64 ? 0 : 1));
+			input_report_abs(dev + n, db9_abs[9], (data[j + 6] & 32 ? 0 : 1) - (data[j + 6] & 16 ? 0 : 1));
+			*/
+			input_report_abs(dev + n, db9_abs[6], data[j + 7]);
+			input_report_abs(dev + n, db9_abs[7], data[j + 8]);
+			input_report_abs(dev + n, db9_abs[5], data[j + 9]);
+			break;
+		case 0xd3: /* sankyo ff (analog 1 axis + stop btn) */
+			input_report_key(dev + n, BTN_A, data[j + 3] & 0x80);
+			input_report_abs(dev + n, db9_abs[2], data[j + 3] & 0x7f);
+			break;
+		case 0xe3: /* shuttle mouse (analog 2 axis + buttons. signed value) */
+			input_report_key(dev + n, BTN_START, data[j + 1] & 0x08);
+			input_report_key(dev + n, BTN_A, data[j + 1] & 0x04);
+			input_report_key(dev + n, BTN_C, data[j + 1] & 0x02);
+			input_report_key(dev + n, BTN_B, data[j + 1] & 0x01);
+			input_report_abs(dev + n, db9_abs[2], data[j + 2] ^ 0x80);
+			input_report_abs(dev + n, db9_abs[3], (0xff-(data[j + 3] ^ 0x80))+1); /* */
+			break;
+		case 0xff:
+		default: /* no pad */
+			input_report_abs(dev + n, db9_abs[0], 0);
+			input_report_abs(dev + n, db9_abs[1], 0);
+			for (i = 0; i < 9; i++)
+				input_report_key(dev + n, db9_cd32_btn[i], 0);
+			break;
+		}
+	}
+	return n;
+}
+
+static int db9_saturn(int mode, struct parport *port, struct input_dev *dev)
+{
+	unsigned char id, data[60];
+	int type, n, max_pads;
+	int tmp, i;
+
+	switch (mode) {
+	case DB9_SATURN_PAD:
+		type = 0;
+		n = 1;
+		break;
+	case DB9_SATURN_DPP:
+		type = 1;
+		n = 1;
+		break;
+	case DB9_SATURN_DPP_2:
+		type = 1;
+		n = 2;
+		break;
+	default:
+		return -1;
+	}
+	max_pads = min(db9_max_pads[mode], DB9_MAX_DEVICES);
+	for (tmp = 0, i = 0; i < n; i++) {
+		id = db9_saturn_read_packet(port, data, type + i, 1);
+		tmp = db9_saturn_report(id, data, dev, tmp, max_pads);
+	}
+	return 0;
+}
+
+static void db9_timer(unsigned long private)
+{
+	struct db9 *db9 = (void *) private;
+	struct parport *port = db9->pd->port;
+	struct input_dev *dev = db9->dev;
+	int data, i;
+
+	switch(db9->mode) {
+		case DB9_MULTI_0802_2:
+
+			data = parport_read_data(port) >> 3;
+
+			input_report_abs(dev + 1, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev + 1, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+			input_report_key(dev + 1, BTN_TRIGGER, ~data & DB9_FIRE1);
+
+		case DB9_MULTI_0802:
+
+			data = parport_read_status(port) >> 3;
+
+			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+			input_report_key(dev, BTN_TRIGGER, data & DB9_FIRE1);
+			break;
+
+		case DB9_MULTI_STICK:
+
+			data = parport_read_data(port);
+
+			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+			input_report_key(dev, BTN_TRIGGER, ~data & DB9_FIRE1);
+			break;
+
+		case DB9_MULTI2_STICK:
+
+			data = parport_read_data(port);
+
+			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+			input_report_key(dev, BTN_TRIGGER, ~data & DB9_FIRE1);
+			input_report_key(dev, BTN_THUMB,   ~data & DB9_FIRE2);
+			break;
+
+		case DB9_GENESIS_PAD:
+
+			parport_write_control(port, DB9_NOSELECT);
+			data = parport_read_data(port);
+
+			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+			input_report_key(dev, BTN_B, ~data & DB9_FIRE1);
+			input_report_key(dev, BTN_C, ~data & DB9_FIRE2);
+
+			parport_write_control(port, DB9_NORMAL);
+			data=parport_read_data(port);
+
+			input_report_key(dev, BTN_A,     ~data & DB9_FIRE1);
+			input_report_key(dev, BTN_START, ~data & DB9_FIRE2);
+			break;
+
+		case DB9_GENESIS5_PAD:
+
+			parport_write_control(port, DB9_NOSELECT);
+			data=parport_read_data(port);
+
+			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+			input_report_key(dev, BTN_B, ~data & DB9_FIRE1);
+			input_report_key(dev, BTN_C, ~data & DB9_FIRE2);
+
+			parport_write_control(port, DB9_NORMAL);
+			data=parport_read_data(port);
+
+			input_report_key(dev, BTN_A,     ~data & DB9_FIRE1);
+			input_report_key(dev, BTN_X,     ~data & DB9_FIRE2);
+			input_report_key(dev, BTN_Y,     ~data & DB9_LEFT);
+			input_report_key(dev, BTN_START, ~data & DB9_RIGHT);
+			break;
+
+		case DB9_GENESIS6_PAD:
+
+			parport_write_control(port, DB9_NOSELECT); /* 1 */
+			udelay(DB9_GENESIS6_DELAY);
+			data=parport_read_data(port);
+
+			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+			input_report_key(dev, BTN_B, ~data & DB9_FIRE1);
+			input_report_key(dev, BTN_C, ~data & DB9_FIRE2);
+
+			parport_write_control(port, DB9_NORMAL);
+			udelay(DB9_GENESIS6_DELAY);
+			data=parport_read_data(port);
+
+			input_report_key(dev, BTN_A, ~data & DB9_FIRE1);
+			input_report_key(dev, BTN_START, ~data & DB9_FIRE2);
+
+			parport_write_control(port, DB9_NOSELECT); /* 2 */
+			udelay(DB9_GENESIS6_DELAY);
+			parport_write_control(port, DB9_NORMAL);
+			udelay(DB9_GENESIS6_DELAY);
+			parport_write_control(port, DB9_NOSELECT); /* 3 */
+			udelay(DB9_GENESIS6_DELAY);
+			data=parport_read_data(port);
+
+			input_report_key(dev, BTN_X,    ~data & DB9_LEFT);
+			input_report_key(dev, BTN_Y,    ~data & DB9_DOWN);
+			input_report_key(dev, BTN_Z,    ~data & DB9_UP);
+			input_report_key(dev, BTN_MODE, ~data & DB9_RIGHT);
+
+			parport_write_control(port, DB9_NORMAL);
+			udelay(DB9_GENESIS6_DELAY);
+			parport_write_control(port, DB9_NOSELECT); /* 4 */
+			udelay(DB9_GENESIS6_DELAY);
+			parport_write_control(port, DB9_NORMAL);
+			break;
+
+		case DB9_SATURN_PAD:
+		case DB9_SATURN_DPP:
+		case DB9_SATURN_DPP_2:
+
+			db9_saturn(db9->mode, port, dev);
+			break;
+
+		case DB9_CD32_PAD:
+
+			data=parport_read_data(port);
+
+			input_report_abs(dev, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+			input_report_abs(dev, ABS_Y, (data & DB9_DOWN  ? 0 : 1) - (data & DB9_UP   ? 0 : 1));
+
+			parport_write_control(port, 0x0a);
+
+			for (i = 0; i < 7; i++) {
+				data = parport_read_data(port);
+				parport_write_control(port, 0x02);
+				parport_write_control(port, 0x0a);
+				input_report_key(dev, db9_cd32_btn[i], ~data & DB9_FIRE2);
+				}
+
+			parport_write_control(port, 0x00);
+			break;
+		}
+
+	input_sync(dev);
+
+	mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME);
+}
+
+static int db9_open(struct input_dev *dev)
+{
+	struct db9 *db9 = dev->private;
+	struct parport *port = db9->pd->port;
+
+	if (!db9->used++) {
+		parport_claim(db9->pd);
+		parport_write_data(port, 0xff);
+		if (db9_reverse[db9->mode]) {
+			parport_data_reverse(port);
+			parport_write_control(port, DB9_NORMAL);
+		}
+		mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME);
+	}
+
+	return 0;
+}
+
+static void db9_close(struct input_dev *dev)
+{
+	struct db9 *db9 = dev->private;
+	struct parport *port = db9->pd->port;
+
+	if (!--db9->used) {
+		del_timer(&db9->timer);
+		parport_write_control(port, 0x00);
+		parport_data_forward(port);
+		parport_release(db9->pd);
+	}
+}
+
+static struct db9 __init *db9_probe(int *config, int nargs)
+{
+	struct db9 *db9;
+	struct parport *pp;
+	int i, j;
+
+	if (config[0] < 0)
+		return NULL;
+
+	if (nargs < 2) {
+		printk(KERN_ERR "db9.c: Device type must be specified.\n");
+		return NULL;
+	}
+
+	if (config[1] < 1 || config[1] >= DB9_MAX_PAD || !db9_buttons[config[1]]) {
+		printk(KERN_ERR "db9.c: bad config\n");
+		return NULL;
+	}
+
+	pp = parport_find_number(config[0]);
+	if (!pp) {
+		printk(KERN_ERR "db9.c: no such parport\n");
+		return NULL;
+	}
+
+	if (db9_bidirectional[config[1]]) {
+		if (!(pp->modes & PARPORT_MODE_TRISTATE)) {
+			printk(KERN_ERR "db9.c: specified parport is not bidirectional\n");
+			parport_put_port(pp);
+			return NULL;
+		}
+	}
+
+	if (!(db9 = kmalloc(sizeof(struct db9), GFP_KERNEL))) {
+		parport_put_port(pp);
+		return NULL;
+	}
+	memset(db9, 0, sizeof(struct db9));
+
+	db9->mode = config[1];
+	init_timer(&db9->timer);
+	db9->timer.data = (long) db9;
+	db9->timer.function = db9_timer;
+
+	db9->pd = parport_register_device(pp, "db9", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
+	parport_put_port(pp);
+
+	if (!db9->pd) {
+		printk(KERN_ERR "db9.c: parport busy already - lp.o loaded?\n");
+		kfree(db9);
+		return NULL;
+	}
+
+	for (i = 0; i < (min(db9_max_pads[db9->mode], DB9_MAX_DEVICES)); i++) {
+
+		sprintf(db9->phys[i], "%s/input%d", db9->pd->port->name, i);
+
+		db9->dev[i].private = db9;
+		db9->dev[i].open = db9_open;
+		db9->dev[i].close = db9_close;
+
+		db9->dev[i].name = db9_name[db9->mode];
+		db9->dev[i].phys = db9->phys[i];
+		db9->dev[i].id.bustype = BUS_PARPORT;
+		db9->dev[i].id.vendor = 0x0002;
+		db9->dev[i].id.product = config[1];
+		db9->dev[i].id.version = 0x0100;
+
+		db9->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+		for (j = 0; j < db9_buttons[db9->mode]; j++)
+			set_bit(db9_btn[db9->mode][j], db9->dev[i].keybit);
+		for (j = 0; j < db9_num_axis[db9->mode]; j++) {
+			set_bit(db9_abs[j], db9->dev[i].absbit);
+			if (j < 2) {
+				db9->dev[i].absmin[db9_abs[j]] = -1;
+				db9->dev[i].absmax[db9_abs[j]] = 1;
+			} else {
+				db9->dev[i].absmin[db9_abs[j]] = 1;
+				db9->dev[i].absmax[db9_abs[j]] = 255;
+				db9->dev[i].absflat[db9_abs[j]] = 0;
+			}
+		}
+		input_register_device(db9->dev + i);
+		printk(KERN_INFO "input: %s on %s\n", db9->dev[i].name, db9->pd->port->name);
+	}
+
+	return db9;
+}
+
+static int __init db9_init(void)
+{
+	db9_base[0] = db9_probe(db9, db9_nargs);
+	db9_base[1] = db9_probe(db9_2, db9_nargs_2);
+	db9_base[2] = db9_probe(db9_3, db9_nargs_3);
+
+	if (db9_base[0] || db9_base[1] || db9_base[2])
+		return 0;
+
+	return -ENODEV;
+}
+
+static void __exit db9_exit(void)
+{
+	int i, j;
+
+	for (i = 0; i < 3; i++)
+		if (db9_base[i]) {
+			for (j = 0; j < min(db9_max_pads[db9_base[i]->mode], DB9_MAX_DEVICES); j++)
+				input_unregister_device(db9_base[i]->dev + j);
+		parport_unregister_device(db9_base[i]->pd);
+	}
+}
+
+module_init(db9_init);
+module_exit(db9_exit);
diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c
new file mode 100644
index 0000000..8732f52
--- /dev/null
+++ b/drivers/input/joystick/gamecon.c
@@ -0,0 +1,697 @@
+/*
+ * NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux
+ *
+ *  Copyright (c) 1999-2004 	Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2004 		Peter Nelson <rufus-kernel@hackish.org>
+ *
+ *  Based on the work of:
+ *  	Andree Borrmann		John Dahlstrom
+ *  	David Kuder		Nathan Hand
+ */
+
+/*
+ * 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/kernel.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/parport.h>
+#include <linux/input.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("NES, SNES, N64, MultiSystem, PSX gamepad driver");
+MODULE_LICENSE("GPL");
+
+static int gc[] __initdata = { -1, 0, 0, 0, 0, 0 };
+static int gc_nargs __initdata = 0;
+module_param_array_named(map, gc, int, &gc_nargs, 0);
+MODULE_PARM_DESC(map, "Describers first set of devices (<parport#>,<pad1>,<pad2>,..<pad5>)");
+
+static int gc_2[] __initdata = { -1, 0, 0, 0, 0, 0 };
+static int gc_nargs_2 __initdata = 0;
+module_param_array_named(map2, gc_2, int, &gc_nargs_2, 0);
+MODULE_PARM_DESC(map2, "Describers second set of devices");
+
+static int gc_3[] __initdata = { -1, 0, 0, 0, 0, 0 };
+static int gc_nargs_3 __initdata = 0;
+module_param_array_named(map3, gc_3, int, &gc_nargs_3, 0);
+MODULE_PARM_DESC(map3, "Describers third set of devices");
+
+__obsolete_setup("gc=");
+__obsolete_setup("gc_2=");
+__obsolete_setup("gc_3=");
+
+/* see also gs_psx_delay parameter in PSX support section */
+
+#define GC_SNES		1
+#define GC_NES		2
+#define GC_NES4		3
+#define GC_MULTI	4
+#define GC_MULTI2	5
+#define GC_N64		6
+#define GC_PSX		7
+#define GC_DDR		8
+
+#define GC_MAX		8
+
+#define GC_REFRESH_TIME	HZ/100
+
+struct gc {
+	struct pardevice *pd;
+	struct input_dev dev[5];
+	struct timer_list timer;
+	unsigned char pads[GC_MAX + 1];
+	int used;
+	char phys[5][32];
+};
+
+static struct gc *gc_base[3];
+
+static int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 };
+
+static char *gc_names[] = { NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick",
+				"Multisystem 2-button joystick", "N64 controller", "PSX controller",
+				"PSX DDR controller" };
+/*
+ * N64 support.
+ */
+
+static unsigned char gc_n64_bytes[] = { 0, 1, 13, 15, 14, 12, 10, 11, 2, 3 };
+static short gc_n64_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_TRIGGER, BTN_START };
+
+#define GC_N64_LENGTH		32		/* N64 bit length, not including stop bit */
+#define GC_N64_REQUEST_LENGTH	37		/* transmit request sequence is 9 bits long */
+#define GC_N64_DELAY		133		/* delay between transmit request, and response ready (us) */
+#define GC_N64_REQUEST		0x1dd1111111ULL /* the request data command (encoded for 000000011) */
+#define GC_N64_DWS		3		/* delay between write segments (required for sound playback because of ISA DMA) */
+						/* GC_N64_DWS > 24 is known to fail */
+#define GC_N64_POWER_W		0xe2		/* power during write (transmit request) */
+#define GC_N64_POWER_R		0xfd		/* power during read */
+#define GC_N64_OUT		0x1d		/* output bits to the 4 pads */
+						/* Reading the main axes of any N64 pad is known to fail if the corresponding bit */
+						/* in GC_N64_OUT is pulled low on the output port (by any routine) for more */
+						/* than 123 us */
+#define GC_N64_CLOCK		0x02		/* clock bits for read */
+
+/*
+ * gc_n64_read_packet() reads an N64 packet.
+ * Each pad uses one bit per byte. So all pads connected to this port are read in parallel.
+ */
+
+static void gc_n64_read_packet(struct gc *gc, unsigned char *data)
+{
+	int i;
+	unsigned long flags;
+
+/*
+ * Request the pad to transmit data
+ */
+
+	local_irq_save(flags);
+	for (i = 0; i < GC_N64_REQUEST_LENGTH; i++) {
+		parport_write_data(gc->pd->port, GC_N64_POWER_W | ((GC_N64_REQUEST >> i) & 1 ? GC_N64_OUT : 0));
+		udelay(GC_N64_DWS);
+	}
+	local_irq_restore(flags);
+
+/*
+ * Wait for the pad response to be loaded into the 33-bit register of the adapter
+ */
+
+	udelay(GC_N64_DELAY);
+
+/*
+ * Grab data (ignoring the last bit, which is a stop bit)
+ */
+
+	for (i = 0; i < GC_N64_LENGTH; i++) {
+		parport_write_data(gc->pd->port, GC_N64_POWER_R);
+		data[i] = parport_read_status(gc->pd->port);
+		parport_write_data(gc->pd->port, GC_N64_POWER_R | GC_N64_CLOCK);
+	 }
+
+/*
+ * We must wait 200 ms here for the controller to reinitialize before the next read request.
+ * No worries as long as gc_read is polled less frequently than this.
+ */
+
+}
+
+/*
+ * NES/SNES support.
+ */
+
+#define GC_NES_DELAY	6	/* Delay between bits - 6us */
+#define GC_NES_LENGTH	8	/* The NES pads use 8 bits of data */
+#define GC_SNES_LENGTH	12	/* The SNES true length is 16, but the last 4 bits are unused */
+
+#define GC_NES_POWER	0xfc
+#define GC_NES_CLOCK	0x01
+#define GC_NES_LATCH	0x02
+
+static unsigned char gc_nes_bytes[] = { 0, 1, 2, 3 };
+static unsigned char gc_snes_bytes[] = { 8, 0, 2, 3, 9, 1, 10, 11 };
+static short gc_snes_btn[] = { BTN_A, BTN_B, BTN_SELECT, BTN_START, BTN_X, BTN_Y, BTN_TL, BTN_TR };
+
+/*
+ * gc_nes_read_packet() reads a NES/SNES packet.
+ * Each pad uses one bit per byte. So all pads connected to
+ * this port are read in parallel.
+ */
+
+static void gc_nes_read_packet(struct gc *gc, int length, unsigned char *data)
+{
+	int i;
+
+	parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK | GC_NES_LATCH);
+	udelay(GC_NES_DELAY * 2);
+	parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK);
+
+	for (i = 0; i < length; i++) {
+		udelay(GC_NES_DELAY);
+		parport_write_data(gc->pd->port, GC_NES_POWER);
+		data[i] = parport_read_status(gc->pd->port) ^ 0x7f;
+		udelay(GC_NES_DELAY);
+		parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK);
+	}
+}
+
+/*
+ * Multisystem joystick support
+ */
+
+#define GC_MULTI_LENGTH		5	/* Multi system joystick packet length is 5 */
+#define GC_MULTI2_LENGTH	6	/* One more bit for one more button */
+
+/*
+ * gc_multi_read_packet() reads a Multisystem joystick packet.
+ */
+
+static void gc_multi_read_packet(struct gc *gc, int length, unsigned char *data)
+{
+	int i;
+
+	for (i = 0; i < length; i++) {
+		parport_write_data(gc->pd->port, ~(1 << i));
+		data[i] = parport_read_status(gc->pd->port) ^ 0x7f;
+	}
+}
+
+/*
+ * PSX support
+ *
+ * See documentation at:
+ *	http://www.dim.com/~mackys/psxmemcard/ps-eng2.txt
+ *	http://www.gamesx.com/controldata/psxcont/psxcont.htm
+ *	ftp://milano.usal.es/pablo/
+ *
+ */
+
+#define GC_PSX_DELAY	25		/* 25 usec */
+#define GC_PSX_LENGTH	8		/* talk to the controller in bits */
+#define GC_PSX_BYTES	6		/* the maximum number of bytes to read off the controller */
+
+#define GC_PSX_MOUSE	1		/* Mouse */
+#define GC_PSX_NEGCON	2		/* NegCon */
+#define GC_PSX_NORMAL	4		/* Digital / Analog or Rumble in Digital mode  */
+#define GC_PSX_ANALOG	5		/* Analog in Analog mode / Rumble in Green mode */
+#define GC_PSX_RUMBLE	7		/* Rumble in Red mode */
+
+#define GC_PSX_CLOCK	0x04		/* Pin 4 */
+#define GC_PSX_COMMAND	0x01		/* Pin 2 */
+#define GC_PSX_POWER	0xf8		/* Pins 5-9 */
+#define GC_PSX_SELECT	0x02		/* Pin 3 */
+
+#define GC_PSX_ID(x)	((x) >> 4)	/* High nibble is device type */
+#define GC_PSX_LEN(x)	(((x) & 0xf) << 1)	/* Low nibble is length in bytes/2 */
+
+static int gc_psx_delay = GC_PSX_DELAY;
+module_param_named(psx_delay, gc_psx_delay, uint, 0);
+MODULE_PARM_DESC(psx_delay, "Delay when accessing Sony PSX controller (usecs)");
+
+__obsolete_setup("gc_psx_delay=");
+
+static short gc_psx_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_HAT0X, ABS_HAT0Y };
+static short gc_psx_btn[] = { BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_A, BTN_B, BTN_X, BTN_Y,
+				BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR };
+static short gc_psx_ddr_btn[] = { BTN_0, BTN_1, BTN_2, BTN_3 };
+
+/*
+ * gc_psx_command() writes 8bit command and reads 8bit data from
+ * the psx pad.
+ */
+
+static void gc_psx_command(struct gc *gc, int b, unsigned char data[5])
+{
+	int i, j, cmd, read;
+	for (i = 0; i < 5; i++)
+		data[i] = 0;
+
+	for (i = 0; i < GC_PSX_LENGTH; i++, b >>= 1) {
+		cmd = (b & 1) ? GC_PSX_COMMAND : 0;
+		parport_write_data(gc->pd->port, cmd | GC_PSX_POWER);
+		udelay(gc_psx_delay);
+		read = parport_read_status(gc->pd->port) ^ 0x80;
+		for (j = 0; j < 5; j++)
+			data[j] |= (read & gc_status_bit[j] & (gc->pads[GC_PSX] | gc->pads[GC_DDR])) ? (1 << i) : 0;
+		parport_write_data(gc->pd->port, cmd | GC_PSX_CLOCK | GC_PSX_POWER);
+		udelay(gc_psx_delay);
+	}
+}
+
+/*
+ * gc_psx_read_packet() reads a whole psx packet and returns
+ * device identifier code.
+ */
+
+static void gc_psx_read_packet(struct gc *gc, unsigned char data[5][GC_PSX_BYTES], unsigned char id[5])
+{
+	int i, j, max_len = 0;
+	unsigned long flags;
+	unsigned char data2[5];
+
+	parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER);	/* Select pad */
+	udelay(gc_psx_delay);
+	parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_POWER);			/* Deselect, begin command */
+	udelay(gc_psx_delay);
+
+	local_irq_save(flags);
+
+	gc_psx_command(gc, 0x01, data2);						/* Access pad */
+	gc_psx_command(gc, 0x42, id);							/* Get device ids */
+	gc_psx_command(gc, 0, data2);							/* Dump status */
+
+	for (i =0; i < 5; i++)								/* Find the longest pad */
+		if((gc_status_bit[i] & (gc->pads[GC_PSX] | gc->pads[GC_DDR]))
+			&& (GC_PSX_LEN(id[i]) > max_len)
+			&& (GC_PSX_LEN(id[i]) <= GC_PSX_BYTES))
+			max_len = GC_PSX_LEN(id[i]);
+
+	for (i = 0; i < max_len; i++) {						/* Read in all the data */
+		gc_psx_command(gc, 0, data2);
+		for (j = 0; j < 5; j++)
+			data[j][i] = data2[j];
+	}
+
+	local_irq_restore(flags);
+
+	parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER);
+
+	for(i = 0; i < 5; i++)								/* Set id's to the real value */
+		id[i] = GC_PSX_ID(id[i]);
+}
+
+/*
+ * gc_timer() reads and analyzes console pads data.
+ */
+
+#define GC_MAX_LENGTH GC_N64_LENGTH
+
+static void gc_timer(unsigned long private)
+{
+	struct gc *gc = (void *) private;
+	struct input_dev *dev = gc->dev;
+	unsigned char data[GC_MAX_LENGTH];
+	unsigned char data_psx[5][GC_PSX_BYTES];
+	int i, j, s;
+
+/*
+ * N64 pads - must be read first, any read confuses them for 200 us
+ */
+
+	if (gc->pads[GC_N64]) {
+
+		gc_n64_read_packet(gc, data);
+
+		for (i = 0; i < 5; i++) {
+
+			s = gc_status_bit[i];
+
+			if (s & gc->pads[GC_N64] & ~(data[8] | data[9])) {
+
+				signed char axes[2];
+				axes[0] = axes[1] = 0;
+
+				for (j = 0; j < 8; j++) {
+					if (data[23 - j] & s) axes[0] |= 1 << j;
+					if (data[31 - j] & s) axes[1] |= 1 << j;
+				}
+
+				input_report_abs(dev + i, ABS_X,  axes[0]);
+				input_report_abs(dev + i, ABS_Y, -axes[1]);
+
+				input_report_abs(dev + i, ABS_HAT0X, !(s & data[6]) - !(s & data[7]));
+				input_report_abs(dev + i, ABS_HAT0Y, !(s & data[4]) - !(s & data[5]));
+
+				for (j = 0; j < 10; j++)
+					input_report_key(dev + i, gc_n64_btn[j], s & data[gc_n64_bytes[j]]);
+
+				input_sync(dev + i);
+			}
+		}
+	}
+
+/*
+ * NES and SNES pads
+ */
+
+	if (gc->pads[GC_NES] || gc->pads[GC_SNES]) {
+
+		gc_nes_read_packet(gc, gc->pads[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH, data);
+
+		for (i = 0; i < 5; i++) {
+
+			s = gc_status_bit[i];
+
+			if (s & (gc->pads[GC_NES] | gc->pads[GC_SNES])) {
+				input_report_abs(dev + i, ABS_X, !(s & data[6]) - !(s & data[7]));
+				input_report_abs(dev + i, ABS_Y, !(s & data[4]) - !(s & data[5]));
+			}
+
+			if (s & gc->pads[GC_NES])
+				for (j = 0; j < 4; j++)
+					input_report_key(dev + i, gc_snes_btn[j], s & data[gc_nes_bytes[j]]);
+
+			if (s & gc->pads[GC_SNES])
+				for (j = 0; j < 8; j++)
+					input_report_key(dev + i, gc_snes_btn[j], s & data[gc_snes_bytes[j]]);
+
+			input_sync(dev + i);
+		}
+	}
+
+/*
+ * Multi and Multi2 joysticks
+ */
+
+	if (gc->pads[GC_MULTI] || gc->pads[GC_MULTI2]) {
+
+		gc_multi_read_packet(gc, gc->pads[GC_MULTI2] ? GC_MULTI2_LENGTH : GC_MULTI_LENGTH, data);
+
+		for (i = 0; i < 5; i++) {
+
+			s = gc_status_bit[i];
+
+			if (s & (gc->pads[GC_MULTI] | gc->pads[GC_MULTI2])) {
+				input_report_abs(dev + i, ABS_X,  !(s & data[2]) - !(s & data[3]));
+				input_report_abs(dev + i, ABS_Y,  !(s & data[0]) - !(s & data[1]));
+				input_report_key(dev + i, BTN_TRIGGER, s & data[4]);
+			}
+
+			if (s & gc->pads[GC_MULTI2])
+				input_report_key(dev + i, BTN_THUMB, s & data[5]);
+
+			input_sync(dev + i);
+		}
+	}
+
+/*
+ * PSX controllers
+ */
+
+	if (gc->pads[GC_PSX] || gc->pads[GC_DDR]) {
+
+		gc_psx_read_packet(gc, data_psx, data);
+
+		for (i = 0; i < 5; i++) {
+	 		switch (data[i]) {
+
+				case GC_PSX_RUMBLE:
+
+					input_report_key(dev + i, BTN_THUMBL, ~data_psx[i][0] & 0x04);
+					input_report_key(dev + i, BTN_THUMBR, ~data_psx[i][0] & 0x02);
+
+				case GC_PSX_NEGCON:
+				case GC_PSX_ANALOG:
+
+					if(gc->pads[GC_DDR] & gc_status_bit[i]) {
+						for(j = 0; j < 4; j++)
+							input_report_key(dev + i, gc_psx_ddr_btn[j], ~data_psx[i][0] & (0x10 << j));
+					} else {
+						for (j = 0; j < 4; j++)
+							input_report_abs(dev + i, gc_psx_abs[j+2], data_psx[i][j + 2]);
+
+						input_report_abs(dev + i, ABS_X, 128 + !(data_psx[i][0] & 0x20) * 127 - !(data_psx[i][0] & 0x80) * 128);
+						input_report_abs(dev + i, ABS_Y, 128 + !(data_psx[i][0] & 0x40) * 127 - !(data_psx[i][0] & 0x10) * 128);
+					}
+
+					for (j = 0; j < 8; j++)
+						input_report_key(dev + i, gc_psx_btn[j], ~data_psx[i][1] & (1 << j));
+
+					input_report_key(dev + i, BTN_START,  ~data_psx[i][0] & 0x08);
+					input_report_key(dev + i, BTN_SELECT, ~data_psx[i][0] & 0x01);
+
+					input_sync(dev + i);
+
+					break;
+
+				case GC_PSX_NORMAL:
+					if(gc->pads[GC_DDR] & gc_status_bit[i]) {
+						for(j = 0; j < 4; j++)
+							input_report_key(dev + i, gc_psx_ddr_btn[j], ~data_psx[i][0] & (0x10 << j));
+					} else {
+						input_report_abs(dev + i, ABS_X, 128 + !(data_psx[i][0] & 0x20) * 127 - !(data_psx[i][0] & 0x80) * 128);
+						input_report_abs(dev + i, ABS_Y, 128 + !(data_psx[i][0] & 0x40) * 127 - !(data_psx[i][0] & 0x10) * 128);
+
+						/* for some reason if the extra axes are left unset they drift */
+						/* for (j = 0; j < 4; j++)
+							input_report_abs(dev + i, gc_psx_abs[j+2], 128);
+						 * This needs to be debugged properly,
+						 * maybe fuzz processing needs to be done in input_sync()
+						 *				 --vojtech
+						 */
+					}
+
+					for (j = 0; j < 8; j++)
+						input_report_key(dev + i, gc_psx_btn[j], ~data_psx[i][1] & (1 << j));
+
+					input_report_key(dev + i, BTN_START,  ~data_psx[i][0] & 0x08);
+					input_report_key(dev + i, BTN_SELECT, ~data_psx[i][0] & 0x01);
+
+					input_sync(dev + i);
+
+					break;
+
+				case 0: /* not a pad, ignore */
+					break;
+			}
+		}
+	}
+
+	mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME);
+}
+
+static int gc_open(struct input_dev *dev)
+{
+	struct gc *gc = dev->private;
+	if (!gc->used++) {
+		parport_claim(gc->pd);
+		parport_write_control(gc->pd->port, 0x04);
+		mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME);
+	}
+	return 0;
+}
+
+static void gc_close(struct input_dev *dev)
+{
+	struct gc *gc = dev->private;
+	if (!--gc->used) {
+		del_timer(&gc->timer);
+		parport_write_control(gc->pd->port, 0x00);
+		parport_release(gc->pd);
+	}
+}
+
+static struct gc __init *gc_probe(int *config, int nargs)
+{
+	struct gc *gc;
+	struct parport *pp;
+	int i, j;
+
+	if (config[0] < 0)
+		return NULL;
+
+	if (nargs < 2) {
+		printk(KERN_ERR "gamecon.c: at least one device must be specified\n");
+		return NULL;
+	}
+
+	pp = parport_find_number(config[0]);
+
+	if (!pp) {
+		printk(KERN_ERR "gamecon.c: no such parport\n");
+		return NULL;
+	}
+
+	if (!(gc = kmalloc(sizeof(struct gc), GFP_KERNEL))) {
+		parport_put_port(pp);
+		return NULL;
+	}
+	memset(gc, 0, sizeof(struct gc));
+
+	gc->pd = parport_register_device(pp, "gamecon", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
+
+	parport_put_port(pp);
+
+	if (!gc->pd) {
+		printk(KERN_ERR "gamecon.c: parport busy already - lp.o loaded?\n");
+		kfree(gc);
+		return NULL;
+	}
+
+	parport_claim(gc->pd);
+
+	init_timer(&gc->timer);
+	gc->timer.data = (long) gc;
+	gc->timer.function = gc_timer;
+
+	for (i = 0; i < nargs - 1; i++) {
+
+		if (!config[i + 1])
+			continue;
+
+		if (config[i + 1] < 1 || config[i + 1] > GC_MAX) {
+			printk(KERN_WARNING "gamecon.c: Pad type %d unknown\n", config[i + 1]);
+			continue;
+		}
+
+                gc->dev[i].private = gc;
+                gc->dev[i].open = gc_open;
+                gc->dev[i].close = gc_close;
+
+                gc->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+		for (j = 0; j < 2; j++) {
+			set_bit(ABS_X + j, gc->dev[i].absbit);
+			gc->dev[i].absmin[ABS_X + j] = -1;
+			gc->dev[i].absmax[ABS_X + j] =  1;
+		}
+
+		gc->pads[0] |= gc_status_bit[i];
+		gc->pads[config[i + 1]] |= gc_status_bit[i];
+
+		switch(config[i + 1]) {
+
+			case GC_N64:
+				for (j = 0; j < 10; j++)
+					set_bit(gc_n64_btn[j], gc->dev[i].keybit);
+
+				for (j = 0; j < 2; j++) {
+					set_bit(ABS_X + j, gc->dev[i].absbit);
+					gc->dev[i].absmin[ABS_X + j] = -127;
+					gc->dev[i].absmax[ABS_X + j] =  126;
+					gc->dev[i].absflat[ABS_X + j] = 2;
+					set_bit(ABS_HAT0X + j, gc->dev[i].absbit);
+					gc->dev[i].absmin[ABS_HAT0X + j] = -1;
+					gc->dev[i].absmax[ABS_HAT0X + j] =  1;
+				}
+
+				break;
+
+			case GC_SNES:
+				for (j = 4; j < 8; j++)
+					set_bit(gc_snes_btn[j], gc->dev[i].keybit);
+			case GC_NES:
+				for (j = 0; j < 4; j++)
+					set_bit(gc_snes_btn[j], gc->dev[i].keybit);
+				break;
+
+			case GC_MULTI2:
+				set_bit(BTN_THUMB, gc->dev[i].keybit);
+			case GC_MULTI:
+				set_bit(BTN_TRIGGER, gc->dev[i].keybit);
+				break;
+
+			case GC_PSX:
+			case GC_DDR:
+				if(config[i + 1] == GC_DDR) {
+					for (j = 0; j < 4; j++)
+						set_bit(gc_psx_ddr_btn[j], gc->dev[i].keybit);
+				} else {
+					for (j = 0; j < 6; j++) {
+						set_bit(gc_psx_abs[j], gc->dev[i].absbit);
+						gc->dev[i].absmin[gc_psx_abs[j]] = 4;
+						gc->dev[i].absmax[gc_psx_abs[j]] = 252;
+						gc->dev[i].absflat[gc_psx_abs[j]] = 2;
+					}
+				}
+
+				for (j = 0; j < 12; j++)
+					set_bit(gc_psx_btn[j], gc->dev[i].keybit);
+
+				break;
+		}
+
+		sprintf(gc->phys[i], "%s/input%d", gc->pd->port->name, i);
+
+                gc->dev[i].name = gc_names[config[i + 1]];
+		gc->dev[i].phys = gc->phys[i];
+                gc->dev[i].id.bustype = BUS_PARPORT;
+                gc->dev[i].id.vendor = 0x0001;
+                gc->dev[i].id.product = config[i + 1];
+                gc->dev[i].id.version = 0x0100;
+	}
+
+	parport_release(gc->pd);
+
+	if (!gc->pads[0]) {
+		parport_unregister_device(gc->pd);
+		kfree(gc);
+		return NULL;
+	}
+
+	for (i = 0; i < 5; i++)
+		if (gc->pads[0] & gc_status_bit[i]) {
+			input_register_device(gc->dev + i);
+			printk(KERN_INFO "input: %s on %s\n", gc->dev[i].name, gc->pd->port->name);
+		}
+
+	return gc;
+}
+
+static int __init gc_init(void)
+{
+	gc_base[0] = gc_probe(gc, gc_nargs);
+	gc_base[1] = gc_probe(gc_2, gc_nargs_2);
+	gc_base[2] = gc_probe(gc_3, gc_nargs_3);
+
+	if (gc_base[0] || gc_base[1] || gc_base[2])
+		return 0;
+
+	return -ENODEV;
+}
+
+static void __exit gc_exit(void)
+{
+	int i, j;
+
+	for (i = 0; i < 3; i++)
+		if (gc_base[i]) {
+			for (j = 0; j < 5; j++)
+				if (gc_base[i]->pads[0] & gc_status_bit[j])
+					input_unregister_device(gc_base[i]->dev + j);
+			parport_unregister_device(gc_base[i]->pd);
+		}
+}
+
+module_init(gc_init);
+module_exit(gc_exit);
diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c
new file mode 100644
index 0000000..ad13f09
--- /dev/null
+++ b/drivers/input/joystick/gf2k.c
@@ -0,0 +1,380 @@
+/*
+ * $Id: gf2k.c,v 1.19 2002/01/22 20:27:43 vojtech Exp $
+ *
+ *  Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * Genius Flight 2000 joystick 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/gameport.h>
+
+#define DRIVER_DESC	"Genius Flight 2000 joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define GF2K_START		400	/* The time we wait for the first bit [400 us] */
+#define GF2K_STROBE		40	/* The time we wait for the first bit [40 us] */
+#define GF2K_TIMEOUT		4	/* Wait for everything to settle [4 ms] */
+#define GF2K_LENGTH		80	/* Max number of triplets in a packet */
+
+/*
+ * Genius joystick ids ...
+ */
+
+#define GF2K_ID_G09		1
+#define GF2K_ID_F30D		2
+#define GF2K_ID_F30		3
+#define GF2K_ID_F31D		4
+#define GF2K_ID_F305		5
+#define GF2K_ID_F23P		6
+#define GF2K_ID_F31		7
+#define GF2K_ID_MAX		7
+
+static char gf2k_length[] = { 40, 40, 40, 40, 40, 40, 40, 40 };
+static char gf2k_hat_to_axis[][2] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+static char *gf2k_names[] = {"", "Genius G-09D", "Genius F-30D", "Genius F-30", "Genius MaxFighter F-31D",
+				"Genius F-30-5", "Genius Flight2000 F-23", "Genius F-31"};
+static unsigned char gf2k_hats[] = { 0, 2, 0, 0, 2, 0, 2, 0 };
+static unsigned char gf2k_axes[] = { 0, 2, 0, 0, 4, 0, 4, 0 };
+static unsigned char gf2k_joys[] = { 0, 0, 0, 0,10, 0, 8, 0 };
+static unsigned char gf2k_pads[] = { 0, 6, 0, 0, 0, 0, 0, 0 };
+static unsigned char gf2k_lens[] = { 0,18, 0, 0,18, 0,18, 0 };
+
+static unsigned char gf2k_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_GAS, ABS_BRAKE };
+static short gf2k_btn_joy[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4 };
+static short gf2k_btn_pad[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_START, BTN_SELECT };
+
+
+static short gf2k_seq_reset[] = { 240, 340, 0 };
+static short gf2k_seq_digital[] = { 590, 320, 860, 0 };
+
+struct gf2k {
+	struct gameport *gameport;
+	struct input_dev dev;
+	int reads;
+	int bads;
+	unsigned char id;
+	unsigned char length;
+	char phys[32];
+};
+
+/*
+ * gf2k_read_packet() reads a Genius Flight2000 packet.
+ */
+
+static int gf2k_read_packet(struct gameport *gameport, int length, char *data)
+{
+	unsigned char u, v;
+	int i;
+	unsigned int t, p;
+	unsigned long flags;
+
+	t = gameport_time(gameport, GF2K_START);
+	p = gameport_time(gameport, GF2K_STROBE);
+
+	i = 0;
+
+	local_irq_save(flags);
+
+	gameport_trigger(gameport);
+	v = gameport_read(gameport);
+
+	while (t > 0 && i < length) {
+		t--; u = v;
+		v = gameport_read(gameport);
+		if (v & ~u & 0x10) {
+			data[i++] = v >> 5;
+			t = p;
+		}
+	}
+
+	local_irq_restore(flags);
+
+	return i;
+}
+
+/*
+ * gf2k_trigger_seq() initializes a Genius Flight2000 joystick
+ * into digital mode.
+ */
+
+static void gf2k_trigger_seq(struct gameport *gameport, short *seq)
+{
+
+	unsigned long flags;
+	int i, t;
+
+        local_irq_save(flags);
+
+	i = 0;
+        do {
+		gameport_trigger(gameport);
+		t = gameport_time(gameport, GF2K_TIMEOUT * 1000);
+		while ((gameport_read(gameport) & 1) && t) t--;
+                udelay(seq[i]);
+        } while (seq[++i]);
+
+	gameport_trigger(gameport);
+
+	local_irq_restore(flags);
+}
+
+/*
+ * js_sw_get_bits() composes bits from the triplet buffer into a __u64.
+ * Parameter 'pos' is bit number inside packet where to start at, 'num' is number
+ * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits
+ * is number of bits per triplet.
+ */
+
+#define GB(p,n,s)	gf2k_get_bits(data, p, n, s)
+
+static int gf2k_get_bits(unsigned char *buf, int pos, int num, int shift)
+{
+	__u64 data = 0;
+	int i;
+
+	for (i = 0; i < num / 3 + 2; i++)
+		data |= buf[pos / 3 + i] << (i * 3);
+	data >>= pos % 3;
+	data &= (1 << num) - 1;
+	data <<= shift;
+
+	return data;
+}
+
+static void gf2k_read(struct gf2k *gf2k, unsigned char *data)
+{
+	struct input_dev *dev = &gf2k->dev;
+	int i, t;
+
+	for (i = 0; i < 4 && i < gf2k_axes[gf2k->id]; i++)
+		input_report_abs(dev, gf2k_abs[i], GB(i<<3,8,0) | GB(i+46,1,8) | GB(i+50,1,9));
+
+	for (i = 0; i < 2 && i < gf2k_axes[gf2k->id] - 4; i++)
+		input_report_abs(dev, gf2k_abs[i], GB(i*9+60,8,0) | GB(i+54,1,9));
+
+	t = GB(40,4,0);
+
+	for (i = 0; i < gf2k_hats[gf2k->id]; i++)
+		input_report_abs(dev, ABS_HAT0X + i, gf2k_hat_to_axis[t][i]);
+
+	t = GB(44,2,0) | GB(32,8,2) | GB(78,2,10);
+
+	for (i = 0; i < gf2k_joys[gf2k->id]; i++)
+		input_report_key(dev, gf2k_btn_joy[i], (t >> i) & 1);
+
+	for (i = 0; i < gf2k_pads[gf2k->id]; i++)
+		input_report_key(dev, gf2k_btn_pad[i], (t >> i) & 1);
+
+	input_sync(dev);
+}
+
+/*
+ * gf2k_poll() reads and analyzes Genius joystick data.
+ */
+
+static void gf2k_poll(struct gameport *gameport)
+{
+	struct gf2k *gf2k = gameport_get_drvdata(gameport);
+	unsigned char data[GF2K_LENGTH];
+
+	gf2k->reads++;
+
+	if (gf2k_read_packet(gf2k->gameport, gf2k_length[gf2k->id], data) < gf2k_length[gf2k->id])
+		gf2k->bads++;
+	else
+		gf2k_read(gf2k, data);
+}
+
+static int gf2k_open(struct input_dev *dev)
+{
+	struct gf2k *gf2k = dev->private;
+
+	gameport_start_polling(gf2k->gameport);
+	return 0;
+}
+
+static void gf2k_close(struct input_dev *dev)
+{
+	struct gf2k *gf2k = dev->private;
+
+	gameport_stop_polling(gf2k->gameport);
+}
+
+/*
+ * gf2k_connect() probes for Genius id joysticks.
+ */
+
+static int gf2k_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct gf2k *gf2k;
+	unsigned char data[GF2K_LENGTH];
+	int i, err;
+
+	if (!(gf2k = kcalloc(1, sizeof(struct gf2k), GFP_KERNEL)))
+		return -ENOMEM;
+
+	gf2k->gameport = gameport;
+
+	gameport_set_drvdata(gameport, gf2k);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	gf2k_trigger_seq(gameport, gf2k_seq_reset);
+
+	msleep(GF2K_TIMEOUT);
+
+	gf2k_trigger_seq(gameport, gf2k_seq_digital);
+
+	msleep(GF2K_TIMEOUT);
+
+	if (gf2k_read_packet(gameport, GF2K_LENGTH, data) < 12) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	if (!(gf2k->id = GB(7,2,0) | GB(3,3,2) | GB(0,3,5))) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+#ifdef RESET_WORKS
+	if ((gf2k->id != (GB(19,2,0) | GB(15,3,2) | GB(12,3,5))) ||
+	    (gf2k->id != (GB(31,2,0) | GB(27,3,2) | GB(24,3,5)))) {
+		err = -ENODEV;
+		goto fail2;
+	}
+#else
+	gf2k->id = 6;
+#endif
+
+	if (gf2k->id > GF2K_ID_MAX || !gf2k_axes[gf2k->id]) {
+		printk(KERN_WARNING "gf2k.c: Not yet supported joystick on %s. [id: %d type:%s]\n",
+			gameport->phys, gf2k->id, gf2k->id > GF2K_ID_MAX ? "Unknown" : gf2k_names[gf2k->id]);
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	gameport_set_poll_handler(gameport, gf2k_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	sprintf(gf2k->phys, "%s/input0", gameport->phys);
+
+	gf2k->length = gf2k_lens[gf2k->id];
+
+	init_input_dev(&gf2k->dev);
+
+	gf2k->dev.private = gf2k;
+	gf2k->dev.open = gf2k_open;
+	gf2k->dev.close = gf2k_close;
+	gf2k->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+	gf2k->dev.name = gf2k_names[gf2k->id];
+	gf2k->dev.phys = gf2k->phys;
+	gf2k->dev.id.bustype = BUS_GAMEPORT;
+	gf2k->dev.id.vendor = GAMEPORT_ID_VENDOR_GENIUS;
+	gf2k->dev.id.product = gf2k->id;
+	gf2k->dev.id.version = 0x0100;
+
+	for (i = 0; i < gf2k_axes[gf2k->id]; i++)
+		set_bit(gf2k_abs[i], gf2k->dev.absbit);
+
+	for (i = 0; i < gf2k_hats[gf2k->id]; i++) {
+		set_bit(ABS_HAT0X + i, gf2k->dev.absbit);
+		gf2k->dev.absmin[ABS_HAT0X + i] = -1;
+		gf2k->dev.absmax[ABS_HAT0X + i] = 1;
+	}
+
+	for (i = 0; i < gf2k_joys[gf2k->id]; i++)
+		set_bit(gf2k_btn_joy[i], gf2k->dev.keybit);
+
+	for (i = 0; i < gf2k_pads[gf2k->id]; i++)
+		set_bit(gf2k_btn_pad[i], gf2k->dev.keybit);
+
+	gf2k_read_packet(gameport, gf2k->length, data);
+	gf2k_read(gf2k, data);
+
+	for (i = 0; i < gf2k_axes[gf2k->id]; i++) {
+		gf2k->dev.absmax[gf2k_abs[i]] = (i < 2) ? gf2k->dev.abs[gf2k_abs[i]] * 2 - 32 :
+	      		  gf2k->dev.abs[gf2k_abs[0]] + gf2k->dev.abs[gf2k_abs[1]] - 32;
+		gf2k->dev.absmin[gf2k_abs[i]] = 32;
+		gf2k->dev.absfuzz[gf2k_abs[i]] = 8;
+		gf2k->dev.absflat[gf2k_abs[i]] = (i < 2) ? 24 : 0;
+	}
+
+	input_register_device(&gf2k->dev);
+	printk(KERN_INFO "input: %s on %s\n", gf2k_names[gf2k->id], gameport->phys);
+
+	return 0;
+
+fail2:	gameport_close(gameport);
+fail1:	gameport_set_drvdata(gameport, NULL);
+	kfree(gf2k);
+	return err;
+}
+
+static void gf2k_disconnect(struct gameport *gameport)
+{
+	struct gf2k *gf2k = gameport_get_drvdata(gameport);
+
+	input_unregister_device(&gf2k->dev);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(gf2k);
+}
+
+static struct gameport_driver gf2k_drv = {
+	.driver		= {
+		.name	= "gf2k",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= gf2k_connect,
+	.disconnect	= gf2k_disconnect,
+};
+
+static int __init gf2k_init(void)
+{
+	gameport_register_driver(&gf2k_drv);
+	return 0;
+}
+
+static void __exit gf2k_exit(void)
+{
+	gameport_unregister_driver(&gf2k_drv);
+}
+
+module_init(gf2k_init);
+module_exit(gf2k_exit);
diff --git a/drivers/input/joystick/grip.c b/drivers/input/joystick/grip.c
new file mode 100644
index 0000000..d1500d2
--- /dev/null
+++ b/drivers/input/joystick/grip.c
@@ -0,0 +1,422 @@
+/*
+ * $Id: grip.c,v 1.21 2002/01/22 20:27:57 vojtech Exp $
+ *
+ *  Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * Gravis/Kensington GrIP protocol joystick and gamepad 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+
+#define DRIVER_DESC	"Gravis GrIP protocol joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define GRIP_MODE_GPP		1
+#define GRIP_MODE_BD		2
+#define GRIP_MODE_XT		3
+#define GRIP_MODE_DC		4
+
+#define GRIP_LENGTH_GPP		24
+#define GRIP_STROBE_GPP		200	/* 200 us */
+#define GRIP_LENGTH_XT		4
+#define GRIP_STROBE_XT		64	/* 64 us */
+#define GRIP_MAX_CHUNKS_XT	10
+#define GRIP_MAX_BITS_XT	30
+
+struct grip {
+	struct gameport *gameport;
+	struct input_dev dev[2];
+	unsigned char mode[2];
+	int reads;
+	int bads;
+	char phys[2][32];
+};
+
+static int grip_btn_gpp[] = { BTN_START, BTN_SELECT, BTN_TR2, BTN_Y, 0, BTN_TL2, BTN_A, BTN_B, BTN_X, 0, BTN_TL, BTN_TR, -1 };
+static int grip_btn_bd[] = { BTN_THUMB, BTN_THUMB2, BTN_TRIGGER, BTN_TOP, BTN_BASE, -1 };
+static int grip_btn_xt[] = { BTN_TRIGGER, BTN_THUMB, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_SELECT, BTN_START, BTN_MODE, -1 };
+static int grip_btn_dc[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, -1 };
+
+static int grip_abs_gpp[] = { ABS_X, ABS_Y, -1 };
+static int grip_abs_bd[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
+static int grip_abs_xt[] = { ABS_X, ABS_Y, ABS_BRAKE, ABS_GAS, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, -1 };
+static int grip_abs_dc[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
+
+static char *grip_name[] = { NULL, "Gravis GamePad Pro", "Gravis Blackhawk Digital",
+				"Gravis Xterminator Digital", "Gravis Xterminator DualControl" };
+static int *grip_abs[] = { NULL, grip_abs_gpp, grip_abs_bd, grip_abs_xt, grip_abs_dc };
+static int *grip_btn[] = { NULL, grip_btn_gpp, grip_btn_bd, grip_btn_xt, grip_btn_dc };
+static char grip_anx[] = { 0, 0, 3, 5, 5 };
+static char grip_cen[] = { 0, 0, 2, 2, 4 };
+
+/*
+ * grip_gpp_read_packet() reads a Gravis GamePad Pro packet.
+ */
+
+static int grip_gpp_read_packet(struct gameport *gameport, int shift, unsigned int *data)
+{
+	unsigned long flags;
+	unsigned char u, v;
+	unsigned int t;
+	int i;
+
+	int strobe = gameport_time(gameport, GRIP_STROBE_GPP);
+
+	data[0] = 0;
+	t = strobe;
+	i = 0;
+
+	local_irq_save(flags);
+
+	v = gameport_read(gameport) >> shift;
+
+	do {
+		t--;
+		u = v; v = (gameport_read(gameport) >> shift) & 3;
+		if (~v & u & 1) {
+			data[0] |= (v >> 1) << i++;
+			t = strobe;
+		}
+	} while (i < GRIP_LENGTH_GPP && t > 0);
+
+	local_irq_restore(flags);
+
+	if (i < GRIP_LENGTH_GPP) return -1;
+
+	for (i = 0; i < GRIP_LENGTH_GPP && (data[0] & 0xfe4210) ^ 0x7c0000; i++)
+		data[0] = data[0] >> 1 | (data[0] & 1) << (GRIP_LENGTH_GPP - 1);
+
+	return -(i == GRIP_LENGTH_GPP);
+}
+
+/*
+ * grip_xt_read_packet() reads a Gravis Xterminator packet.
+ */
+
+static int grip_xt_read_packet(struct gameport *gameport, int shift, unsigned int *data)
+{
+	unsigned int i, j, buf, crc;
+	unsigned char u, v, w;
+	unsigned long flags;
+	unsigned int t;
+	char status;
+
+	int strobe = gameport_time(gameport, GRIP_STROBE_XT);
+
+	data[0] = data[1] = data[2] = data[3] = 0;
+	status = buf = i = j = 0;
+	t = strobe;
+
+	local_irq_save(flags);
+
+	v = w = (gameport_read(gameport) >> shift) & 3;
+
+	do {
+		t--;
+		u = (gameport_read(gameport) >> shift) & 3;
+
+		if (u ^ v) {
+
+			if ((u ^ v) & 1) {
+				buf = (buf << 1) | (u >> 1);
+				t = strobe;
+				i++;
+			} else
+
+			if ((((u ^ v) & (v ^ w)) >> 1) & ~(u | v | w) & 1) {
+				if (i == 20) {
+					crc = buf ^ (buf >> 7) ^ (buf >> 14);
+					if (!((crc ^ (0x25cb9e70 >> ((crc >> 2) & 0x1c))) & 0xf)) {
+						data[buf >> 18] = buf >> 4;
+						status |= 1 << (buf >> 18);
+					}
+					j++;
+				}
+				t = strobe;
+				buf = 0;
+				i = 0;
+			}
+			w = v;
+			v = u;
+		}
+
+	} while (status != 0xf && i < GRIP_MAX_BITS_XT && j < GRIP_MAX_CHUNKS_XT && t > 0);
+
+	local_irq_restore(flags);
+
+	return -(status != 0xf);
+}
+
+/*
+ * grip_timer() repeatedly polls the joysticks and generates events.
+ */
+
+static void grip_poll(struct gameport *gameport)
+{
+	struct grip *grip = gameport_get_drvdata(gameport);
+	unsigned int data[GRIP_LENGTH_XT];
+	struct input_dev *dev;
+	int i, j;
+
+	for (i = 0; i < 2; i++) {
+
+		dev = grip->dev + i;
+		grip->reads++;
+
+		switch (grip->mode[i]) {
+
+			case GRIP_MODE_GPP:
+
+				if (grip_gpp_read_packet(grip->gameport, (i << 1) + 4, data)) {
+					grip->bads++;
+					break;
+				}
+
+				input_report_abs(dev, ABS_X, ((*data >> 15) & 1) - ((*data >> 16) & 1));
+				input_report_abs(dev, ABS_Y, ((*data >> 13) & 1) - ((*data >> 12) & 1));
+
+				for (j = 0; j < 12; j++)
+					if (grip_btn_gpp[j])
+						input_report_key(dev, grip_btn_gpp[j], (*data >> j) & 1);
+
+				break;
+
+			case GRIP_MODE_BD:
+
+				if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) {
+					grip->bads++;
+					break;
+				}
+
+				input_report_abs(dev, ABS_X,        (data[0] >> 2) & 0x3f);
+				input_report_abs(dev, ABS_Y,  63 - ((data[0] >> 8) & 0x3f));
+				input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f);
+
+				input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2]       & 1));
+				input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1));
+
+				for (j = 0; j < 5; j++)
+					input_report_key(dev, grip_btn_bd[j], (data[3] >> (j + 4)) & 1);
+
+				break;
+
+			case GRIP_MODE_XT:
+
+				if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) {
+					grip->bads++;
+					break;
+				}
+
+				input_report_abs(dev, ABS_X,        (data[0] >> 2) & 0x3f);
+				input_report_abs(dev, ABS_Y,  63 - ((data[0] >> 8) & 0x3f));
+				input_report_abs(dev, ABS_BRAKE,    (data[1] >> 2) & 0x3f);
+				input_report_abs(dev, ABS_GAS,	    (data[1] >> 8) & 0x3f);
+				input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f);
+
+				input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2]       & 1));
+				input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1));
+				input_report_abs(dev, ABS_HAT1X, ((data[2] >> 5) & 1) - ((data[2] >> 4) & 1));
+				input_report_abs(dev, ABS_HAT1Y, ((data[2] >> 6) & 1) - ((data[2] >> 7) & 1));
+
+				for (j = 0; j < 11; j++)
+					input_report_key(dev, grip_btn_xt[j], (data[3] >> (j + 3)) & 1);
+				break;
+
+			case GRIP_MODE_DC:
+
+				if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) {
+					grip->bads++;
+					break;
+				}
+
+				input_report_abs(dev, ABS_X,        (data[0] >> 2) & 0x3f);
+				input_report_abs(dev, ABS_Y,        (data[0] >> 8) & 0x3f);
+				input_report_abs(dev, ABS_RX,       (data[1] >> 2) & 0x3f);
+				input_report_abs(dev, ABS_RY,	    (data[1] >> 8) & 0x3f);
+				input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f);
+
+				input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2]       & 1));
+				input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1));
+
+				for (j = 0; j < 9; j++)
+					input_report_key(dev, grip_btn_dc[j], (data[3] >> (j + 3)) & 1);
+				break;
+
+
+		}
+
+		input_sync(dev);
+	}
+}
+
+static int grip_open(struct input_dev *dev)
+{
+	struct grip *grip = dev->private;
+
+	gameport_start_polling(grip->gameport);
+	return 0;
+}
+
+static void grip_close(struct input_dev *dev)
+{
+	struct grip *grip = dev->private;
+
+	gameport_stop_polling(grip->gameport);
+}
+
+static int grip_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct grip *grip;
+	unsigned int data[GRIP_LENGTH_XT];
+	int i, j, t;
+	int err;
+
+	if (!(grip = kcalloc(1, sizeof(struct grip), GFP_KERNEL)))
+		return -ENOMEM;
+
+	grip->gameport = gameport;
+
+	gameport_set_drvdata(gameport, grip);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	for (i = 0; i < 2; i++) {
+		if (!grip_gpp_read_packet(gameport, (i << 1) + 4, data)) {
+			grip->mode[i] = GRIP_MODE_GPP;
+			continue;
+		}
+		if (!grip_xt_read_packet(gameport, (i << 1) + 4, data)) {
+			if (!(data[3] & 7)) {
+				grip->mode[i] = GRIP_MODE_BD;
+				continue;
+			}
+			if (!(data[2] & 0xf0)) {
+				grip->mode[i] = GRIP_MODE_XT;
+				continue;
+			}
+			grip->mode[i] = GRIP_MODE_DC;
+			continue;
+		}
+	}
+
+	if (!grip->mode[0] && !grip->mode[1]) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	gameport_set_poll_handler(gameport, grip_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	for (i = 0; i < 2; i++)
+		if (grip->mode[i]) {
+
+			sprintf(grip->phys[i], "%s/input%d", gameport->phys, i);
+
+			grip->dev[i].private = grip;
+
+			grip->dev[i].open = grip_open;
+			grip->dev[i].close = grip_close;
+
+			grip->dev[i].name = grip_name[grip->mode[i]];
+			grip->dev[i].phys = grip->phys[i];
+			grip->dev[i].id.bustype = BUS_GAMEPORT;
+			grip->dev[i].id.vendor = GAMEPORT_ID_VENDOR_GRAVIS;
+			grip->dev[i].id.product = grip->mode[i];
+			grip->dev[i].id.version = 0x0100;
+
+			grip->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+			for (j = 0; (t = grip_abs[grip->mode[i]][j]) >= 0; j++) {
+
+				if (j < grip_cen[grip->mode[i]])
+					input_set_abs_params(&grip->dev[i], t, 14, 52, 1, 2);
+				else if (j < grip_anx[grip->mode[i]])
+					input_set_abs_params(&grip->dev[i], t, 3, 57, 1, 0);
+				else
+					input_set_abs_params(&grip->dev[i], t, -1, 1, 0, 0);
+			}
+
+			for (j = 0; (t = grip_btn[grip->mode[i]][j]) >= 0; j++)
+				if (t > 0)
+					set_bit(t, grip->dev[i].keybit);
+
+			printk(KERN_INFO "input: %s on %s\n",
+				grip_name[grip->mode[i]], gameport->phys);
+			input_register_device(grip->dev + i);
+		}
+
+	return 0;
+
+fail2:	gameport_close(gameport);
+fail1:	gameport_set_drvdata(gameport, NULL);
+	kfree(grip);
+	return err;
+}
+
+static void grip_disconnect(struct gameport *gameport)
+{
+	struct grip *grip = gameport_get_drvdata(gameport);
+	int i;
+
+	for (i = 0; i < 2; i++)
+		if (grip->mode[i])
+			input_unregister_device(grip->dev + i);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(grip);
+}
+
+static struct gameport_driver grip_drv = {
+	.driver		= {
+		.name	= "grip",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= grip_connect,
+	.disconnect	= grip_disconnect,
+};
+
+static int __init grip_init(void)
+{
+	gameport_register_driver(&grip_drv);
+	return 0;
+}
+
+static void __exit grip_exit(void)
+{
+	gameport_unregister_driver(&grip_drv);
+}
+
+module_init(grip_init);
+module_exit(grip_exit);
diff --git a/drivers/input/joystick/grip_mp.c b/drivers/input/joystick/grip_mp.c
new file mode 100644
index 0000000..42e5005
--- /dev/null
+++ b/drivers/input/joystick/grip_mp.c
@@ -0,0 +1,677 @@
+/*
+ * $Id: grip_mp.c,v 1.9 2002/07/20 19:28:45 bonnland Exp $
+ *
+ *  Driver for the Gravis Grip Multiport, a gamepad "hub" that
+ *  connects up to four 9-pin digital gamepads/joysticks.
+ *  Driver tested on SMP and UP kernel versions 2.4.18-4 and 2.4.18-5.
+ *
+ *  Thanks to Chris Gassib for helpful advice.
+ *
+ *  Copyright (c)      2002 Brian Bonnlander, Bill Soudan
+ *  Copyright (c) 1998-2000 Vojtech Pavlik
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+
+#define DRIVER_DESC	"Gravis Grip Multiport driver"
+
+MODULE_AUTHOR("Brian Bonnlander");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#ifdef GRIP_DEBUG
+#define dbg(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+
+/*
+ * Grip multiport state
+ */
+
+struct grip_mp {
+	struct gameport *gameport;
+	struct input_dev dev[4];
+	int mode[4];
+	int registered[4];
+	int reads;
+	int bads;
+
+	/* individual gamepad states */
+	int buttons[4];
+	int xaxes[4];
+	int yaxes[4];
+	int dirty[4];     /* has the state been updated? */
+};
+
+/*
+ * Multiport packet interpretation
+ */
+
+#define PACKET_FULL          0x80000000       /* packet is full                        */
+#define PACKET_IO_FAST       0x40000000       /* 3 bits per gameport read              */
+#define PACKET_IO_SLOW       0x20000000       /* 1 bit per gameport read               */
+#define PACKET_MP_MORE       0x04000000       /* multiport wants to send more          */
+#define PACKET_MP_DONE       0x02000000       /* multiport done sending                */
+
+/*
+ * Packet status code interpretation
+ */
+
+#define IO_GOT_PACKET        0x0100           /* Got a packet                           */
+#define IO_MODE_FAST         0x0200           /* Used 3 data bits per gameport read     */
+#define IO_SLOT_CHANGE       0x0800           /* Multiport physical slot status changed */
+#define IO_DONE              0x1000           /* Multiport is done sending packets      */
+#define IO_RETRY             0x4000           /* Try again later to get packet          */
+#define IO_RESET             0x8000           /* Force multiport to resend all packets  */
+
+/*
+ * Gamepad configuration data.  Other 9-pin digital joystick devices
+ * may work with the multiport, so this may not be an exhaustive list!
+ * Commodore 64 joystick remains untested.
+ */
+
+#define GRIP_INIT_DELAY         2000          /*  2 ms */
+
+#define GRIP_MODE_NONE		0
+#define GRIP_MODE_RESET         1
+#define GRIP_MODE_GP		2
+#define GRIP_MODE_C64		3
+
+static int grip_btn_gp[]  = { BTN_TR, BTN_TL, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, -1 };
+static int grip_btn_c64[] = { BTN_JOYSTICK, -1 };
+
+static int grip_abs_gp[]  = { ABS_X, ABS_Y, -1 };
+static int grip_abs_c64[] = { ABS_X, ABS_Y, -1 };
+
+static int *grip_abs[] = { NULL, NULL, grip_abs_gp, grip_abs_c64 };
+static int *grip_btn[] = { NULL, NULL, grip_btn_gp, grip_btn_c64 };
+
+static char *grip_name[] = { NULL, NULL, "Gravis Grip Pad", "Commodore 64 Joystick" };
+
+static const int init_seq[] = {
+	1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+	1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1,
+	1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+	0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1 };
+
+/* Maps multiport directional values to X,Y axis values (each axis encoded in 3 bits) */
+
+static int axis_map[] = { 5, 9, 1, 5, 6, 10, 2, 6, 4, 8, 0, 4, 5, 9, 1, 5 };
+
+static void register_slot(int i, struct grip_mp *grip);
+
+/*
+ * Returns whether an odd or even number of bits are on in pkt.
+ */
+
+static int bit_parity(u32 pkt)
+{
+	int x = pkt ^ (pkt >> 16);
+	x ^= x >> 8;
+	x ^= x >> 4;
+	x ^= x >> 2;
+	x ^= x >> 1;
+	return x & 1;
+}
+
+/*
+ * Poll gameport; return true if all bits set in 'onbits' are on and
+ * all bits set in 'offbits' are off.
+ */
+
+static inline int poll_until(u8 onbits, u8 offbits, int u_sec, struct gameport* gp, u8 *data)
+{
+	int i, nloops;
+
+	nloops = gameport_time(gp, u_sec);
+	for (i = 0; i < nloops; i++) {
+		*data = gameport_read(gp);
+		if ((*data & onbits) == onbits &&
+		    (~(*data) & offbits) == offbits)
+			return 1;
+	}
+	dbg("gameport timed out after %d microseconds.\n", u_sec);
+	return 0;
+}
+
+/*
+ * Gets a 28-bit packet from the multiport.
+ *
+ * After getting a packet successfully, commands encoded by sendcode may
+ * be sent to the multiport.
+ *
+ * The multiport clock value is reflected in gameport bit B4.
+ *
+ * Returns a packet status code indicating whether packet is valid, the transfer
+ * mode, and any error conditions.
+ *
+ * sendflags:      current I/O status
+ * sendcode:   data to send to the multiport if sendflags is nonzero
+ */
+
+static int mp_io(struct gameport* gameport, int sendflags, int sendcode, u32 *packet)
+{
+	u8  raw_data;            /* raw data from gameport */
+	u8  data_mask;           /* packet data bits from raw_data */
+	u32 pkt;                 /* packet temporary storage */
+	int bits_per_read;       /* num packet bits per gameport read */
+	int portvals = 0;        /* used for port value sanity check */
+	int i;
+
+	/* Gameport bits B0, B4, B5 should first be off, then B4 should come on. */
+
+	*packet = 0;
+	raw_data = gameport_read(gameport);
+	if (raw_data & 1)
+ 		return IO_RETRY;
+
+	for (i = 0; i < 64; i++) {
+		raw_data = gameport_read(gameport);
+		portvals |= 1 << ((raw_data >> 4) & 3); /* Demux B4, B5 */
+	}
+
+	if (portvals == 1) {                            /* B4, B5 off */
+		raw_data = gameport_read(gameport);
+		portvals = raw_data & 0xf0;
+
+		if (raw_data & 0x31)
+			return IO_RESET;
+		gameport_trigger(gameport);
+
+		if (!poll_until(0x10, 0, 308, gameport, &raw_data))
+			return IO_RESET;
+	} else
+		return IO_RETRY;
+
+	/* Determine packet transfer mode and prepare for packet construction. */
+
+	if (raw_data & 0x20) {                 /* 3 data bits/read */
+		portvals |= raw_data >> 4;     /* Compare B4-B7 before & after trigger */
+
+		if (portvals != 0xb)
+			return 0;
+		data_mask = 7;
+		bits_per_read = 3;
+		pkt = (PACKET_FULL | PACKET_IO_FAST) >> 28;
+	} else {                                 /* 1 data bit/read */
+		data_mask = 1;
+		bits_per_read = 1;
+		pkt = (PACKET_FULL | PACKET_IO_SLOW) >> 28;
+	}
+
+	/* Construct a packet.  Final data bits must be zero. */
+
+	while (1) {
+		if (!poll_until(0, 0x10, 77, gameport, &raw_data))
+			return IO_RESET;
+		raw_data = (raw_data >> 5) & data_mask;
+
+		if (pkt & PACKET_FULL)
+			break;
+		pkt = (pkt << bits_per_read) | raw_data;
+
+		if (!poll_until(0x10, 0, 77, gameport, &raw_data))
+			return IO_RESET;
+	}
+
+	if (raw_data)
+		return IO_RESET;
+
+	/* If 3 bits/read used, drop from 30 bits to 28. */
+
+	if (bits_per_read == 3) {
+		pkt = (pkt & 0xffff0000) | ((pkt << 1) & 0xffff);
+		pkt = (pkt >> 2) | 0xf0000000;
+	}
+
+	if (bit_parity(pkt) == 1)
+		return IO_RESET;
+
+	/* Acknowledge packet receipt */
+
+	if (!poll_until(0x30, 0, 77, gameport, &raw_data))
+		return IO_RESET;
+
+	raw_data = gameport_read(gameport);
+
+	if (raw_data & 1)
+		return IO_RESET;
+
+	gameport_trigger(gameport);
+
+	if (!poll_until(0, 0x20, 77, gameport, &raw_data))
+		return IO_RESET;
+
+        /* Return if we just wanted the packet or multiport wants to send more */
+
+	*packet = pkt;
+	if ((sendflags == 0) || ((sendflags & IO_RETRY) && !(pkt & PACKET_MP_DONE)))
+		return IO_GOT_PACKET;
+
+	if (pkt & PACKET_MP_MORE)
+		return IO_GOT_PACKET | IO_RETRY;
+
+	/* Multiport is done sending packets and is ready to receive data */
+
+	if (!poll_until(0x20, 0, 77, gameport, &raw_data))
+		return IO_GOT_PACKET | IO_RESET;
+
+	raw_data = gameport_read(gameport);
+	if (raw_data & 1)
+		return IO_GOT_PACKET | IO_RESET;
+
+	/* Trigger gameport based on bits in sendcode */
+
+	gameport_trigger(gameport);
+	do {
+		if (!poll_until(0x20, 0x10, 116, gameport, &raw_data))
+			return IO_GOT_PACKET | IO_RESET;
+
+		if (!poll_until(0x30, 0, 193, gameport, &raw_data))
+			return IO_GOT_PACKET | IO_RESET;
+
+		if (raw_data & 1)
+			return IO_GOT_PACKET | IO_RESET;
+
+		if (sendcode & 1)
+			gameport_trigger(gameport);
+
+		sendcode >>= 1;
+	} while (sendcode);
+
+	return IO_GOT_PACKET | IO_MODE_FAST;
+}
+
+/*
+ * Disables and restores interrupts for mp_io(), which does the actual I/O.
+ */
+
+static int multiport_io(struct gameport* gameport, int sendflags, int sendcode, u32 *packet)
+{
+	int status;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	status = mp_io(gameport, sendflags, sendcode, packet);
+	local_irq_restore(flags);
+
+	return status;
+}
+
+/*
+ * Puts multiport into digital mode.  Multiport LED turns green.
+ *
+ * Returns true if a valid digital packet was received, false otherwise.
+ */
+
+static int dig_mode_start(struct gameport *gameport, u32 *packet)
+{
+	int i, seq_len = sizeof(init_seq)/sizeof(int);
+	int flags, tries = 0, bads = 0;
+
+	for (i = 0; i < seq_len; i++) {     /* Send magic sequence */
+		if (init_seq[i])
+			gameport_trigger(gameport);
+		udelay(GRIP_INIT_DELAY);
+	}
+
+	for (i = 0; i < 16; i++)            /* Wait for multiport to settle */
+		udelay(GRIP_INIT_DELAY);
+
+	while (tries < 64 && bads < 8) {    /* Reset multiport and try getting a packet */
+
+		flags = multiport_io(gameport, IO_RESET, 0x27, packet);
+
+		if (flags & IO_MODE_FAST)
+			return 1;
+
+		if (flags & IO_RETRY)
+			tries++;
+		else
+			bads++;
+	}
+	return 0;
+}
+
+/*
+ * Packet structure: B0-B15   => gamepad state
+ *                   B16-B20  => gamepad device type
+ *                   B21-B24  => multiport slot index (1-4)
+ *
+ * Known device types: 0x1f (grip pad), 0x0 (no device).  Others may exist.
+ *
+ * Returns the packet status.
+ */
+
+static int get_and_decode_packet(struct grip_mp *grip, int flags)
+{
+	u32 packet;
+	int joytype = 0;
+	int slot = 0;
+
+	/* Get a packet and check for validity */
+
+	flags &= IO_RESET | IO_RETRY;
+	flags = multiport_io(grip->gameport, flags, 0, &packet);
+	grip->reads++;
+
+	if (packet & PACKET_MP_DONE)
+		flags |= IO_DONE;
+
+	if (flags && !(flags & IO_GOT_PACKET)) {
+		grip->bads++;
+		return flags;
+	}
+
+	/* Ignore non-gamepad packets, e.g. multiport hardware version */
+
+	slot = ((packet >> 21) & 0xf) - 1;
+	if ((slot < 0) || (slot > 3))
+		return flags;
+
+	/*
+	 * Handle "reset" packets, which occur at startup, and when gamepads
+	 * are removed or plugged in.  May contain configuration of a new gamepad.
+	 */
+
+	joytype = (packet >> 16) & 0x1f;
+	if (!joytype) {
+
+		if (grip->registered[slot]) {
+			printk(KERN_INFO "grip_mp: removing %s, slot %d\n",
+			       grip_name[grip->mode[slot]], slot);
+			input_unregister_device(grip->dev + slot);
+			grip->registered[slot] = 0;
+		}
+		dbg("Reset: grip multiport slot %d\n", slot);
+		grip->mode[slot] = GRIP_MODE_RESET;
+		flags |= IO_SLOT_CHANGE;
+		return flags;
+	}
+
+	/* Interpret a grip pad packet */
+
+	if (joytype == 0x1f) {
+
+		int dir = (packet >> 8) & 0xf;          /* eight way directional value */
+		grip->buttons[slot] = (~packet) & 0xff;
+		grip->yaxes[slot] = ((axis_map[dir] >> 2) & 3) - 1;
+		grip->xaxes[slot] = (axis_map[dir] & 3) - 1;
+		grip->dirty[slot] = 1;
+
+		if (grip->mode[slot] == GRIP_MODE_RESET)
+			flags |= IO_SLOT_CHANGE;
+
+		grip->mode[slot] = GRIP_MODE_GP;
+
+		if (!grip->registered[slot]) {
+			dbg("New Grip pad in multiport slot %d.\n", slot);
+			register_slot(slot, grip);
+		}
+		return flags;
+	}
+
+	/* Handle non-grip device codes.  For now, just print diagnostics. */
+
+	{
+		static int strange_code = 0;
+		if (strange_code != joytype) {
+			printk(KERN_INFO "Possible non-grip pad/joystick detected.\n");
+			printk(KERN_INFO "Got joy type 0x%x and packet 0x%x.\n", joytype, packet);
+			strange_code = joytype;
+		}
+	}
+	return flags;
+}
+
+/*
+ * Returns true if all multiport slot states appear valid.
+ */
+
+static int slots_valid(struct grip_mp *grip)
+{
+	int flags, slot, invalid = 0, active = 0;
+
+	flags = get_and_decode_packet(grip, 0);
+	if (!(flags & IO_GOT_PACKET))
+		return 0;
+
+	for (slot = 0; slot < 4; slot++) {
+		if (grip->mode[slot] == GRIP_MODE_RESET)
+			invalid = 1;
+		if (grip->mode[slot] != GRIP_MODE_NONE)
+			active = 1;
+	}
+
+	/* Return true if no active slot but multiport sent all its data */
+	if (!active)
+		return (flags & IO_DONE) ? 1 : 0;
+
+	/* Return false if invalid device code received */
+	return invalid ? 0 : 1;
+}
+
+/*
+ * Returns whether the multiport was placed into digital mode and
+ * able to communicate its state successfully.
+ */
+
+static int multiport_init(struct grip_mp *grip)
+{
+	int dig_mode, initialized = 0, tries = 0;
+	u32 packet;
+
+	dig_mode = dig_mode_start(grip->gameport, &packet);
+	while (!dig_mode && tries < 4) {
+		dig_mode = dig_mode_start(grip->gameport, &packet);
+		tries++;
+	}
+
+	if (dig_mode)
+		dbg("multiport_init(): digital mode activated.\n");
+	else {
+		dbg("multiport_init(): unable to activate digital mode.\n");
+		return 0;
+	}
+
+	/* Get packets, store multiport state, and check state's validity */
+	for (tries = 0; tries < 4096; tries++) {
+		if ( slots_valid(grip) ) {
+			initialized = 1;
+			break;
+		}
+	}
+	dbg("multiport_init(): initialized == %d\n", initialized);
+	return initialized;
+}
+
+/*
+ * Reports joystick state to the linux input layer.
+ */
+
+static void report_slot(struct grip_mp *grip, int slot)
+{
+	struct input_dev *dev = &(grip->dev[slot]);
+	int i, buttons = grip->buttons[slot];
+
+	/* Store button states with linux input driver */
+
+	for (i = 0; i < 8; i++)
+		input_report_key(dev, grip_btn_gp[i], (buttons >> i) & 1);
+
+	/* Store axis states with linux driver */
+
+	input_report_abs(dev, ABS_X, grip->xaxes[slot]);
+	input_report_abs(dev, ABS_Y, grip->yaxes[slot]);
+
+	/* Tell the receiver of the events to process them */
+
+	input_sync(dev);
+
+	grip->dirty[slot] = 0;
+}
+
+/*
+ * Get the multiport state.
+ */
+
+static void grip_poll(struct gameport *gameport)
+{
+	struct grip_mp *grip = gameport_get_drvdata(gameport);
+	int i, npkts, flags;
+
+	for (npkts = 0; npkts < 4; npkts++) {
+		flags = IO_RETRY;
+		for (i = 0; i < 32; i++) {
+			flags = get_and_decode_packet(grip, flags);
+			if ((flags & IO_GOT_PACKET) || !(flags & IO_RETRY))
+				break;
+		}
+		if (flags & IO_DONE)
+			break;
+	}
+
+	for (i = 0; i < 4; i++)
+		if (grip->dirty[i])
+			report_slot(grip, i);
+}
+
+/*
+ * Called when a joystick device file is opened
+ */
+
+static int grip_open(struct input_dev *dev)
+{
+	struct grip_mp *grip = dev->private;
+
+	gameport_start_polling(grip->gameport);
+	return 0;
+}
+
+/*
+ * Called when a joystick device file is closed
+ */
+
+static void grip_close(struct input_dev *dev)
+{
+	struct grip_mp *grip = dev->private;
+
+	gameport_start_polling(grip->gameport);
+}
+
+/*
+ * Tell the linux input layer about a newly plugged-in gamepad.
+ */
+
+static void register_slot(int slot, struct grip_mp *grip)
+{
+	int j, t;
+
+	grip->dev[slot].private = grip;
+	grip->dev[slot].open = grip_open;
+	grip->dev[slot].close = grip_close;
+	grip->dev[slot].name = grip_name[grip->mode[slot]];
+	grip->dev[slot].id.bustype = BUS_GAMEPORT;
+	grip->dev[slot].id.vendor = GAMEPORT_ID_VENDOR_GRAVIS;
+	grip->dev[slot].id.product = 0x0100 + grip->mode[slot];
+	grip->dev[slot].id.version = 0x0100;
+	grip->dev[slot].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+	for (j = 0; (t = grip_abs[grip->mode[slot]][j]) >= 0; j++)
+		input_set_abs_params(&grip->dev[slot], t, -1, 1, 0, 0);
+
+	for (j = 0; (t = grip_btn[grip->mode[slot]][j]) >= 0; j++)
+		if (t > 0)
+			set_bit(t, grip->dev[slot].keybit);
+
+	input_register_device(grip->dev + slot);
+	grip->registered[slot] = 1;
+
+	if (grip->dirty[slot])	            /* report initial state, if any */
+		report_slot(grip, slot);
+
+	printk(KERN_INFO "grip_mp: added %s, slot %d\n",
+	       grip_name[grip->mode[slot]], slot);
+}
+
+static int grip_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct grip_mp *grip;
+	int err;
+
+	if (!(grip = kcalloc(1, sizeof(struct grip_mp), GFP_KERNEL)))
+		return -ENOMEM;
+
+	grip->gameport = gameport;
+
+	gameport_set_drvdata(gameport, grip);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	gameport_set_poll_handler(gameport, grip_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	if (!multiport_init(grip)) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	if (!grip->mode[0] && !grip->mode[1] && !grip->mode[2] && !grip->mode[3]) {
+		/* nothing plugged in */
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	return 0;
+
+fail2:	gameport_close(gameport);
+fail1:	gameport_set_drvdata(gameport, NULL);
+	kfree(grip);
+	return err;
+}
+
+static void grip_disconnect(struct gameport *gameport)
+{
+	struct grip_mp *grip = gameport_get_drvdata(gameport);
+	int i;
+
+	for (i = 0; i < 4; i++)
+		if (grip->registered[i])
+			input_unregister_device(grip->dev + i);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(grip);
+}
+
+static struct gameport_driver grip_drv = {
+	.driver		= {
+		.name	= "grip_mp",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= grip_connect,
+	.disconnect	= grip_disconnect,
+};
+
+static int __init grip_init(void)
+{
+	gameport_register_driver(&grip_drv);
+	return 0;
+}
+
+static void __exit grip_exit(void)
+{
+	gameport_unregister_driver(&grip_drv);
+}
+
+module_init(grip_init);
+module_exit(grip_exit);
diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c
new file mode 100644
index 0000000..f93da7b
--- /dev/null
+++ b/drivers/input/joystick/guillemot.c
@@ -0,0 +1,289 @@
+/*
+ * $Id: guillemot.c,v 1.10 2002/01/22 20:28:12 vojtech Exp $
+ *
+ *  Copyright (c) 2001 Vojtech Pavlik
+ */
+
+/*
+ * Guillemot Digital Interface Protocol 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+
+#define DRIVER_DESC	"Guillemot Digital joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define GUILLEMOT_MAX_START	600	/* 600 us */
+#define GUILLEMOT_MAX_STROBE	60	/* 60 us */
+#define GUILLEMOT_MAX_LENGTH	17	/* 17 bytes */
+
+static short guillemot_abs_pad[] =
+	{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, -1 };
+
+static short guillemot_btn_pad[] =
+	{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_MODE, BTN_SELECT, -1 };
+
+static struct {
+        int x;
+        int y;
+} guillemot_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+struct guillemot_type {
+	unsigned char id;
+	short *abs;
+	short *btn;
+	int hat;
+	char *name;
+};
+
+struct guillemot {
+	struct gameport *gameport;
+	struct input_dev dev;
+	int bads;
+	int reads;
+	struct guillemot_type *type;
+	unsigned char length;
+	char phys[32];
+};
+
+static struct guillemot_type guillemot_type[] = {
+	{ 0x00, guillemot_abs_pad, guillemot_btn_pad, 1, "Guillemot Pad" },
+	{ 0 }};
+
+/*
+ * guillemot_read_packet() reads Guillemot joystick data.
+ */
+
+static int guillemot_read_packet(struct gameport *gameport, u8 *data)
+{
+	unsigned long flags;
+	unsigned char u, v;
+	unsigned int t, s;
+	int i;
+
+	for (i = 0; i < GUILLEMOT_MAX_LENGTH; i++)
+		data[i] = 0;
+
+	i = 0;
+	t = gameport_time(gameport, GUILLEMOT_MAX_START);
+	s = gameport_time(gameport, GUILLEMOT_MAX_STROBE);
+
+	local_irq_save(flags);
+	gameport_trigger(gameport);
+	v = gameport_read(gameport);
+
+	while (t > 0 && i < GUILLEMOT_MAX_LENGTH * 8) {
+		t--;
+		u = v; v = gameport_read(gameport);
+		if (v & ~u & 0x10) {
+			data[i >> 3] |= ((v >> 5) & 1) << (i & 7);
+			i++;
+			t = s;
+		}
+	}
+
+	local_irq_restore(flags);
+
+	return i;
+}
+
+/*
+ * guillemot_poll() reads and analyzes Guillemot joystick data.
+ */
+
+static void guillemot_poll(struct gameport *gameport)
+{
+	struct guillemot *guillemot = gameport_get_drvdata(gameport);
+	struct input_dev *dev = &guillemot->dev;
+	u8 data[GUILLEMOT_MAX_LENGTH];
+	int i;
+
+	guillemot->reads++;
+
+	if (guillemot_read_packet(guillemot->gameport, data) != GUILLEMOT_MAX_LENGTH * 8 ||
+		data[0] != 0x55 || data[16] != 0xaa) {
+		guillemot->bads++;
+	} else {
+
+		for (i = 0; i < 6 && guillemot->type->abs[i] >= 0; i++)
+			input_report_abs(dev, guillemot->type->abs[i], data[i + 5]);
+
+		if (guillemot->type->hat) {
+			input_report_abs(dev, ABS_HAT0X, guillemot_hat_to_axis[data[4] >> 4].x);
+			input_report_abs(dev, ABS_HAT0Y, guillemot_hat_to_axis[data[4] >> 4].y);
+		}
+
+		for (i = 0; i < 16 && guillemot->type->btn[i] >= 0; i++)
+			input_report_key(dev, guillemot->type->btn[i], (data[2 + (i >> 3)] >> (i & 7)) & 1);
+	}
+
+	input_sync(dev);
+}
+
+/*
+ * guillemot_open() is a callback from the input open routine.
+ */
+
+static int guillemot_open(struct input_dev *dev)
+{
+	struct guillemot *guillemot = dev->private;
+
+	gameport_start_polling(guillemot->gameport);
+	return 0;
+}
+
+/*
+ * guillemot_close() is a callback from the input close routine.
+ */
+
+static void guillemot_close(struct input_dev *dev)
+{
+	struct guillemot *guillemot = dev->private;
+
+	gameport_stop_polling(guillemot->gameport);
+}
+
+/*
+ * guillemot_connect() probes for Guillemot joysticks.
+ */
+
+static int guillemot_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct guillemot *guillemot;
+	u8 data[GUILLEMOT_MAX_LENGTH];
+	int i, t;
+	int err;
+
+	if (!(guillemot = kcalloc(1, sizeof(struct guillemot), GFP_KERNEL)))
+		return -ENOMEM;
+
+	guillemot->gameport = gameport;
+
+	gameport_set_drvdata(gameport, guillemot);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	i = guillemot_read_packet(gameport, data);
+
+	if (i != GUILLEMOT_MAX_LENGTH * 8 || data[0] != 0x55 || data[16] != 0xaa) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	for (i = 0; guillemot_type[i].name; i++)
+		if (guillemot_type[i].id == data[11])
+			break;
+
+	if (!guillemot_type[i].name) {
+		printk(KERN_WARNING "guillemot.c: Unknown joystick on %s. [ %02x%02x:%04x, ver %d.%02d ]\n",
+			gameport->phys, data[12], data[13], data[11], data[14], data[15]);
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	gameport_set_poll_handler(gameport, guillemot_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	sprintf(guillemot->phys, "%s/input0", gameport->phys);
+
+	guillemot->type = guillemot_type + i;
+
+	guillemot->dev.private = guillemot;
+	guillemot->dev.open = guillemot_open;
+	guillemot->dev.close = guillemot_close;
+
+	guillemot->dev.name = guillemot_type[i].name;
+	guillemot->dev.phys = guillemot->phys;
+	guillemot->dev.id.bustype = BUS_GAMEPORT;
+	guillemot->dev.id.vendor = GAMEPORT_ID_VENDOR_GUILLEMOT;
+	guillemot->dev.id.product = guillemot_type[i].id;
+	guillemot->dev.id.version = (int)data[14] << 8 | data[15];
+
+	guillemot->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+	for (i = 0; (t = guillemot->type->abs[i]) >= 0; i++)
+		input_set_abs_params(&guillemot->dev, t, 0, 255, 0, 0);
+
+	if (guillemot->type->hat) {
+		input_set_abs_params(&guillemot->dev, ABS_HAT0X, -1, 1, 0, 0);
+		input_set_abs_params(&guillemot->dev, ABS_HAT0Y, -1, 1, 0, 0);
+	}
+
+	for (i = 0; (t = guillemot->type->btn[i]) >= 0; i++)
+		set_bit(t, guillemot->dev.keybit);
+
+	input_register_device(&guillemot->dev);
+	printk(KERN_INFO "input: %s ver %d.%02d on %s\n",
+		guillemot->type->name, data[14], data[15], gameport->phys);
+
+	return 0;
+
+fail2:	gameport_close(gameport);
+fail1:  gameport_set_drvdata(gameport, NULL);
+	kfree(guillemot);
+	return err;
+}
+
+static void guillemot_disconnect(struct gameport *gameport)
+{
+	struct guillemot *guillemot = gameport_get_drvdata(gameport);
+
+	printk(KERN_INFO "guillemot.c: Failed %d reads out of %d on %s\n", guillemot->reads, guillemot->bads, guillemot->phys);
+	input_unregister_device(&guillemot->dev);
+	gameport_close(gameport);
+	kfree(guillemot);
+}
+
+static struct gameport_driver guillemot_drv = {
+	.driver		= {
+		.name	= "guillemot",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= guillemot_connect,
+	.disconnect	= guillemot_disconnect,
+};
+
+static int __init guillemot_init(void)
+{
+	gameport_register_driver(&guillemot_drv);
+	return 0;
+}
+
+static void __exit guillemot_exit(void)
+{
+	gameport_unregister_driver(&guillemot_drv);
+}
+
+module_init(guillemot_init);
+module_exit(guillemot_exit);
diff --git a/drivers/input/joystick/iforce/Kconfig b/drivers/input/joystick/iforce/Kconfig
new file mode 100644
index 0000000..8fde22a
--- /dev/null
+++ b/drivers/input/joystick/iforce/Kconfig
@@ -0,0 +1,32 @@
+#
+# I-Force driver configuration
+#
+config JOYSTICK_IFORCE
+	tristate "I-Force devices"
+	depends on INPUT && INPUT_JOYSTICK
+	help
+	  Say Y here if you have an I-Force joystick or steering wheel
+
+	  You also must choose at least one of the two options below.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called iforce.
+
+config JOYSTICK_IFORCE_USB
+	bool "I-Force USB joysticks and wheels"
+	depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || USB=y) && USB
+	help
+	  Say Y here if you have an I-Force joystick or steering wheel
+	  connected to your USB port.
+
+config JOYSTICK_IFORCE_232
+	bool "I-Force Serial joysticks and wheels"
+	depends on JOYSTICK_IFORCE && (JOYSTICK_IFORCE=m || SERIO=y) && SERIO
+	help
+	  Say Y here if you have an I-Force joystick or steering wheel
+	  connected to your serial (COM) port.
+
+	  You will need an additional utility called inputattach, see
+	  <file:Documentation/input/joystick.txt>
+	  and <file:Documentation/input/ff.txt>.
+
diff --git a/drivers/input/joystick/iforce/Makefile b/drivers/input/joystick/iforce/Makefile
new file mode 100644
index 0000000..17ae42b
--- /dev/null
+++ b/drivers/input/joystick/iforce/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for the I-Force driver
+#
+# By Johann Deneux <deneux@ifrance.com>
+#
+
+# Goal definition
+iforce-objs	:= iforce-ff.o iforce-main.o iforce-packets.o
+
+obj-$(CONFIG_JOYSTICK_IFORCE)	+= iforce.o
+
+ifeq ($(CONFIG_JOYSTICK_IFORCE_232),y)
+	iforce-objs += iforce-serio.o
+endif
+
+ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),y)
+	iforce-objs += iforce-usb.o
+endif
+
+EXTRA_CFLAGS = -Werror-implicit-function-declaration
diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c
new file mode 100644
index 0000000..4678b6d
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-ff.c
@@ -0,0 +1,543 @@
+/*
+ * $Id: iforce-ff.c,v 1.9 2002/02/02 19:28:35 jdeneux Exp $
+ *
+ *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * 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 "iforce.h"
+
+/*
+ * Set the magnitude of a constant force effect
+ * Return error code
+ *
+ * Note: caller must ensure exclusive access to device
+ */
+
+static int make_magnitude_modifier(struct iforce* iforce,
+	struct resource* mod_chunk, int no_alloc, __s16 level)
+{
+	unsigned char data[3];
+
+	if (!no_alloc) {
+		down(&iforce->mem_mutex);
+		if (allocate_resource(&(iforce->device_memory), mod_chunk, 2,
+			iforce->device_memory.start, iforce->device_memory.end, 2L,
+			NULL, NULL)) {
+			up(&iforce->mem_mutex);
+			return -ENOMEM;
+		}
+		up(&iforce->mem_mutex);
+	}
+
+	data[0] = LO(mod_chunk->start);
+	data[1] = HI(mod_chunk->start);
+	data[2] = HIFIX80(level);
+
+	iforce_send_packet(iforce, FF_CMD_MAGNITUDE, data);
+
+	iforce_dump_packet("magnitude: ", FF_CMD_MAGNITUDE, data);
+	return 0;
+}
+
+/*
+ * Upload the component of an effect dealing with the period, phase and magnitude
+ */
+
+static int make_period_modifier(struct iforce* iforce,
+	struct resource* mod_chunk, int no_alloc,
+	__s16 magnitude, __s16 offset, u16 period, u16 phase)
+{
+	unsigned char data[7];
+
+	period = TIME_SCALE(period);
+
+	if (!no_alloc) {
+		down(&iforce->mem_mutex);
+		if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c,
+			iforce->device_memory.start, iforce->device_memory.end, 2L,
+			NULL, NULL)) {
+			up(&iforce->mem_mutex);
+			return -ENOMEM;
+		}
+		up(&iforce->mem_mutex);
+	}
+
+	data[0] = LO(mod_chunk->start);
+	data[1] = HI(mod_chunk->start);
+
+	data[2] = HIFIX80(magnitude);
+	data[3] = HIFIX80(offset);
+	data[4] = HI(phase);
+
+	data[5] = LO(period);
+	data[6] = HI(period);
+
+	iforce_send_packet(iforce, FF_CMD_PERIOD, data);
+
+	return 0;
+}
+
+/*
+ * Uploads the part of an effect setting the envelope of the force
+ */
+
+static int make_envelope_modifier(struct iforce* iforce,
+	struct resource* mod_chunk, int no_alloc,
+	u16 attack_duration, __s16 initial_level,
+	u16 fade_duration, __s16 final_level)
+{
+	unsigned char data[8];
+
+	attack_duration = TIME_SCALE(attack_duration);
+	fade_duration = TIME_SCALE(fade_duration);
+
+	if (!no_alloc) {
+		down(&iforce->mem_mutex);
+		if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e,
+			iforce->device_memory.start, iforce->device_memory.end, 2L,
+			NULL, NULL)) {
+			up(&iforce->mem_mutex);
+			return -ENOMEM;
+		}
+		up(&iforce->mem_mutex);
+	}
+
+	data[0] = LO(mod_chunk->start);
+	data[1] = HI(mod_chunk->start);
+
+	data[2] = LO(attack_duration);
+	data[3] = HI(attack_duration);
+	data[4] = HI(initial_level);
+
+	data[5] = LO(fade_duration);
+	data[6] = HI(fade_duration);
+	data[7] = HI(final_level);
+
+	iforce_send_packet(iforce, FF_CMD_ENVELOPE, data);
+
+	return 0;
+}
+
+/*
+ * Component of spring, friction, inertia... effects
+ */
+
+static int make_condition_modifier(struct iforce* iforce,
+	struct resource* mod_chunk, int no_alloc,
+	__u16 rsat, __u16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center)
+{
+	unsigned char data[10];
+
+	if (!no_alloc) {
+		down(&iforce->mem_mutex);
+		if (allocate_resource(&(iforce->device_memory), mod_chunk, 8,
+			iforce->device_memory.start, iforce->device_memory.end, 2L,
+			NULL, NULL)) {
+			up(&iforce->mem_mutex);
+			return -ENOMEM;
+		}
+		up(&iforce->mem_mutex);
+	}
+
+	data[0] = LO(mod_chunk->start);
+	data[1] = HI(mod_chunk->start);
+
+	data[2] = (100*rk)>>15;	/* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */
+	data[3] = (100*lk)>>15; /* This code is incorrect on cpus lacking arith shift */
+
+	center = (500*center)>>15;
+	data[4] = LO(center);
+	data[5] = HI(center);
+
+	db = (1000*db)>>16;
+	data[6] = LO(db);
+	data[7] = HI(db);
+
+	data[8] = (100*rsat)>>16;
+	data[9] = (100*lsat)>>16;
+
+	iforce_send_packet(iforce, FF_CMD_CONDITION, data);
+	iforce_dump_packet("condition", FF_CMD_CONDITION, data);
+
+	return 0;
+}
+
+static unsigned char find_button(struct iforce *iforce, signed short button)
+{
+	int i;
+	for (i = 1; iforce->type->btn[i] >= 0; i++)
+		if (iforce->type->btn[i] == button)
+			return i + 1;
+	return 0;
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send an condition
+ * parameter packet
+ */
+static int need_condition_modifier(struct iforce* iforce, struct ff_effect* new)
+{
+	int id = new->id;
+	struct ff_effect* old = &iforce->core_effects[id].effect;
+	int ret=0;
+	int i;
+
+	if (new->type != FF_SPRING && new->type != FF_FRICTION) {
+		printk(KERN_WARNING "iforce.c: bad effect type in need_condition_modifier\n");
+		return FALSE;
+	}
+
+	for(i=0; i<2; i++) {
+		ret |= old->u.condition[i].right_saturation != new->u.condition[i].right_saturation
+			|| old->u.condition[i].left_saturation != new->u.condition[i].left_saturation
+			|| old->u.condition[i].right_coeff != new->u.condition[i].right_coeff
+			|| old->u.condition[i].left_coeff != new->u.condition[i].left_coeff
+			|| old->u.condition[i].deadband != new->u.condition[i].deadband
+			|| old->u.condition[i].center != new->u.condition[i].center;
+	}
+	return ret;
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send a magnitude
+ * parameter packet
+ */
+static int need_magnitude_modifier(struct iforce* iforce, struct ff_effect* effect)
+{
+	int id = effect->id;
+	struct ff_effect* old = &iforce->core_effects[id].effect;
+
+	if (effect->type != FF_CONSTANT) {
+		printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n");
+		return FALSE;
+	}
+
+	return (old->u.constant.level != effect->u.constant.level);
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send an envelope
+ * parameter packet
+ */
+static int need_envelope_modifier(struct iforce* iforce, struct ff_effect* effect)
+{
+	int id = effect->id;
+	struct ff_effect* old = &iforce->core_effects[id].effect;
+
+	switch (effect->type) {
+	case FF_CONSTANT:
+		if (old->u.constant.envelope.attack_length != effect->u.constant.envelope.attack_length
+		|| old->u.constant.envelope.attack_level != effect->u.constant.envelope.attack_level
+		|| old->u.constant.envelope.fade_length != effect->u.constant.envelope.fade_length
+		|| old->u.constant.envelope.fade_level != effect->u.constant.envelope.fade_level)
+			return TRUE;
+		break;
+
+	case FF_PERIODIC:
+		if (old->u.periodic.envelope.attack_length != effect->u.periodic.envelope.attack_length
+		|| old->u.periodic.envelope.attack_level != effect->u.periodic.envelope.attack_level
+		|| old->u.periodic.envelope.fade_length != effect->u.periodic.envelope.fade_length
+		|| old->u.periodic.envelope.fade_level != effect->u.periodic.envelope.fade_level)
+			return TRUE;
+		break;
+
+	default:
+		printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n");
+	}
+
+	return FALSE;
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send a periodic
+ * parameter effect
+ */
+static int need_period_modifier(struct iforce* iforce, struct ff_effect* new)
+{
+	int id = new->id;
+	struct ff_effect* old = &iforce->core_effects[id].effect;
+
+	if (new->type != FF_PERIODIC) {
+		printk(KERN_WARNING "iforce.c: bad effect type in need_periodic_modifier\n");
+		return FALSE;
+	}
+
+	return (old->u.periodic.period != new->u.periodic.period
+		|| old->u.periodic.magnitude != new->u.periodic.magnitude
+		|| old->u.periodic.offset != new->u.periodic.offset
+		|| old->u.periodic.phase != new->u.periodic.phase);
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send an effect
+ * packet
+ */
+static int need_core(struct iforce* iforce, struct ff_effect* new)
+{
+	int id = new->id;
+	struct ff_effect* old = &iforce->core_effects[id].effect;
+
+	if (old->direction != new->direction
+		|| old->trigger.button != new->trigger.button
+		|| old->trigger.interval != new->trigger.interval
+		|| old->replay.length != new->replay.length
+		|| old->replay.delay != new->replay.delay)
+		return TRUE;
+
+	return FALSE;
+}
+/*
+ * Send the part common to all effects to the device
+ */
+static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2,
+	u8 effect_type, u8 axes, u16 duration, u16 delay, u16 button,
+	u16 interval, u16 direction)
+{
+	unsigned char data[14];
+
+	duration = TIME_SCALE(duration);
+	delay    = TIME_SCALE(delay);
+	interval = TIME_SCALE(interval);
+
+	data[0]  = LO(id);
+	data[1]  = effect_type;
+	data[2]  = LO(axes) | find_button(iforce, button);
+
+	data[3]  = LO(duration);
+	data[4]  = HI(duration);
+
+	data[5]  = HI(direction);
+
+	data[6]  = LO(interval);
+	data[7]  = HI(interval);
+
+	data[8]  = LO(mod_id1);
+	data[9]  = HI(mod_id1);
+	data[10] = LO(mod_id2);
+	data[11] = HI(mod_id2);
+
+	data[12] = LO(delay);
+	data[13] = HI(delay);
+
+	/* Stop effect */
+/*	iforce_control_playback(iforce, id, 0);*/
+
+	iforce_send_packet(iforce, FF_CMD_EFFECT, data);
+
+	/* If needed, restart effect */
+	if (test_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[id].flags)) {
+		/* BUG: perhaps we should replay n times, instead of 1. But we do not know n */
+		iforce_control_playback(iforce, id, 1);
+	}
+
+	return 0;
+}
+
+/*
+ * Upload a periodic effect to the device
+ * See also iforce_upload_constant.
+ */
+int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int is_update)
+{
+	u8 wave_code;
+	int core_id = effect->id;
+	struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
+	struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
+	struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
+	int param1_err = 1;
+	int param2_err = 1;
+	int core_err = 0;
+
+	if (!is_update || need_period_modifier(iforce, effect)) {
+		param1_err = make_period_modifier(iforce, mod1_chunk,
+			is_update,
+			effect->u.periodic.magnitude, effect->u.periodic.offset,
+			effect->u.periodic.period, effect->u.periodic.phase);
+		if (param1_err) return param1_err;
+		set_bit(FF_MOD1_IS_USED, core_effect->flags);
+	}
+
+	if (!is_update || need_envelope_modifier(iforce, effect)) {
+		param2_err = make_envelope_modifier(iforce, mod2_chunk,
+			is_update,
+			effect->u.periodic.envelope.attack_length,
+			effect->u.periodic.envelope.attack_level,
+			effect->u.periodic.envelope.fade_length,
+			effect->u.periodic.envelope.fade_level);
+		if (param2_err) return param2_err;
+		set_bit(FF_MOD2_IS_USED, core_effect->flags);
+	}
+
+	switch (effect->u.periodic.waveform) {
+		case FF_SQUARE:		wave_code = 0x20; break;
+		case FF_TRIANGLE:	wave_code = 0x21; break;
+		case FF_SINE:		wave_code = 0x22; break;
+		case FF_SAW_UP:		wave_code = 0x23; break;
+		case FF_SAW_DOWN:	wave_code = 0x24; break;
+		default:		wave_code = 0x20; break;
+	}
+
+	if (!is_update || need_core(iforce, effect)) {
+		core_err = make_core(iforce, effect->id,
+			mod1_chunk->start,
+			mod2_chunk->start,
+			wave_code,
+			0x20,
+			effect->replay.length,
+			effect->replay.delay,
+			effect->trigger.button,
+			effect->trigger.interval,
+			effect->direction);
+	}
+
+	/* If one of the parameter creation failed, we already returned an
+	 * error code.
+	 * If the core creation failed, we return its error code.
+	 * Else: if one parameter at least was created, we return 0
+	 *       else we return 1;
+	 */
+	return core_err < 0 ? core_err : (param1_err && param2_err);
+}
+
+/*
+ * Upload a constant force effect
+ * Return value:
+ *  <0 Error code
+ *  0 Ok, effect created or updated
+ *  1 effect did not change since last upload, and no packet was therefore sent
+ */
+int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect, int is_update)
+{
+	int core_id = effect->id;
+	struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
+	struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
+	struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
+	int param1_err = 1;
+	int param2_err = 1;
+	int core_err = 0;
+
+	if (!is_update || need_magnitude_modifier(iforce, effect)) {
+		param1_err = make_magnitude_modifier(iforce, mod1_chunk,
+			is_update,
+			effect->u.constant.level);
+		if (param1_err) return param1_err;
+		set_bit(FF_MOD1_IS_USED, core_effect->flags);
+	}
+
+	if (!is_update || need_envelope_modifier(iforce, effect)) {
+		param2_err = make_envelope_modifier(iforce, mod2_chunk,
+			is_update,
+			effect->u.constant.envelope.attack_length,
+			effect->u.constant.envelope.attack_level,
+			effect->u.constant.envelope.fade_length,
+			effect->u.constant.envelope.fade_level);
+		if (param2_err) return param2_err;
+		set_bit(FF_MOD2_IS_USED, core_effect->flags);
+	}
+
+	if (!is_update || need_core(iforce, effect)) {
+		core_err = make_core(iforce, effect->id,
+			mod1_chunk->start,
+			mod2_chunk->start,
+			0x00,
+			0x20,
+			effect->replay.length,
+			effect->replay.delay,
+			effect->trigger.button,
+			effect->trigger.interval,
+			effect->direction);
+	}
+
+	/* If one of the parameter creation failed, we already returned an
+	 * error code.
+	 * If the core creation failed, we return its error code.
+	 * Else: if one parameter at least was created, we return 0
+	 *       else we return 1;
+	 */
+	return core_err < 0 ? core_err : (param1_err && param2_err);
+}
+
+/*
+ * Upload an condition effect. Those are for example friction, inertia, springs...
+ */
+int iforce_upload_condition(struct iforce* iforce, struct ff_effect* effect, int is_update)
+{
+	int core_id = effect->id;
+	struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
+	struct resource* mod1_chunk = &(core_effect->mod1_chunk);
+	struct resource* mod2_chunk = &(core_effect->mod2_chunk);
+	u8 type;
+	int param_err = 1;
+	int core_err = 0;
+
+	switch (effect->type) {
+		case FF_SPRING:		type = 0x40; break;
+		case FF_DAMPER:		type = 0x41; break;
+		default: return -1;
+	}
+
+	if (!is_update || need_condition_modifier(iforce, effect)) {
+		param_err = make_condition_modifier(iforce, mod1_chunk,
+			is_update,
+			effect->u.condition[0].right_saturation,
+			effect->u.condition[0].left_saturation,
+			effect->u.condition[0].right_coeff,
+			effect->u.condition[0].left_coeff,
+			effect->u.condition[0].deadband,
+			effect->u.condition[0].center);
+		if (param_err) return param_err;
+		set_bit(FF_MOD1_IS_USED, core_effect->flags);
+
+		param_err = make_condition_modifier(iforce, mod2_chunk,
+			is_update,
+			effect->u.condition[1].right_saturation,
+			effect->u.condition[1].left_saturation,
+			effect->u.condition[1].right_coeff,
+			effect->u.condition[1].left_coeff,
+			effect->u.condition[1].deadband,
+			effect->u.condition[1].center);
+		if (param_err) return param_err;
+		set_bit(FF_MOD2_IS_USED, core_effect->flags);
+
+	}
+
+	if (!is_update || need_core(iforce, effect)) {
+		core_err = make_core(iforce, effect->id,
+			mod1_chunk->start, mod2_chunk->start,
+			type, 0xc0,
+			effect->replay.length, effect->replay.delay,
+			effect->trigger.button, effect->trigger.interval,
+			effect->direction);
+	}
+
+	/* If the parameter creation failed, we already returned an
+	 * error code.
+	 * If the core creation failed, we return its error code.
+	 * Else: if a parameter  was created, we return 0
+	 *       else we return 1;
+	 */
+	return core_err < 0 ? core_err : param_err;
+}
diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c
new file mode 100644
index 0000000..028f351
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-main.c
@@ -0,0 +1,557 @@
+/*
+ * $Id: iforce-main.c,v 1.19 2002/07/07 10:22:50 jdeneux Exp $
+ *
+ *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * 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 "iforce.h"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <deneux@ifrance.com>");
+MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver");
+MODULE_LICENSE("GPL");
+
+static signed short btn_joystick[] =
+{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
+  BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 };
+
+static signed short btn_avb_pegasus[] =
+{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
+  BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 };
+
+static signed short btn_wheel[] =
+{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
+  BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 };
+
+static signed short btn_avb_tw[] =
+{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE,
+  BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 };
+
+static signed short btn_avb_wheel[] =
+{ BTN_GEAR_DOWN, BTN_GEAR_UP, BTN_BASE, BTN_BASE2, BTN_BASE3,
+  BTN_BASE4, BTN_BASE5, BTN_BASE6, -1 };
+
+static signed short abs_joystick[] =
+{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
+
+static signed short abs_avb_pegasus[] =
+{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_HAT0X, ABS_HAT0Y,
+  ABS_HAT1X, ABS_HAT1Y, -1 };
+
+static signed short abs_wheel[] =
+{ ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, -1 };
+
+static signed short ff_iforce[] =
+{ FF_PERIODIC, FF_CONSTANT, FF_SPRING, FF_DAMPER,
+  FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, FF_SAW_DOWN, FF_GAIN,
+  FF_AUTOCENTER, -1 };
+
+static struct iforce_device iforce_device[] = {
+	{ 0x044f, 0xa01c, "Thrustmaster Motor Sport GT",		btn_wheel, abs_wheel, ff_iforce },
+	{ 0x046d, 0xc281, "Logitech WingMan Force",			btn_joystick, abs_joystick, ff_iforce },
+	{ 0x046d, 0xc291, "Logitech WingMan Formula Force",		btn_wheel, abs_wheel, ff_iforce },
+	{ 0x05ef, 0x020a, "AVB Top Shot Pegasus",			btn_avb_pegasus, abs_avb_pegasus, ff_iforce },
+	{ 0x05ef, 0x8884, "AVB Mag Turbo Force",			btn_avb_wheel, abs_wheel, ff_iforce },
+	{ 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel",	btn_avb_tw, abs_wheel, ff_iforce }, //?
+	{ 0x061c, 0xc0a4, "ACT LABS Force RS",                          btn_wheel, abs_wheel, ff_iforce }, //?
+	{ 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback",	btn_wheel, abs_wheel, ff_iforce }, //?
+	{ 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel",	btn_wheel, abs_wheel, ff_iforce }, //?
+	{ 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]",		btn_joystick, abs_joystick, ff_iforce }
+};
+
+
+
+static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	struct iforce* iforce = (struct iforce*)(dev->private);
+	unsigned char data[3];
+
+	if (type != EV_FF)
+		return -1;
+
+	switch (code) {
+
+		case FF_GAIN:
+
+			data[0] = value >> 9;
+			iforce_send_packet(iforce, FF_CMD_GAIN, data);
+
+			return 0;
+
+		case FF_AUTOCENTER:
+
+			data[0] = 0x03;
+			data[1] = value >> 9;
+			iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
+
+			data[0] = 0x04;
+			data[1] = 0x01;
+			iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
+
+			return 0;
+
+		default: /* Play or stop an effect */
+
+			if (!CHECK_OWNERSHIP(code, iforce)) {
+				return -1;
+			}
+			if (value > 0) {
+				set_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags);
+			}
+			else {
+				clear_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags);
+			}
+
+			iforce_control_playback(iforce, code, value);
+			return 0;
+	}
+
+	return -1;
+}
+
+/*
+ * Function called when an ioctl is performed on the event dev entry.
+ * It uploads an effect to the device
+ */
+static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
+{
+	struct iforce* iforce = (struct iforce*)(dev->private);
+	int id;
+	int ret;
+	int is_update;
+
+/* Check this effect type is supported by this device */
+	if (!test_bit(effect->type, iforce->dev.ffbit))
+		return -EINVAL;
+
+/*
+ * If we want to create a new effect, get a free id
+ */
+	if (effect->id == -1) {
+
+		for (id=0; id < FF_EFFECTS_MAX; ++id)
+			if (!test_and_set_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags)) break;
+
+		if ( id == FF_EFFECTS_MAX || id >= iforce->dev.ff_effects_max)
+			return -ENOMEM;
+
+		effect->id = id;
+		iforce->core_effects[id].owner = current->pid;
+		iforce->core_effects[id].flags[0] = (1<<FF_CORE_IS_USED);	/* Only IS_USED bit must be set */
+
+		is_update = FALSE;
+	}
+	else {
+		/* We want to update an effect */
+		if (!CHECK_OWNERSHIP(effect->id, iforce)) return -EACCES;
+
+		/* Parameter type cannot be updated */
+		if (effect->type != iforce->core_effects[effect->id].effect.type)
+			return -EINVAL;
+
+		/* Check the effect is not already being updated */
+		if (test_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags)) {
+			return -EAGAIN;
+		}
+
+		is_update = TRUE;
+	}
+
+/*
+ * Upload the effect
+ */
+	switch (effect->type) {
+
+		case FF_PERIODIC:
+			ret = iforce_upload_periodic(iforce, effect, is_update);
+			break;
+
+		case FF_CONSTANT:
+			ret = iforce_upload_constant(iforce, effect, is_update);
+			break;
+
+		case FF_SPRING:
+		case FF_DAMPER:
+			ret = iforce_upload_condition(iforce, effect, is_update);
+			break;
+
+		default:
+			return -EINVAL;
+	}
+	if (ret == 0) {
+		/* A packet was sent, forbid new updates until we are notified
+		 * that the packet was updated
+		 */
+		set_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags);
+	}
+	iforce->core_effects[effect->id].effect = *effect;
+	return ret;
+}
+
+/*
+ * Erases an effect: it frees the effect id and mark as unused the memory
+ * allocated for the parameters
+ */
+static int iforce_erase_effect(struct input_dev *dev, int effect_id)
+{
+	struct iforce* iforce = (struct iforce*)(dev->private);
+	int err = 0;
+	struct iforce_core_effect* core_effect;
+
+	/* Check who is trying to erase this effect */
+	if (iforce->core_effects[effect_id].owner != current->pid) {
+		printk(KERN_WARNING "iforce-main.c: %d tried to erase an effect belonging to %d\n", current->pid, iforce->core_effects[effect_id].owner);
+		return -EACCES;
+	}
+
+	if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX)
+		return -EINVAL;
+
+	core_effect = iforce->core_effects + effect_id;
+
+	if (test_bit(FF_MOD1_IS_USED, core_effect->flags))
+		err = release_resource(&(iforce->core_effects[effect_id].mod1_chunk));
+
+	if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags))
+		err = release_resource(&(iforce->core_effects[effect_id].mod2_chunk));
+
+	/*TODO: remember to change that if more FF_MOD* bits are added */
+	core_effect->flags[0] = 0;
+
+	return err;
+}
+
+static int iforce_open(struct input_dev *dev)
+{
+	struct iforce *iforce = dev->private;
+
+	switch (iforce->bus) {
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+		case IFORCE_USB:
+			iforce->irq->dev = iforce->usbdev;
+			if (usb_submit_urb(iforce->irq, GFP_KERNEL))
+				return -EIO;
+			break;
+#endif
+	}
+
+	/* Enable force feedback */
+	iforce_send_packet(iforce, FF_CMD_ENABLE, "\004");
+
+	return 0;
+}
+
+static int iforce_flush(struct input_dev *dev, struct file *file)
+{
+	struct iforce *iforce = dev->private;
+	int i;
+
+	/* Erase all effects this process owns */
+	for (i=0; i<dev->ff_effects_max; ++i) {
+
+		if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) &&
+			current->pid == iforce->core_effects[i].owner) {
+
+			/* Stop effect */
+			input_report_ff(dev, i, 0);
+
+			/* Free ressources assigned to effect */
+			if (iforce_erase_effect(dev, i)) {
+				printk(KERN_WARNING "iforce_flush: erase effect %d failed\n", i);
+			}
+		}
+
+	}
+	return 0;
+}
+
+static void iforce_release(struct input_dev *dev)
+{
+	struct iforce *iforce = dev->private;
+	int i;
+
+	/* Check: no effect should be present in memory */
+	for (i=0; i<dev->ff_effects_max; ++i) {
+		if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags))
+			break;
+	}
+	if (i<dev->ff_effects_max) {
+		printk(KERN_WARNING "iforce_release: Device still owns effects\n");
+	}
+
+	/* Disable force feedback playback */
+	iforce_send_packet(iforce, FF_CMD_ENABLE, "\001");
+
+	switch (iforce->bus) {
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+		case IFORCE_USB:
+			usb_unlink_urb(iforce->irq);
+
+			/* The device was unplugged before the file
+			 * was released */
+			if (iforce->usbdev == NULL) {
+				iforce_delete_device(iforce);
+				kfree(iforce);
+			}
+		break;
+#endif
+	}
+}
+
+void iforce_delete_device(struct iforce *iforce)
+{
+	switch (iforce->bus) {
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+	case IFORCE_USB:
+		iforce_usb_delete(iforce);
+		break;
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+	case IFORCE_232:
+		//TODO: Wait for the last packets to be sent
+		break;
+#endif
+	}
+}
+
+int iforce_init_device(struct iforce *iforce)
+{
+	unsigned char c[] = "CEOV";
+	int i;
+
+	init_waitqueue_head(&iforce->wait);
+	spin_lock_init(&iforce->xmit_lock);
+	init_MUTEX(&iforce->mem_mutex);
+	iforce->xmit.buf = iforce->xmit_data;
+
+	iforce->dev.ff_effects_max = 10;
+
+/*
+ * Input device fields.
+ */
+
+	switch (iforce->bus) {
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+	case IFORCE_USB:
+		iforce->dev.id.bustype = BUS_USB;
+		iforce->dev.dev = &iforce->usbdev->dev;
+		break;
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+	case IFORCE_232:
+		iforce->dev.id.bustype = BUS_RS232;
+		iforce->dev.dev = &iforce->serio->dev;
+		break;
+#endif
+	}
+
+	iforce->dev.private = iforce;
+	iforce->dev.name = "Unknown I-Force device";
+	iforce->dev.open = iforce_open;
+	iforce->dev.close = iforce_release;
+	iforce->dev.flush = iforce_flush;
+	iforce->dev.event = iforce_input_event;
+	iforce->dev.upload_effect = iforce_upload_effect;
+	iforce->dev.erase_effect = iforce_erase_effect;
+
+/*
+ * On-device memory allocation.
+ */
+
+	iforce->device_memory.name = "I-Force device effect memory";
+	iforce->device_memory.start = 0;
+	iforce->device_memory.end = 200;
+	iforce->device_memory.flags = IORESOURCE_MEM;
+	iforce->device_memory.parent = NULL;
+	iforce->device_memory.child = NULL;
+	iforce->device_memory.sibling = NULL;
+
+/*
+ * Wait until device ready - until it sends its first response.
+ */
+
+	for (i = 0; i < 20; i++)
+		if (!iforce_get_id_packet(iforce, "O"))
+			break;
+
+	if (i == 20) { /* 5 seconds */
+		printk(KERN_ERR "iforce-main.c: Timeout waiting for response from device.\n");
+		return -1;
+	}
+
+/*
+ * Get device info.
+ */
+
+	if (!iforce_get_id_packet(iforce, "M"))
+		iforce->dev.id.vendor = (iforce->edata[2] << 8) | iforce->edata[1];
+	else
+		printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet M\n");
+
+	if (!iforce_get_id_packet(iforce, "P"))
+		iforce->dev.id.product = (iforce->edata[2] << 8) | iforce->edata[1];
+	else
+		printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet P\n");
+
+	if (!iforce_get_id_packet(iforce, "B"))
+		iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1];
+	else
+		printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet B\n");
+
+	if (!iforce_get_id_packet(iforce, "N"))
+		iforce->dev.ff_effects_max = iforce->edata[1];
+	else
+		printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet N\n");
+
+	/* Check if the device can store more effects than the driver can really handle */
+	if (iforce->dev.ff_effects_max > FF_EFFECTS_MAX) {
+		printk(KERN_WARNING "input??: Device can handle %d effects, but N_EFFECTS_MAX is set to %d in iforce.h\n",
+			iforce->dev.ff_effects_max, FF_EFFECTS_MAX);
+		iforce->dev.ff_effects_max = FF_EFFECTS_MAX;
+	}
+
+/*
+ * Display additional info.
+ */
+
+	for (i = 0; c[i]; i++)
+		if (!iforce_get_id_packet(iforce, c + i))
+			iforce_dump_packet("info", iforce->ecmd, iforce->edata);
+
+/*
+ * Disable spring, enable force feedback.
+ * FIXME: We should use iforce_set_autocenter() et al here.
+ */
+
+	iforce_send_packet(iforce, FF_CMD_AUTOCENTER, "\004\000");
+
+/*
+ * Find appropriate device entry
+ */
+
+	for (i = 0; iforce_device[i].idvendor; i++)
+		if (iforce_device[i].idvendor == iforce->dev.id.vendor &&
+		    iforce_device[i].idproduct == iforce->dev.id.product)
+			break;
+
+	iforce->type = iforce_device + i;
+	iforce->dev.name = iforce->type->name;
+
+/*
+ * Set input device bitfields and ranges.
+ */
+
+	iforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF) | BIT(EV_FF_STATUS);
+
+	for (i = 0; iforce->type->btn[i] >= 0; i++) {
+		signed short t = iforce->type->btn[i];
+		set_bit(t, iforce->dev.keybit);
+	}
+	set_bit(BTN_DEAD, iforce->dev.keybit);
+
+	for (i = 0; iforce->type->abs[i] >= 0; i++) {
+
+		signed short t = iforce->type->abs[i];
+		set_bit(t, iforce->dev.absbit);
+
+		switch (t) {
+
+			case ABS_X:
+			case ABS_Y:
+			case ABS_WHEEL:
+
+				iforce->dev.absmax[t] =  1920;
+				iforce->dev.absmin[t] = -1920;
+				iforce->dev.absflat[t] = 128;
+				iforce->dev.absfuzz[t] = 16;
+
+				set_bit(t, iforce->dev.ffbit);
+				break;
+
+			case ABS_THROTTLE:
+			case ABS_GAS:
+			case ABS_BRAKE:
+
+				iforce->dev.absmax[t] = 255;
+				iforce->dev.absmin[t] = 0;
+				break;
+
+			case ABS_RUDDER:
+
+				iforce->dev.absmax[t] = 127;
+				iforce->dev.absmin[t] = -128;
+				break;
+
+			case ABS_HAT0X:
+			case ABS_HAT0Y:
+		        case ABS_HAT1X:
+		        case ABS_HAT1Y:
+				iforce->dev.absmax[t] =  1;
+				iforce->dev.absmin[t] = -1;
+				break;
+		}
+	}
+
+	for (i = 0; iforce->type->ff[i] >= 0; i++)
+		set_bit(iforce->type->ff[i], iforce->dev.ffbit);
+
+/*
+ * Register input device.
+ */
+
+	input_register_device(&iforce->dev);
+
+	printk(KERN_DEBUG "iforce->dev.open = %p\n", iforce->dev.open);
+
+	printk(KERN_INFO "input: %s [%d effects, %ld bytes memory]\n",
+		iforce->dev.name, iforce->dev.ff_effects_max,
+		iforce->device_memory.end);
+
+	return 0;
+}
+
+static int __init iforce_init(void)
+{
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+	usb_register(&iforce_usb_driver);
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+	serio_register_driver(&iforce_serio_drv);
+#endif
+	return 0;
+}
+
+static void __exit iforce_exit(void)
+{
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+	usb_deregister(&iforce_usb_driver);
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+	serio_unregister_driver(&iforce_serio_drv);
+#endif
+}
+
+module_init(iforce_init);
+module_exit(iforce_exit);
diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c
new file mode 100644
index 0000000..58728eb
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-packets.c
@@ -0,0 +1,319 @@
+/*
+ * $Id: iforce-packets.c,v 1.16 2002/07/07 10:22:50 jdeneux Exp $
+ *
+ *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * 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 "iforce.h"
+
+static struct {
+	__s32 x;
+	__s32 y;
+} iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+
+void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data)
+{
+	int i;
+
+	printk(KERN_DEBUG "iforce.c: %s ( cmd = %04x, data = ", msg, cmd);
+	for (i = 0; i < LO(cmd); i++)
+		printk("%02x ", data[i]);
+	printk(")\n");
+}
+
+/*
+ * Send a packet of bytes to the device
+ */
+int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data)
+{
+	/* Copy data to buffer */
+	int n = LO(cmd);
+	int c;
+	int empty;
+	int head, tail;
+	unsigned long flags;
+
+/*
+ * Update head and tail of xmit buffer
+ */
+	spin_lock_irqsave(&iforce->xmit_lock, flags);
+
+	head = iforce->xmit.head;
+	tail = iforce->xmit.tail;
+
+	if (CIRC_SPACE(head, tail, XMIT_SIZE) < n+2) {
+		printk(KERN_WARNING "iforce.c: not enough space in xmit buffer to send new packet\n");
+		spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+		return -1;
+	}
+
+	empty = head == tail;
+	XMIT_INC(iforce->xmit.head, n+2);
+
+/*
+ * Store packet in xmit buffer
+ */
+	iforce->xmit.buf[head] = HI(cmd);
+	XMIT_INC(head, 1);
+	iforce->xmit.buf[head] = LO(cmd);
+	XMIT_INC(head, 1);
+
+	c = CIRC_SPACE_TO_END(head, tail, XMIT_SIZE);
+	if (n < c) c=n;
+
+	memcpy(&iforce->xmit.buf[head],
+	       data,
+	       c);
+	if (n != c) {
+		memcpy(&iforce->xmit.buf[0],
+		       data + c,
+		       n - c);
+	}
+	XMIT_INC(head, n);
+
+	spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+/*
+ * If necessary, start the transmission
+ */
+	switch (iforce->bus) {
+
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+		case IFORCE_232:
+		if (empty)
+			iforce_serial_xmit(iforce);
+		break;
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+		case IFORCE_USB:
+
+		if (iforce->usbdev && empty &&
+			!test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) {
+
+			iforce_usb_xmit(iforce);
+		}
+		break;
+#endif
+	}
+	return 0;
+}
+
+/* Start or stop an effect */
+int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value)
+{
+	unsigned char data[3];
+
+printk(KERN_DEBUG "iforce-packets.c: control_playback %d %d\n", id, value);
+
+	data[0] = LO(id);
+	data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0;
+	data[2] = LO(value);
+	return iforce_send_packet(iforce, FF_CMD_PLAY, data);
+}
+
+/* Mark an effect that was being updated as ready. That means it can be updated
+ * again */
+static int mark_core_as_ready(struct iforce *iforce, unsigned short addr)
+{
+	int i;
+	for (i=0; i<iforce->dev.ff_effects_max; ++i) {
+		if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) &&
+		    (iforce->core_effects[i].mod1_chunk.start == addr ||
+		     iforce->core_effects[i].mod2_chunk.start == addr)) {
+			clear_bit(FF_CORE_UPDATE, iforce->core_effects[i].flags);
+			return 0;
+		}
+	}
+	printk(KERN_WARNING "iforce-packets.c: unused effect %04x updated !!!\n", addr);
+	return -1;
+}
+
+void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data, struct pt_regs *regs)
+{
+	struct input_dev *dev = &iforce->dev;
+	int i;
+	static int being_used = 0;
+
+	if (being_used)
+		printk(KERN_WARNING "iforce-packets.c: re-entrant call to iforce_process %d\n", being_used);
+	being_used++;
+
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+	if (HI(iforce->expect_packet) == HI(cmd)) {
+		iforce->expect_packet = 0;
+		iforce->ecmd = cmd;
+		memcpy(iforce->edata, data, IFORCE_MAX_LENGTH);
+		wake_up(&iforce->wait);
+	}
+#endif
+
+	if (!iforce->type) {
+		being_used--;
+		return;
+	}
+
+	switch (HI(cmd)) {
+
+		case 0x01:	/* joystick position data */
+		case 0x03:	/* wheel position data */
+
+			input_regs(dev, regs);
+
+			if (HI(cmd) == 1) {
+				input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0]));
+				input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2]));
+				input_report_abs(dev, ABS_THROTTLE, 255 - data[4]);
+				if (LO(cmd) >= 8 && test_bit(ABS_RUDDER ,dev->absbit))
+					input_report_abs(dev, ABS_RUDDER, (__s8)data[7]);
+			} else {
+				input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0]));
+				input_report_abs(dev, ABS_GAS,   255 - data[2]);
+				input_report_abs(dev, ABS_BRAKE, 255 - data[3]);
+			}
+
+			input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x);
+			input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y);
+
+			for (i = 0; iforce->type->btn[i] >= 0; i++)
+				input_report_key(dev, iforce->type->btn[i], data[(i >> 3) + 5] & (1 << (i & 7)));
+
+			/* If there are untouched bits left, interpret them as the second hat */
+			if (i <= 8) {
+				int btns = data[6];
+				if (test_bit(ABS_HAT1X, dev->absbit)) {
+					if (btns & 8) input_report_abs(dev, ABS_HAT1X, -1);
+					else if (btns & 2) input_report_abs(dev, ABS_HAT1X, 1);
+					else input_report_abs(dev, ABS_HAT1X, 0);
+				}
+				if (test_bit(ABS_HAT1Y, dev->absbit)) {
+					if (btns & 1) input_report_abs(dev, ABS_HAT1Y, -1);
+					else if (btns & 4) input_report_abs(dev, ABS_HAT1Y, 1);
+					else input_report_abs(dev, ABS_HAT1Y, 0);
+				}
+			}
+
+			input_sync(dev);
+
+			break;
+
+		case 0x02:	/* status report */
+			input_regs(dev, regs);
+			input_report_key(dev, BTN_DEAD, data[0] & 0x02);
+			input_sync(dev);
+
+			/* Check if an effect was just started or stopped */
+			i = data[1] & 0x7f;
+			if (data[1] & 0x80) {
+				if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
+				/* Report play event */
+				input_report_ff_status(dev, i, FF_STATUS_PLAYING);
+				}
+			}
+			else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
+				/* Report stop event */
+				input_report_ff_status(dev, i, FF_STATUS_STOPPED);
+			}
+			if (LO(cmd) > 3) {
+				int j;
+				for (j=3; j<LO(cmd); j+=2) {
+					mark_core_as_ready(iforce, data[j] | (data[j+1]<<8));
+				}
+			}
+			break;
+	}
+	being_used--;
+}
+
+int iforce_get_id_packet(struct iforce *iforce, char *packet)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	int timeout = HZ; /* 1 second */
+
+	switch (iforce->bus) {
+
+	case IFORCE_USB:
+
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+		iforce->cr.bRequest = packet[0];
+		iforce->ctrl->dev = iforce->usbdev;
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		add_wait_queue(&iforce->wait, &wait);
+
+		if (usb_submit_urb(iforce->ctrl, GFP_ATOMIC)) {
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&iforce->wait, &wait);
+			return -1;
+		}
+
+		while (timeout && iforce->ctrl->status == -EINPROGRESS)
+			timeout = schedule_timeout(timeout);
+
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&iforce->wait, &wait);
+
+		if (!timeout) {
+			usb_unlink_urb(iforce->ctrl);
+			return -1;
+		}
+#else
+		printk(KERN_ERR "iforce_get_id_packet: iforce->bus = USB!\n");
+#endif
+		break;
+
+	case IFORCE_232:
+
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+		iforce->expect_packet = FF_CMD_QUERY;
+		iforce_send_packet(iforce, FF_CMD_QUERY, packet);
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		add_wait_queue(&iforce->wait, &wait);
+
+		while (timeout && iforce->expect_packet)
+			timeout = schedule_timeout(timeout);
+
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&iforce->wait, &wait);
+
+		if (!timeout) {
+			iforce->expect_packet = 0;
+			return -1;
+		}
+#else
+		printk(KERN_ERR "iforce_get_id_packet: iforce->bus = SERIO!\n");
+#endif
+		break;
+
+	default:
+		printk(KERN_ERR "iforce_get_id_packet: iforce->bus = %d\n",
+		       iforce->bus);
+		break;
+	}
+
+	return -(iforce->edata[0] != packet[0]);
+}
+
diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c
new file mode 100644
index 0000000..11f5190
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-serio.c
@@ -0,0 +1,193 @@
+/*
+ * $Id: iforce-serio.c,v 1.4 2002/01/28 22:45:00 jdeneux Exp $
+ *
+ *  Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001 Johann Deneux <deneux@ifrance.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * 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 "iforce.h"
+
+void iforce_serial_xmit(struct iforce *iforce)
+{
+	unsigned char cs;
+	int i;
+	unsigned long flags;
+
+	if (test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) {
+		set_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags);
+		return;
+	}
+
+	spin_lock_irqsave(&iforce->xmit_lock, flags);
+
+again:
+	if (iforce->xmit.head == iforce->xmit.tail) {
+		clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+		spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+		return;
+	}
+
+	cs = 0x2b;
+
+	serio_write(iforce->serio, 0x2b);
+
+	serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
+	cs ^= iforce->xmit.buf[iforce->xmit.tail];
+	XMIT_INC(iforce->xmit.tail, 1);
+
+	for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) {
+		serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
+		cs ^= iforce->xmit.buf[iforce->xmit.tail];
+		XMIT_INC(iforce->xmit.tail, 1);
+	}
+
+	serio_write(iforce->serio, cs);
+
+	if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags))
+		goto again;
+
+	clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+
+	spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+}
+
+static void iforce_serio_write_wakeup(struct serio *serio)
+{
+	struct iforce *iforce = serio_get_drvdata(serio);
+
+	iforce_serial_xmit(iforce);
+}
+
+static irqreturn_t iforce_serio_irq(struct serio *serio,
+		unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+	struct iforce *iforce = serio_get_drvdata(serio);
+
+	if (!iforce->pkt) {
+		if (data == 0x2b)
+			iforce->pkt = 1;
+		goto out;
+	}
+
+	if (!iforce->id) {
+		if (data > 3 && data != 0xff)
+			iforce->pkt = 0;
+		else
+			iforce->id = data;
+		goto out;
+	}
+
+	if (!iforce->len) {
+		if (data > IFORCE_MAX_LENGTH) {
+			iforce->pkt = 0;
+			iforce->id = 0;
+		} else {
+			iforce->len = data;
+		}
+		goto out;
+	}
+
+	if (iforce->idx < iforce->len) {
+		iforce->csum += iforce->data[iforce->idx++] = data;
+		goto out;
+	}
+
+	if (iforce->idx == iforce->len) {
+		iforce_process_packet(iforce, (iforce->id << 8) | iforce->idx, iforce->data, regs);
+		iforce->pkt = 0;
+		iforce->id  = 0;
+		iforce->len = 0;
+		iforce->idx = 0;
+		iforce->csum = 0;
+	}
+out:
+	return IRQ_HANDLED;
+}
+
+static int iforce_serio_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct iforce *iforce;
+	int err;
+
+	if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(iforce, 0, sizeof(struct iforce));
+
+	iforce->bus = IFORCE_232;
+	iforce->serio = serio;
+
+	serio_set_drvdata(serio, iforce);
+
+	err = serio_open(serio, drv);
+	if (err) {
+		serio_set_drvdata(serio, NULL);
+		kfree(iforce);
+		return err;
+	}
+
+	if (iforce_init_device(iforce)) {
+		serio_close(serio);
+		serio_set_drvdata(serio, NULL);
+		kfree(iforce);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void iforce_serio_disconnect(struct serio *serio)
+{
+	struct iforce *iforce = serio_get_drvdata(serio);
+
+	input_unregister_device(&iforce->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(iforce);
+}
+
+static struct serio_device_id iforce_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_IFORCE,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, iforce_serio_ids);
+
+struct serio_driver iforce_serio_drv = {
+	.driver		= {
+		.name	= "iforce",
+	},
+	.description	= "RS232 I-Force joysticks and wheels driver",
+	.id_table	= iforce_serio_ids,
+	.write_wakeup	= iforce_serio_write_wakeup,
+	.interrupt	= iforce_serio_irq,
+	.connect	= iforce_serio_connect,
+	.disconnect	= iforce_serio_disconnect,
+};
diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c
new file mode 100644
index 0000000..617c0b0
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-usb.c
@@ -0,0 +1,243 @@
+ /*
+ * $Id: iforce-usb.c,v 1.16 2002/06/09 11:08:04 jdeneux Exp $
+ *
+ *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * 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 "iforce.h"
+
+void iforce_usb_xmit(struct iforce *iforce)
+{
+	int n, c;
+	unsigned long flags;
+
+	spin_lock_irqsave(&iforce->xmit_lock, flags);
+
+	if (iforce->xmit.head == iforce->xmit.tail) {
+		clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+		spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+		return;
+	}
+
+	((char *)iforce->out->transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail];
+	XMIT_INC(iforce->xmit.tail, 1);
+	n = iforce->xmit.buf[iforce->xmit.tail];
+	XMIT_INC(iforce->xmit.tail, 1);
+
+	iforce->out->transfer_buffer_length = n + 1;
+	iforce->out->dev = iforce->usbdev;
+
+	/* Copy rest of data then */
+	c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE);
+	if (n < c) c=n;
+
+	memcpy(iforce->out->transfer_buffer + 1,
+	       &iforce->xmit.buf[iforce->xmit.tail],
+	       c);
+	if (n != c) {
+		memcpy(iforce->out->transfer_buffer + 1 + c,
+		       &iforce->xmit.buf[0],
+		       n-c);
+	}
+	XMIT_INC(iforce->xmit.tail, n);
+
+	if ( (n=usb_submit_urb(iforce->out, GFP_ATOMIC)) ) {
+		printk(KERN_WARNING "iforce-usb.c: iforce_usb_xmit: usb_submit_urb failed %d\n", n);
+	}
+
+	/* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended.
+	 * As long as the urb completion handler is not called, the transmiting
+	 * is considered to be running */
+	spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+}
+
+static void iforce_usb_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct iforce *iforce = urb->context;
+	int status;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d",
+		    __FUNCTION__, urb->status);
+		return;
+	default:
+		dbg("%s - urb has status of: %d", __FUNCTION__, urb->status);
+		goto exit;
+	}
+
+	iforce_process_packet(iforce,
+		(iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1, regs);
+
+exit:
+	status = usb_submit_urb (urb, GFP_ATOMIC);
+	if (status)
+		err ("%s - usb_submit_urb failed with result %d",
+		     __FUNCTION__, status);
+}
+
+static void iforce_usb_out(struct urb *urb, struct pt_regs *regs)
+{
+	struct iforce *iforce = urb->context;
+
+	if (urb->status) {
+		printk(KERN_DEBUG "iforce_usb_out: urb->status %d, exiting", urb->status);
+		return;
+	}
+
+	iforce_usb_xmit(iforce);
+
+	wake_up(&iforce->wait);
+}
+
+static void iforce_usb_ctrl(struct urb *urb, struct pt_regs *regs)
+{
+	struct iforce *iforce = urb->context;
+	if (urb->status) return;
+	iforce->ecmd = 0xff00 | urb->actual_length;
+	wake_up(&iforce->wait);
+}
+
+static int iforce_usb_probe(struct usb_interface *intf,
+				const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_host_interface *interface;
+	struct usb_endpoint_descriptor *epirq, *epout;
+	struct iforce *iforce;
+
+	interface = intf->cur_altsetting;
+
+	epirq = &interface->endpoint[0].desc;
+	epout = &interface->endpoint[1].desc;
+
+	if (!(iforce = kmalloc(sizeof(struct iforce) + 32, GFP_KERNEL)))
+		goto fail;
+
+	memset(iforce, 0, sizeof(struct iforce));
+
+	if (!(iforce->irq = usb_alloc_urb(0, GFP_KERNEL))) {
+		goto fail;
+	}
+
+	if (!(iforce->out = usb_alloc_urb(0, GFP_KERNEL))) {
+		goto fail;
+	}
+
+	if (!(iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL))) {
+		goto fail;
+	}
+
+	iforce->bus = IFORCE_USB;
+	iforce->usbdev = dev;
+
+	iforce->cr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE;
+	iforce->cr.wIndex = 0;
+	iforce->cr.wLength = 16;
+
+	usb_fill_int_urb(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress),
+			iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval);
+
+	usb_fill_bulk_urb(iforce->out, dev, usb_sndbulkpipe(dev, epout->bEndpointAddress),
+			iforce + 1, 32, iforce_usb_out, iforce);
+
+	usb_fill_control_urb(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0),
+			(void*) &iforce->cr, iforce->edata, 16, iforce_usb_ctrl, iforce);
+
+	if (iforce_init_device(iforce)) goto fail;
+
+	usb_set_intfdata(intf, iforce);
+	return 0;
+
+fail:
+	if (iforce) {
+		if (iforce->irq) usb_free_urb(iforce->irq);
+		if (iforce->out) usb_free_urb(iforce->out);
+		if (iforce->ctrl) usb_free_urb(iforce->ctrl);
+		kfree(iforce);
+	}
+
+	return -ENODEV;
+}
+
+/* Called by iforce_delete() */
+void iforce_usb_delete(struct iforce* iforce)
+{
+	usb_unlink_urb(iforce->irq);
+/* Is it ok to unlink those ? */
+	usb_unlink_urb(iforce->out);
+	usb_unlink_urb(iforce->ctrl);
+
+	usb_free_urb(iforce->irq);
+	usb_free_urb(iforce->out);
+	usb_free_urb(iforce->ctrl);
+}
+
+static void iforce_usb_disconnect(struct usb_interface *intf)
+{
+	struct iforce *iforce = usb_get_intfdata(intf);
+	int open = 0; /* FIXME! iforce->dev.handle->open; */
+
+	usb_set_intfdata(intf, NULL);
+	if (iforce) {
+		iforce->usbdev = NULL;
+		input_unregister_device(&iforce->dev);
+
+		if (!open) {
+			iforce_delete_device(iforce);
+			kfree(iforce);
+		}
+	}
+}
+
+static struct usb_device_id iforce_usb_ids [] = {
+	{ USB_DEVICE(0x044f, 0xa01c) },		/* Thrustmaster Motor Sport GT */
+	{ USB_DEVICE(0x046d, 0xc281) },		/* Logitech WingMan Force */
+	{ USB_DEVICE(0x046d, 0xc291) },		/* Logitech WingMan Formula Force */
+	{ USB_DEVICE(0x05ef, 0x020a) },		/* AVB Top Shot Pegasus */
+	{ USB_DEVICE(0x05ef, 0x8884) },		/* AVB Mag Turbo Force */
+	{ USB_DEVICE(0x05ef, 0x8888) },		/* AVB Top Shot FFB Racing Wheel */
+	{ USB_DEVICE(0x061c, 0xc0a4) },         /* ACT LABS Force RS */
+	{ USB_DEVICE(0x06f8, 0x0001) },		/* Guillemot Race Leader Force Feedback */
+	{ USB_DEVICE(0x06f8, 0x0004) },		/* Guillemot Force Feedback Racing Wheel */
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, iforce_usb_ids);
+
+struct usb_driver iforce_usb_driver = {
+	.owner =	THIS_MODULE,
+	.name =		"iforce",
+	.probe =	iforce_usb_probe,
+	.disconnect =	iforce_usb_disconnect,
+	.id_table =	iforce_usb_ids,
+};
diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h
new file mode 100644
index 0000000..bce247b
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce.h
@@ -0,0 +1,191 @@
+/*
+ * $Id: iforce.h,v 1.13 2002/07/07 10:22:50 jdeneux Exp $
+ *
+ *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/serio.h>
+#include <linux/config.h>
+#include <linux/circ_buf.h>
+#include <asm/semaphore.h>
+
+/* This module provides arbitrary resource management routines.
+ * I use it to manage the device's memory.
+ * Despite the name of this module, I am *not* going to access the ioports.
+ */
+#include <linux/ioport.h>
+
+#define IFORCE_MAX_LENGTH	16
+
+/* iforce::bus */
+#define IFORCE_232	1
+#define IFORCE_USB	2
+
+#define FALSE 0
+#define TRUE 1
+
+#define FF_EFFECTS_MAX	32
+
+/* Each force feedback effect is made of one core effect, which can be
+ * associated to at most to effect modifiers
+ */
+#define FF_MOD1_IS_USED		0
+#define FF_MOD2_IS_USED		1
+#define FF_CORE_IS_USED		2
+#define FF_CORE_IS_PLAYED	3	/* Effect is currently being played */
+#define FF_CORE_SHOULD_PLAY	4	/* User wants the effect to be played */
+#define FF_CORE_UPDATE		5	/* Effect is being updated */
+#define FF_MODCORE_MAX		5
+
+#define CHECK_OWNERSHIP(i, iforce)	\
+	((i) < FF_EFFECTS_MAX && i >= 0 && \
+	test_bit(FF_CORE_IS_USED, (iforce)->core_effects[(i)].flags) && \
+	(current->pid == 0 || \
+	(iforce)->core_effects[(i)].owner == current->pid))
+
+struct iforce_core_effect {
+	/* Information about where modifiers are stored in the device's memory */
+	struct resource mod1_chunk;
+	struct resource mod2_chunk;
+	unsigned long flags[NBITS(FF_MODCORE_MAX)];
+	pid_t owner;
+	/* Used to keep track of parameters of an effect. They are needed
+	 * to know what parts of an effect changed in an update operation.
+	 * We try to send only parameter packets if possible, as sending
+	 * effect parameter requires the effect to be stoped and restarted
+	 */
+	struct ff_effect effect;
+};
+
+#define FF_CMD_EFFECT		0x010e
+#define FF_CMD_ENVELOPE		0x0208
+#define FF_CMD_MAGNITUDE	0x0303
+#define FF_CMD_PERIOD		0x0407
+#define FF_CMD_CONDITION	0x050a
+
+#define FF_CMD_AUTOCENTER	0x4002
+#define FF_CMD_PLAY		0x4103
+#define FF_CMD_ENABLE		0x4201
+#define FF_CMD_GAIN		0x4301
+
+#define FF_CMD_QUERY		0xff01
+
+/* Buffer for async write */
+#define XMIT_SIZE		256
+#define XMIT_INC(var, n)	(var)+=n; (var)&= XMIT_SIZE -1
+/* iforce::xmit_flags */
+#define IFORCE_XMIT_RUNNING	0
+#define IFORCE_XMIT_AGAIN	1
+
+struct iforce_device {
+	u16 idvendor;
+	u16 idproduct;
+	char *name;
+	signed short *btn;
+	signed short *abs;
+	signed short *ff;
+};
+
+struct iforce {
+	struct input_dev dev;		/* Input device interface */
+	struct iforce_device *type;
+	int bus;
+
+	unsigned char data[IFORCE_MAX_LENGTH];
+	unsigned char edata[IFORCE_MAX_LENGTH];
+	u16 ecmd;
+	u16 expect_packet;
+
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+	struct serio *serio;		/* RS232 transfer */
+	int idx, pkt, len, id;
+	unsigned char csum;
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+	struct usb_device *usbdev;	/* USB transfer */
+	struct urb *irq, *out, *ctrl;
+	struct usb_ctrlrequest cr;
+#endif
+	spinlock_t xmit_lock;
+	/* Buffer used for asynchronous sending of bytes to the device */
+	struct circ_buf xmit;
+	unsigned char xmit_data[XMIT_SIZE];
+	long xmit_flags[1];
+
+					/* Force Feedback */
+	wait_queue_head_t wait;
+	struct resource device_memory;
+	struct iforce_core_effect core_effects[FF_EFFECTS_MAX];
+	struct semaphore mem_mutex;
+};
+
+/* Get hi and low bytes of a 16-bits int */
+#define HI(a)	((unsigned char)((a) >> 8))
+#define LO(a)	((unsigned char)((a) & 0xff))
+
+/* For many parameters, it seems that 0x80 is a special value that should
+ * be avoided. Instead, we replace this value by 0x7f
+ */
+#define HIFIX80(a) ((unsigned char)(((a)<0? (a)+255 : (a))>>8))
+
+/* Encode a time value */
+#define TIME_SCALE(a)	(a)
+
+
+/* Public functions */
+/* iforce-serio.c */
+void iforce_serial_xmit(struct iforce *iforce);
+
+/* iforce-usb.c */
+void iforce_usb_xmit(struct iforce *iforce);
+void iforce_usb_delete(struct iforce *iforce);
+
+/* iforce-main.c */
+int iforce_init_device(struct iforce *iforce);
+void iforce_delete_device(struct iforce *iforce);
+
+/* iforce-packets.c */
+int iforce_control_playback(struct iforce*, u16 id, unsigned int);
+void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data, struct pt_regs *regs);
+int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data);
+void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data) ;
+int iforce_get_id_packet(struct iforce *iforce, char *packet);
+
+/* iforce-ff.c */
+int iforce_upload_periodic(struct iforce*, struct ff_effect*, int is_update);
+int iforce_upload_constant(struct iforce*, struct ff_effect*, int is_update);
+int iforce_upload_condition(struct iforce*, struct ff_effect*, int is_update);
+
+/* Public variables */
+extern struct serio_driver iforce_serio_drv;
+extern struct usb_driver iforce_usb_driver;
diff --git a/drivers/input/joystick/interact.c b/drivers/input/joystick/interact.c
new file mode 100644
index 0000000..9d3f8c3
--- /dev/null
+++ b/drivers/input/joystick/interact.c
@@ -0,0 +1,322 @@
+/*
+ * $Id: interact.c,v 1.16 2002/01/22 20:28:25 vojtech Exp $
+ *
+ *  Copyright (c) 2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Toby Deshane
+ */
+
+/*
+ * InterAct digital gamepad/joystick 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+
+#define DRIVER_DESC	"InterAct digital joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define INTERACT_MAX_START	600	/* 400 us */
+#define INTERACT_MAX_STROBE	60	/* 40 us */
+#define INTERACT_MAX_LENGTH	32	/* 32 bits */
+
+#define INTERACT_TYPE_HHFX	0	/* HammerHead/FX */
+#define INTERACT_TYPE_PP8D	1	/* ProPad 8 */
+
+struct interact {
+	struct gameport *gameport;
+	struct input_dev dev;
+	int bads;
+	int reads;
+	unsigned char type;
+	unsigned char length;
+	char phys[32];
+};
+
+static short interact_abs_hhfx[] =
+	{ ABS_RX, ABS_RY, ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y, -1 };
+static short interact_abs_pp8d[] =
+	{ ABS_X, ABS_Y, -1 };
+
+static short interact_btn_hhfx[] =
+	{ BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL, BTN_TL2, BTN_TR2, BTN_MODE, BTN_SELECT, -1 };
+static short interact_btn_pp8d[] =
+	{ BTN_C, BTN_TL, BTN_TR, BTN_A, BTN_B, BTN_Y, BTN_Z, BTN_X, -1 };
+
+struct interact_type {
+	int id;
+	short *abs;
+	short *btn;
+	char *name;
+	unsigned char length;
+	unsigned char b8;
+};
+
+static struct interact_type interact_type[] = {
+	{ 0x6202, interact_abs_hhfx, interact_btn_hhfx, "InterAct HammerHead/FX",    32, 4 },
+	{ 0x53f8, interact_abs_pp8d, interact_btn_pp8d, "InterAct ProPad 8 Digital", 16, 0 },
+	{ 0 }};
+
+/*
+ * interact_read_packet() reads and InterAct joystick data.
+ */
+
+static int interact_read_packet(struct gameport *gameport, int length, u32 *data)
+{
+	unsigned long flags;
+	unsigned char u, v;
+	unsigned int t, s;
+	int i;
+
+	i = 0;
+	data[0] = data[1] = data[2] = 0;
+	t = gameport_time(gameport, INTERACT_MAX_START);
+	s = gameport_time(gameport, INTERACT_MAX_STROBE);
+
+	local_irq_save(flags);
+	gameport_trigger(gameport);
+	v = gameport_read(gameport);
+
+	while (t > 0 && i < length) {
+		t--;
+		u = v; v = gameport_read(gameport);
+		if (v & ~u & 0x40) {
+			data[0] = (data[0] << 1) | ((v >> 4) & 1);
+			data[1] = (data[1] << 1) | ((v >> 5) & 1);
+			data[2] = (data[2] << 1) | ((v >> 7) & 1);
+			i++;
+			t = s;
+		}
+	}
+
+	local_irq_restore(flags);
+
+	return i;
+}
+
+/*
+ * interact_poll() reads and analyzes InterAct joystick data.
+ */
+
+static void interact_poll(struct gameport *gameport)
+{
+	struct interact *interact = gameport_get_drvdata(gameport);
+	struct input_dev *dev = &interact->dev;
+	u32 data[3];
+	int i;
+
+	interact->reads++;
+
+	if (interact_read_packet(interact->gameport, interact->length, data) < interact->length) {
+		interact->bads++;
+	} else {
+
+		for (i = 0; i < 3; i++)
+			data[i] <<= INTERACT_MAX_LENGTH - interact->length;
+
+		switch (interact->type) {
+
+			case INTERACT_TYPE_HHFX:
+
+				for (i = 0; i < 4; i++)
+					input_report_abs(dev, interact_abs_hhfx[i], (data[i & 1] >> ((i >> 1) << 3)) & 0xff);
+
+				for (i = 0; i < 2; i++)
+					input_report_abs(dev, ABS_HAT0Y - i,
+						((data[1] >> ((i << 1) + 17)) & 1)  - ((data[1] >> ((i << 1) + 16)) & 1));
+
+				for (i = 0; i < 8; i++)
+					input_report_key(dev, interact_btn_hhfx[i], (data[0] >> (i + 16)) & 1);
+
+				for (i = 0; i < 4; i++)
+					input_report_key(dev, interact_btn_hhfx[i + 8], (data[1] >> (i + 20)) & 1);
+
+				break;
+
+			case INTERACT_TYPE_PP8D:
+
+				for (i = 0; i < 2; i++)
+					input_report_abs(dev, interact_abs_pp8d[i],
+						((data[0] >> ((i << 1) + 20)) & 1)  - ((data[0] >> ((i << 1) + 21)) & 1));
+
+				for (i = 0; i < 8; i++)
+					input_report_key(dev, interact_btn_pp8d[i], (data[1] >> (i + 16)) & 1);
+
+				break;
+		}
+	}
+
+	input_sync(dev);
+}
+
+/*
+ * interact_open() is a callback from the input open routine.
+ */
+
+static int interact_open(struct input_dev *dev)
+{
+	struct interact *interact = dev->private;
+
+	gameport_start_polling(interact->gameport);
+	return 0;
+}
+
+/*
+ * interact_close() is a callback from the input close routine.
+ */
+
+static void interact_close(struct input_dev *dev)
+{
+	struct interact *interact = dev->private;
+
+	gameport_stop_polling(interact->gameport);
+}
+
+/*
+ * interact_connect() probes for InterAct joysticks.
+ */
+
+static int interact_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct interact *interact;
+	__u32 data[3];
+	int i, t;
+	int err;
+
+	if (!(interact = kcalloc(1, sizeof(struct interact), GFP_KERNEL)))
+		return -ENOMEM;
+
+	interact->gameport = gameport;
+
+	gameport_set_drvdata(gameport, interact);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	i = interact_read_packet(gameport, INTERACT_MAX_LENGTH * 2, data);
+
+	if (i != 32 || (data[0] >> 24) != 0x0c || (data[1] >> 24) != 0x02) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	for (i = 0; interact_type[i].length; i++)
+		if (interact_type[i].id == (data[2] >> 16))
+			break;
+
+	if (!interact_type[i].length) {
+		printk(KERN_WARNING "interact.c: Unknown joystick on %s. [len %d d0 %08x d1 %08x i2 %08x]\n",
+			gameport->phys, i, data[0], data[1], data[2]);
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	gameport_set_poll_handler(gameport, interact_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	sprintf(interact->phys, "%s/input0", gameport->phys);
+
+	interact->type = i;
+	interact->length = interact_type[i].length;
+
+	interact->dev.private = interact;
+	interact->dev.open = interact_open;
+	interact->dev.close = interact_close;
+
+	interact->dev.name = interact_type[i].name;
+	interact->dev.phys = interact->phys;
+	interact->dev.id.bustype = BUS_GAMEPORT;
+	interact->dev.id.vendor = GAMEPORT_ID_VENDOR_INTERACT;
+	interact->dev.id.product = interact_type[i].id;
+	interact->dev.id.version = 0x0100;
+
+	interact->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+	for (i = 0; (t = interact_type[interact->type].abs[i]) >= 0; i++) {
+		set_bit(t, interact->dev.absbit);
+		if (i < interact_type[interact->type].b8) {
+			interact->dev.absmin[t] = 0;
+			interact->dev.absmax[t] = 255;
+		} else {
+			interact->dev.absmin[t] = -1;
+			interact->dev.absmax[t] = 1;
+		}
+	}
+
+	for (i = 0; (t = interact_type[interact->type].btn[i]) >= 0; i++)
+		set_bit(t, interact->dev.keybit);
+
+	input_register_device(&interact->dev);
+	printk(KERN_INFO "input: %s on %s\n",
+		interact_type[interact->type].name, gameport->phys);
+
+	return 0;
+
+fail2:	gameport_close(gameport);
+fail1:  gameport_set_drvdata(gameport, NULL);
+	kfree(interact);
+	return err;
+}
+
+static void interact_disconnect(struct gameport *gameport)
+{
+	struct interact *interact = gameport_get_drvdata(gameport);
+
+	input_unregister_device(&interact->dev);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(interact);
+}
+
+static struct gameport_driver interact_drv = {
+	.driver		= {
+		.name	= "interact",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= interact_connect,
+	.disconnect	= interact_disconnect,
+};
+
+static int __init interact_init(void)
+{
+	gameport_register_driver(&interact_drv);
+	return 0;
+}
+
+static void __exit interact_exit(void)
+{
+	gameport_unregister_driver(&interact_drv);
+}
+
+module_init(interact_init);
+module_exit(interact_exit);
diff --git a/drivers/input/joystick/joydump.c b/drivers/input/joystick/joydump.c
new file mode 100644
index 0000000..4234cca
--- /dev/null
+++ b/drivers/input/joystick/joydump.c
@@ -0,0 +1,175 @@
+/*
+ * $Id: joydump.c,v 1.1 2002/01/23 06:56:16 jsimmons Exp $
+ *
+ *  Copyright (c) 1996-2001 Vojtech Pavlik
+ */
+
+/*
+ * This is just a very simple driver that can dump the data
+ * out of the joystick port into the syslog ...
+ */
+
+/*
+ * 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/gameport.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC	"Gameport data dumper module"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define BUF_SIZE 256
+
+struct joydump {
+	unsigned int time;
+	unsigned char data;
+};
+
+static int joydump_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct joydump *buf;	/* all entries */
+	struct joydump *dump, *prev;	/* one entry each */
+	int axes[4], buttons;
+	int i, j, t, timeout;
+	unsigned long flags;
+	unsigned char u;
+
+	printk(KERN_INFO "joydump: ,------------------ START ----------------.\n");
+	printk(KERN_INFO "joydump: | Dumping: %30s |\n", gameport->phys);
+	printk(KERN_INFO "joydump: | Speed: %28d kHz |\n", gameport->speed);
+
+	if (gameport_open(gameport, drv, GAMEPORT_MODE_RAW)) {
+
+		printk(KERN_INFO "joydump: | Raw mode not available - trying cooked.    |\n");
+
+		if (gameport_open(gameport, drv, GAMEPORT_MODE_COOKED)) {
+
+			printk(KERN_INFO "joydump: | Cooked not available either. Failing.   |\n");
+			printk(KERN_INFO "joydump: `------------------- END -----------------'\n");
+			return -ENODEV;
+		}
+
+		gameport_cooked_read(gameport, axes, &buttons);
+
+		for (i = 0; i < 4; i++)
+			printk(KERN_INFO "joydump: | Axis %d: %4d.                           |\n", i, axes[i]);
+		printk(KERN_INFO "joydump: | Buttons %02x.                             |\n", buttons);
+		printk(KERN_INFO "joydump: `------------------- END -----------------'\n");
+	}
+
+	timeout = gameport_time(gameport, 10000); /* 10 ms */
+
+	buf = kmalloc(BUF_SIZE * sizeof(struct joydump), GFP_KERNEL);
+	if (!buf) {
+		printk(KERN_INFO "joydump: no memory for testing\n");
+		goto jd_end;
+	}
+	dump = buf;
+	t = 0;
+	i = 1;
+
+	local_irq_save(flags);
+
+	u = gameport_read(gameport);
+
+	dump->data = u;
+	dump->time = t;
+	dump++;
+
+	gameport_trigger(gameport);
+
+	while (i < BUF_SIZE && t < timeout) {
+
+		dump->data = gameport_read(gameport);
+
+		if (dump->data ^ u) {
+			u = dump->data;
+			dump->time = t;
+			i++;
+			dump++;
+		}
+		t++;
+	}
+
+	local_irq_restore(flags);
+
+/*
+ * Dump data.
+ */
+
+	t = i;
+	dump = buf;
+	prev = dump;
+
+	printk(KERN_INFO "joydump: >------------------ DATA -----------------<\n");
+	printk(KERN_INFO "joydump: | index: %3d delta: %3d us data: ", 0, 0);
+	for (j = 7; j >= 0; j--)
+		printk("%d", (dump->data >> j) & 1);
+	printk(" |\n");
+	dump++;
+
+	for (i = 1; i < t; i++, dump++, prev++) {
+		printk(KERN_INFO "joydump: | index: %3d delta: %3d us data: ",
+			i, dump->time - prev->time);
+		for (j = 7; j >= 0; j--)
+			printk("%d", (dump->data >> j) & 1);
+		printk(" |\n");
+	}
+	kfree(buf);
+
+jd_end:
+	printk(KERN_INFO "joydump: `------------------- END -----------------'\n");
+
+	return 0;
+}
+
+static void joydump_disconnect(struct gameport *gameport)
+{
+	gameport_close(gameport);
+}
+
+static struct gameport_driver joydump_drv = {
+	.driver		= {
+		.name	= "joydump",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= joydump_connect,
+	.disconnect	= joydump_disconnect,
+};
+
+static int __init joydump_init(void)
+{
+	gameport_register_driver(&joydump_drv);
+	return 0;
+}
+
+static void __exit joydump_exit(void)
+{
+	gameport_unregister_driver(&joydump_drv);
+}
+
+module_init(joydump_init);
+module_exit(joydump_exit);
diff --git a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c
new file mode 100644
index 0000000..1ba5036
--- /dev/null
+++ b/drivers/input/joystick/magellan.c
@@ -0,0 +1,247 @@
+/*
+ * $Id: magellan.c,v 1.16 2002/01/22 20:28:39 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Magellan and Space Mouse 6dof controller 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/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC	"Magellan and SpaceMouse 6dof controller driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define	MAGELLAN_MAX_LENGTH	32
+
+static int magellan_buttons[] = { BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 };
+static int magellan_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ };
+static char *magellan_name = "LogiCad3D Magellan / SpaceMouse";
+
+/*
+ * Per-Magellan data.
+ */
+
+struct magellan {
+	struct input_dev dev;
+	int idx;
+	unsigned char data[MAGELLAN_MAX_LENGTH];
+	char phys[32];
+};
+
+/*
+ * magellan_crunch_nibbles() verifies that the bytes sent from the Magellan
+ * have correct upper nibbles for the lower ones, if not, the packet will
+ * be thrown away. It also strips these upper halves to simplify further
+ * processing.
+ */
+
+static int magellan_crunch_nibbles(unsigned char *data, int count)
+{
+	static unsigned char nibbles[16] = "0AB3D56GH9:K<MN?";
+
+	do {
+		if (data[count] == nibbles[data[count] & 0xf])
+			data[count] = data[count] & 0xf;
+		else
+			return -1;
+	} while (--count);
+
+	return 0;
+}
+
+static void magellan_process_packet(struct magellan* magellan, struct pt_regs *regs)
+{
+	struct input_dev *dev = &magellan->dev;
+	unsigned char *data = magellan->data;
+	int i, t;
+
+	if (!magellan->idx) return;
+
+	input_regs(dev, regs);
+
+	switch (magellan->data[0]) {
+
+		case 'd':				/* Axis data */
+			if (magellan->idx != 25) return;
+			if (magellan_crunch_nibbles(data, 24)) return;
+			for (i = 0; i < 6; i++)
+				input_report_abs(dev, magellan_axes[i],
+					(data[(i << 2) + 1] << 12 | data[(i << 2) + 2] << 8 |
+					 data[(i << 2) + 3] <<  4 | data[(i << 2) + 4]) - 32768);
+			break;
+
+		case 'k':				/* Button data */
+			if (magellan->idx != 4) return;
+			if (magellan_crunch_nibbles(data, 3)) return;
+			t = (data[1] << 1) | (data[2] << 5) | data[3];
+			for (i = 0; i < 9; i++) input_report_key(dev, magellan_buttons[i], (t >> i) & 1);
+			break;
+	}
+
+	input_sync(dev);
+}
+
+static irqreturn_t magellan_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+	struct magellan* magellan = serio_get_drvdata(serio);
+
+	if (data == '\r') {
+		magellan_process_packet(magellan, regs);
+		magellan->idx = 0;
+	} else {
+		if (magellan->idx < MAGELLAN_MAX_LENGTH)
+			magellan->data[magellan->idx++] = data;
+	}
+	return IRQ_HANDLED;
+}
+
+/*
+ * magellan_disconnect() is the opposite of magellan_connect()
+ */
+
+static void magellan_disconnect(struct serio *serio)
+{
+	struct magellan* magellan = serio_get_drvdata(serio);
+
+	input_unregister_device(&magellan->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(magellan);
+}
+
+/*
+ * magellan_connect() is the routine that is called when someone adds a
+ * new serio device that supports Magellan protocol and registers it as
+ * an input device.
+ */
+
+static int magellan_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct magellan *magellan;
+	int i, t;
+	int err;
+
+	if (!(magellan = kmalloc(sizeof(struct magellan), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(magellan, 0, sizeof(struct magellan));
+
+	magellan->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+	for (i = 0; i < 9; i++)
+		set_bit(magellan_buttons[i], magellan->dev.keybit);
+
+	for (i = 0; i < 6; i++) {
+		t = magellan_axes[i];
+		set_bit(t, magellan->dev.absbit);
+		magellan->dev.absmin[t] = -360;
+		magellan->dev.absmax[t] =  360;
+	}
+
+	sprintf(magellan->phys, "%s/input0", serio->phys);
+
+	init_input_dev(&magellan->dev);
+	magellan->dev.private = magellan;
+	magellan->dev.name = magellan_name;
+	magellan->dev.phys = magellan->phys;
+	magellan->dev.id.bustype = BUS_RS232;
+	magellan->dev.id.vendor = SERIO_MAGELLAN;
+	magellan->dev.id.product = 0x0001;
+	magellan->dev.id.version = 0x0100;
+	magellan->dev.dev = &serio->dev;
+
+	serio_set_drvdata(serio, magellan);
+
+	err = serio_open(serio, drv);
+	if (err) {
+		serio_set_drvdata(serio, NULL);
+		kfree(magellan);
+		return err;
+	}
+
+	input_register_device(&magellan->dev);
+
+	printk(KERN_INFO "input: %s on %s\n", magellan_name, serio->phys);
+
+	return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id magellan_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MAGELLAN,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, magellan_serio_ids);
+
+static struct serio_driver magellan_drv = {
+	.driver		= {
+		.name	= "magellan",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= magellan_serio_ids,
+	.interrupt	= magellan_interrupt,
+	.connect	= magellan_connect,
+	.disconnect	= magellan_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init magellan_init(void)
+{
+	serio_register_driver(&magellan_drv);
+	return 0;
+}
+
+static void __exit magellan_exit(void)
+{
+	serio_unregister_driver(&magellan_drv);
+}
+
+module_init(magellan_init);
+module_exit(magellan_exit);
diff --git a/drivers/input/joystick/sidewinder.c b/drivers/input/joystick/sidewinder.c
new file mode 100644
index 0000000..47144a7
--- /dev/null
+++ b/drivers/input/joystick/sidewinder.c
@@ -0,0 +1,807 @@
+/*
+ *  Copyright (c) 1998-2005 Vojtech Pavlik
+ */
+
+/*
+ * Microsoft SideWinder joystick family 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/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/gameport.h>
+
+#define DRIVER_DESC	"Microsoft SideWinder joystick family driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * These are really magic values. Changing them can make a problem go away,
+ * as well as break everything.
+ */
+
+#undef SW_DEBUG
+#undef SW_DEBUG_DATA
+
+#define SW_START	600	/* The time we wait for the first bit [600 us] */
+#define SW_STROBE	60	/* Max time per bit [60 us] */
+#define SW_TIMEOUT	6	/* Wait for everything to settle [6 ms] */
+#define SW_KICK		45	/* Wait after A0 fall till kick [45 us] */
+#define SW_END		8	/* Number of bits before end of packet to kick */
+#define SW_FAIL		16	/* Number of packet read errors to fail and reinitialize */
+#define SW_BAD		2	/* Number of packet read errors to switch off 3d Pro optimization */
+#define SW_OK		64	/* Number of packet read successes to switch optimization back on */
+#define SW_LENGTH	512	/* Max number of bits in a packet */
+
+#ifdef SW_DEBUG
+#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+
+/*
+ * SideWinder joystick types ...
+ */
+
+#define SW_ID_3DP	0
+#define SW_ID_GP	1
+#define SW_ID_PP	2
+#define SW_ID_FFP	3
+#define SW_ID_FSP	4
+#define SW_ID_FFW	5
+
+/*
+ * Names, buttons, axes ...
+ */
+
+static char *sw_name[] = {	"3D Pro", "GamePad", "Precision Pro", "Force Feedback Pro", "FreeStyle Pro",
+				"Force Feedback Wheel" };
+
+static char sw_abs[][7] = {
+	{ ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
+	{ ABS_X, ABS_Y },
+	{ ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
+	{ ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
+	{ ABS_X, ABS_Y,         ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y },
+	{ ABS_RX, ABS_RUDDER,   ABS_THROTTLE }};
+
+static char sw_bit[][7] = {
+	{ 10, 10,  9, 10,  1,  1 },
+	{  1,  1                 },
+	{ 10, 10,  6,  7,  1,  1 },
+	{ 10, 10,  6,  7,  1,  1 },
+	{ 10, 10,  6,  1,  1     },
+	{ 10,  7,  7,  1,  1     }};
+
+static short sw_btn[][12] = {
+	{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_MODE },
+	{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE },
+	{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT },
+	{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT },
+	{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT },
+	{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4 }};
+
+static struct {
+	int x;
+	int y;
+} sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+struct sw {
+	struct gameport *gameport;
+	struct input_dev dev[4];
+	char name[64];
+	char phys[4][32];
+	int length;
+	int type;
+	int bits;
+	int number;
+	int fail;
+	int ok;
+	int reads;
+	int bads;
+};
+
+/*
+ * sw_read_packet() is a function which reads either a data packet, or an
+ * identification packet from a SideWinder joystick. The protocol is very,
+ * very, very braindamaged. Microsoft patented it in US patent #5628686.
+ */
+
+static int sw_read_packet(struct gameport *gameport, unsigned char *buf, int length, int id)
+{
+	unsigned long flags;
+	int timeout, bitout, sched, i, kick, start, strobe;
+	unsigned char pending, u, v;
+
+	i = -id;						/* Don't care about data, only want ID */
+	timeout = id ? gameport_time(gameport, SW_TIMEOUT * 1000) : 0; /* Set up global timeout for ID packet */
+	kick = id ? gameport_time(gameport, SW_KICK) : 0;	/* Set up kick timeout for ID packet */
+	start = gameport_time(gameport, SW_START);
+	strobe = gameport_time(gameport, SW_STROBE);
+	bitout = start;
+	pending = 0;
+	sched = 0;
+
+        local_irq_save(flags);					/* Quiet, please */
+
+	gameport_trigger(gameport);				/* Trigger */
+	v = gameport_read(gameport);
+
+	do {
+		bitout--;
+		u = v;
+		v = gameport_read(gameport);
+	} while (!(~v & u & 0x10) && (bitout > 0));		/* Wait for first falling edge on clock */
+
+	if (bitout > 0)
+		bitout = strobe;				/* Extend time if not timed out */
+
+	while ((timeout > 0 || bitout > 0) && (i < length)) {
+
+		timeout--;
+		bitout--;					/* Decrement timers */
+		sched--;
+
+		u = v;
+		v = gameport_read(gameport);
+
+		if ((~u & v & 0x10) && (bitout > 0)) {		/* Rising edge on clock - data bit */
+			if (i >= 0)				/* Want this data */
+				buf[i] = v >> 5;		/* Store it */
+			i++;					/* Advance index */
+			bitout = strobe;			/* Extend timeout for next bit */
+		}
+
+		if (kick && (~v & u & 0x01)) {			/* Falling edge on axis 0 */
+			sched = kick;				/* Schedule second trigger */
+			kick = 0;				/* Don't schedule next time on falling edge */
+			pending = 1;				/* Mark schedule */
+		}
+
+		if (pending && sched < 0 && (i > -SW_END)) {	/* Second trigger time */
+			gameport_trigger(gameport);		/* Trigger */
+			bitout = start;				/* Long bit timeout */
+			pending = 0;				/* Unmark schedule */
+			timeout = 0;				/* Switch from global to bit timeouts */
+		}
+	}
+
+	local_irq_restore(flags);					/* Done - relax */
+
+#ifdef SW_DEBUG_DATA
+	{
+		int j;
+		printk(KERN_DEBUG "sidewinder.c: Read %d triplets. [", i);
+		for (j = 0; j < i; j++) printk("%d", buf[j]);
+		printk("]\n");
+	}
+#endif
+
+	return i;
+}
+
+/*
+ * sw_get_bits() and GB() compose bits from the triplet buffer into a __u64.
+ * Parameter 'pos' is bit number inside packet where to start at, 'num' is number
+ * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits
+ * is number of bits per triplet.
+ */
+
+#define GB(pos,num) sw_get_bits(buf, pos, num, sw->bits)
+
+static __u64 sw_get_bits(unsigned char *buf, int pos, int num, char bits)
+{
+	__u64 data = 0;
+	int tri = pos % bits;						/* Start position */
+	int i   = pos / bits;
+	int bit = 0;
+
+	while (num--) {
+		data |= (__u64)((buf[i] >> tri++) & 1) << bit++;	/* Transfer bit */
+		if (tri == bits) {
+			i++;						/* Next triplet */
+			tri = 0;
+		}
+	}
+
+	return data;
+}
+
+/*
+ * sw_init_digital() initializes a SideWinder 3D Pro joystick
+ * into digital mode.
+ */
+
+static void sw_init_digital(struct gameport *gameport)
+{
+	int seq[] = { 140, 140+725, 140+300, 0 };
+	unsigned long flags;
+	int i, t;
+
+        local_irq_save(flags);
+
+	i = 0;
+        do {
+                gameport_trigger(gameport);			/* Trigger */
+		t = gameport_time(gameport, SW_TIMEOUT * 1000);
+		while ((gameport_read(gameport) & 1) && t) t--;	/* Wait for axis to fall back to 0 */
+                udelay(seq[i]);					/* Delay magic time */
+        } while (seq[++i]);
+
+	gameport_trigger(gameport);				/* Last trigger */
+
+	local_irq_restore(flags);
+}
+
+/*
+ * sw_parity() computes parity of __u64
+ */
+
+static int sw_parity(__u64 t)
+{
+	int x = t ^ (t >> 32);
+
+	x ^= x >> 16;
+	x ^= x >> 8;
+	x ^= x >> 4;
+	x ^= x >> 2;
+	x ^= x >> 1;
+	return x & 1;
+}
+
+/*
+ * sw_ccheck() checks synchronization bits and computes checksum of nibbles.
+ */
+
+static int sw_check(__u64 t)
+{
+	unsigned char sum = 0;
+
+	if ((t & 0x8080808080808080ULL) ^ 0x80)			/* Sync */
+		return -1;
+
+	while (t) {						/* Sum */
+		sum += t & 0xf;
+		t >>= 4;
+	}
+
+	return sum & 0xf;
+}
+
+/*
+ * sw_parse() analyzes SideWinder joystick data, and writes the results into
+ * the axes and buttons arrays.
+ */
+
+static int sw_parse(unsigned char *buf, struct sw *sw)
+{
+	int hat, i, j;
+	struct input_dev *dev = sw->dev;
+
+	switch (sw->type) {
+
+		case SW_ID_3DP:
+
+			if (sw_check(GB(0,64)) || (hat = (GB(6,1) << 3) | GB(60,3)) > 8)
+				return -1;
+
+			input_report_abs(dev, ABS_X,        (GB( 3,3) << 7) | GB(16,7));
+			input_report_abs(dev, ABS_Y,        (GB( 0,3) << 7) | GB(24,7));
+			input_report_abs(dev, ABS_RZ,       (GB(35,2) << 7) | GB(40,7));
+			input_report_abs(dev, ABS_THROTTLE, (GB(32,3) << 7) | GB(48,7));
+
+			input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x);
+			input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y);
+
+			for (j = 0; j < 7; j++)
+				input_report_key(dev, sw_btn[SW_ID_3DP][j], !GB(j+8,1));
+
+			input_report_key(dev, BTN_BASE4, !GB(38,1));
+			input_report_key(dev, BTN_BASE5, !GB(37,1));
+
+			input_sync(dev);
+
+			return 0;
+
+		case SW_ID_GP:
+
+			for (i = 0; i < sw->number; i ++) {
+
+				if (sw_parity(GB(i*15,15)))
+					return -1;
+
+				input_report_abs(dev + i, ABS_X, GB(i*15+3,1) - GB(i*15+2,1));
+				input_report_abs(dev + i, ABS_Y, GB(i*15+0,1) - GB(i*15+1,1));
+
+				for (j = 0; j < 10; j++)
+					input_report_key(dev + i, sw_btn[SW_ID_GP][j], !GB(i*15+j+4,1));
+
+				input_sync(dev + i);
+			}
+
+			return 0;
+
+		case SW_ID_PP:
+		case SW_ID_FFP:
+
+			if (!sw_parity(GB(0,48)) || (hat = GB(42,4)) > 8)
+				return -1;
+
+			input_report_abs(dev, ABS_X,        GB( 9,10));
+			input_report_abs(dev, ABS_Y,        GB(19,10));
+			input_report_abs(dev, ABS_RZ,       GB(36, 6));
+			input_report_abs(dev, ABS_THROTTLE, GB(29, 7));
+
+			input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x);
+			input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y);
+
+			for (j = 0; j < 9; j++)
+				input_report_key(dev, sw_btn[SW_ID_PP][j], !GB(j,1));
+
+			input_sync(dev);
+
+			return 0;
+
+		case SW_ID_FSP:
+
+			if (!sw_parity(GB(0,43)) || (hat = GB(28,4)) > 8)
+				return -1;
+
+			input_report_abs(dev, ABS_X,        GB( 0,10));
+			input_report_abs(dev, ABS_Y,        GB(16,10));
+			input_report_abs(dev, ABS_THROTTLE, GB(32, 6));
+
+			input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x);
+			input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y);
+
+			for (j = 0; j < 6; j++)
+				input_report_key(dev, sw_btn[SW_ID_FSP][j], !GB(j+10,1));
+
+			input_report_key(dev, BTN_TR,     !GB(26,1));
+			input_report_key(dev, BTN_START,  !GB(27,1));
+			input_report_key(dev, BTN_MODE,   !GB(38,1));
+			input_report_key(dev, BTN_SELECT, !GB(39,1));
+
+			input_sync(dev);
+
+			return 0;
+
+		case SW_ID_FFW:
+
+			if (!sw_parity(GB(0,33)))
+				return -1;
+
+			input_report_abs(dev, ABS_RX,       GB( 0,10));
+			input_report_abs(dev, ABS_RUDDER,   GB(10, 6));
+			input_report_abs(dev, ABS_THROTTLE, GB(16, 6));
+
+			for (j = 0; j < 8; j++)
+				input_report_key(dev, sw_btn[SW_ID_FFW][j], !GB(j+22,1));
+
+			input_sync(dev);
+
+			return 0;
+	}
+
+	return -1;
+}
+
+/*
+ * sw_read() reads SideWinder joystick data, and reinitializes
+ * the joystick in case of persistent problems. This is the function that is
+ * called from the generic code to poll the joystick.
+ */
+
+static int sw_read(struct sw *sw)
+{
+	unsigned char buf[SW_LENGTH];
+	int i;
+
+	i = sw_read_packet(sw->gameport, buf, sw->length, 0);
+
+	if (sw->type == SW_ID_3DP && sw->length == 66 && i != 66) {		/* Broken packet, try to fix */
+
+		if (i == 64 && !sw_check(sw_get_bits(buf,0,64,1))) {		/* Last init failed, 1 bit mode */
+			printk(KERN_WARNING "sidewinder.c: Joystick in wrong mode on %s"
+				" - going to reinitialize.\n", sw->gameport->phys);
+			sw->fail = SW_FAIL;					/* Reinitialize */
+			i = 128;						/* Bogus value */
+		}
+
+		if (i < 66 && GB(0,64) == GB(i*3-66,64))			/* 1 == 3 */
+			i = 66;							/* Everything is fine */
+
+		if (i < 66 && GB(0,64) == GB(66,64))				/* 1 == 2 */
+			i = 66;							/* Everything is fine */
+
+		if (i < 66 && GB(i*3-132,64) == GB(i*3-66,64)) {		/* 2 == 3 */
+			memmove(buf, buf + i - 22, 22);				/* Move data */
+			i = 66;							/* Carry on */
+		}
+	}
+
+	if (i == sw->length && !sw_parse(buf, sw)) {				/* Parse data */
+
+		sw->fail = 0;
+		sw->ok++;
+
+		if (sw->type == SW_ID_3DP && sw->length == 66			/* Many packets OK */
+			&& sw->ok > SW_OK) {
+
+			printk(KERN_INFO "sidewinder.c: No more trouble on %s"
+				" - enabling optimization again.\n", sw->gameport->phys);
+			sw->length = 22;
+		}
+
+		return 0;
+	}
+
+	sw->ok = 0;
+	sw->fail++;
+
+	if (sw->type == SW_ID_3DP && sw->length == 22 && sw->fail > SW_BAD) {	/* Consecutive bad packets */
+
+		printk(KERN_INFO "sidewinder.c: Many bit errors on %s"
+			" - disabling optimization.\n", sw->gameport->phys);
+		sw->length = 66;
+	}
+
+	if (sw->fail < SW_FAIL)
+		return -1;							/* Not enough, don't reinitialize yet */
+
+	printk(KERN_WARNING "sidewinder.c: Too many bit errors on %s"
+		" - reinitializing joystick.\n", sw->gameport->phys);
+
+	if (!i && sw->type == SW_ID_3DP) {					/* 3D Pro can be in analog mode */
+		mdelay(3 * SW_TIMEOUT);
+		sw_init_digital(sw->gameport);
+	}
+
+	mdelay(SW_TIMEOUT);
+	i = sw_read_packet(sw->gameport, buf, SW_LENGTH, 0);			/* Read normal data packet */
+	mdelay(SW_TIMEOUT);
+	sw_read_packet(sw->gameport, buf, SW_LENGTH, i);			/* Read ID packet, this initializes the stick */
+
+	sw->fail = SW_FAIL;
+
+	return -1;
+}
+
+static void sw_poll(struct gameport *gameport)
+{
+	struct sw *sw = gameport_get_drvdata(gameport);
+
+	sw->reads++;
+	if (sw_read(sw))
+		sw->bads++;
+}
+
+static int sw_open(struct input_dev *dev)
+{
+	struct sw *sw = dev->private;
+
+	gameport_start_polling(sw->gameport);
+	return 0;
+}
+
+static void sw_close(struct input_dev *dev)
+{
+	struct sw *sw = dev->private;
+
+	gameport_stop_polling(sw->gameport);
+}
+
+/*
+ * sw_print_packet() prints the contents of a SideWinder packet.
+ */
+
+static void sw_print_packet(char *name, int length, unsigned char *buf, char bits)
+{
+	int i;
+
+	printk(KERN_INFO "sidewinder.c: %s packet, %d bits. [", name, length);
+	for (i = (((length + 3) >> 2) - 1); i >= 0; i--)
+		printk("%x", (int)sw_get_bits(buf, i << 2, 4, bits));
+	printk("]\n");
+}
+
+/*
+ * sw_3dp_id() translates the 3DP id into a human legible string.
+ * Unfortunately I don't know how to do this for the other SW types.
+ */
+
+static void sw_3dp_id(unsigned char *buf, char *comment)
+{
+	int i;
+	char pnp[8], rev[9];
+
+	for (i = 0; i < 7; i++)						/* ASCII PnP ID */
+		pnp[i] = sw_get_bits(buf, 24+8*i, 8, 1);
+
+	for (i = 0; i < 8; i++)						/* ASCII firmware revision */
+		rev[i] = sw_get_bits(buf, 88+8*i, 8, 1);
+
+	pnp[7] = rev[8] = 0;
+
+	sprintf(comment, " [PnP %d.%02d id %s rev %s]",
+		(int) ((sw_get_bits(buf, 8, 6, 1) << 6) |		/* Two 6-bit values */
+			sw_get_bits(buf, 16, 6, 1)) / 100,
+		(int) ((sw_get_bits(buf, 8, 6, 1) << 6) |
+			sw_get_bits(buf, 16, 6, 1)) % 100,
+		 pnp, rev);
+}
+
+/*
+ * sw_guess_mode() checks the upper two button bits for toggling -
+ * indication of that the joystick is in 3-bit mode. This is documented
+ * behavior for 3DP ID packet, and for example the FSP does this in
+ * normal packets instead. Fun ...
+ */
+
+static int sw_guess_mode(unsigned char *buf, int len)
+{
+	int i;
+	unsigned char xor = 0;
+
+	for (i = 1; i < len; i++)
+		xor |= (buf[i - 1] ^ buf[i]) & 6;
+
+	return !!xor * 2 + 1;
+}
+
+/*
+ * sw_connect() probes for SideWinder type joysticks.
+ */
+
+static int sw_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	struct sw *sw;
+	int i, j, k, l;
+	int err;
+	unsigned char *buf = NULL;	/* [SW_LENGTH] */
+	unsigned char *idbuf = NULL;	/* [SW_LENGTH] */
+	unsigned char m = 1;
+	char comment[40];
+
+	comment[0] = 0;
+
+	sw = kcalloc(1, sizeof(struct sw), GFP_KERNEL);
+	buf = kmalloc(SW_LENGTH, GFP_KERNEL);
+	idbuf = kmalloc(SW_LENGTH, GFP_KERNEL);
+	if (!sw || !buf || !idbuf) {
+		err = -ENOMEM;
+		goto fail1;
+	}
+
+	sw->gameport = gameport;
+
+	gameport_set_drvdata(gameport, sw);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	dbg("Init 0: Opened %s, io %#x, speed %d",
+		gameport->phys, gameport->io, gameport->speed);
+
+	i = sw_read_packet(gameport, buf, SW_LENGTH, 0);		/* Read normal packet */
+	msleep(SW_TIMEOUT);
+	dbg("Init 1: Mode %d. Length %d.", m , i);
+
+	if (!i) {							/* No data. 3d Pro analog mode? */
+		sw_init_digital(gameport);				/* Switch to digital */
+		msleep(SW_TIMEOUT);
+		i = sw_read_packet(gameport, buf, SW_LENGTH, 0);	/* Retry reading packet */
+		msleep(SW_TIMEOUT);
+		dbg("Init 1b: Length %d.", i);
+		if (!i) {						/* No data -> FAIL */
+			err = -ENODEV;
+			goto fail2;
+		}
+	}
+
+	j = sw_read_packet(gameport, idbuf, SW_LENGTH, i);		/* Read ID. This initializes the stick */
+	m |= sw_guess_mode(idbuf, j);					/* ID packet should carry mode info [3DP] */
+	dbg("Init 2: Mode %d. ID Length %d.", m, j);
+
+	if (j <= 0) {							/* Read ID failed. Happens in 1-bit mode on PP */
+		msleep(SW_TIMEOUT);
+		i = sw_read_packet(gameport, buf, SW_LENGTH, 0);	/* Retry reading packet */
+		m |= sw_guess_mode(buf, i);
+		dbg("Init 2b: Mode %d. Length %d.", m, i);
+		if (!i) {
+			err = -ENODEV;
+			goto fail2;
+		}
+		msleep(SW_TIMEOUT);
+		j = sw_read_packet(gameport, idbuf, SW_LENGTH, i);	/* Retry reading ID */
+		dbg("Init 2c: ID Length %d.", j);
+	}
+
+	sw->type = -1;
+	k = SW_FAIL;							/* Try SW_FAIL times */
+	l = 0;
+
+	do {
+		k--;
+		msleep(SW_TIMEOUT);
+		i = sw_read_packet(gameport, buf, SW_LENGTH, 0);	/* Read data packet */
+		dbg("Init 3: Mode %d. Length %d. Last %d. Tries %d.", m, i, l, k);
+
+		if (i > l) {						/* Longer? As we can only lose bits, it makes */
+									/* no sense to try detection for a packet shorter */
+			l = i;						/* than the previous one */
+
+			sw->number = 1;
+			sw->gameport = gameport;
+			sw->length = i;
+			sw->bits = m;
+
+			dbg("Init 3a: Case %d.\n", i * m);
+
+			switch (i * m) {
+				case 60:
+					sw->number++;
+				case 45:				/* Ambiguous packet length */
+					if (j <= 40) {			/* ID length less or eq 40 -> FSP */
+				case 43:
+						sw->type = SW_ID_FSP;
+						break;
+					}
+					sw->number++;
+				case 30:
+					sw->number++;
+				case 15:
+					sw->type = SW_ID_GP;
+					break;
+				case 33:
+				case 31:
+					sw->type = SW_ID_FFW;
+					break;
+				case 48:				/* Ambiguous */
+					if (j == 14) {			/* ID length 14*3 -> FFP */
+						sw->type = SW_ID_FFP;
+						sprintf(comment, " [AC %s]", sw_get_bits(idbuf,38,1,3) ? "off" : "on");
+					} else
+					sw->type = SW_ID_PP;
+					break;
+				case 66:
+					sw->bits = 3;
+				case 198:
+					sw->length = 22;
+				case 64:
+					sw->type = SW_ID_3DP;
+					if (j == 160) sw_3dp_id(idbuf, comment);
+					break;
+			}
+		}
+
+	} while (k && sw->type == -1);
+
+	if (sw->type == -1) {
+		printk(KERN_WARNING "sidewinder.c: unknown joystick device detected "
+			"on %s, contact <vojtech@ucw.cz>\n", gameport->phys);
+		sw_print_packet("ID", j * 3, idbuf, 3);
+		sw_print_packet("Data", i * m, buf, m);
+		err = -ENODEV;
+		goto fail2;
+	}
+
+#ifdef SW_DEBUG
+	sw_print_packet("ID", j * 3, idbuf, 3);
+	sw_print_packet("Data", i * m, buf, m);
+#endif
+
+	gameport_set_poll_handler(gameport, sw_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	k = i;
+	l = j;
+
+	for (i = 0; i < sw->number; i++) {
+		int bits, code;
+
+		sprintf(sw->name, "Microsoft SideWinder %s", sw_name[sw->type]);
+		sprintf(sw->phys[i], "%s/input%d", gameport->phys, i);
+
+		sw->dev[i].private = sw;
+
+		sw->dev[i].open = sw_open;
+		sw->dev[i].close = sw_close;
+
+		sw->dev[i].name = sw->name;
+		sw->dev[i].phys = sw->phys[i];
+		sw->dev[i].id.bustype = BUS_GAMEPORT;
+		sw->dev[i].id.vendor = GAMEPORT_ID_VENDOR_MICROSOFT;
+		sw->dev[i].id.product = sw->type;
+		sw->dev[i].id.version = 0x0100;
+
+		sw->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+		for (j = 0; (bits = sw_bit[sw->type][j]); j++) {
+			code = sw_abs[sw->type][j];
+			set_bit(code, sw->dev[i].absbit);
+			sw->dev[i].absmax[code] = (1 << bits) - 1;
+			sw->dev[i].absmin[code] = (bits == 1) ? -1 : 0;
+			sw->dev[i].absfuzz[code] = ((bits >> 1) >= 2) ? (1 << ((bits >> 1) - 2)) : 0;
+			if (code != ABS_THROTTLE)
+				sw->dev[i].absflat[code] = (bits >= 5) ? (1 << (bits - 5)) : 0;
+		}
+
+		for (j = 0; (code = sw_btn[sw->type][j]); j++)
+			set_bit(code, sw->dev[i].keybit);
+
+		input_register_device(sw->dev + i);
+		printk(KERN_INFO "input: %s%s on %s [%d-bit id %d data %d]\n",
+			sw->name, comment, gameport->phys, m, l, k);
+	}
+
+	return 0;
+
+fail2:	gameport_close(gameport);
+fail1:	gameport_set_drvdata(gameport, NULL);
+	kfree(sw);
+	kfree(buf);
+	kfree(idbuf);
+	return err;
+}
+
+static void sw_disconnect(struct gameport *gameport)
+{
+	struct sw *sw = gameport_get_drvdata(gameport);
+	int i;
+
+	for (i = 0; i < sw->number; i++)
+		input_unregister_device(sw->dev + i);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(sw);
+}
+
+static struct gameport_driver sw_drv = {
+	.driver		= {
+		.name	= "sidewinder",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= sw_connect,
+	.disconnect	= sw_disconnect,
+};
+
+static int __init sw_init(void)
+{
+	gameport_register_driver(&sw_drv);
+	return 0;
+}
+
+static void __exit sw_exit(void)
+{
+	gameport_unregister_driver(&sw_drv);
+}
+
+module_init(sw_init);
+module_exit(sw_exit);
diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c
new file mode 100644
index 0000000..ec0a2a6
--- /dev/null
+++ b/drivers/input/joystick/spaceball.c
@@ -0,0 +1,319 @@
+/*
+ * $Id: spaceball.c,v 1.17 2002/01/22 20:29:03 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *  	David Thompson
+ *  	Joseph Krahn
+ */
+
+/*
+ * SpaceTec SpaceBall 2003/3003/4000 FLX 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"SpaceTec SpaceBall 2003/3003/4000 FLX driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define SPACEBALL_MAX_LENGTH	128
+#define SPACEBALL_MAX_ID	8
+
+#define SPACEBALL_1003      1
+#define SPACEBALL_2003B     3
+#define SPACEBALL_2003C     4
+#define SPACEBALL_3003C     7
+#define SPACEBALL_4000FLX   8
+#define SPACEBALL_4000FLX_L 9
+
+static int spaceball_axes[] = { ABS_X, ABS_Z, ABS_Y, ABS_RX, ABS_RZ, ABS_RY };
+static char *spaceball_names[] = {
+	"?", "SpaceTec SpaceBall 1003", "SpaceTec SpaceBall 2003", "SpaceTec SpaceBall 2003B",
+	"SpaceTec SpaceBall 2003C", "SpaceTec SpaceBall 3003", "SpaceTec SpaceBall SpaceController",
+	"SpaceTec SpaceBall 3003C", "SpaceTec SpaceBall 4000FLX", "SpaceTec SpaceBall 4000FLX Lefty" };
+
+/*
+ * Per-Ball data.
+ */
+
+struct spaceball {
+	struct input_dev dev;
+	struct serio *serio;
+	int idx;
+	int escape;
+	unsigned char data[SPACEBALL_MAX_LENGTH];
+	char phys[32];
+};
+
+/*
+ * spaceball_process_packet() decodes packets the driver receives from the
+ * SpaceBall.
+ */
+
+static void spaceball_process_packet(struct spaceball* spaceball, struct pt_regs *regs)
+{
+	struct input_dev *dev = &spaceball->dev;
+	unsigned char *data = spaceball->data;
+	int i;
+
+	if (spaceball->idx < 2) return;
+
+	input_regs(dev, regs);
+
+	switch (spaceball->data[0]) {
+
+		case 'D':					/* Ball data */
+			if (spaceball->idx != 15) return;
+			for (i = 0; i < 6; i++)
+				input_report_abs(dev, spaceball_axes[i],
+					(__s16)((data[2 * i + 3] << 8) | data[2 * i + 2]));
+			break;
+
+		case 'K':					/* Button data */
+			if (spaceball->idx != 3) return;
+			input_report_key(dev, BTN_1, (data[2] & 0x01) || (data[2] & 0x20));
+			input_report_key(dev, BTN_2, data[2] & 0x02);
+			input_report_key(dev, BTN_3, data[2] & 0x04);
+			input_report_key(dev, BTN_4, data[2] & 0x08);
+			input_report_key(dev, BTN_5, data[1] & 0x01);
+			input_report_key(dev, BTN_6, data[1] & 0x02);
+			input_report_key(dev, BTN_7, data[1] & 0x04);
+			input_report_key(dev, BTN_8, data[1] & 0x10);
+			break;
+
+		case '.':					/* Advanced button data */
+			if (spaceball->idx != 3) return;
+			input_report_key(dev, BTN_1, data[2] & 0x01);
+			input_report_key(dev, BTN_2, data[2] & 0x02);
+			input_report_key(dev, BTN_3, data[2] & 0x04);
+			input_report_key(dev, BTN_4, data[2] & 0x08);
+			input_report_key(dev, BTN_5, data[2] & 0x10);
+			input_report_key(dev, BTN_6, data[2] & 0x20);
+			input_report_key(dev, BTN_7, data[2] & 0x80);
+			input_report_key(dev, BTN_8, data[1] & 0x01);
+			input_report_key(dev, BTN_9, data[1] & 0x02);
+			input_report_key(dev, BTN_A, data[1] & 0x04);
+			input_report_key(dev, BTN_B, data[1] & 0x08);
+			input_report_key(dev, BTN_C, data[1] & 0x10);
+			input_report_key(dev, BTN_MODE, data[1] & 0x20);
+			break;
+
+		case 'E':					/* Device error */
+			spaceball->data[spaceball->idx - 1] = 0;
+			printk(KERN_ERR "spaceball: Device error. [%s]\n", spaceball->data + 1);
+			break;
+
+		case '?':					/* Bad command packet */
+			spaceball->data[spaceball->idx - 1] = 0;
+			printk(KERN_ERR "spaceball: Bad command. [%s]\n", spaceball->data + 1);
+			break;
+	}
+
+	input_sync(dev);
+}
+
+/*
+ * Spaceball 4000 FLX packets all start with a one letter packet-type decriptor,
+ * and end in 0x0d. It uses '^' as an escape for CR, XOFF and XON characters which
+ * can occur in the axis values.
+ */
+
+static irqreturn_t spaceball_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+	struct spaceball *spaceball = serio_get_drvdata(serio);
+
+	switch (data) {
+		case 0xd:
+			spaceball_process_packet(spaceball, regs);
+			spaceball->idx = 0;
+			spaceball->escape = 0;
+			break;
+		case '^':
+			if (!spaceball->escape) {
+				spaceball->escape = 1;
+				break;
+			}
+			spaceball->escape = 0;
+		case 'M':
+		case 'Q':
+		case 'S':
+			if (spaceball->escape) {
+				spaceball->escape = 0;
+				data &= 0x1f;
+			}
+		default:
+			if (spaceball->escape)
+				spaceball->escape = 0;
+			if (spaceball->idx < SPACEBALL_MAX_LENGTH)
+				spaceball->data[spaceball->idx++] = data;
+			break;
+	}
+	return IRQ_HANDLED;
+}
+
+/*
+ * spaceball_disconnect() is the opposite of spaceball_connect()
+ */
+
+static void spaceball_disconnect(struct serio *serio)
+{
+	struct spaceball* spaceball = serio_get_drvdata(serio);
+
+	input_unregister_device(&spaceball->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(spaceball);
+}
+
+/*
+ * spaceball_connect() is the routine that is called when someone adds a
+ * new serio device that supports Spaceball protocol and registers it as
+ * an input device.
+ */
+
+static int spaceball_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct spaceball *spaceball;
+	int i, t, id;
+	int err;
+
+	if ((id = serio->id.id) > SPACEBALL_MAX_ID)
+		return -ENODEV;
+
+	if (!(spaceball = kmalloc(sizeof(struct spaceball), GFP_KERNEL)))
+		return - ENOMEM;
+
+	memset(spaceball, 0, sizeof(struct spaceball));
+
+	spaceball->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+	switch (id) {
+		case SPACEBALL_4000FLX:
+		case SPACEBALL_4000FLX_L:
+			spaceball->dev.keybit[LONG(BTN_0)] |= BIT(BTN_9);
+			spaceball->dev.keybit[LONG(BTN_A)] |= BIT(BTN_A) | BIT(BTN_B) | BIT(BTN_C) | BIT(BTN_MODE);
+		default:
+			spaceball->dev.keybit[LONG(BTN_0)] |= BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4)
+				| BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7) | BIT(BTN_8);
+		case SPACEBALL_3003C:
+			spaceball->dev.keybit[LONG(BTN_0)] |= BIT(BTN_1) | BIT(BTN_8);
+	}
+
+	for (i = 0; i < 6; i++) {
+		t = spaceball_axes[i];
+		set_bit(t, spaceball->dev.absbit);
+		spaceball->dev.absmin[t] = i < 3 ? -8000 : -1600;
+		spaceball->dev.absmax[t] = i < 3 ?  8000 :  1600;
+		spaceball->dev.absflat[t] = i < 3 ? 40 : 8;
+		spaceball->dev.absfuzz[t] = i < 3 ? 8 : 2;
+	}
+
+	spaceball->serio = serio;
+	spaceball->dev.private = spaceball;
+
+	sprintf(spaceball->phys, "%s/input0", serio->phys);
+
+	init_input_dev(&spaceball->dev);
+	spaceball->dev.name = spaceball_names[id];
+	spaceball->dev.phys = spaceball->phys;
+	spaceball->dev.id.bustype = BUS_RS232;
+	spaceball->dev.id.vendor = SERIO_SPACEBALL;
+	spaceball->dev.id.product = id;
+	spaceball->dev.id.version = 0x0100;
+	spaceball->dev.dev = &serio->dev;
+
+	serio_set_drvdata(serio, spaceball);
+
+	err = serio_open(serio, drv);
+	if (err) {
+		serio_set_drvdata(serio, NULL);
+		kfree(spaceball);
+		return err;
+	}
+
+	input_register_device(&spaceball->dev);
+
+	printk(KERN_INFO "input: %s on serio%s\n",
+		spaceball_names[id], serio->phys);
+
+	return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id spaceball_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_SPACEBALL,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, spaceball_serio_ids);
+
+static struct serio_driver spaceball_drv = {
+	.driver		= {
+		.name	= "spaceball",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= spaceball_serio_ids,
+	.interrupt	= spaceball_interrupt,
+	.connect	= spaceball_connect,
+	.disconnect	= spaceball_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init spaceball_init(void)
+{
+	serio_register_driver(&spaceball_drv);
+	return 0;
+}
+
+static void __exit spaceball_exit(void)
+{
+	serio_unregister_driver(&spaceball_drv);
+}
+
+module_init(spaceball_init);
+module_exit(spaceball_exit);
diff --git a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c
new file mode 100644
index 0000000..c76cf8f
--- /dev/null
+++ b/drivers/input/joystick/spaceorb.c
@@ -0,0 +1,263 @@
+/*
+ * $Id: spaceorb.c,v 1.15 2002/01/22 20:29:19 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *  	David Thompson
+ */
+
+/*
+ * SpaceTec SpaceOrb 360 and Avenger 6dof controller 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC	"SpaceTec SpaceOrb 360 and Avenger 6dof controller driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define SPACEORB_MAX_LENGTH	64
+
+static int spaceorb_buttons[] = { BTN_TL, BTN_TR, BTN_Y, BTN_X, BTN_B, BTN_A };
+static int spaceorb_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ };
+static char *spaceorb_name = "SpaceTec SpaceOrb 360 / Avenger";
+
+/*
+ * Per-Orb data.
+ */
+
+struct spaceorb {
+	struct input_dev dev;
+	struct serio *serio;
+	int idx;
+	unsigned char data[SPACEORB_MAX_LENGTH];
+	char phys[32];
+};
+
+static unsigned char spaceorb_xor[] = "SpaceWare";
+
+static unsigned char *spaceorb_errors[] = { "EEPROM storing 0 failed", "Receive queue overflow", "Transmit queue timeout",
+		"Bad packet", "Power brown-out", "EEPROM checksum error", "Hardware fault" };
+
+/*
+ * spaceorb_process_packet() decodes packets the driver receives from the
+ * SpaceOrb.
+ */
+
+static void spaceorb_process_packet(struct spaceorb *spaceorb, struct pt_regs *regs)
+{
+	struct input_dev *dev = &spaceorb->dev;
+	unsigned char *data = spaceorb->data;
+	unsigned char c = 0;
+	int axes[6];
+	int i;
+
+	if (spaceorb->idx < 2) return;
+	for (i = 0; i < spaceorb->idx; i++) c ^= data[i];
+	if (c) return;
+
+	input_regs(dev, regs);
+
+	switch (data[0]) {
+
+		case 'R':				/* Reset packet */
+			spaceorb->data[spaceorb->idx - 1] = 0;
+			for (i = 1; i < spaceorb->idx && spaceorb->data[i] == ' '; i++);
+			printk(KERN_INFO "input: %s [%s] on %s\n",
+				 spaceorb_name, spaceorb->data + i, spaceorb->serio->phys);
+			break;
+
+		case 'D':				/* Ball + button data */
+			if (spaceorb->idx != 12) return;
+			for (i = 0; i < 9; i++) spaceorb->data[i+2] ^= spaceorb_xor[i];
+			axes[0] = ( data[2]	 << 3) | (data[ 3] >> 4);
+			axes[1] = ((data[3] & 0x0f) << 6) | (data[ 4] >> 1);
+			axes[2] = ((data[4] & 0x01) << 9) | (data[ 5] << 2) | (data[4] >> 5);
+			axes[3] = ((data[6] & 0x1f) << 5) | (data[ 7] >> 2);
+			axes[4] = ((data[7] & 0x03) << 8) | (data[ 8] << 1) | (data[7] >> 6);
+			axes[5] = ((data[9] & 0x3f) << 4) | (data[10] >> 3);
+			for (i = 0; i < 6; i++)
+				input_report_abs(dev, spaceorb_axes[i], axes[i] - ((axes[i] & 0x200) ? 1024 : 0));
+			for (i = 0; i < 6; i++)
+				input_report_key(dev, spaceorb_buttons[i], (data[1] >> i) & 1);
+			break;
+
+		case 'K':				/* Button data */
+			if (spaceorb->idx != 5) return;
+			for (i = 0; i < 7; i++)
+				input_report_key(dev, spaceorb_buttons[i], (data[2] >> i) & 1);
+
+			break;
+
+		case 'E':				/* Error packet */
+			if (spaceorb->idx != 4) return;
+			printk(KERN_ERR "joy-spaceorb: Device error. [ ");
+			for (i = 0; i < 7; i++) if (data[1] & (1 << i)) printk("%s ", spaceorb_errors[i]);
+			printk("]\n");
+			break;
+	}
+
+	input_sync(dev);
+}
+
+static irqreturn_t spaceorb_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+	struct spaceorb* spaceorb = serio_get_drvdata(serio);
+
+	if (~data & 0x80) {
+		if (spaceorb->idx) spaceorb_process_packet(spaceorb, regs);
+		spaceorb->idx = 0;
+	}
+	if (spaceorb->idx < SPACEORB_MAX_LENGTH)
+		spaceorb->data[spaceorb->idx++] = data & 0x7f;
+	return IRQ_HANDLED;
+}
+
+/*
+ * spaceorb_disconnect() is the opposite of spaceorb_connect()
+ */
+
+static void spaceorb_disconnect(struct serio *serio)
+{
+	struct spaceorb* spaceorb = serio_get_drvdata(serio);
+
+	input_unregister_device(&spaceorb->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(spaceorb);
+}
+
+/*
+ * spaceorb_connect() is the routine that is called when someone adds a
+ * new serio device that supports SpaceOrb/Avenger protocol and registers
+ * it as an input device.
+ */
+
+static int spaceorb_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct spaceorb *spaceorb;
+	int i, t;
+	int err;
+
+	if (!(spaceorb = kmalloc(sizeof(struct spaceorb), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(spaceorb, 0, sizeof(struct spaceorb));
+
+	spaceorb->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+	for (i = 0; i < 6; i++)
+		set_bit(spaceorb_buttons[i], spaceorb->dev.keybit);
+
+	for (i = 0; i < 6; i++) {
+		t = spaceorb_axes[i];
+		set_bit(t, spaceorb->dev.absbit);
+		spaceorb->dev.absmin[t] = -508;
+		spaceorb->dev.absmax[t] =  508;
+	}
+
+	spaceorb->serio = serio;
+	spaceorb->dev.private = spaceorb;
+
+	sprintf(spaceorb->phys, "%s/input0", serio->phys);
+
+	init_input_dev(&spaceorb->dev);
+	spaceorb->dev.name = spaceorb_name;
+	spaceorb->dev.phys = spaceorb->phys;
+	spaceorb->dev.id.bustype = BUS_RS232;
+	spaceorb->dev.id.vendor = SERIO_SPACEORB;
+	spaceorb->dev.id.product = 0x0001;
+	spaceorb->dev.id.version = 0x0100;
+	spaceorb->dev.dev = &serio->dev;
+
+	serio_set_drvdata(serio, spaceorb);
+
+	err = serio_open(serio, drv);
+	if (err) {
+		serio_set_drvdata(serio, NULL);
+		kfree(spaceorb);
+		return err;
+	}
+
+	input_register_device(&spaceorb->dev);
+
+	return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id spaceorb_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_SPACEORB,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, spaceorb_serio_ids);
+
+static struct serio_driver spaceorb_drv = {
+	.driver		= {
+		.name	= "spaceorb",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= spaceorb_serio_ids,
+	.interrupt	= spaceorb_interrupt,
+	.connect	= spaceorb_connect,
+	.disconnect	= spaceorb_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init spaceorb_init(void)
+{
+	serio_register_driver(&spaceorb_drv);
+	return 0;
+}
+
+static void __exit spaceorb_exit(void)
+{
+	serio_unregister_driver(&spaceorb_drv);
+}
+
+module_init(spaceorb_init);
+module_exit(spaceorb_exit);
diff --git a/drivers/input/joystick/stinger.c b/drivers/input/joystick/stinger.c
new file mode 100644
index 0000000..6f6e675
--- /dev/null
+++ b/drivers/input/joystick/stinger.c
@@ -0,0 +1,236 @@
+/*
+ * $Id: stinger.c,v 1.10 2002/01/22 20:29:31 vojtech Exp $
+ *
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *  Copyright (c) 2000 Mark Fletcher
+ */
+
+/*
+ * Gravis Stinger gamepad driver for Linux
+ */
+
+/*
+ * This program is free warftware; 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/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC	"Gravis Stinger gamepad driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define STINGER_MAX_LENGTH 8
+
+static char *stinger_name = "Gravis Stinger";
+
+/*
+ * Per-Stinger data.
+ */
+
+struct stinger {
+	struct input_dev dev;
+	int idx;
+	unsigned char data[STINGER_MAX_LENGTH];
+	char phys[32];
+};
+
+/*
+ * stinger_process_packet() decodes packets the driver receives from the
+ * Stinger. It updates the data accordingly.
+ */
+
+static void stinger_process_packet(struct stinger *stinger, struct pt_regs *regs)
+{
+	struct input_dev *dev = &stinger->dev;
+	unsigned char *data = stinger->data;
+
+	if (!stinger->idx) return;
+
+	input_regs(dev, regs);
+
+	input_report_key(dev, BTN_A,	  ((data[0] & 0x20) >> 5));
+	input_report_key(dev, BTN_B,	  ((data[0] & 0x10) >> 4));
+	input_report_key(dev, BTN_C,	  ((data[0] & 0x08) >> 3));
+	input_report_key(dev, BTN_X,	  ((data[0] & 0x04) >> 2));
+	input_report_key(dev, BTN_Y,	  ((data[3] & 0x20) >> 5));
+	input_report_key(dev, BTN_Z,	  ((data[3] & 0x10) >> 4));
+	input_report_key(dev, BTN_TL,     ((data[3] & 0x08) >> 3));
+	input_report_key(dev, BTN_TR,     ((data[3] & 0x04) >> 2));
+	input_report_key(dev, BTN_SELECT, ((data[3] & 0x02) >> 1));
+	input_report_key(dev, BTN_START,   (data[3] & 0x01));
+
+	input_report_abs(dev, ABS_X, (data[1] & 0x3F) - ((data[0] & 0x01) << 6));
+	input_report_abs(dev, ABS_Y, ((data[0] & 0x02) << 5) - (data[2] & 0x3F));
+
+	input_sync(dev);
+
+	return;
+}
+
+/*
+ * stinger_interrupt() is called by the low level driver when characters
+ * are ready for us. We then buffer them for further processing, or call the
+ * packet processing routine.
+ */
+
+static irqreturn_t stinger_interrupt(struct serio *serio,
+	unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+	struct stinger *stinger = serio_get_drvdata(serio);
+
+	/* All Stinger packets are 4 bytes */
+
+	if (stinger->idx < STINGER_MAX_LENGTH)
+		stinger->data[stinger->idx++] = data;
+
+	if (stinger->idx == 4) {
+		stinger_process_packet(stinger, regs);
+		stinger->idx = 0;
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * stinger_disconnect() is the opposite of stinger_connect()
+ */
+
+static void stinger_disconnect(struct serio *serio)
+{
+	struct stinger *stinger = serio_get_drvdata(serio);
+
+	input_unregister_device(&stinger->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(stinger);
+}
+
+/*
+ * stinger_connect() is the routine that is called when someone adds a
+ * new serio device that supports Stinger protocol and registers it as
+ * an input device.
+ */
+
+static int stinger_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct stinger *stinger;
+	int i;
+	int err;
+
+	if (!(stinger = kmalloc(sizeof(struct stinger), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(stinger, 0, sizeof(struct stinger));
+
+	stinger->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+	stinger->dev.keybit[LONG(BTN_A)] = BIT(BTN_A) | BIT(BTN_B) | BIT(BTN_C) | BIT(BTN_X) | \
+					   BIT(BTN_Y) | BIT(BTN_Z) | BIT(BTN_TL) | BIT(BTN_TR) | \
+					   BIT(BTN_START) | BIT(BTN_SELECT);
+	stinger->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+
+	sprintf(stinger->phys, "%s/serio0", serio->phys);
+
+	init_input_dev(&stinger->dev);
+	stinger->dev.name = stinger_name;
+	stinger->dev.phys = stinger->phys;
+	stinger->dev.id.bustype = BUS_RS232;
+	stinger->dev.id.vendor = SERIO_STINGER;
+	stinger->dev.id.product = 0x0001;
+	stinger->dev.id.version = 0x0100;
+	stinger->dev.dev = &serio->dev;
+
+	for (i = 0; i < 2; i++) {
+		stinger->dev.absmax[ABS_X+i] =  64;
+		stinger->dev.absmin[ABS_X+i] = -64;
+		stinger->dev.absflat[ABS_X+i] = 4;
+	}
+
+	stinger->dev.private = stinger;
+
+	serio_set_drvdata(serio, stinger);
+
+	err = serio_open(serio, drv);
+	if (err) {
+		serio_set_drvdata(serio, NULL);
+		kfree(stinger);
+		return err;
+	}
+
+	input_register_device(&stinger->dev);
+
+	printk(KERN_INFO "input: %s on %s\n",  stinger_name, serio->phys);
+
+	return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id stinger_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_STINGER,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, stinger_serio_ids);
+
+static struct serio_driver stinger_drv = {
+	.driver		= {
+		.name	= "stinger",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= stinger_serio_ids,
+	.interrupt	= stinger_interrupt,
+	.connect	= stinger_connect,
+	.disconnect	= stinger_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init stinger_init(void)
+{
+	serio_register_driver(&stinger_drv);
+	return 0;
+}
+
+static void __exit stinger_exit(void)
+{
+	serio_unregister_driver(&stinger_drv);
+}
+
+module_init(stinger_init);
+module_exit(stinger_exit);
diff --git a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c
new file mode 100644
index 0000000..aaee52c
--- /dev/null
+++ b/drivers/input/joystick/tmdc.c
@@ -0,0 +1,384 @@
+/*
+ * $Id: tmdc.c,v 1.31 2002/01/22 20:29:52 vojtech Exp $
+ *
+ *  Copyright (c) 1998-2001 Vojtech Pavlik
+ *
+ *   Based on the work of:
+ *	Trystan Larey-Williams
+ */
+
+/*
+ * ThrustMaster DirectConnect (BSP) joystick family 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+
+#define DRIVER_DESC	"ThrustMaster DirectConnect joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define TMDC_MAX_START		600	/* 600 us */
+#define TMDC_MAX_STROBE		60	/* 60 us */
+#define TMDC_MAX_LENGTH		13
+
+#define TMDC_MODE_M3DI		1
+#define TMDC_MODE_3DRP		3
+#define TMDC_MODE_AT		4
+#define TMDC_MODE_FM		8
+#define TMDC_MODE_FGP		163
+
+#define TMDC_BYTE_ID		10
+#define TMDC_BYTE_REV		11
+#define TMDC_BYTE_DEF		12
+
+#define TMDC_ABS		7
+#define TMDC_ABS_HAT		4
+#define TMDC_BTN		16
+
+static unsigned char tmdc_byte_a[16] = { 0, 1, 3, 4, 6, 7 };
+static unsigned char tmdc_byte_d[16] = { 2, 5, 8, 9 };
+
+static signed char tmdc_abs[TMDC_ABS] =
+	{ ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE, ABS_RX, ABS_RY, ABS_RZ };
+static signed char tmdc_abs_hat[TMDC_ABS_HAT] =
+	{ ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y };
+static signed char tmdc_abs_at[TMDC_ABS] =
+	{ ABS_X, ABS_Y, ABS_RUDDER, -1, ABS_THROTTLE };
+static signed char tmdc_abs_fm[TMDC_ABS] =
+	{ ABS_RX, ABS_RY, ABS_X, ABS_Y };
+
+static short tmdc_btn_pad[TMDC_BTN] =
+	{ BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_START, BTN_SELECT, BTN_TL, BTN_TR };
+static short tmdc_btn_joy[TMDC_BTN] =
+	{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_THUMB2, BTN_PINKIE,
+ 	  BTN_BASE3, BTN_BASE4, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z };
+static short tmdc_btn_fm[TMDC_BTN] =
+        { BTN_TRIGGER, BTN_C, BTN_B, BTN_A, BTN_THUMB, BTN_X, BTN_Y, BTN_Z, BTN_TOP, BTN_TOP2 };
+static short tmdc_btn_at[TMDC_BTN] =
+        { BTN_TRIGGER, BTN_THUMB2, BTN_PINKIE, BTN_THUMB, BTN_BASE6, BTN_BASE5, BTN_BASE4,
+          BTN_BASE3, BTN_BASE2, BTN_BASE };
+
+static struct {
+        int x;
+        int y;
+} tmdc_hat_to_axis[] = {{ 0, 0}, { 1, 0}, { 0,-1}, {-1, 0}, { 0, 1}};
+
+struct tmdc {
+	struct gameport *gameport;
+	struct input_dev dev[2];
+	char name[2][64];
+	char phys[2][32];
+	int mode[2];
+	signed char *abs[2];
+	short *btn[2];
+	unsigned char absc[2];
+	unsigned char btnc[2][4];
+	unsigned char btno[2][4];
+	int reads;
+	int bads;
+	unsigned char exists;
+};
+
+/*
+ * tmdc_read_packet() reads a ThrustMaster packet.
+ */
+
+static int tmdc_read_packet(struct gameport *gameport, unsigned char data[2][TMDC_MAX_LENGTH])
+{
+	unsigned char u, v, w, x;
+	unsigned long flags;
+	int i[2], j[2], t[2], p, k;
+
+	p = gameport_time(gameport, TMDC_MAX_STROBE);
+
+	for (k = 0; k < 2; k++) {
+		t[k] = gameport_time(gameport, TMDC_MAX_START);
+		i[k] = j[k] = 0;
+	}
+
+	local_irq_save(flags);
+	gameport_trigger(gameport);
+
+	w = gameport_read(gameport) >> 4;
+
+	do {
+		x = w;
+		w = gameport_read(gameport) >> 4;
+
+		for (k = 0, v = w, u = x; k < 2; k++, v >>= 2, u >>= 2) {
+			if (~v & u & 2) {
+				if (t[k] <= 0 || i[k] >= TMDC_MAX_LENGTH) continue;
+				t[k] = p;
+				if (j[k] == 0) {				 /* Start bit */
+					if (~v & 1) t[k] = 0;
+					data[k][i[k]] = 0; j[k]++; continue;
+				}
+				if (j[k] == 9) {				/* Stop bit */
+					if (v & 1) t[k] = 0;
+					j[k] = 0; i[k]++; continue;
+				}
+				data[k][i[k]] |= (~v & 1) << (j[k]++ - 1);	/* Data bit */
+			}
+			t[k]--;
+		}
+	} while (t[0] > 0 || t[1] > 0);
+
+	local_irq_restore(flags);
+
+	return (i[0] == TMDC_MAX_LENGTH) | ((i[1] == TMDC_MAX_LENGTH) << 1);
+}
+
+/*
+ * tmdc_poll() reads and analyzes ThrustMaster joystick data.
+ */
+
+static void tmdc_poll(struct gameport *gameport)
+{
+	unsigned char data[2][TMDC_MAX_LENGTH];
+	struct tmdc *tmdc = gameport_get_drvdata(gameport);
+	struct input_dev *dev;
+	unsigned char r, bad = 0;
+	int i, j, k, l;
+
+	tmdc->reads++;
+
+	if ((r = tmdc_read_packet(tmdc->gameport, data)) != tmdc->exists)
+		bad = 1;
+	else
+
+	for (j = 0; j < 2; j++)
+		if (r & (1 << j) & tmdc->exists) {
+
+			if (data[j][TMDC_BYTE_ID] != tmdc->mode[j]) {
+				bad = 1;
+				continue;
+			}
+
+			dev = tmdc->dev + j;
+
+			for (i = 0; i < tmdc->absc[j]; i++) {
+				if (tmdc->abs[j][i] < 0) continue;
+				input_report_abs(dev, tmdc->abs[j][i], data[j][tmdc_byte_a[i]]);
+			}
+
+			switch (tmdc->mode[j]) {
+
+				case TMDC_MODE_M3DI:
+
+					i = tmdc_byte_d[0];
+					input_report_abs(dev, ABS_HAT0X, ((data[j][i] >> 3) & 1) - ((data[j][i] >> 1) & 1));
+					input_report_abs(dev, ABS_HAT0Y, ((data[j][i] >> 2) & 1) - ( data[j][i]       & 1));
+					break;
+
+				case TMDC_MODE_AT:
+
+					i = tmdc_byte_a[3];
+					input_report_abs(dev, ABS_HAT0X, tmdc_hat_to_axis[(data[j][i] - 141) / 25].x);
+					input_report_abs(dev, ABS_HAT0Y, tmdc_hat_to_axis[(data[j][i] - 141) / 25].y);
+					break;
+
+			}
+
+			for (k = l = 0; k < 4; k++) {
+				for (i = 0; i < tmdc->btnc[j][k]; i++)
+					input_report_key(dev, tmdc->btn[j][i + l],
+						((data[j][tmdc_byte_d[k]] >> (i + tmdc->btno[j][k])) & 1));
+				l += tmdc->btnc[j][k];
+			}
+
+			input_sync(dev);
+	}
+
+	tmdc->bads += bad;
+}
+
+static int tmdc_open(struct input_dev *dev)
+{
+	struct tmdc *tmdc = dev->private;
+
+	gameport_start_polling(tmdc->gameport);
+	return 0;
+}
+
+static void tmdc_close(struct input_dev *dev)
+{
+	struct tmdc *tmdc = dev->private;
+
+	gameport_stop_polling(tmdc->gameport);
+}
+
+/*
+ * tmdc_probe() probes for ThrustMaster type joysticks.
+ */
+
+static int tmdc_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+	static struct models {
+		unsigned char id;
+		char *name;
+		char abs;
+		char hats;
+		char btnc[4];
+		char btno[4];
+		signed char *axes;
+		short *buttons;
+	} models[] = {	{   1, "ThrustMaster Millenium 3D Inceptor",	  6, 2, { 4, 2 }, { 4, 6 }, tmdc_abs, tmdc_btn_joy },
+			{   3, "ThrustMaster Rage 3D Gamepad",		  2, 0, { 8, 2 }, { 0, 0 }, tmdc_abs, tmdc_btn_pad },
+			{   4, "ThrustMaster Attack Throttle",		  5, 2, { 4, 6 }, { 4, 2 }, tmdc_abs_at, tmdc_btn_at },
+			{   8, "ThrustMaster FragMaster",		  4, 0, { 8, 2 }, { 0, 0 }, tmdc_abs_fm, tmdc_btn_fm },
+			{ 163, "Thrustmaster Fusion GamePad",		  2, 0, { 8, 2 }, { 0, 0 }, tmdc_abs, tmdc_btn_pad },
+			{   0, "Unknown %d-axis, %d-button TM device %d", 0, 0, { 0, 0 }, { 0, 0 }, tmdc_abs, tmdc_btn_joy }};
+
+	unsigned char data[2][TMDC_MAX_LENGTH];
+	struct tmdc *tmdc;
+	int i, j, k, l, m;
+	int err;
+
+	if (!(tmdc = kcalloc(1, sizeof(struct tmdc), GFP_KERNEL)))
+		return -ENOMEM;
+
+	tmdc->gameport = gameport;
+
+	gameport_set_drvdata(gameport, tmdc);
+
+	err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+	if (err)
+		goto fail1;
+
+	if (!(tmdc->exists = tmdc_read_packet(gameport, data))) {
+		err = -ENODEV;
+		goto fail2;
+	}
+
+	gameport_set_poll_handler(gameport, tmdc_poll);
+	gameport_set_poll_interval(gameport, 20);
+
+	for (j = 0; j < 2; j++)
+		if (tmdc->exists & (1 << j)) {
+
+			tmdc->mode[j] = data[j][TMDC_BYTE_ID];
+
+			for (m = 0; models[m].id && models[m].id != tmdc->mode[j]; m++);
+
+			tmdc->abs[j] = models[m].axes;
+			tmdc->btn[j] = models[m].buttons;
+
+			if (!models[m].id) {
+				models[m].abs = data[j][TMDC_BYTE_DEF] >> 4;
+				for (k = 0; k < 4; k++)
+					models[m].btnc[k] = k < (data[j][TMDC_BYTE_DEF] & 0xf) ? 8 : 0;
+			}
+
+			tmdc->absc[j] = models[m].abs;
+			for (k = 0; k < 4; k++) {
+				tmdc->btnc[j][k] = models[m].btnc[k];
+				tmdc->btno[j][k] = models[m].btno[k];
+			}
+
+			sprintf(tmdc->name[j], models[m].name, models[m].abs,
+				(data[j][TMDC_BYTE_DEF] & 0xf) << 3, tmdc->mode[j]);
+
+			sprintf(tmdc->phys[j], "%s/input%d", gameport->phys, j);
+
+			tmdc->dev[j].private = tmdc;
+			tmdc->dev[j].open = tmdc_open;
+			tmdc->dev[j].close = tmdc_close;
+
+			tmdc->dev[j].name = tmdc->name[j];
+			tmdc->dev[j].phys = tmdc->phys[j];
+			tmdc->dev[j].id.bustype = BUS_GAMEPORT;
+			tmdc->dev[j].id.vendor = GAMEPORT_ID_VENDOR_THRUSTMASTER;
+			tmdc->dev[j].id.product = models[m].id;
+			tmdc->dev[j].id.version = 0x0100;
+
+			tmdc->dev[j].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+			for (i = 0; i < models[m].abs && i < TMDC_ABS; i++)
+				if (tmdc->abs[j][i] >= 0)
+					input_set_abs_params(&tmdc->dev[j], tmdc->abs[j][i], 8, 248, 2, 4);
+
+			for (i = 0; i < models[m].hats && i < TMDC_ABS_HAT; i++)
+				input_set_abs_params(&tmdc->dev[j], tmdc_abs_hat[i], -1, 1, 0, 0);
+
+
+			for (k = l = 0; k < 4; k++) {
+				for (i = 0; i < models[m].btnc[k] && i < TMDC_BTN; i++)
+					set_bit(tmdc->btn[j][i + l], tmdc->dev[j].keybit);
+				l += models[m].btnc[k];
+			}
+
+			input_register_device(tmdc->dev + j);
+			printk(KERN_INFO "input: %s on %s\n", tmdc->name[j], gameport->phys);
+		}
+
+	return 0;
+
+fail2:	gameport_close(gameport);
+fail1:	gameport_set_drvdata(gameport, NULL);
+	kfree(tmdc);
+	return err;
+}
+
+static void tmdc_disconnect(struct gameport *gameport)
+{
+	struct tmdc *tmdc = gameport_get_drvdata(gameport);
+	int i;
+
+	for (i = 0; i < 2; i++)
+		if (tmdc->exists & (1 << i))
+			input_unregister_device(tmdc->dev + i);
+	gameport_close(gameport);
+	gameport_set_drvdata(gameport, NULL);
+	kfree(tmdc);
+}
+
+static struct gameport_driver tmdc_drv = {
+	.driver		= {
+		.name	= "tmdc",
+	},
+	.description	= DRIVER_DESC,
+	.connect	= tmdc_connect,
+	.disconnect	= tmdc_disconnect,
+};
+
+static int __init tmdc_init(void)
+{
+	gameport_register_driver(&tmdc_drv);
+	return 0;
+}
+
+static void __exit tmdc_exit(void)
+{
+	gameport_unregister_driver(&tmdc_drv);
+}
+
+module_init(tmdc_init);
+module_exit(tmdc_exit);
diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c
new file mode 100644
index 0000000..dd88b9c
--- /dev/null
+++ b/drivers/input/joystick/turbografx.c
@@ -0,0 +1,258 @@
+/*
+ * $Id: turbografx.c,v 1.14 2002/01/22 20:30:39 vojtech Exp $
+ *
+ *  Copyright (c) 1998-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Steffen Schwenke
+ */
+
+/*
+ * TurboGraFX parallel port interface 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/kernel.h>
+#include <linux/parport.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("TurboGraFX parallel port interface driver");
+MODULE_LICENSE("GPL");
+
+static int tgfx[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 };
+static int tgfx_nargs __initdata = 0;
+module_param_array_named(map, tgfx, int, &tgfx_nargs, 0);
+MODULE_PARM_DESC(map, "Describes first set of devices (<parport#>,<js1>,<js2>,..<js7>");
+
+static int tgfx_2[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 };
+static int tgfx_nargs_2 __initdata = 0;
+module_param_array_named(map2, tgfx_2, int, &tgfx_nargs_2, 0);
+MODULE_PARM_DESC(map2, "Describes second set of devices");
+
+static int tgfx_3[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 };
+static int tgfx_nargs_3 __initdata = 0;
+module_param_array_named(map3, tgfx_3, int, &tgfx_nargs_3, 0);
+MODULE_PARM_DESC(map3, "Describes third set of devices");
+
+__obsolete_setup("tgfx=");
+__obsolete_setup("tgfx_2=");
+__obsolete_setup("tgfx_3=");
+
+#define TGFX_REFRESH_TIME	HZ/100	/* 10 ms */
+
+#define TGFX_TRIGGER		0x08
+#define TGFX_UP			0x10
+#define TGFX_DOWN		0x20
+#define TGFX_LEFT		0x40
+#define TGFX_RIGHT		0x80
+
+#define TGFX_THUMB		0x02
+#define TGFX_THUMB2		0x04
+#define TGFX_TOP		0x01
+#define TGFX_TOP2		0x08
+
+static int tgfx_buttons[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2 };
+static char *tgfx_name = "TurboGraFX Multisystem joystick";
+
+static struct tgfx {
+	struct pardevice *pd;
+	struct timer_list timer;
+	struct input_dev dev[7];
+	char phys[7][32];
+	int sticks;
+	int used;
+} *tgfx_base[3];
+
+/*
+ * tgfx_timer() reads and analyzes TurboGraFX joystick data.
+ */
+
+static void tgfx_timer(unsigned long private)
+{
+	struct tgfx *tgfx = (void *) private;
+	struct input_dev *dev;
+	int data1, data2, i;
+
+	for (i = 0; i < 7; i++)
+		if (tgfx->sticks & (1 << i)) {
+
+ 			dev = tgfx->dev + i;
+
+			parport_write_data(tgfx->pd->port, ~(1 << i));
+			data1 = parport_read_status(tgfx->pd->port) ^ 0x7f;
+			data2 = parport_read_control(tgfx->pd->port) ^ 0x04;	/* CAVEAT parport */
+
+			input_report_abs(dev, ABS_X, !!(data1 & TGFX_RIGHT) - !!(data1 & TGFX_LEFT));
+			input_report_abs(dev, ABS_Y, !!(data1 & TGFX_DOWN ) - !!(data1 & TGFX_UP  ));
+
+			input_report_key(dev, BTN_TRIGGER, (data1 & TGFX_TRIGGER));
+			input_report_key(dev, BTN_THUMB,   (data2 & TGFX_THUMB  ));
+			input_report_key(dev, BTN_THUMB2,  (data2 & TGFX_THUMB2 ));
+			input_report_key(dev, BTN_TOP,     (data2 & TGFX_TOP    ));
+			input_report_key(dev, BTN_TOP2,    (data2 & TGFX_TOP2   ));
+
+			input_sync(dev);
+		}
+
+	mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME);
+}
+
+static int tgfx_open(struct input_dev *dev)
+{
+        struct tgfx *tgfx = dev->private;
+        if (!tgfx->used++) {
+		parport_claim(tgfx->pd);
+		parport_write_control(tgfx->pd->port, 0x04);
+                mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME);
+	}
+        return 0;
+}
+
+static void tgfx_close(struct input_dev *dev)
+{
+        struct tgfx *tgfx = dev->private;
+        if (!--tgfx->used) {
+                del_timer(&tgfx->timer);
+		parport_write_control(tgfx->pd->port, 0x00);
+        	parport_release(tgfx->pd);
+	}
+}
+
+/*
+ * tgfx_probe() probes for tg gamepads.
+ */
+
+static struct tgfx __init *tgfx_probe(int *config, int nargs)
+{
+	struct tgfx *tgfx;
+	struct parport *pp;
+	int i, j;
+
+	if (config[0] < 0)
+		return NULL;
+
+	if (nargs < 2) {
+		printk(KERN_ERR "turbografx.c: at least one joystick must be specified\n");
+		return NULL;
+	}
+
+	pp = parport_find_number(config[0]);
+
+	if (!pp) {
+		printk(KERN_ERR "turbografx.c: no such parport\n");
+		return NULL;
+	}
+
+	if (!(tgfx = kmalloc(sizeof(struct tgfx), GFP_KERNEL))) {
+		parport_put_port(pp);
+		return NULL;
+	}
+	memset(tgfx, 0, sizeof(struct tgfx));
+
+	tgfx->pd = parport_register_device(pp, "turbografx", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
+
+	parport_put_port(pp);
+
+	if (!tgfx->pd) {
+		printk(KERN_ERR "turbografx.c: parport busy already - lp.o loaded?\n");
+		kfree(tgfx);
+		return NULL;
+	}
+
+	init_timer(&tgfx->timer);
+	tgfx->timer.data = (long) tgfx;
+	tgfx->timer.function = tgfx_timer;
+
+	tgfx->sticks = 0;
+
+	for (i = 0; i < nargs - 1; i++)
+		if (config[i+1] > 0 && config[i+1] < 6) {
+
+			tgfx->sticks |= (1 << i);
+
+			tgfx->dev[i].private = tgfx;
+			tgfx->dev[i].open = tgfx_open;
+			tgfx->dev[i].close = tgfx_close;
+
+			sprintf(tgfx->phys[i], "%s/input0", tgfx->pd->port->name);
+
+			tgfx->dev[i].name = tgfx_name;
+			tgfx->dev[i].phys = tgfx->phys[i];
+			tgfx->dev[i].id.bustype = BUS_PARPORT;
+			tgfx->dev[i].id.vendor = 0x0003;
+			tgfx->dev[i].id.product = config[i+1];
+			tgfx->dev[i].id.version = 0x0100;
+
+			tgfx->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+			tgfx->dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+
+			for (j = 0; j < config[i+1]; j++)
+				set_bit(tgfx_buttons[j], tgfx->dev[i].keybit);
+
+			tgfx->dev[i].absmin[ABS_X] = -1; tgfx->dev[i].absmax[ABS_X] = 1;
+			tgfx->dev[i].absmin[ABS_Y] = -1; tgfx->dev[i].absmax[ABS_Y] = 1;
+
+			input_register_device(tgfx->dev + i);
+			printk(KERN_INFO "input: %d-button Multisystem joystick on %s\n",
+				config[i+1], tgfx->pd->port->name);
+		}
+
+        if (!tgfx->sticks) {
+		parport_unregister_device(tgfx->pd);
+		kfree(tgfx);
+		return NULL;
+        }
+
+	return tgfx;
+}
+
+static int __init tgfx_init(void)
+{
+	tgfx_base[0] = tgfx_probe(tgfx, tgfx_nargs);
+	tgfx_base[1] = tgfx_probe(tgfx_2, tgfx_nargs_2);
+	tgfx_base[2] = tgfx_probe(tgfx_3, tgfx_nargs_3);
+
+	if (tgfx_base[0] || tgfx_base[1] || tgfx_base[2])
+		return 0;
+
+	return -ENODEV;
+}
+
+static void __exit tgfx_exit(void)
+{
+	int i, j;
+
+	for (i = 0; i < 3; i++)
+		if (tgfx_base[i]) {
+			for (j = 0; j < 7; j++)
+				if (tgfx_base[i]->sticks & (1 << j))
+					input_unregister_device(tgfx_base[i]->dev + j);
+		parport_unregister_device(tgfx_base[i]->pd);
+	}
+}
+
+module_init(tgfx_init);
+module_exit(tgfx_exit);
diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c
new file mode 100644
index 0000000..0379bc1
--- /dev/null
+++ b/drivers/input/joystick/twidjoy.c
@@ -0,0 +1,296 @@
+/*
+ * $Id: twidjoy.c,v 1.5 2002/01/22 20:31:53 vojtech Exp $
+ *
+ *  derived from CVS-ID "stinger.c,v 1.5 2001/05/29 12:57:18 vojtech Exp"
+ *
+ *  Copyright (c) 2001 Arndt Schoenewald
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *  Copyright (c) 2000 Mark Fletcher
+ *
+ *  Sponsored by Quelltext AG (http://www.quelltext-ag.de), Dortmund, Germany
+ */
+
+/*
+ * Driver to use Handykey's Twiddler (the first edition, i.e. the one with
+ * the RS232 interface) as a joystick under Linux
+ *
+ * The Twiddler is a one-handed chording keyboard featuring twelve buttons on
+ * the front, six buttons on the top, and a built-in tilt sensor. The buttons
+ * on the front, which are grouped as four rows of three buttons, are pressed
+ * by the four fingers (this implies only one button per row can be held down
+ * at the same time) and the buttons on the top are for the thumb. The tilt
+ * sensor delivers X and Y axis data depending on how the Twiddler is held.
+ * Additional information can be found at http://www.handykey.com.
+ *
+ * This driver does not use the Twiddler for its intended purpose, i.e. as
+ * a chording keyboard, but as a joystick: pressing and releasing a button
+ * immediately sends a corresponding button event, and tilting it generates
+ * corresponding ABS_X and ABS_Y events. This turns the Twiddler into a game
+ * controller with amazing 18 buttons :-)
+ *
+ * Note: The Twiddler2 (the successor of the Twiddler that connects directly
+ * to the PS/2 keyboard and mouse ports) is NOT supported by this driver!
+ *
+ * For questions or feedback regarding this driver module please contact:
+ * Arndt Schoenewald <arndt@quelltext.com>
+ */
+
+/*
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC	"Handykey Twiddler keyboard as a joystick driver"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define TWIDJOY_MAX_LENGTH 5
+
+static char *twidjoy_name = "Handykey Twiddler";
+
+static struct twidjoy_button_spec {
+	int bitshift;
+	int bitmask;
+	int buttons[3];
+}
+twidjoy_buttons[] = {
+	{  0, 3, { BTN_A,      BTN_B,     BTN_C    } },
+	{  2, 3, { BTN_X,      BTN_Y,     BTN_Z    } },
+	{  4, 3, { BTN_TL,     BTN_TR,    BTN_TR2  } },
+	{  6, 3, { BTN_SELECT, BTN_START, BTN_MODE } },
+	{  8, 1, { BTN_BASE5                       } },
+	{  9, 1, { BTN_BASE                        } },
+	{ 10, 1, { BTN_BASE3                       } },
+	{ 11, 1, { BTN_BASE4                       } },
+	{ 12, 1, { BTN_BASE2                       } },
+	{ 13, 1, { BTN_BASE6                       } },
+	{ 0,  0, { 0                               } }
+};
+
+/*
+ * Per-Twiddler data.
+ */
+
+struct twidjoy {
+	struct input_dev dev;
+	int idx;
+	unsigned char data[TWIDJOY_MAX_LENGTH];
+	char phys[32];
+};
+
+/*
+ * twidjoy_process_packet() decodes packets the driver receives from the
+ * Twiddler. It updates the data accordingly.
+ */
+
+static void twidjoy_process_packet(struct twidjoy *twidjoy, struct pt_regs *regs)
+{
+	if (twidjoy->idx == TWIDJOY_MAX_LENGTH) {
+		struct input_dev *dev = &twidjoy->dev;
+		unsigned char *data = twidjoy->data;
+		struct twidjoy_button_spec *bp;
+		int button_bits, abs_x, abs_y;
+
+		button_bits = ((data[1] & 0x7f) << 7) | (data[0] & 0x7f);
+
+		input_regs(dev, regs);
+
+		for (bp = twidjoy_buttons; bp->bitmask; bp++) {
+			int value = (button_bits & (bp->bitmask << bp->bitshift)) >> bp->bitshift;
+			int i;
+
+			for (i = 0; i < bp->bitmask; i++)
+				input_report_key(dev, bp->buttons[i], i+1 == value);
+		}
+
+		abs_x = ((data[4] & 0x07) << 5) | ((data[3] & 0x7C) >> 2);
+		if (data[4] & 0x08) abs_x -= 256;
+
+		abs_y = ((data[3] & 0x01) << 7) | ((data[2] & 0x7F) >> 0);
+		if (data[3] & 0x02) abs_y -= 256;
+
+		input_report_abs(dev, ABS_X, -abs_x);
+		input_report_abs(dev, ABS_Y, +abs_y);
+
+		input_sync(dev);
+	}
+
+	return;
+}
+
+/*
+ * twidjoy_interrupt() is called by the low level driver when characters
+ * are ready for us. We then buffer them for further processing, or call the
+ * packet processing routine.
+ */
+
+static irqreturn_t twidjoy_interrupt(struct serio *serio, unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+	struct twidjoy *twidjoy = serio_get_drvdata(serio);
+
+	/* All Twiddler packets are 5 bytes. The fact that the first byte
+	 * has a MSB of 0 and all other bytes have a MSB of 1 can be used
+	 * to check and regain sync. */
+
+	if ((data & 0x80) == 0)
+		twidjoy->idx = 0;	/* this byte starts a new packet */
+	else if (twidjoy->idx == 0)
+		return IRQ_HANDLED;	/* wrong MSB -- ignore this byte */
+
+	if (twidjoy->idx < TWIDJOY_MAX_LENGTH)
+		twidjoy->data[twidjoy->idx++] = data;
+
+	if (twidjoy->idx == TWIDJOY_MAX_LENGTH) {
+		twidjoy_process_packet(twidjoy, regs);
+		twidjoy->idx = 0;
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * twidjoy_disconnect() is the opposite of twidjoy_connect()
+ */
+
+static void twidjoy_disconnect(struct serio *serio)
+{
+	struct twidjoy *twidjoy = serio_get_drvdata(serio);
+
+	input_unregister_device(&twidjoy->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(twidjoy);
+}
+
+/*
+ * twidjoy_connect() is the routine that is called when someone adds a
+ * new serio device. It looks for the Twiddler, and if found, registers
+ * it as an input device.
+ */
+
+static int twidjoy_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct twidjoy_button_spec *bp;
+	struct twidjoy *twidjoy;
+	int i;
+	int err;
+
+	if (!(twidjoy = kmalloc(sizeof(struct twidjoy), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(twidjoy, 0, sizeof(struct twidjoy));
+
+	sprintf(twidjoy->phys, "%s/input0", serio->phys);
+
+	init_input_dev(&twidjoy->dev);
+	twidjoy->dev.name = twidjoy_name;
+	twidjoy->dev.phys = twidjoy->phys;
+	twidjoy->dev.id.bustype = BUS_RS232;
+	twidjoy->dev.id.vendor = SERIO_TWIDJOY;
+	twidjoy->dev.id.product = 0x0001;
+	twidjoy->dev.id.version = 0x0100;
+	twidjoy->dev.dev = &serio->dev;
+
+	twidjoy->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+	for (bp = twidjoy_buttons; bp->bitmask; bp++) {
+		for (i = 0; i < bp->bitmask; i++)
+			set_bit(bp->buttons[i], twidjoy->dev.keybit);
+	}
+
+	twidjoy->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+
+	for (i = 0; i < 2; i++) {
+		twidjoy->dev.absmax[ABS_X+i] =  50;
+		twidjoy->dev.absmin[ABS_X+i] = -50;
+
+		/* TODO: arndt 20010708: Are these values appropriate? */
+		twidjoy->dev.absfuzz[ABS_X+i] = 4;
+		twidjoy->dev.absflat[ABS_X+i] = 4;
+	}
+
+	twidjoy->dev.private = twidjoy;
+
+	serio_set_drvdata(serio, twidjoy);
+
+	err = serio_open(serio, drv);
+	if (err) {
+		serio_set_drvdata(serio, NULL);
+		kfree(twidjoy);
+		return err;
+	}
+
+	input_register_device(&twidjoy->dev);
+
+	printk(KERN_INFO "input: %s on %s\n", twidjoy_name, serio->phys);
+
+	return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id twidjoy_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_TWIDJOY,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, twidjoy_serio_ids);
+
+static struct serio_driver twidjoy_drv = {
+	.driver		= {
+		.name	= "twidjoy",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= twidjoy_serio_ids,
+	.interrupt	= twidjoy_interrupt,
+	.connect	= twidjoy_connect,
+	.disconnect	= twidjoy_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+int __init twidjoy_init(void)
+{
+	serio_register_driver(&twidjoy_drv);
+	return 0;
+}
+
+void __exit twidjoy_exit(void)
+{
+	serio_unregister_driver(&twidjoy_drv);
+}
+
+module_init(twidjoy_init);
+module_exit(twidjoy_exit);
diff --git a/drivers/input/joystick/warrior.c b/drivers/input/joystick/warrior.c
new file mode 100644
index 0000000..6976a21
--- /dev/null
+++ b/drivers/input/joystick/warrior.c
@@ -0,0 +1,248 @@
+/*
+ * $Id: warrior.c,v 1.14 2002/01/22 20:32:10 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Logitech WingMan Warrior joystick driver for Linux
+ */
+
+/*
+ * This program is free warftware; 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/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC	"Logitech WingMan Warrior joystick driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define WARRIOR_MAX_LENGTH	16
+static char warrior_lengths[] = { 0, 4, 12, 3, 4, 4, 0, 0 };
+static char *warrior_name = "Logitech WingMan Warrior";
+
+/*
+ * Per-Warrior data.
+ */
+
+struct warrior {
+	struct input_dev dev;
+	int idx, len;
+	unsigned char data[WARRIOR_MAX_LENGTH];
+	char phys[32];
+};
+
+/*
+ * warrior_process_packet() decodes packets the driver receives from the
+ * Warrior. It updates the data accordingly.
+ */
+
+static void warrior_process_packet(struct warrior *warrior, struct pt_regs *regs)
+{
+	struct input_dev *dev = &warrior->dev;
+	unsigned char *data = warrior->data;
+
+	if (!warrior->idx) return;
+
+	input_regs(dev, regs);
+
+	switch ((data[0] >> 4) & 7) {
+		case 1:					/* Button data */
+			input_report_key(dev, BTN_TRIGGER,  data[3]       & 1);
+			input_report_key(dev, BTN_THUMB,   (data[3] >> 1) & 1);
+			input_report_key(dev, BTN_TOP,     (data[3] >> 2) & 1);
+			input_report_key(dev, BTN_TOP2,    (data[3] >> 3) & 1);
+			break;
+		case 3:					/* XY-axis info->data */
+			input_report_abs(dev, ABS_X, ((data[0] & 8) << 5) - (data[2] | ((data[0] & 4) << 5)));
+			input_report_abs(dev, ABS_Y, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7));
+			break;
+		case 5:					/* Throttle, spinner, hat info->data */
+			input_report_abs(dev, ABS_THROTTLE, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7));
+			input_report_abs(dev, ABS_HAT0X, (data[3] & 2 ? 1 : 0) - (data[3] & 1 ? 1 : 0));
+			input_report_abs(dev, ABS_HAT0Y, (data[3] & 8 ? 1 : 0) - (data[3] & 4 ? 1 : 0));
+			input_report_rel(dev, REL_DIAL,  (data[2] | ((data[0] & 4) << 5)) - ((data[0] & 8) << 5));
+			break;
+	}
+	input_sync(dev);
+}
+
+/*
+ * warrior_interrupt() is called by the low level driver when characters
+ * are ready for us. We then buffer them for further processing, or call the
+ * packet processing routine.
+ */
+
+static irqreturn_t warrior_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+	struct warrior *warrior = serio_get_drvdata(serio);
+
+	if (data & 0x80) {
+		if (warrior->idx) warrior_process_packet(warrior, regs);
+		warrior->idx = 0;
+		warrior->len = warrior_lengths[(data >> 4) & 7];
+	}
+
+	if (warrior->idx < warrior->len)
+		warrior->data[warrior->idx++] = data;
+
+	if (warrior->idx == warrior->len) {
+		if (warrior->idx) warrior_process_packet(warrior, regs);
+		warrior->idx = 0;
+		warrior->len = 0;
+	}
+	return IRQ_HANDLED;
+}
+
+/*
+ * warrior_disconnect() is the opposite of warrior_connect()
+ */
+
+static void warrior_disconnect(struct serio *serio)
+{
+	struct warrior *warrior = serio_get_drvdata(serio);
+
+	input_unregister_device(&warrior->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(warrior);
+}
+
+/*
+ * warrior_connect() is the routine that is called when someone adds a
+ * new serio device. It looks for the Warrior, and if found, registers
+ * it as an input device.
+ */
+
+static int warrior_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct warrior *warrior;
+	int i;
+	int err;
+
+	if (!(warrior = kmalloc(sizeof(struct warrior), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(warrior, 0, sizeof(struct warrior));
+
+	warrior->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
+	warrior->dev.keybit[LONG(BTN_TRIGGER)] = BIT(BTN_TRIGGER) | BIT(BTN_THUMB) | BIT(BTN_TOP) | BIT(BTN_TOP2);
+	warrior->dev.relbit[0] = BIT(REL_DIAL);
+	warrior->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y);
+
+	sprintf(warrior->phys, "%s/input0", serio->phys);
+
+	init_input_dev(&warrior->dev);
+	warrior->dev.name = warrior_name;
+	warrior->dev.phys = warrior->phys;
+	warrior->dev.id.bustype = BUS_RS232;
+	warrior->dev.id.vendor = SERIO_WARRIOR;
+	warrior->dev.id.product = 0x0001;
+	warrior->dev.id.version = 0x0100;
+	warrior->dev.dev = &serio->dev;
+
+	for (i = 0; i < 2; i++) {
+		warrior->dev.absmax[ABS_X+i] = -64;
+		warrior->dev.absmin[ABS_X+i] =  64;
+		warrior->dev.absflat[ABS_X+i] = 8;
+	}
+
+	warrior->dev.absmax[ABS_THROTTLE] = -112;
+	warrior->dev.absmin[ABS_THROTTLE] =  112;
+
+	for (i = 0; i < 2; i++) {
+		warrior->dev.absmax[ABS_HAT0X+i] = -1;
+		warrior->dev.absmin[ABS_HAT0X+i] =  1;
+	}
+
+	warrior->dev.private = warrior;
+
+	serio_set_drvdata(serio, warrior);
+
+	err = serio_open(serio, drv);
+	if (err) {
+		serio_set_drvdata(serio, NULL);
+		kfree(warrior);
+		return err;
+	}
+
+	input_register_device(&warrior->dev);
+
+	printk(KERN_INFO "input: Logitech WingMan Warrior on %s\n", serio->phys);
+
+	return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id warrior_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_WARRIOR,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, warrior_serio_ids);
+
+static struct serio_driver warrior_drv = {
+	.driver		= {
+		.name	= "warrior",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= warrior_serio_ids,
+	.interrupt	= warrior_interrupt,
+	.connect	= warrior_connect,
+	.disconnect	= warrior_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init warrior_init(void)
+{
+	serio_register_driver(&warrior_drv);
+	return 0;
+}
+
+static void __exit warrior_exit(void)
+{
+	serio_unregister_driver(&warrior_drv);
+}
+
+module_init(warrior_init);
+module_exit(warrior_exit);
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
new file mode 100644
index 0000000..e55dee3
--- /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 0000000..b02eece
--- /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 0000000..4e8e8ea
--- /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 0000000..f7304f0
--- /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 0000000..0f1220a
--- /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 0000000..ef78bff
--- /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 0000000..eecb77d
--- /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 0000000..dc33f69
--- /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 0000000..2694ff2
--- /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 0000000..d3e9dd6
--- /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 0000000..859ed77
--- /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 0000000..2e8ce16
--- /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 0000000..596964c
--- /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 0000000..19eaec7
--- /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);
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
new file mode 100644
index 0000000..bb934e6
--- /dev/null
+++ b/drivers/input/misc/Kconfig
@@ -0,0 +1,60 @@
+#
+# Input misc drivers configuration
+#
+menuconfig INPUT_MISC
+	bool "Miscellaneous devices"
+	help
+	  Say Y here, and a list of miscellaneous input drivers will be displayed.
+	  Everything that didn't fit into the other categories is here. This option
+	  doesn't affect the kernel.
+
+	  If unsure, say Y.
+
+if INPUT_MISC
+
+config INPUT_PCSPKR
+	tristate "PC Speaker support"
+	depends on ALPHA || X86 || X86_64 || MIPS || PPC_PREP || PPC_CHRP || PPC_PSERIES
+	help
+	  Say Y here if you want the standard PC Speaker to be used for
+	  bells and whistles.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pcspkr.
+
+config INPUT_SPARCSPKR
+	tristate "SPARC Speaker support"
+	depends on PCI && (SPARC32 || SPARC64)
+	help
+	  Say Y here if you want the standard Speaker on Sparc PCI systems
+	  to be used for bells and whistles.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sparcspkr.
+
+config INPUT_M68K_BEEP
+	tristate "M68k Beeper support"
+	depends on M68K
+
+config INPUT_UINPUT
+	tristate "User level driver support"
+	help
+	  Say Y here if you want to support user level drivers for input
+	  subsystem accessible under char device 10:223 - /dev/input/uinput.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called uinput.
+
+config HP_SDC_RTC
+	tristate "HP SDC Real Time Clock"       
+	depends on GSC
+	select HP_SDC
+	help
+	  Say Y here if you want to support the built-in real time clock
+	  of the HP SDC controller.
+
+endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
new file mode 100644
index 0000000..f8d01c6
--- /dev/null
+++ b/drivers/input/misc/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the input misc drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_INPUT_SPARCSPKR)		+= sparcspkr.o
+obj-$(CONFIG_INPUT_PCSPKR)		+= pcspkr.o
+obj-$(CONFIG_INPUT_M68K_BEEP)		+= m68kspkr.o
+obj-$(CONFIG_INPUT_98SPKR)		+= 98spkr.o
+obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
+obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c
new file mode 100644
index 0000000..1cd7657
--- /dev/null
+++ b/drivers/input/misc/hp_sdc_rtc.c
@@ -0,0 +1,724 @@
+/*
+ * HP i8042 SDC + MSM-58321 BBRTC driver.
+ *
+ * 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:
+ * System Device Controller Microprocessor Firmware Theory of Operation
+ *      for Part Number 1820-4784 Revision B.  Dwg No. A-1820-4784-2
+ * efirtc.c by Stephane Eranian/Hewlett Packard
+ *
+ */
+
+#include <linux/hp_sdc.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/miscdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/poll.h>
+#include <linux/rtc.h>
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION("HP i8042 SDC + MSM-58321 RTC Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define RTC_VERSION "1.10d"
+
+static unsigned long epoch = 2000;
+
+static struct semaphore i8042tregs;
+
+static hp_sdc_irqhook hp_sdc_rtc_isr;
+
+static struct fasync_struct *hp_sdc_rtc_async_queue;
+
+static DECLARE_WAIT_QUEUE_HEAD(hp_sdc_rtc_wait);
+
+static loff_t hp_sdc_rtc_llseek(struct file *file, loff_t offset, int origin);
+
+static ssize_t hp_sdc_rtc_read(struct file *file, char *buf,
+			       size_t count, loff_t *ppos);
+
+static int hp_sdc_rtc_ioctl(struct inode *inode, struct file *file,
+			    unsigned int cmd, unsigned long arg);
+
+static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait);
+
+static int hp_sdc_rtc_open(struct inode *inode, struct file *file);
+static int hp_sdc_rtc_release(struct inode *inode, struct file *file);
+static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on);
+
+static int hp_sdc_rtc_read_proc(char *page, char **start, off_t off,
+				int count, int *eof, void *data);
+
+static void hp_sdc_rtc_isr (int irq, void *dev_id, 
+			    uint8_t status, uint8_t data) 
+{
+	return;
+}
+
+static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
+{
+	struct semaphore tsem;
+	hp_sdc_transaction t;
+	uint8_t tseq[91];
+	int i;
+	
+	i = 0;
+	while (i < 91) {
+		tseq[i++] = HP_SDC_ACT_DATAREG |
+			HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN;
+		tseq[i++] = 0x01;			/* write i8042[0x70] */
+	  	tseq[i]   = i / 7;			/* BBRTC reg address */
+		i++;
+		tseq[i++] = HP_SDC_CMD_DO_RTCR;		/* Trigger command   */
+		tseq[i++] = 2;		/* expect 1 stat/dat pair back.   */
+		i++; i++;               /* buffer for stat/dat pair       */
+	}
+	tseq[84] |= HP_SDC_ACT_SEMAPHORE;
+	t.endidx =		91;
+	t.seq =			tseq;
+	t.act.semaphore =	&tsem;
+	init_MUTEX_LOCKED(&tsem);
+	
+	if (hp_sdc_enqueue_transaction(&t)) return -1;
+	
+	down_interruptible(&tsem);  /* Put ourselves to sleep for results. */
+	
+	/* Check for nonpresence of BBRTC */
+	if (!((tseq[83] | tseq[90] | tseq[69] | tseq[76] |
+	       tseq[55] | tseq[62] | tseq[34] | tseq[41] |
+	       tseq[20] | tseq[27] | tseq[6]  | tseq[13]) & 0x0f))
+		return -1;
+
+	memset(rtctm, 0, sizeof(struct rtc_time));
+	rtctm->tm_year = (tseq[83] & 0x0f) + (tseq[90] & 0x0f) * 10;
+	rtctm->tm_mon  = (tseq[69] & 0x0f) + (tseq[76] & 0x0f) * 10;
+	rtctm->tm_mday = (tseq[55] & 0x0f) + (tseq[62] & 0x0f) * 10;
+	rtctm->tm_wday = (tseq[48] & 0x0f);
+	rtctm->tm_hour = (tseq[34] & 0x0f) + (tseq[41] & 0x0f) * 10;
+	rtctm->tm_min  = (tseq[20] & 0x0f) + (tseq[27] & 0x0f) * 10;
+	rtctm->tm_sec  = (tseq[6]  & 0x0f) + (tseq[13] & 0x0f) * 10;
+	
+	return 0;
+}
+
+static int hp_sdc_rtc_read_bbrtc (struct rtc_time *rtctm)
+{
+	struct rtc_time tm, tm_last;
+	int i = 0;
+
+	/* MSM-58321 has no read latch, so must read twice and compare. */
+
+	if (hp_sdc_rtc_do_read_bbrtc(&tm_last)) return -1;
+	if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
+
+	while (memcmp(&tm, &tm_last, sizeof(struct rtc_time))) {
+		if (i++ > 4) return -1;
+		memcpy(&tm_last, &tm, sizeof(struct rtc_time));
+		if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
+	}
+
+	memcpy(rtctm, &tm, sizeof(struct rtc_time));
+
+	return 0;
+}
+
+
+static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
+{
+	hp_sdc_transaction t;
+	uint8_t tseq[26] = {
+		HP_SDC_ACT_PRECMD | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
+		0,
+		HP_SDC_CMD_READ_T1, 2, 0, 0,
+		HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 
+		HP_SDC_CMD_READ_T2, 2, 0, 0,
+		HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 
+		HP_SDC_CMD_READ_T3, 2, 0, 0,
+		HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 
+		HP_SDC_CMD_READ_T4, 2, 0, 0,
+		HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 
+		HP_SDC_CMD_READ_T5, 2, 0, 0
+	};
+
+	t.endidx = numreg * 5;
+
+	tseq[1] = loadcmd;
+	tseq[t.endidx - 4] |= HP_SDC_ACT_SEMAPHORE; /* numreg assumed > 1 */
+
+	t.seq =			tseq;
+	t.act.semaphore =	&i8042tregs;
+
+	down_interruptible(&i8042tregs);  /* Sleep if output regs in use. */
+
+	if (hp_sdc_enqueue_transaction(&t)) return -1;
+	
+	down_interruptible(&i8042tregs);  /* Sleep until results come back. */
+	up(&i8042tregs);
+
+	return (tseq[5] | 
+		((uint64_t)(tseq[10]) << 8)  | ((uint64_t)(tseq[15]) << 16) |
+		((uint64_t)(tseq[20]) << 24) | ((uint64_t)(tseq[25]) << 32));
+}
+
+
+/* Read the i8042 real-time clock */
+static inline int hp_sdc_rtc_read_rt(struct timeval *res) {
+	int64_t raw;
+	uint32_t tenms; 
+	unsigned int days;
+
+	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_RT, 5);
+	if (raw < 0) return -1;
+
+	tenms = (uint32_t)raw & 0xffffff;
+	days  = (unsigned int)(raw >> 24) & 0xffff;
+
+	res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+	res->tv_sec =  (time_t)(tenms / 100) + days * 86400;
+
+	return 0;
+}
+
+
+/* Read the i8042 fast handshake timer */
+static inline int hp_sdc_rtc_read_fhs(struct timeval *res) {
+	uint64_t raw;
+	unsigned int tenms;
+
+	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_FHS, 2);
+	if (raw < 0) return -1;
+
+	tenms = (unsigned int)raw & 0xffff;
+
+	res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+	res->tv_sec  = (time_t)(tenms / 100);
+
+	return 0;
+}
+
+
+/* Read the i8042 match timer (a.k.a. alarm) */
+static inline int hp_sdc_rtc_read_mt(struct timeval *res) {
+	int64_t raw;	
+	uint32_t tenms; 
+
+	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_MT, 3);
+	if (raw < 0) return -1;
+
+	tenms = (uint32_t)raw & 0xffffff;
+
+	res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+	res->tv_sec  = (time_t)(tenms / 100);
+
+	return 0;
+}
+
+
+/* Read the i8042 delay timer */
+static inline int hp_sdc_rtc_read_dt(struct timeval *res) {
+	int64_t raw;
+	uint32_t tenms;
+
+	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_DT, 3);
+	if (raw < 0) return -1;
+
+	tenms = (uint32_t)raw & 0xffffff;
+
+	res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+	res->tv_sec  = (time_t)(tenms / 100);
+
+	return 0;
+}
+
+
+/* Read the i8042 cycle timer (a.k.a. periodic) */
+static inline int hp_sdc_rtc_read_ct(struct timeval *res) {
+	int64_t raw;
+	uint32_t tenms;
+
+	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_CT, 3);
+	if (raw < 0) return -1;
+
+	tenms = (uint32_t)raw & 0xffffff;
+
+	res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+	res->tv_sec  = (time_t)(tenms / 100);
+
+	return 0;
+}
+
+
+/* Set the i8042 real-time clock */
+static int hp_sdc_rtc_set_rt (struct timeval *setto)
+{
+	uint32_t tenms;
+	unsigned int days;
+	hp_sdc_transaction t;
+	uint8_t tseq[11] = {
+		HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+		HP_SDC_CMD_SET_RTMS, 3, 0, 0, 0,
+		HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+		HP_SDC_CMD_SET_RTD, 2, 0, 0 
+	};
+
+	t.endidx = 10;
+
+	if (0xffff < setto->tv_sec / 86400) return -1;
+	days = setto->tv_sec / 86400;
+	if (0xffff < setto->tv_usec / 1000000 / 86400) return -1;
+	days += ((setto->tv_sec % 86400) + setto->tv_usec / 1000000) / 86400;
+	if (days > 0xffff) return -1;
+
+	if (0xffffff < setto->tv_sec) return -1;
+	tenms  = setto->tv_sec * 100;
+	if (0xffffff < setto->tv_usec / 10000) return -1;
+	tenms += setto->tv_usec / 10000;
+	if (tenms > 0xffffff) return -1;
+
+	tseq[3] = (uint8_t)(tenms & 0xff);
+	tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
+	tseq[5] = (uint8_t)((tenms >> 16) & 0xff);
+
+	tseq[9] = (uint8_t)(days & 0xff);
+	tseq[10] = (uint8_t)((days >> 8) & 0xff);
+
+	t.seq =	tseq;
+
+	if (hp_sdc_enqueue_transaction(&t)) return -1;
+	return 0;
+}
+
+/* Set the i8042 fast handshake timer */
+static int hp_sdc_rtc_set_fhs (struct timeval *setto)
+{
+	uint32_t tenms;
+	hp_sdc_transaction t;
+	uint8_t tseq[5] = {
+		HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+		HP_SDC_CMD_SET_FHS, 2, 0, 0
+	};
+
+	t.endidx = 4;
+
+	if (0xffff < setto->tv_sec) return -1;
+	tenms  = setto->tv_sec * 100;
+	if (0xffff < setto->tv_usec / 10000) return -1;
+	tenms += setto->tv_usec / 10000;
+	if (tenms > 0xffff) return -1;
+
+	tseq[3] = (uint8_t)(tenms & 0xff);
+	tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
+
+	t.seq =	tseq;
+
+	if (hp_sdc_enqueue_transaction(&t)) return -1;
+	return 0;
+}
+
+
+/* Set the i8042 match timer (a.k.a. alarm) */
+#define hp_sdc_rtc_set_mt (setto) \
+	hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_MT)
+
+/* Set the i8042 delay timer */
+#define hp_sdc_rtc_set_dt (setto) \
+	hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_DT)
+
+/* Set the i8042 cycle timer (a.k.a. periodic) */
+#define hp_sdc_rtc_set_ct (setto) \
+	hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_CT)
+
+/* Set one of the i8042 3-byte wide timers */
+static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd)
+{
+	uint32_t tenms;
+	hp_sdc_transaction t;
+	uint8_t tseq[6] = {
+		HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+		0, 3, 0, 0, 0
+	};
+
+	t.endidx = 6;
+
+	if (0xffffff < setto->tv_sec) return -1;
+	tenms  = setto->tv_sec * 100;
+	if (0xffffff < setto->tv_usec / 10000) return -1;
+	tenms += setto->tv_usec / 10000;
+	if (tenms > 0xffffff) return -1;
+
+	tseq[1] = setcmd;
+	tseq[3] = (uint8_t)(tenms & 0xff);
+	tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
+	tseq[5] = (uint8_t)((tenms >> 16)  & 0xff);
+
+	t.seq =			tseq;
+
+	if (hp_sdc_enqueue_transaction(&t)) { 
+		return -1;
+	}
+	return 0;
+}
+
+static loff_t hp_sdc_rtc_llseek(struct file *file, loff_t offset, int origin)
+{
+        return -ESPIPE;
+}
+
+static ssize_t hp_sdc_rtc_read(struct file *file, char *buf,
+			       size_t count, loff_t *ppos) {
+	ssize_t retval;
+
+        if (count < sizeof(unsigned long))
+                return -EINVAL;
+
+	retval = put_user(68, (unsigned long *)buf);
+	return retval;
+}
+
+static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait)
+{
+        unsigned long l;
+
+	l = 0;
+        if (l != 0)
+                return POLLIN | POLLRDNORM;
+        return 0;
+}
+
+static int hp_sdc_rtc_open(struct inode *inode, struct file *file)
+{
+        return 0;
+}
+
+static int hp_sdc_rtc_release(struct inode *inode, struct file *file)
+{
+	/* Turn off interrupts? */
+
+        if (file->f_flags & FASYNC) {
+                hp_sdc_rtc_fasync (-1, file, 0);
+        }
+
+        return 0;
+}
+
+static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on)
+{
+        return fasync_helper (fd, filp, on, &hp_sdc_rtc_async_queue);
+}
+
+static int hp_sdc_rtc_proc_output (char *buf)
+{
+#define YN(bit) ("no")
+#define NY(bit) ("yes")
+        char *p;
+        struct rtc_time tm;
+	struct timeval tv;
+
+	memset(&tm, 0, sizeof(struct rtc_time));
+
+	p = buf;
+
+	if (hp_sdc_rtc_read_bbrtc(&tm)) {
+		p += sprintf(p, "BBRTC\t\t: READ FAILED!\n");
+	} else {
+		p += sprintf(p,
+			     "rtc_time\t: %02d:%02d:%02d\n"
+			     "rtc_date\t: %04d-%02d-%02d\n"
+			     "rtc_epoch\t: %04lu\n",
+			     tm.tm_hour, tm.tm_min, tm.tm_sec,
+			     tm.tm_year + 1900, tm.tm_mon + 1, 
+			     tm.tm_mday, epoch);
+	}
+
+	if (hp_sdc_rtc_read_rt(&tv)) {
+		p += sprintf(p, "i8042 rtc\t: READ FAILED!\n");
+	} else {
+		p += sprintf(p, "i8042 rtc\t: %ld.%02d seconds\n", 
+			     tv.tv_sec, tv.tv_usec/1000);
+	}
+
+	if (hp_sdc_rtc_read_fhs(&tv)) {
+		p += sprintf(p, "handshake\t: READ FAILED!\n");
+	} else {
+        	p += sprintf(p, "handshake\t: %ld.%02d seconds\n", 
+			     tv.tv_sec, tv.tv_usec/1000);
+	}
+
+	if (hp_sdc_rtc_read_mt(&tv)) {
+		p += sprintf(p, "alarm\t\t: READ FAILED!\n");
+	} else {
+		p += sprintf(p, "alarm\t\t: %ld.%02d seconds\n", 
+			     tv.tv_sec, tv.tv_usec/1000);
+	}
+
+	if (hp_sdc_rtc_read_dt(&tv)) {
+		p += sprintf(p, "delay\t\t: READ FAILED!\n");
+	} else {
+		p += sprintf(p, "delay\t\t: %ld.%02d seconds\n", 
+			     tv.tv_sec, tv.tv_usec/1000);
+	}
+
+	if (hp_sdc_rtc_read_ct(&tv)) {
+		p += sprintf(p, "periodic\t: READ FAILED!\n");
+	} else {
+		p += sprintf(p, "periodic\t: %ld.%02d seconds\n", 
+			     tv.tv_sec, tv.tv_usec/1000);
+	}
+
+        p += sprintf(p,
+                     "DST_enable\t: %s\n"
+                     "BCD\t\t: %s\n"
+                     "24hr\t\t: %s\n"
+                     "square_wave\t: %s\n"
+                     "alarm_IRQ\t: %s\n"
+                     "update_IRQ\t: %s\n"
+                     "periodic_IRQ\t: %s\n"
+		     "periodic_freq\t: %ld\n"
+                     "batt_status\t: %s\n",
+                     YN(RTC_DST_EN),
+                     NY(RTC_DM_BINARY),
+                     YN(RTC_24H),
+                     YN(RTC_SQWE),
+                     YN(RTC_AIE),
+                     YN(RTC_UIE),
+                     YN(RTC_PIE),
+                     1UL,
+                     1 ? "okay" : "dead");
+
+        return  p - buf;
+#undef YN
+#undef NY
+}
+
+static int hp_sdc_rtc_read_proc(char *page, char **start, off_t off,
+                         int count, int *eof, void *data)
+{
+	int len = hp_sdc_rtc_proc_output (page);
+        if (len <= off+count) *eof = 1;
+        *start = page + off;
+        len -= off;
+        if (len>count) len = count;
+        if (len<0) len = 0;
+        return len;
+}
+
+static int hp_sdc_rtc_ioctl(struct inode *inode, struct file *file, 
+			    unsigned int cmd, unsigned long arg)
+{
+#if 1
+	return -EINVAL;
+#else
+	
+        struct rtc_time wtime; 
+	struct timeval ttime;
+	int use_wtime = 0;
+
+	/* This needs major work. */
+
+        switch (cmd) {
+
+        case RTC_AIE_OFF:       /* Mask alarm int. enab. bit    */
+        case RTC_AIE_ON:        /* Allow alarm interrupts.      */
+	case RTC_PIE_OFF:       /* Mask periodic int. enab. bit */
+        case RTC_PIE_ON:        /* Allow periodic ints          */
+        case RTC_UIE_ON:        /* Allow ints for RTC updates.  */
+        case RTC_UIE_OFF:       /* Allow ints for RTC updates.  */
+        {
+		/* We cannot mask individual user timers and we
+		   cannot tell them apart when they occur, so it 
+		   would be disingenuous to succeed these IOCTLs */
+		return -EINVAL;
+        }
+        case RTC_ALM_READ:      /* Read the present alarm time */
+        {
+		if (hp_sdc_rtc_read_mt(&ttime)) return -EFAULT;
+		if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
+
+		wtime.tm_hour = ttime.tv_sec / 3600;  ttime.tv_sec %= 3600;
+		wtime.tm_min  = ttime.tv_sec / 60;    ttime.tv_sec %= 60;
+		wtime.tm_sec  = ttime.tv_sec;
+                
+		break;
+        }
+        case RTC_IRQP_READ:     /* Read the periodic IRQ rate.  */
+        {
+                return put_user(hp_sdc_rtc_freq, (unsigned long *)arg);
+        }
+        case RTC_IRQP_SET:      /* Set periodic IRQ rate.       */
+        {
+                /* 
+                 * The max we can do is 100Hz.
+		 */
+
+                if ((arg < 1) || (arg > 100)) return -EINVAL;
+		ttime.tv_sec = 0;
+		ttime.tv_usec = 1000000 / arg;
+		if (hp_sdc_rtc_set_ct(&ttime)) return -EFAULT;
+		hp_sdc_rtc_freq = arg;
+                return 0;
+        }
+        case RTC_ALM_SET:       /* Store a time into the alarm */
+        {
+                /*
+                 * This expects a struct hp_sdc_rtc_time. Writing 0xff means
+                 * "don't care" or "match all" for PC timers.  The HP SDC
+		 * does not support that perk, but it could be emulated fairly
+		 * easily.  Only the tm_hour, tm_min and tm_sec are used.
+		 * We could do it with 10ms accuracy with the HP SDC, if the 
+		 * rtc interface left us a way to do that.
+                 */
+                struct hp_sdc_rtc_time alm_tm;
+
+                if (copy_from_user(&alm_tm, (struct hp_sdc_rtc_time*)arg,
+                                   sizeof(struct hp_sdc_rtc_time)))
+                       return -EFAULT;
+
+                if (alm_tm.tm_hour > 23) return -EINVAL;
+		if (alm_tm.tm_min  > 59) return -EINVAL;
+		if (alm_tm.tm_sec  > 59) return -EINVAL;  
+
+		ttime.sec = alm_tm.tm_hour * 3600 + 
+		  alm_tm.tm_min * 60 + alm_tm.tm_sec;
+		ttime.usec = 0;
+		if (hp_sdc_rtc_set_mt(&ttime)) return -EFAULT;
+                return 0;
+        }
+        case RTC_RD_TIME:       /* Read the time/date from RTC  */
+        {
+		if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
+                break;
+        }
+        case RTC_SET_TIME:      /* Set the RTC */
+        {
+                struct rtc_time hp_sdc_rtc_tm;
+                unsigned char mon, day, hrs, min, sec, leap_yr;
+                unsigned int yrs;
+
+                if (!capable(CAP_SYS_TIME))
+                        return -EACCES;
+		if (copy_from_user(&hp_sdc_rtc_tm, (struct rtc_time *)arg,
+                                   sizeof(struct rtc_time)))
+                        return -EFAULT;
+
+                yrs = hp_sdc_rtc_tm.tm_year + 1900;
+                mon = hp_sdc_rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
+                day = hp_sdc_rtc_tm.tm_mday;
+                hrs = hp_sdc_rtc_tm.tm_hour;
+                min = hp_sdc_rtc_tm.tm_min;
+                sec = hp_sdc_rtc_tm.tm_sec;
+
+                if (yrs < 1970)
+                        return -EINVAL;
+
+                leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
+
+                if ((mon > 12) || (day == 0))
+                        return -EINVAL;
+                if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
+                        return -EINVAL;
+		if ((hrs >= 24) || (min >= 60) || (sec >= 60))
+                        return -EINVAL;
+
+                if ((yrs -= eH) > 255)    /* They are unsigned */
+                        return -EINVAL;
+
+
+                return 0;
+        }
+        case RTC_EPOCH_READ:    /* Read the epoch.      */
+        {
+                return put_user (epoch, (unsigned long *)arg);
+        }
+        case RTC_EPOCH_SET:     /* Set the epoch.       */
+        {
+                /* 
+                 * There were no RTC clocks before 1900.
+                 */
+                if (arg < 1900)
+		  return -EINVAL;
+		if (!capable(CAP_SYS_TIME))
+		  return -EACCES;
+		
+                epoch = arg;
+                return 0;
+        }
+        default:
+                return -EINVAL;
+        }
+        return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
+#endif
+}
+
+static struct file_operations hp_sdc_rtc_fops = {
+        .owner =	THIS_MODULE,
+        .llseek =	hp_sdc_rtc_llseek,
+        .read =		hp_sdc_rtc_read,
+        .poll =		hp_sdc_rtc_poll,
+        .ioctl =	hp_sdc_rtc_ioctl,
+        .open =		hp_sdc_rtc_open,
+        .release =	hp_sdc_rtc_release,
+        .fasync =	hp_sdc_rtc_fasync,
+};
+
+static struct miscdevice hp_sdc_rtc_dev = {
+        .minor =	RTC_MINOR,
+        .name =		"rtc_HIL",
+        .fops =		&hp_sdc_rtc_fops
+};
+
+static int __init hp_sdc_rtc_init(void)
+{
+	int ret;
+
+	init_MUTEX(&i8042tregs);
+
+	if ((ret = hp_sdc_request_timer_irq(&hp_sdc_rtc_isr)))
+		return ret;
+	misc_register(&hp_sdc_rtc_dev);
+        create_proc_read_entry ("driver/rtc", 0, 0, 
+				hp_sdc_rtc_read_proc, NULL);
+
+	printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support loaded "
+			 "(RTC v " RTC_VERSION ")\n");
+
+	return 0;
+}
+
+static void __exit hp_sdc_rtc_exit(void)
+{
+	remove_proc_entry ("driver/rtc", NULL);
+        misc_deregister(&hp_sdc_rtc_dev);
+	hp_sdc_release_timer_irq(hp_sdc_rtc_isr);
+        printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support unloaded\n");
+}
+
+module_init(hp_sdc_rtc_init);
+module_exit(hp_sdc_rtc_exit);
diff --git a/drivers/input/misc/m68kspkr.c b/drivers/input/misc/m68kspkr.c
new file mode 100644
index 0000000..64abdd9
--- /dev/null
+++ b/drivers/input/misc/m68kspkr.c
@@ -0,0 +1,83 @@
+/*
+ *  m68k beeper driver for Linux
+ *
+ *  Copyright (c) 2002 Richard Zidlicky
+ *  Copyright (c) 2002 Vojtech Pavlik
+ *  Copyright (c) 1992 Orest Zborowski
+ *
+ */
+
+/*
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Richard Zidlicky <rz@linux-m68k.org>");
+MODULE_DESCRIPTION("m68k beeper driver");
+MODULE_LICENSE("GPL");
+
+static char m68kspkr_name[] = "m68k beeper";
+static char m68kspkr_phys[] = "m68k/generic";
+static struct input_dev m68kspkr_dev;
+
+static int m68kspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	unsigned int count = 0;
+
+	if (type != EV_SND)
+		return -1;
+
+	switch (code) {
+		case SND_BELL: if (value) value = 1000;
+		case SND_TONE: break;
+		default: return -1;
+	}
+
+	if (value > 20 && value < 32767)
+		count = 1193182 / value;
+
+	mach_beep(count, -1);
+
+	return 0;
+}
+
+static int __init m68kspkr_init(void)
+{
+        if (!mach_beep){
+		printk("%s: no lowlevel beep support\n", m68kspkr_name);
+		return -1;
+        }
+
+	m68kspkr_dev.evbit[0] = BIT(EV_SND);
+	m68kspkr_dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
+	m68kspkr_dev.event = m68kspkr_event;
+
+	m68kspkr_dev.name = m68kspkr_name;
+	m68kspkr_dev.phys = m68kspkr_phys;
+	m68kspkr_dev.id.bustype = BUS_HOST;
+	m68kspkr_dev.id.vendor = 0x001f;
+	m68kspkr_dev.id.product = 0x0001;
+	m68kspkr_dev.id.version = 0x0100;
+
+	input_register_device(&m68kspkr_dev);
+
+        printk(KERN_INFO "input: %s\n", m68kspkr_name);
+
+	return 0;
+}
+
+static void __exit m68kspkr_exit(void)
+{
+        input_unregister_device(&m68kspkr_dev);
+}
+
+module_init(m68kspkr_init);
+module_exit(m68kspkr_exit);
diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c
new file mode 100644
index 0000000..3013194
--- /dev/null
+++ b/drivers/input/misc/pcspkr.c
@@ -0,0 +1,97 @@
+/*
+ *  PC Speaker beeper driver for Linux
+ *
+ *  Copyright (c) 2002 Vojtech Pavlik
+ *  Copyright (c) 1992 Orest Zborowski
+ *
+ */
+
+/*
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <asm/8253pit.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("PC Speaker beeper driver");
+MODULE_LICENSE("GPL");
+
+static char pcspkr_name[] = "PC Speaker";
+static char pcspkr_phys[] = "isa0061/input0";
+static struct input_dev pcspkr_dev;
+
+static DEFINE_SPINLOCK(i8253_beep_lock);
+
+static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	unsigned int count = 0;
+	unsigned long flags;
+
+	if (type != EV_SND)
+		return -1;
+
+	switch (code) {
+		case SND_BELL: if (value) value = 1000;
+		case SND_TONE: break;
+		default: return -1;
+	}
+
+	if (value > 20 && value < 32767)
+		count = PIT_TICK_RATE / value;
+
+	spin_lock_irqsave(&i8253_beep_lock, flags);
+
+	if (count) {
+		/* enable counter 2 */
+		outb_p(inb_p(0x61) | 3, 0x61);
+		/* set command for counter 2, 2 byte write */
+		outb_p(0xB6, 0x43);
+		/* select desired HZ */
+		outb_p(count & 0xff, 0x42);
+		outb((count >> 8) & 0xff, 0x42);
+	} else {
+		/* disable counter 2 */
+		outb(inb_p(0x61) & 0xFC, 0x61);
+	}
+
+	spin_unlock_irqrestore(&i8253_beep_lock, flags);
+
+	return 0;
+}
+
+static int __init pcspkr_init(void)
+{
+	pcspkr_dev.evbit[0] = BIT(EV_SND);
+	pcspkr_dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
+	pcspkr_dev.event = pcspkr_event;
+
+	pcspkr_dev.name = pcspkr_name;
+	pcspkr_dev.phys = pcspkr_phys;
+	pcspkr_dev.id.bustype = BUS_ISA;
+	pcspkr_dev.id.vendor = 0x001f;
+	pcspkr_dev.id.product = 0x0001;
+	pcspkr_dev.id.version = 0x0100;
+
+	input_register_device(&pcspkr_dev);
+
+        printk(KERN_INFO "input: %s\n", pcspkr_name);
+
+	return 0;
+}
+
+static void __exit pcspkr_exit(void)
+{
+        input_unregister_device(&pcspkr_dev);
+	/* turn off the speaker */
+	pcspkr_event(NULL, EV_SND, SND_BELL, 0);
+}
+
+module_init(pcspkr_init);
+module_exit(pcspkr_exit);
diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c
new file mode 100644
index 0000000..cdc3fb3
--- /dev/null
+++ b/drivers/input/misc/sparcspkr.c
@@ -0,0 +1,189 @@
+/*
+ *  Driver for PC-speaker like devices found on various Sparc systems.
+ *
+ *  Copyright (c) 2002 Vojtech Pavlik
+ *  Copyright (c) 2002 David S. Miller (davem@redhat.com)
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+
+#include <asm/io.h>
+#include <asm/ebus.h>
+#ifdef CONFIG_SPARC64
+#include <asm/isa.h>
+#endif
+
+MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
+MODULE_DESCRIPTION("PC Speaker beeper driver");
+MODULE_LICENSE("GPL");
+
+static unsigned long beep_iobase;
+
+static char *sparcspkr_isa_name = "Sparc ISA Speaker";
+static char *sparcspkr_ebus_name = "Sparc EBUS Speaker";
+static char *sparcspkr_phys = "sparc/input0";
+static struct input_dev sparcspkr_dev;
+
+DEFINE_SPINLOCK(beep_lock);
+
+static void __init init_sparcspkr_struct(void)
+{
+	sparcspkr_dev.evbit[0] = BIT(EV_SND);
+	sparcspkr_dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE);
+
+	sparcspkr_dev.phys = sparcspkr_phys;
+	sparcspkr_dev.id.bustype = BUS_ISA;
+	sparcspkr_dev.id.vendor = 0x001f;
+	sparcspkr_dev.id.product = 0x0001;
+	sparcspkr_dev.id.version = 0x0100;
+}
+
+static int ebus_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	unsigned int count = 0;
+	unsigned long flags;
+
+	if (type != EV_SND)
+		return -1;
+
+	switch (code) {
+		case SND_BELL: if (value) value = 1000;
+		case SND_TONE: break;
+		default: return -1;
+	}
+
+	if (value > 20 && value < 32767)
+		count = 1193182 / value;
+
+	spin_lock_irqsave(&beep_lock, flags);
+
+	/* EBUS speaker only has on/off state, the frequency does not
+	 * appear to be programmable.
+	 */
+	if (count) {
+		if (beep_iobase & 0x2UL)
+			outb(1, beep_iobase);
+		else
+			outl(1, beep_iobase);
+	} else {
+		if (beep_iobase & 0x2UL)
+			outb(0, beep_iobase);
+		else
+			outl(0, beep_iobase);
+	}
+
+	spin_unlock_irqrestore(&beep_lock, flags);
+
+	return 0;
+}
+
+static int __init init_ebus_beep(struct linux_ebus_device *edev)
+{
+	beep_iobase = edev->resource[0].start;
+
+	init_sparcspkr_struct();
+
+	sparcspkr_dev.name = sparcspkr_ebus_name;
+	sparcspkr_dev.event = ebus_spkr_event;
+
+	input_register_device(&sparcspkr_dev);
+
+        printk(KERN_INFO "input: %s\n", sparcspkr_ebus_name);
+	return 0;
+}
+
+#ifdef CONFIG_SPARC64
+static int isa_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	unsigned int count = 0;
+	unsigned long flags;
+
+	if (type != EV_SND)
+		return -1;
+
+	switch (code) {
+		case SND_BELL: if (value) value = 1000;
+		case SND_TONE: break;
+		default: return -1;
+	}
+
+	if (value > 20 && value < 32767)
+		count = 1193182 / value;
+
+	spin_lock_irqsave(&beep_lock, flags);
+
+	if (count) {
+		/* enable counter 2 */
+		outb(inb(beep_iobase + 0x61) | 3, beep_iobase + 0x61);
+		/* set command for counter 2, 2 byte write */
+		outb(0xB6, beep_iobase + 0x43);
+		/* select desired HZ */
+		outb(count & 0xff, beep_iobase + 0x42);
+		outb((count >> 8) & 0xff, beep_iobase + 0x42);
+	} else {
+		/* disable counter 2 */
+		outb(inb_p(beep_iobase + 0x61) & 0xFC, beep_iobase + 0x61);
+	}
+
+	spin_unlock_irqrestore(&beep_lock, flags);
+
+	return 0;
+}
+
+static int __init init_isa_beep(struct sparc_isa_device *isa_dev)
+{
+	beep_iobase = isa_dev->resource.start;
+
+	init_sparcspkr_struct();
+
+	sparcspkr_dev.name = sparcspkr_isa_name;
+	sparcspkr_dev.event = isa_spkr_event;
+	sparcspkr_dev.id.bustype = BUS_ISA;
+
+	input_register_device(&sparcspkr_dev);
+
+        printk(KERN_INFO "input: %s\n", sparcspkr_isa_name);
+	return 0;
+}
+#endif
+
+static int __init sparcspkr_init(void)
+{
+	struct linux_ebus *ebus;
+	struct linux_ebus_device *edev = NULL;
+#ifdef CONFIG_SPARC64
+	struct sparc_isa_bridge *isa_br;
+	struct sparc_isa_device *isa_dev;
+#endif
+
+	for_each_ebus(ebus) {
+		for_each_ebusdev(edev, ebus) {
+			if (!strcmp(edev->prom_name, "beep"))
+				return init_ebus_beep(edev);
+		}
+	}
+#ifdef CONFIG_SPARC64
+	for_each_isa(isa_br) {
+		for_each_isadev(isa_dev, isa_br) {
+			/* A hack, the beep device's base lives in
+			 * the DMA isa node.
+			 */
+			if (!strcmp(isa_dev->prom_name, "dma"))
+				return init_isa_beep(isa_dev);
+		}
+	}
+#endif
+
+	return -ENODEV;
+}
+
+static void __exit sparcspkr_exit(void)
+{
+	input_unregister_device(&sparcspkr_dev);
+}
+
+module_init(sparcspkr_init);
+module_exit(sparcspkr_exit);
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
new file mode 100644
index 0000000..158c8e8
--- /dev/null
+++ b/drivers/input/misc/uinput.c
@@ -0,0 +1,620 @@
+/*
+ *  User level driver support for input subsystem
+ *
+ * Heavily based on evdev.c by Vojtech Pavlik
+ *
+ * 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
+ *
+ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
+ *
+ * Changes/Revisions:
+ *	0.2	16/10/2004 (Micah Dowty <micah@navi.cx>)
+ *		- added force feedback support
+ *              - added UI_SET_PHYS
+ *	0.1	20/06/2002
+ *		- first public version
+ */
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/smp_lock.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uinput.h>
+
+static int uinput_dev_open(struct input_dev *dev)
+{
+	return 0;
+}
+
+static void uinput_dev_close(struct input_dev *dev)
+{
+
+}
+
+static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	struct uinput_device	*udev;
+
+	udev = dev->private;
+
+	udev->buff[udev->head].type = type;
+	udev->buff[udev->head].code = code;
+	udev->buff[udev->head].value = value;
+	do_gettimeofday(&udev->buff[udev->head].time);
+	udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
+
+	wake_up_interruptible(&udev->waitq);
+
+	return 0;
+}
+
+static int uinput_request_alloc_id(struct input_dev *dev, struct uinput_request *request)
+{
+	/* Atomically allocate an ID for the given request. Returns 0 on success. */
+	struct uinput_device *udev = dev->private;
+	int id;
+
+	down(&udev->requests_sem);
+	for (id=0; id<UINPUT_NUM_REQUESTS; id++)
+		if (!udev->requests[id]) {
+			udev->requests[id] = request;
+			request->id = id;
+			up(&udev->requests_sem);
+			return 0;
+		}
+	up(&udev->requests_sem);
+	return -1;
+}
+
+static struct uinput_request* uinput_request_find(struct uinput_device *udev, int id)
+{
+	/* Find an input request, by ID. Returns NULL if the ID isn't valid. */
+	if (id >= UINPUT_NUM_REQUESTS || id < 0)
+		return NULL;
+	if (udev->requests[id]->completed)
+		return NULL;
+	return udev->requests[id];
+}
+
+static void uinput_request_init(struct input_dev *dev, struct uinput_request *request, int code)
+{
+	struct uinput_device *udev = dev->private;
+
+	memset(request, 0, sizeof(struct uinput_request));
+	request->code = code;
+	init_waitqueue_head(&request->waitq);
+
+	/* Allocate an ID. If none are available right away, wait. */
+	request->retval = wait_event_interruptible(udev->requests_waitq,
+				       !uinput_request_alloc_id(dev, request));
+}
+
+static void uinput_request_submit(struct input_dev *dev, struct uinput_request *request)
+{
+	struct uinput_device *udev = dev->private;
+	int retval;
+
+	/* Tell our userspace app about this new request by queueing an input event */
+	uinput_dev_event(dev, EV_UINPUT, request->code, request->id);
+
+	/* Wait for the request to complete */
+	retval = wait_event_interruptible(request->waitq, request->completed);
+	if (retval)
+		request->retval = retval;
+
+	/* Release this request's ID, let others know it's available */
+	udev->requests[request->id] = NULL;
+	wake_up_interruptible(&udev->requests_waitq);
+}
+
+static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect)
+{
+	struct uinput_request request;
+
+	if (!test_bit(EV_FF, dev->evbit))
+		return -ENOSYS;
+
+	uinput_request_init(dev, &request, UI_FF_UPLOAD);
+	if (request.retval)
+		return request.retval;
+	request.u.effect = effect;
+	uinput_request_submit(dev, &request);
+	return request.retval;
+}
+
+static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
+{
+	struct uinput_request request;
+
+	if (!test_bit(EV_FF, dev->evbit))
+		return -ENOSYS;
+
+	uinput_request_init(dev, &request, UI_FF_ERASE);
+	if (request.retval)
+		return request.retval;
+	request.u.effect_id = effect_id;
+	uinput_request_submit(dev, &request);
+	return request.retval;
+}
+
+static int uinput_create_device(struct uinput_device *udev)
+{
+	if (!udev->dev->name) {
+		printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
+		return -EINVAL;
+	}
+
+	udev->dev->open = uinput_dev_open;
+	udev->dev->close = uinput_dev_close;
+	udev->dev->event = uinput_dev_event;
+	udev->dev->upload_effect = uinput_dev_upload_effect;
+	udev->dev->erase_effect = uinput_dev_erase_effect;
+	udev->dev->private = udev;
+
+	init_waitqueue_head(&(udev->waitq));
+
+	input_register_device(udev->dev);
+
+	set_bit(UIST_CREATED, &(udev->state));
+
+	return 0;
+}
+
+static int uinput_destroy_device(struct uinput_device *udev)
+{
+	if (!test_bit(UIST_CREATED, &(udev->state))) {
+		printk(KERN_WARNING "%s: create the device first\n", UINPUT_NAME);
+		return -EINVAL;
+	}
+
+	input_unregister_device(udev->dev);
+
+	clear_bit(UIST_CREATED, &(udev->state));
+
+	return 0;
+}
+
+static int uinput_open(struct inode *inode, struct file *file)
+{
+	struct uinput_device	*newdev;
+	struct input_dev	*newinput;
+
+	newdev = kmalloc(sizeof(struct uinput_device), GFP_KERNEL);
+	if (!newdev)
+		goto error;
+	memset(newdev, 0, sizeof(struct uinput_device));
+	init_MUTEX(&newdev->requests_sem);
+	init_waitqueue_head(&newdev->requests_waitq);
+
+	newinput = kmalloc(sizeof(struct input_dev), GFP_KERNEL);
+	if (!newinput)
+		goto cleanup;
+	memset(newinput, 0, sizeof(struct input_dev));
+
+	newdev->dev = newinput;
+
+	file->private_data = newdev;
+
+	return 0;
+cleanup:
+	kfree(newdev);
+error:
+	return -ENOMEM;
+}
+
+static int uinput_validate_absbits(struct input_dev *dev)
+{
+	unsigned int cnt;
+	int retval = 0;
+
+	for (cnt = 0; cnt < ABS_MAX + 1; cnt++) {
+		if (!test_bit(cnt, dev->absbit))
+			continue;
+
+		if ((dev->absmax[cnt] <= dev->absmin[cnt])) {
+			printk(KERN_DEBUG
+				"%s: invalid abs[%02x] min:%d max:%d\n",
+				UINPUT_NAME, cnt,
+				dev->absmin[cnt], dev->absmax[cnt]);
+			retval = -EINVAL;
+			break;
+		}
+
+		if (dev->absflat[cnt] > (dev->absmax[cnt] - dev->absmin[cnt])) {
+			printk(KERN_DEBUG
+				"%s: absflat[%02x] out of range: %d "
+				"(min:%d/max:%d)\n",
+				UINPUT_NAME, cnt, dev->absflat[cnt],
+				dev->absmin[cnt], dev->absmax[cnt]);
+			retval = -EINVAL;
+			break;
+		}
+	}
+	return retval;
+}
+
+static int uinput_alloc_device(struct file *file, const char __user *buffer, size_t count)
+{
+	struct uinput_user_dev	*user_dev;
+	struct input_dev	*dev;
+	struct uinput_device	*udev;
+	int			size,
+				retval;
+
+	retval = count;
+
+	udev = file->private_data;
+	dev = udev->dev;
+
+	user_dev = kmalloc(sizeof(*user_dev), GFP_KERNEL);
+	if (!user_dev) {
+		retval = -ENOMEM;
+		goto exit;
+	}
+
+	if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) {
+		retval = -EFAULT;
+		goto exit;
+	}
+
+	if (NULL != dev->name)
+		kfree(dev->name);
+
+	size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1;
+	dev->name = kmalloc(size, GFP_KERNEL);
+	if (!dev->name) {
+		retval = -ENOMEM;
+		goto exit;
+	}
+
+	strlcpy(dev->name, user_dev->name, size);
+	dev->id.bustype	= user_dev->id.bustype;
+	dev->id.vendor	= user_dev->id.vendor;
+	dev->id.product	= user_dev->id.product;
+	dev->id.version	= user_dev->id.version;
+	dev->ff_effects_max = user_dev->ff_effects_max;
+
+	size = sizeof(int) * (ABS_MAX + 1);
+	memcpy(dev->absmax, user_dev->absmax, size);
+	memcpy(dev->absmin, user_dev->absmin, size);
+	memcpy(dev->absfuzz, user_dev->absfuzz, size);
+	memcpy(dev->absflat, user_dev->absflat, size);
+
+	/* check if absmin/absmax/absfuzz/absflat are filled as
+	 * told in Documentation/input/input-programming.txt */
+	if (test_bit(EV_ABS, dev->evbit)) {
+		retval = uinput_validate_absbits(dev);
+		if (retval < 0)
+			kfree(dev->name);
+	}
+
+exit:
+	kfree(user_dev);
+	return retval;
+}
+
+static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
+{
+	struct uinput_device *udev = file->private_data;
+
+	if (test_bit(UIST_CREATED, &(udev->state))) {
+		struct input_event	ev;
+
+		if (copy_from_user(&ev, buffer, sizeof(struct input_event)))
+			return -EFAULT;
+		input_event(udev->dev, ev.type, ev.code, ev.value);
+	}
+	else
+		count = uinput_alloc_device(file, buffer, count);
+
+	return count;
+}
+
+static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+{
+	struct uinput_device *udev = file->private_data;
+	int retval = 0;
+
+	if (!test_bit(UIST_CREATED, &(udev->state)))
+		return -ENODEV;
+
+	if ((udev->head == udev->tail) && (file->f_flags & O_NONBLOCK))
+		return -EAGAIN;
+
+	retval = wait_event_interruptible(udev->waitq,
+			(udev->head != udev->tail) ||
+			!test_bit(UIST_CREATED, &(udev->state)));
+
+	if (retval)
+		return retval;
+
+	if (!test_bit(UIST_CREATED, &(udev->state)))
+		return -ENODEV;
+
+	while ((udev->head != udev->tail) &&
+	    (retval + sizeof(struct input_event) <= count)) {
+		if (copy_to_user(buffer + retval, &(udev->buff[udev->tail]),
+		    sizeof(struct input_event))) return -EFAULT;
+		udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
+		retval += sizeof(struct input_event);
+	}
+
+	return retval;
+}
+
+static unsigned int uinput_poll(struct file *file, poll_table *wait)
+{
+	struct uinput_device *udev = file->private_data;
+
+	poll_wait(file, &udev->waitq, wait);
+
+	if (udev->head != udev->tail)
+		return POLLIN | POLLRDNORM;
+
+	return 0;
+}
+
+static int uinput_burn_device(struct uinput_device *udev)
+{
+	if (test_bit(UIST_CREATED, &(udev->state)))
+		uinput_destroy_device(udev);
+
+	if (NULL != udev->dev->name)
+		kfree(udev->dev->name);
+	if (NULL != udev->dev->phys)
+		kfree(udev->dev->phys);
+
+	kfree(udev->dev);
+	kfree(udev);
+
+	return 0;
+}
+
+static int uinput_close(struct inode *inode, struct file *file)
+{
+	return uinput_burn_device(file->private_data);
+}
+
+static int uinput_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int			retval = 0;
+	struct uinput_device	*udev;
+	void __user             *p = (void __user *)arg;
+	struct uinput_ff_upload ff_up;
+	struct uinput_ff_erase  ff_erase;
+	struct uinput_request   *req;
+	int                     length;
+
+	udev = file->private_data;
+
+	/* device attributes can not be changed after the device is created */
+	switch (cmd) {
+		case UI_SET_EVBIT:
+		case UI_SET_KEYBIT:
+		case UI_SET_RELBIT:
+		case UI_SET_ABSBIT:
+		case UI_SET_MSCBIT:
+		case UI_SET_LEDBIT:
+		case UI_SET_SNDBIT:
+		case UI_SET_FFBIT:
+		case UI_SET_PHYS:
+			if (test_bit(UIST_CREATED, &(udev->state)))
+				return -EINVAL;
+	}
+
+	switch (cmd) {
+		case UI_DEV_CREATE:
+			retval = uinput_create_device(udev);
+			break;
+
+		case UI_DEV_DESTROY:
+			retval = uinput_destroy_device(udev);
+			break;
+
+		case UI_SET_EVBIT:
+			if (arg > EV_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->evbit);
+			break;
+
+		case UI_SET_KEYBIT:
+			if (arg > KEY_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->keybit);
+			break;
+
+		case UI_SET_RELBIT:
+			if (arg > REL_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->relbit);
+			break;
+
+		case UI_SET_ABSBIT:
+			if (arg > ABS_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->absbit);
+			break;
+
+		case UI_SET_MSCBIT:
+			if (arg > MSC_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->mscbit);
+			break;
+
+		case UI_SET_LEDBIT:
+			if (arg > LED_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->ledbit);
+			break;
+
+		case UI_SET_SNDBIT:
+			if (arg > SND_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->sndbit);
+			break;
+
+		case UI_SET_FFBIT:
+			if (arg > FF_MAX) {
+				retval = -EINVAL;
+				break;
+			}
+			set_bit(arg, udev->dev->ffbit);
+			break;
+
+		case UI_SET_PHYS:
+			length = strnlen_user(p, 1024);
+			if (length <= 0) {
+				retval = -EFAULT;
+				break;
+			}
+			if (NULL != udev->dev->phys)
+				kfree(udev->dev->phys);
+			udev->dev->phys = kmalloc(length, GFP_KERNEL);
+			if (!udev->dev->phys) {
+				retval = -ENOMEM;
+				break;
+			}
+			if (copy_from_user(udev->dev->phys, p, length)) {
+				retval = -EFAULT;
+				kfree(udev->dev->phys);
+				udev->dev->phys = NULL;
+				break;
+			}
+			udev->dev->phys[length-1] = '\0';
+			break;
+
+		case UI_BEGIN_FF_UPLOAD:
+			if (copy_from_user(&ff_up, p, sizeof(ff_up))) {
+				retval = -EFAULT;
+				break;
+			}
+			req = uinput_request_find(udev, ff_up.request_id);
+			if (!(req && req->code==UI_FF_UPLOAD && req->u.effect)) {
+				retval = -EINVAL;
+				break;
+			}
+			ff_up.retval = 0;
+			memcpy(&ff_up.effect, req->u.effect, sizeof(struct ff_effect));
+			if (copy_to_user(p, &ff_up, sizeof(ff_up))) {
+				retval = -EFAULT;
+				break;
+			}
+			break;
+
+		case UI_BEGIN_FF_ERASE:
+			if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
+				retval = -EFAULT;
+				break;
+			}
+			req = uinput_request_find(udev, ff_erase.request_id);
+			if (!(req && req->code==UI_FF_ERASE)) {
+				retval = -EINVAL;
+				break;
+			}
+			ff_erase.retval = 0;
+			ff_erase.effect_id = req->u.effect_id;
+			if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
+				retval = -EFAULT;
+				break;
+			}
+			break;
+
+		case UI_END_FF_UPLOAD:
+			if (copy_from_user(&ff_up, p, sizeof(ff_up))) {
+				retval = -EFAULT;
+				break;
+			}
+			req = uinput_request_find(udev, ff_up.request_id);
+			if (!(req && req->code==UI_FF_UPLOAD && req->u.effect)) {
+				retval = -EINVAL;
+				break;
+			}
+			req->retval = ff_up.retval;
+			memcpy(req->u.effect, &ff_up.effect, sizeof(struct ff_effect));
+			req->completed = 1;
+			wake_up_interruptible(&req->waitq);
+			break;
+
+		case UI_END_FF_ERASE:
+			if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
+				retval = -EFAULT;
+				break;
+			}
+			req = uinput_request_find(udev, ff_erase.request_id);
+			if (!(req && req->code==UI_FF_ERASE)) {
+				retval = -EINVAL;
+				break;
+			}
+			req->retval = ff_erase.retval;
+			req->completed = 1;
+			wake_up_interruptible(&req->waitq);
+			break;
+
+		default:
+			retval = -EINVAL;
+	}
+	return retval;
+}
+
+static struct file_operations uinput_fops = {
+	.owner =	THIS_MODULE,
+	.open =		uinput_open,
+	.release =	uinput_close,
+	.read =		uinput_read,
+	.write =	uinput_write,
+	.poll =		uinput_poll,
+	.ioctl =	uinput_ioctl,
+};
+
+static struct miscdevice uinput_misc = {
+	.fops =		&uinput_fops,
+	.minor =	UINPUT_MINOR,
+	.name =		UINPUT_NAME,
+};
+
+static int __init uinput_init(void)
+{
+	return misc_register(&uinput_misc);
+}
+
+static void __exit uinput_exit(void)
+{
+	misc_deregister(&uinput_misc);
+}
+
+MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
+MODULE_DESCRIPTION("User level driver support for input subsystem");
+MODULE_LICENSE("GPL");
+
+module_init(uinput_init);
+module_exit(uinput_exit);
+
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
new file mode 100644
index 0000000..537154d
--- /dev/null
+++ b/drivers/input/mouse/Kconfig
@@ -0,0 +1,138 @@
+#
+# Mouse driver configuration
+#
+menuconfig INPUT_MOUSE
+	bool "Mouse"
+	default y
+	help
+	  Say Y here, and a list of supported mice will be displayed.
+	  This option doesn't affect the kernel.
+
+	  If unsure, say Y.
+
+if INPUT_MOUSE
+
+config MOUSE_PS2
+	tristate "PS/2 mouse"
+	default y
+	select SERIO
+	select SERIO_LIBPS2
+	select SERIO_I8042 if PC
+	select SERIO_GSCPS2 if GSC
+	---help---
+	  Say Y here if you have a PS/2 mouse connected to your system. This
+	  includes the standard 2 or 3-button PS/2 mouse, as well as PS/2
+	  mice with wheels and extra buttons, Microsoft, Logitech or Genius
+	  compatible.
+
+	  Synaptics TouchPad users might be interested in a specialized
+	  XFree86 driver at:
+		<http://w1.894.telia.com/~u89404340/touchpad/index.html>
+	  and a new version of GPM at:
+		<http://www.geocities.com/dt_or/gpm/gpm.html>
+	  to take advantage of the advanced features of the touchpad.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called psmouse.
+
+config MOUSE_SERIAL
+	tristate "Serial mouse"
+	select SERIO
+	---help---
+	  Say Y here if you have a serial (RS-232, COM port) mouse connected
+	  to your system. This includes Sun, MouseSystems, Microsoft,
+	  Logitech and all other compatible serial mice.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sermouse.
+
+config MOUSE_INPORT
+	tristate "InPort/MS/ATIXL busmouse"
+	depends on ISA
+	help
+	  Say Y here if you have an InPort, Microsoft or ATI XL busmouse.
+	  They are rather rare these days.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called inport.
+
+config MOUSE_ATIXL
+	bool "ATI XL variant"
+	depends on MOUSE_INPORT
+	help
+	  Say Y here if your mouse is of the ATI XL variety.
+
+config MOUSE_LOGIBM
+	tristate "Logitech busmouse"
+	depends on ISA
+	help
+	  Say Y here if you have a Logitech busmouse.
+	  They are rather rare these days.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called logibm.
+
+config MOUSE_PC110PAD
+	tristate "IBM PC110 touchpad"
+	depends on ISA
+	help
+	  Say Y if you have the IBM PC-110 micro-notebook and want its
+	  touchpad supported.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pc110pad.
+
+config MOUSE_MAPLE
+	tristate "Maple bus mouse"
+	depends on SH_DREAMCAST && MAPLE
+	help
+	  Say Y if you have a DreamCast console and a mouse attached to
+	  its Maple bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called maplemouse.
+
+config MOUSE_AMIGA
+	tristate "Amiga mouse"
+	depends on AMIGA
+	help
+	  Say Y here if you have an Amiga and want its native mouse
+	  supported by the kernel.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called amimouse.
+
+config MOUSE_RISCPC
+	tristate "Acorn RiscPC mouse"
+	depends on ARCH_ACORN
+	help
+	  Say Y here if you have the Acorn RiscPC computer and want its
+	  native mouse supported.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rpcmouse.
+
+config MOUSE_VSXXXAA
+	tristate "DEC VSXXX-AA/GA mouse and VSXXX-AB tablet"
+	select SERIO
+	help
+	  Say Y (or M) if you want to use a DEC VSXXX-AA (hockey
+	  puck) or a VSXXX-GA (rectangular) mouse. Theses mice are
+	  typically used on DECstations or VAXstations, but can also
+	  be used on any box capable of RS232 (with some adaptor
+	  described in the source file). This driver also works with the
+	  digitizer (VSXXX-AB) DEC produced.
+
+config MOUSE_HIL
+	tristate "HIL pointers (mice etc)."     
+	depends on GSC
+	select HP_SDC
+	select HIL_MLC
+	help
+	  Say Y here to support HIL pointers.
+
+endif
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
new file mode 100644
index 0000000..a786419
--- /dev/null
+++ b/drivers/input/mouse/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the mouse drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_MOUSE_AMIGA)	+= amimouse.o
+obj-$(CONFIG_MOUSE_RISCPC)	+= rpcmouse.o
+obj-$(CONFIG_MOUSE_INPORT)	+= inport.o
+obj-$(CONFIG_MOUSE_LOGIBM)	+= logibm.o
+obj-$(CONFIG_MOUSE_MAPLE)	+= maplemouse.o
+obj-$(CONFIG_MOUSE_PC110PAD)	+= pc110pad.o
+obj-$(CONFIG_MOUSE_PS2)		+= psmouse.o
+obj-$(CONFIG_MOUSE_SERIAL)	+= sermouse.o
+obj-$(CONFIG_MOUSE_HIL)		+= hil_ptr.o
+obj-$(CONFIG_MOUSE_VSXXXAA)	+= vsxxxaa.o
+
+psmouse-objs  := psmouse-base.o alps.o logips2pp.o synaptics.o
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
new file mode 100644
index 0000000..1f85a97
--- /dev/null
+++ b/drivers/input/mouse/alps.c
@@ -0,0 +1,477 @@
+/*
+ * ALPS touchpad PS/2 mouse driver
+ *
+ * Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au>
+ * Copyright (c) 2003 Peter Osterlund <petero2@telia.com>
+ * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
+ *
+ * ALPS detection, tap switching and status querying info is taken from
+ * tpconfig utility (by C. Scott Ananian and Bruce Kall).
+ *
+ * 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/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+
+#include "psmouse.h"
+#include "alps.h"
+
+#undef DEBUG
+#ifdef DEBUG
+#define dbg(format, arg...) printk(KERN_INFO "alps.c: " format "\n", ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+
+#define ALPS_DUALPOINT	0x01
+#define ALPS_WHEEL	0x02
+#define ALPS_FW_BK	0x04
+#define ALPS_4BTN	0x08
+#define ALPS_OLDPROTO	0x10
+#define ALPS_PASS	0x20
+
+static struct alps_model_info alps_model_data[] = {
+	{ { 0x33, 0x02, 0x0a },	0x88, 0xf8, ALPS_OLDPROTO },		/* UMAX-530T */
+	{ { 0x53, 0x02, 0x0a },	0xf8, 0xf8, 0 },
+	{ { 0x53, 0x02, 0x14 },	0xf8, 0xf8, 0 },
+	{ { 0x63, 0x02, 0x0a },	0xf8, 0xf8, 0 },
+	{ { 0x63, 0x02, 0x14 },	0xf8, 0xf8, 0 },
+	{ { 0x63, 0x02, 0x28 },	0xf8, 0xf8, 0 },
+	{ { 0x63, 0x02, 0x3c },	0x8f, 0x8f, ALPS_WHEEL },		/* Toshiba Satellite S2400-103 */
+	{ { 0x63, 0x02, 0x50 },	0xef, 0xef, ALPS_FW_BK },		/* NEC Versa L320 */
+	{ { 0x63, 0x02, 0x64 },	0xf8, 0xf8, 0 },
+	{ { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS },		/* Dell Latitude D800 */
+	{ { 0x73, 0x02, 0x0a },	0xf8, 0xf8, 0 },
+	{ { 0x73, 0x02, 0x14 },	0xf8, 0xf8, 0 },
+	{ { 0x20, 0x02, 0x0e },	0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */
+	{ { 0x22, 0x02, 0x0a },	0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
+	{ { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
+};
+
+/*
+ * XXX - this entry is suspicious. First byte has zero lower nibble,
+ * which is what a normal mouse would report. Also, the value 0x0e
+ * isn't valid per PS/2 spec.
+ */
+
+/*
+ * ALPS abolute Mode - new format
+ * 
+ * byte 0:  1    ?    ?    ?    1    ?    ?    ? 
+ * byte 1:  0   x6   x5   x4   x3   x2   x1   x0
+ * byte 2:  0   x10  x9   x8   x7    ?  fin  ges
+ * byte 3:  0   y9   y8   y7    1    M    R    L 
+ * byte 4:  0   y6   y5   y4   y3   y2   y1   y0
+ * byte 5:  0   z6   z5   z4   z3   z2   z1   z0
+ *
+ * ?'s can have different meanings on different models,
+ * such as wheel rotation, extra buttons, stick buttons
+ * on a dualpoint, etc.
+ */
+
+static void alps_process_packet(struct psmouse *psmouse, struct pt_regs *regs)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char *packet = psmouse->packet;
+	struct input_dev *dev = &psmouse->dev;
+	struct input_dev *dev2 = &priv->dev2;
+	int x, y, z, ges, fin, left, right, middle;
+
+	input_regs(dev, regs);
+
+	if ((packet[0] & 0xc8) == 0x08) {   /* 3-byte PS/2 packet */
+		input_report_key(dev2, BTN_LEFT,   packet[0] & 1);    
+		input_report_key(dev2, BTN_RIGHT,  packet[0] & 2);
+		input_report_key(dev2, BTN_MIDDLE, packet[0] & 4);
+		input_report_rel(dev2, REL_X,
+			packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0);
+		input_report_rel(dev2, REL_Y,
+			packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0);
+		input_sync(dev2);
+		return;
+	}
+
+	if (priv->i->flags & ALPS_OLDPROTO) {
+		left = packet[2] & 0x08;
+		right = packet[2] & 0x10;
+		middle = 0;
+		x = packet[1] | ((packet[0] & 0x07) << 7);
+		y = packet[4] | ((packet[3] & 0x07) << 7);
+		z = packet[5];
+	} else {
+		left = packet[3] & 1;
+		right = packet[3] & 2;
+		middle = packet[3] & 4;
+		x = packet[1] | ((packet[2] & 0x78) << (7 - 3));
+		y = packet[4] | ((packet[3] & 0x70) << (7 - 4));
+		z = packet[5];
+	}
+
+	ges = packet[2] & 1;
+	fin = packet[2] & 2;
+
+	input_report_key(dev, BTN_LEFT, left);
+	input_report_key(dev, BTN_RIGHT, right);
+	input_report_key(dev, BTN_MIDDLE, middle);
+
+	if ((priv->i->flags & ALPS_DUALPOINT) && z == 127) {
+		input_report_rel(dev2, REL_X,  (x > 383 ? (x - 768) : x));
+		input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y));
+		input_sync(dev);
+		input_sync(dev2);
+		return;
+	}
+
+	/* Convert hardware tap to a reasonable Z value */
+	if (ges && !fin) z = 40;
+
+	/*
+	 * A "tap and drag" operation is reported by the hardware as a transition
+	 * from (!fin && ges) to (fin && ges). This should be translated to the
+	 * sequence Z>0, Z==0, Z>0, so the Z==0 event has to be generated manually.
+	 */
+	if (ges && fin && !priv->prev_fin) {
+		input_report_abs(dev, ABS_X, x);
+		input_report_abs(dev, ABS_Y, y);
+		input_report_abs(dev, ABS_PRESSURE, 0);
+		input_report_key(dev, BTN_TOOL_FINGER, 0);
+		input_sync(dev);
+	}
+	priv->prev_fin = fin;
+
+	if (z > 30) input_report_key(dev, BTN_TOUCH, 1);
+	if (z < 25) input_report_key(dev, BTN_TOUCH, 0);
+
+	if (z > 0) {
+		input_report_abs(dev, ABS_X, x);
+		input_report_abs(dev, ABS_Y, y);
+	}
+
+	input_report_abs(dev, ABS_PRESSURE, z);
+	input_report_key(dev, BTN_TOOL_FINGER, z > 0);
+
+
+	if (priv->i->flags & ALPS_WHEEL)
+		input_report_rel(dev, REL_WHEEL, ((packet[0] >> 4) & 0x07) | ((packet[2] >> 2) & 0x08));
+
+	if (priv->i->flags & ALPS_FW_BK) {
+		input_report_key(dev, BTN_FORWARD, packet[0] & 0x10);
+		input_report_key(dev, BTN_BACK, packet[2] & 0x04);
+	}
+
+	input_sync(dev);
+}
+
+static psmouse_ret_t alps_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
+{
+	struct alps_data *priv = psmouse->private;
+
+	if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
+		if (psmouse->pktcnt == 3) {
+			alps_process_packet(psmouse, regs);
+			return PSMOUSE_FULL_PACKET;
+		}
+		return PSMOUSE_GOOD_DATA;
+	}
+
+	if ((psmouse->packet[0] & priv->i->mask0) != priv->i->byte0)
+		return PSMOUSE_BAD_DATA;
+
+	/* Bytes 2 - 6 should have 0 in the highest bit */
+	if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 6 &&
+	    (psmouse->packet[psmouse->pktcnt - 1] & 0x80))
+		return PSMOUSE_BAD_DATA;
+
+	if (psmouse->pktcnt == 6) {
+		alps_process_packet(psmouse, regs);
+		return PSMOUSE_FULL_PACKET;
+	}
+
+	return PSMOUSE_GOOD_DATA;
+}
+
+static struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 };
+	unsigned char param[4];
+	int i;
+
+	/*
+	 * First try "E6 report".
+	 * ALPS should return 0,0,10 or 0,0,100
+	 */
+	param[0] = 0;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
+	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||
+	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11) ||
+	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11))
+		return NULL;
+
+	param[0] = param[1] = param[2] = 0xff;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+		return NULL;
+
+	dbg("E6 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
+
+	if (param[0] != 0 || param[1] != 0 || (param[2] != 10 && param[2] != 100))
+		return NULL;
+
+	/*
+	 * Now try "E7 report". Allowed responses are in
+	 * alps_model_data[].signature
+	 */
+	param[0] = 0;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) ||
+	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
+	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21) ||
+	    ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE21))
+		return NULL;
+
+	param[0] = param[1] = param[2] = 0xff;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+		return NULL;
+
+	dbg("E7 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
+
+	for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++);
+	*version = (param[0] << 8) | (param[1] << 4) | i;
+
+	for (i = 0; i < ARRAY_SIZE(alps_model_data); i++)
+		if (!memcmp(param, alps_model_data[i].signature, sizeof(alps_model_data[i].signature)))
+			return alps_model_data + i;
+
+	return NULL;
+}
+
+/*
+ * For DualPoint devices select the device that should respond to
+ * subsequent commands. It looks like glidepad is behind stickpointer,
+ * I'd thought it would be other way around...
+ */
+static int alps_passthrough_mode(struct psmouse *psmouse, int enable)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[3];
+	int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11;
+
+	if (ps2_command(ps2dev, NULL, cmd) ||
+	    ps2_command(ps2dev, NULL, cmd) ||
+	    ps2_command(ps2dev, NULL, cmd) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE))
+		return -1;
+
+	/* we may get 3 more bytes, just ignore them */
+	ps2_command(ps2dev, param, 0x0300);
+
+	return 0;
+}
+
+static int alps_absolute_mode(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+	/* Try ALPS magic knock - 4 disable before enable */
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE))
+		return -1;
+
+	/*
+	 * Switch mouse to poll (remote) mode so motion data will not
+	 * get in our way
+	 */
+	return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL);
+}
+
+static int alps_get_status(struct psmouse *psmouse, char *param)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+
+	/* Get status: 0xF5 0xF5 0xF5 0xE9 */
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+		return -1;
+
+	dbg("Status: %2.2x %2.2x %2.2x", param[0], param[1], param[2]);
+
+	return 0;
+}
+
+/*
+ * Turn touchpad tapping on or off. The sequences are:
+ * 0xE9 0xF5 0xF5 0xF3 0x0A to enable,
+ * 0xE9 0xF5 0xF5 0xE8 0x00 to disable.
+ * My guess that 0xE9 (GetInfo) is here as a sync point.
+ * For models that also have stickpointer (DualPoints) its tapping
+ * is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but
+ * we don't fiddle with it.
+ */
+static int alps_tap_mode(struct psmouse *psmouse, int enable)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	int cmd = enable ? PSMOUSE_CMD_SETRATE : PSMOUSE_CMD_SETRES;
+	unsigned char tap_arg = enable ? 0x0A : 0x00;
+	unsigned char param[4];
+
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) ||
+	    ps2_command(ps2dev, &tap_arg, cmd))
+		return -1;
+
+	if (alps_get_status(psmouse, param))
+		return -1;
+
+	return 0;
+}
+
+static int alps_reconnect(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	unsigned char param[4];
+	int version;
+
+	if (!(priv->i = alps_get_model(psmouse, &version)))
+		return -1;
+
+	if (priv->i->flags & ALPS_PASS && alps_passthrough_mode(psmouse, 1))
+		return -1;
+
+	if (alps_get_status(psmouse, param))
+		return -1;
+
+	if (param[0] & 0x04)
+		alps_tap_mode(psmouse, 1);
+
+	if (alps_absolute_mode(psmouse)) {
+		printk(KERN_ERR "alps.c: Failed to enable absolute mode\n");
+		return -1;
+	}
+
+	if (priv->i->flags == ALPS_PASS && alps_passthrough_mode(psmouse, 0))
+		return -1;
+
+	return 0;
+}
+
+static void alps_disconnect(struct psmouse *psmouse)
+{
+	struct alps_data *priv = psmouse->private;
+	psmouse_reset(psmouse);
+	input_unregister_device(&priv->dev2);
+	kfree(priv);
+}
+
+int alps_init(struct psmouse *psmouse)
+{
+	struct alps_data *priv;
+	unsigned char param[4];
+	int version;
+
+	psmouse->private = priv = kmalloc(sizeof(struct alps_data), GFP_KERNEL);
+	if (!priv)
+		goto init_fail;
+	memset(priv, 0, sizeof(struct alps_data));
+
+	if (!(priv->i = alps_get_model(psmouse, &version)))
+		goto init_fail;
+
+	if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 1))
+		goto init_fail;
+
+	if (alps_get_status(psmouse, param)) {
+		printk(KERN_ERR "alps.c: touchpad status report request failed\n");
+		goto init_fail;
+	}
+
+	if (param[0] & 0x04) {
+		printk(KERN_INFO "  Enabling hardware tapping\n");
+		if (alps_tap_mode(psmouse, 1))
+			printk(KERN_WARNING "alps.c: Failed to enable hardware tapping\n");
+	}
+
+	if (alps_absolute_mode(psmouse)) {
+		printk(KERN_ERR "alps.c: Failed to enable absolute mode\n");
+		goto init_fail;
+	}
+
+	if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 0))
+		goto init_fail;
+
+	psmouse->dev.evbit[LONG(EV_KEY)] |= BIT(EV_KEY);
+	psmouse->dev.keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH);
+	psmouse->dev.keybit[LONG(BTN_TOOL_FINGER)] |= BIT(BTN_TOOL_FINGER);
+	psmouse->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
+
+	psmouse->dev.evbit[LONG(EV_ABS)] |= BIT(EV_ABS);
+	input_set_abs_params(&psmouse->dev, ABS_X, 0, 1023, 0, 0);
+	input_set_abs_params(&psmouse->dev, ABS_Y, 0, 767, 0, 0);
+	input_set_abs_params(&psmouse->dev, ABS_PRESSURE, 0, 127, 0, 0);
+
+	if (priv->i->flags & ALPS_WHEEL) {
+		psmouse->dev.evbit[LONG(EV_REL)] |= BIT(EV_REL);
+		psmouse->dev.relbit[LONG(REL_WHEEL)] |= BIT(REL_WHEEL);
+	}
+
+	if (priv->i->flags & ALPS_FW_BK) {
+		psmouse->dev.keybit[LONG(BTN_FORWARD)] |= BIT(BTN_FORWARD);
+		psmouse->dev.keybit[LONG(BTN_BACK)] |= BIT(BTN_BACK);
+	}
+
+	sprintf(priv->phys, "%s/input1", psmouse->ps2dev.serio->phys);
+	priv->dev2.phys = priv->phys;
+	priv->dev2.name = (priv->i->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse";
+	priv->dev2.id.bustype = BUS_I8042;
+	priv->dev2.id.vendor = 0x0002;
+	priv->dev2.id.product = PSMOUSE_ALPS;
+	priv->dev2.id.version = 0x0000; 
+	
+	priv->dev2.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+	priv->dev2.relbit[LONG(REL_X)] |= BIT(REL_X) | BIT(REL_Y);
+	priv->dev2.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
+
+	input_register_device(&priv->dev2);
+
+	printk(KERN_INFO "input: %s on %s\n", priv->dev2.name, psmouse->ps2dev.serio->phys);
+
+	psmouse->protocol_handler = alps_process_byte;
+	psmouse->disconnect = alps_disconnect;
+	psmouse->reconnect = alps_reconnect;
+	psmouse->pktsize = 6;
+
+	return 0;
+
+init_fail:
+	kfree(priv);
+	return -1;
+}
+
+int alps_detect(struct psmouse *psmouse, int set_properties)
+{
+	int version;
+	struct alps_model_info *model; 
+
+	if (!(model = alps_get_model(psmouse, &version)))
+		return -1;
+
+	if (set_properties) {
+		psmouse->vendor = "ALPS";
+		if (model->flags & ALPS_DUALPOINT) 
+			psmouse->name = "DualPoint TouchPad";
+		else
+			psmouse->name = "GlidePoint";
+		psmouse->model = version;
+	}
+	return 0;
+}
+
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
new file mode 100644
index 0000000..aba103d
--- /dev/null
+++ b/drivers/input/mouse/alps.h
@@ -0,0 +1,32 @@
+/*
+ * ALPS touchpad PS/2 mouse driver
+ *
+ * Copyright (c) 2003 Peter Osterlund <petero2@telia.com>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
+ *
+ * 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.
+ */
+
+#ifndef _ALPS_H
+#define _ALPS_H
+
+int alps_detect(struct psmouse *psmouse, int set_properties);
+int alps_init(struct psmouse *psmouse);
+
+struct alps_model_info {
+        unsigned char signature[3];
+        unsigned char byte0, mask0;
+        unsigned char flags;
+};
+
+struct alps_data {
+	struct input_dev dev2;		/* Relative device */
+	char name[32];			/* Name */
+	char phys[32];			/* Phys */
+	struct alps_model_info *i; 	/* Info */
+	int prev_fin;			/* Finger bit from previous packet */
+};
+
+#endif
diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c
new file mode 100644
index 0000000..7baa09c
--- /dev/null
+++ b/drivers/input/mouse/amimouse.c
@@ -0,0 +1,137 @@
+/*
+ *  Amiga mouse driver for Linux/m68k
+ *
+ *  Copyright (c) 2000-2002 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Michael Rausch		James Banks
+ *	Matther Dillon		David Giller
+ *	Nathan Laredo		Linus Torvalds
+ *	Johan Myreen		Jes Sorensen
+ *	Russell King
+ */
+
+/*
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Amiga mouse driver");
+MODULE_LICENSE("GPL");
+
+static int amimouse_used = 0;
+static int amimouse_lastx, amimouse_lasty;
+static struct input_dev amimouse_dev;
+
+static char *amimouse_name = "Amiga mouse";
+static char *amimouse_phys = "amimouse/input0";
+
+static irqreturn_t amimouse_interrupt(int irq, void *dummy, struct pt_regs *fp)
+{
+	unsigned short joy0dat, potgor;
+	int nx, ny, dx, dy;
+
+	joy0dat = custom.joy0dat;
+
+	nx = joy0dat & 0xff;
+	ny = joy0dat >> 8;
+
+	dx = nx - amimouse_lastx;
+	dy = ny - amimouse_lasty;
+
+	if (dx < -127) dx = (256 + nx) - amimouse_lastx;
+	if (dx >  127) dx = (nx - 256) - amimouse_lastx;
+	if (dy < -127) dy = (256 + ny) - amimouse_lasty;
+	if (dy >  127) dy = (ny - 256) - amimouse_lasty;
+
+	amimouse_lastx = nx;
+	amimouse_lasty = ny;
+
+	potgor = custom.potgor;
+
+	input_regs(&amimouse_dev, fp);
+
+	input_report_rel(&amimouse_dev, REL_X, dx);
+	input_report_rel(&amimouse_dev, REL_Y, dy);
+
+	input_report_key(&amimouse_dev, BTN_LEFT,   ciaa.pra & 0x40);
+	input_report_key(&amimouse_dev, BTN_MIDDLE, potgor & 0x0100);
+	input_report_key(&amimouse_dev, BTN_RIGHT,  potgor & 0x0400);
+
+	input_sync(&amimouse_dev);
+
+	return IRQ_HANDLED;
+}
+
+static int amimouse_open(struct input_dev *dev)
+{
+	unsigned short joy0dat;
+
+        if (amimouse_used++)
+                return 0;
+
+	joy0dat = custom.joy0dat;
+
+	amimouse_lastx = joy0dat & 0xff;
+	amimouse_lasty = joy0dat >> 8;
+
+	if (request_irq(IRQ_AMIGA_VERTB, amimouse_interrupt, 0, "amimouse", amimouse_interrupt)) {
+                amimouse_used--;
+                printk(KERN_ERR "amimouse.c: Can't allocate irq %d\n", IRQ_AMIGA_VERTB);
+                return -EBUSY;
+        }
+
+        return 0;
+}
+
+static void amimouse_close(struct input_dev *dev)
+{
+        if (!--amimouse_used)
+		free_irq(IRQ_AMIGA_VERTB, amimouse_interrupt);
+}
+
+static int __init amimouse_init(void)
+{
+	if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_MOUSE))
+		return -ENODEV;
+
+	amimouse_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+	amimouse_dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
+	amimouse_dev.keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
+	amimouse_dev.open = amimouse_open;
+	amimouse_dev.close = amimouse_close;
+
+	amimouse_dev.name = amimouse_name;
+	amimouse_dev.phys = amimouse_phys;
+	amimouse_dev.id.bustype = BUS_AMIGA;
+	amimouse_dev.id.vendor = 0x0001;
+	amimouse_dev.id.product = 0x0002;
+	amimouse_dev.id.version = 0x0100;
+
+	input_register_device(&amimouse_dev);
+
+        printk(KERN_INFO "input: %s at joy0dat\n", amimouse_name);
+	return 0;
+}
+
+static void __exit amimouse_exit(void)
+{
+        input_unregister_device(&amimouse_dev);
+}
+
+module_init(amimouse_init);
+module_exit(amimouse_exit);
diff --git a/drivers/input/mouse/hil_ptr.c b/drivers/input/mouse/hil_ptr.c
new file mode 100644
index 0000000..bc22849
--- /dev/null
+++ b/drivers/input/mouse/hil_ptr.c
@@ -0,0 +1,414 @@
+/*
+ * Generic linux-input device driver for axis-bearing 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 PTR: "
+#define HIL_GENERIC_NAME "HIL pointer device"
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+
+#define TABLET_SIMULATES_MOUSE	/* allow tablet to be used as mouse */
+#undef  TABLET_AUTOADJUST	/* auto-adjust valid tablet ranges */
+
+
+#define HIL_PTR_MAX_LENGTH 16
+
+struct hil_ptr {
+	struct input_dev dev;
+	struct serio *serio;
+
+	/* Input buffer and index for packets from HIL bus. */
+	hil_packet data[HIL_PTR_MAX_LENGTH];
+	int idx4; /* four counts per packet */
+
+	/* Raw device info records from HIL bus, see hil.h for fields. */
+	char	idd[HIL_PTR_MAX_LENGTH];	/* DID byte and IDD record */
+	char	rsc[HIL_PTR_MAX_LENGTH];	/* RSC record */
+	char	exd[HIL_PTR_MAX_LENGTH];	/* EXD record */
+	char	rnm[HIL_PTR_MAX_LENGTH + 1];	/* RNM record + NULL term. */
+
+	/* Extra device details not contained in struct input_dev. */
+	unsigned int nbtn, naxes;
+	unsigned int btnmap[7];
+
+	/* Something to sleep around with. */
+	struct semaphore sem;
+};
+
+/* Process a complete packet after transfer from the HIL */
+static void hil_ptr_process_record(struct hil_ptr *ptr)
+{
+	struct input_dev *dev = &ptr->dev;
+	hil_packet *data = ptr->data;
+	hil_packet p;
+	int idx, i, cnt, laxis;
+	int ax16, absdev;
+
+	idx = ptr->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++)
+			ptr->idd[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
+		for (; i < HIL_PTR_MAX_LENGTH; i++)
+			ptr->idd[i] = 0;
+		break;
+	case HIL_CMD_RSC:
+		for (i = 0; i < idx; i++)
+			ptr->rsc[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
+		for (; i < HIL_PTR_MAX_LENGTH; i++)
+			ptr->rsc[i] = 0;
+		break;
+	case HIL_CMD_EXD:
+		for (i = 0; i < idx; i++)
+			ptr->exd[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
+		for (; i < HIL_PTR_MAX_LENGTH; i++)
+			ptr->exd[i] = 0;
+		break;
+	case HIL_CMD_RNM:
+		for (i = 0; i < idx; i++)
+			ptr->rnm[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
+		for (; i < HIL_PTR_MAX_LENGTH + 1; i++)
+			ptr->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:
+	if ((p & HIL_CMDCT_POL) != idx - 1) {
+		printk(KERN_WARNING PREFIX "Malformed poll packet %x (idx = %i)\n", p, idx);
+		goto out;
+	}
+
+	i = (ptr->data[0] & HIL_POL_AXIS_ALT) ? 3 : 0;
+	laxis = ptr->data[0] & HIL_POL_NUM_AXES_MASK;
+	laxis += i;
+
+	ax16 = ptr->idd[1] & HIL_IDD_HEADER_16BIT; /* 8 or 16bit resolution */
+	absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS; 
+
+	for (cnt = 1; i < laxis; i++) {
+		unsigned int lo,hi,val;
+		lo = ptr->data[cnt++] & HIL_PKT_DATA_MASK;
+		hi = ax16 ? (ptr->data[cnt++] & HIL_PKT_DATA_MASK) : 0;
+		if (absdev) {
+			val = lo + (hi<<8);
+#ifdef TABLET_AUTOADJUST
+			if (val < ptr->dev.absmin[ABS_X + i])
+				ptr->dev.absmin[ABS_X + i] = val;
+			if (val > ptr->dev.absmax[ABS_X + i])
+				ptr->dev.absmax[ABS_X + i] = val;
+#endif
+			if (i%3) val = ptr->dev.absmax[ABS_X + i] - val;
+			input_report_abs(dev, ABS_X + i, val);
+		} else {
+			val = (int) (((int8_t)lo) | ((int8_t)hi<<8));
+			if (i%3) val *= -1;
+			input_report_rel(dev, REL_X + i, val);
+		}
+	}
+
+	while (cnt < idx - 1) {
+		unsigned int btn;
+		int up;
+		btn = ptr->data[cnt++];
+		up = btn & 1;
+		btn &= 0xfe;
+		if (btn == 0x8e) {
+			continue; /* TODO: proximity == touch? */
+		}
+		else if ((btn > 0x8c) || (btn < 0x80)) continue;
+		btn = (btn - 0x80) >> 1;
+		btn = ptr->btnmap[btn];
+		input_report_key(dev, btn, !up);
+	}
+	input_sync(dev);
+ out:
+	ptr->idx4 = 0;
+	up(&ptr->sem);
+}
+
+static void hil_ptr_process_err(struct hil_ptr *ptr) {
+	printk(KERN_WARNING PREFIX "errored HIL packet\n");
+	ptr->idx4 = 0;
+	up(&ptr->sem);
+	return;
+}
+
+static irqreturn_t hil_ptr_interrupt(struct serio *serio, 
+        unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+	struct hil_ptr *ptr;
+	hil_packet packet;
+	int idx;
+
+	ptr = (struct hil_ptr *)serio->private;
+	if (ptr == NULL) {
+		BUG();
+		return IRQ_HANDLED;
+	}
+
+	if (ptr->idx4 >= (HIL_PTR_MAX_LENGTH * sizeof(hil_packet))) {
+		hil_ptr_process_err(ptr);
+		return IRQ_HANDLED;
+	}
+	idx = ptr->idx4/4;
+	if (!(ptr->idx4 % 4)) ptr->data[idx] = 0;
+	packet = ptr->data[idx];
+	packet |= ((hil_packet)data) << ((3 - (ptr->idx4 % 4)) * 8);
+	ptr->data[idx] = packet;
+
+	/* Records of N 4-byte hil_packets must terminate with a command. */
+	if ((++(ptr->idx4)) % 4) return IRQ_HANDLED;
+	if ((packet & 0xffff0000) != HIL_ERR_INT) {
+		hil_ptr_process_err(ptr);
+		return IRQ_HANDLED;
+	}
+	if (packet & HIL_PKT_CMD) 
+		hil_ptr_process_record(ptr);
+	return IRQ_HANDLED;
+}
+
+static void hil_ptr_disconnect(struct serio *serio)
+{
+	struct hil_ptr *ptr;
+
+	ptr = (struct hil_ptr *)serio->private;
+	if (ptr == NULL) {
+		BUG();
+		return;
+	}
+
+	input_unregister_device(&ptr->dev);
+	serio_close(serio);
+	kfree(ptr);
+}
+
+static void hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
+{
+	struct hil_ptr	*ptr;
+	char		*txt;
+	unsigned int	i, naxsets, btntype;
+	uint8_t		did, *idd;
+
+	if (serio->type != (SERIO_HIL_MLC | SERIO_HIL)) return;
+
+	if (!(ptr = kmalloc(sizeof(struct hil_ptr), GFP_KERNEL))) return;
+	memset(ptr, 0, sizeof(struct hil_ptr));
+
+	if (serio_open(serio, driver)) goto bail0;
+
+	serio->private = ptr;
+	ptr->serio = serio;
+	ptr->dev.private = ptr;
+
+	init_MUTEX_LOCKED(&(ptr->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(&(ptr->sem));
+
+	serio->write(serio, 0);
+	serio->write(serio, 0);
+	serio->write(serio, HIL_PKT_CMD >> 8);
+	serio->write(serio, HIL_CMD_RSC);
+	down(&(ptr->sem));
+
+	serio->write(serio, 0);
+	serio->write(serio, 0);
+	serio->write(serio, HIL_PKT_CMD >> 8);
+	serio->write(serio, HIL_CMD_RNM);
+	down(&(ptr->sem));
+
+	serio->write(serio, 0);
+	serio->write(serio, 0);
+	serio->write(serio, HIL_PKT_CMD >> 8);
+	serio->write(serio, HIL_CMD_EXD);
+	down(&(ptr->sem));
+
+	up(&(ptr->sem));
+
+	init_input_dev(&ptr->dev);
+	did = ptr->idd[0];
+	idd = ptr->idd + 1;
+	txt = "unknown";
+	if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) {
+		ptr->dev.evbit[0] = BIT(EV_REL);
+		txt = "relative";
+	}
+
+	if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_ABS) {
+		ptr->dev.evbit[0] = BIT(EV_ABS);
+		txt = "absolute";
+	}
+	if (!ptr->dev.evbit[0]) {
+		goto bail1;
+	}
+
+	ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd);
+	if (ptr->nbtn) ptr->dev.evbit[0] |= BIT(EV_KEY);
+
+	naxsets = HIL_IDD_NUM_AXSETS(*idd);
+	ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd);
+
+	printk(KERN_INFO PREFIX "HIL pointer device found (did: 0x%02x, axis: %s)\n",
+			did, txt);
+	printk(KERN_INFO PREFIX "HIL pointer has %i buttons and %i sets of %i axes\n",
+			ptr->nbtn, naxsets, ptr->naxes);
+	
+	btntype = BTN_MISC;
+	if ((did & HIL_IDD_DID_ABS_TABLET_MASK) == HIL_IDD_DID_ABS_TABLET)
+#ifdef TABLET_SIMULATES_MOUSE
+		btntype = BTN_TOUCH;
+#else
+		btntype = BTN_DIGI;
+#endif
+	if ((did & HIL_IDD_DID_ABS_TSCREEN_MASK) == HIL_IDD_DID_ABS_TSCREEN)
+		btntype = BTN_TOUCH;
+		
+	if ((did & HIL_IDD_DID_REL_MOUSE_MASK) == HIL_IDD_DID_REL_MOUSE)
+		btntype = BTN_MOUSE;
+
+	for (i = 0; i < ptr->nbtn; i++) {
+		set_bit(btntype | i, ptr->dev.keybit);
+		ptr->btnmap[i] = btntype | i;
+	}
+
+	if (btntype == BTN_MOUSE) {
+		/* Swap buttons 2 and 3 */
+		ptr->btnmap[1] = BTN_MIDDLE;
+		ptr->btnmap[2] = BTN_RIGHT;
+	}
+
+	if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) {
+		for (i = 0; i < ptr->naxes; i++) {
+			set_bit(REL_X + i, ptr->dev.relbit);
+		}
+		for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) {
+			set_bit(REL_X + i, ptr->dev.relbit);
+		}
+	} else {
+		for (i = 0; i < ptr->naxes; i++) {
+	  		set_bit(ABS_X + i, ptr->dev.absbit);
+			ptr->dev.absmin[ABS_X + i] = 0;
+			ptr->dev.absmax[ABS_X + i] = 
+				HIL_IDD_AXIS_MAX((ptr->idd + 1), i);
+		}
+		for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) {
+			set_bit(ABS_X + i, ptr->dev.absbit);
+			ptr->dev.absmin[ABS_X + i] = 0;
+			ptr->dev.absmax[ABS_X + i] = 
+				HIL_IDD_AXIS_MAX((ptr->idd + 1), (i - 3));
+		}
+#ifdef TABLET_AUTOADJUST
+		for (i = 0; i < ABS_MAX; i++) {
+			int diff = ptr->dev.absmax[ABS_X + i] / 10;
+			ptr->dev.absmin[ABS_X + i] += diff;
+			ptr->dev.absmax[ABS_X + i] -= diff;
+		}
+#endif
+	}
+
+	ptr->dev.name = strlen(ptr->rnm) ? ptr->rnm : HIL_GENERIC_NAME;
+
+	ptr->dev.id.bustype	= BUS_HIL;
+	ptr->dev.id.vendor	= PCI_VENDOR_ID_HP;
+	ptr->dev.id.product	= 0x0001; /* TODO: get from ptr->rsc */
+	ptr->dev.id.version	= 0x0100; /* TODO: get from ptr->rsc */
+	ptr->dev.dev		= &serio->dev;
+
+	input_register_device(&ptr->dev);
+	printk(KERN_INFO "input: %s (%s), ID: %d\n",
+                ptr->dev.name, 
+		(btntype == BTN_MOUSE) ? "HIL mouse":"HIL tablet or touchpad",
+		did);
+
+	return;
+ bail1:
+	serio_close(serio);
+ bail0:
+	kfree(ptr);
+	return;
+}
+
+
+static struct serio_driver hil_ptr_serio_driver = {
+	.driver		= {
+		.name	= "hil_ptr",
+	},
+	.description	= "HP HIL mouse/tablet driver",
+	.connect =	hil_ptr_connect,
+	.disconnect =	hil_ptr_disconnect,
+	.interrupt =	hil_ptr_interrupt
+};
+
+static int __init hil_ptr_init(void)
+{
+	serio_register_driver(&hil_ptr_serio_driver);
+        return 0;
+}
+                
+static void __exit hil_ptr_exit(void)
+{
+	serio_unregister_driver(&hil_ptr_serio_driver);
+}
+                        
+module_init(hil_ptr_init);
+module_exit(hil_ptr_exit);
diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c
new file mode 100644
index 0000000..ca4e968
--- /dev/null
+++ b/drivers/input/mouse/inport.c
@@ -0,0 +1,196 @@
+/*
+ * $Id: inport.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Teemu Rantanen		Derrick Cole
+ *	Peter Cervasio		Christoph Niemann
+ *	Philip Blundell		Russell King
+ *	Bob Harris
+ */
+
+/*
+ * Inport (ATI XL and Microsoft) busmouse 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/config.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Inport (ATI XL and Microsoft) busmouse driver");
+MODULE_LICENSE("GPL");
+
+#define INPORT_BASE		0x23c
+#define INPORT_EXTENT		4
+
+#define INPORT_CONTROL_PORT	INPORT_BASE + 0
+#define INPORT_DATA_PORT	INPORT_BASE + 1
+#define INPORT_SIGNATURE_PORT	INPORT_BASE + 2
+
+#define INPORT_REG_BTNS	0x00
+#define INPORT_REG_X		0x01
+#define INPORT_REG_Y		0x02
+#define INPORT_REG_MODE		0x07
+#define INPORT_RESET		0x80
+
+#ifdef CONFIG_INPUT_ATIXL
+#define INPORT_NAME		"ATI XL Mouse"
+#define INPORT_VENDOR		0x0002
+#define INPORT_SPEED_30HZ	0x01
+#define INPORT_SPEED_50HZ	0x02
+#define INPORT_SPEED_100HZ	0x03
+#define INPORT_SPEED_200HZ	0x04
+#define INPORT_MODE_BASE	INPORT_SPEED_100HZ
+#define INPORT_MODE_IRQ		0x08
+#else
+#define INPORT_NAME		"Microsoft InPort Mouse"
+#define INPORT_VENDOR		0x0001
+#define INPORT_MODE_BASE	0x10
+#define INPORT_MODE_IRQ		0x01
+#endif
+#define INPORT_MODE_HOLD	0x20
+
+#define INPORT_IRQ		5
+
+static int inport_irq = INPORT_IRQ;
+module_param_named(irq, inport_irq, uint, 0);
+MODULE_PARM_DESC(irq, "IRQ number (5=default)");
+
+__obsolete_setup("inport_irq=");
+
+static int inport_used;
+
+static irqreturn_t inport_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static int inport_open(struct input_dev *dev)
+{
+	if (!inport_used++) {
+		if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL))
+			return -EBUSY;
+		outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+		outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
+	}
+
+	return 0;
+}
+
+static void inport_close(struct input_dev *dev)
+{
+	if (!--inport_used) {
+		outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+		outb(INPORT_MODE_BASE, INPORT_DATA_PORT);
+		free_irq(inport_irq, NULL);
+	}
+}
+
+static struct input_dev inport_dev = {
+	.evbit	= { BIT(EV_KEY) | BIT(EV_REL) },
+	.keybit	= { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT) },
+	.relbit	= { BIT(REL_X) | BIT(REL_Y) },
+	.open	= inport_open,
+	.close	= inport_close,
+	.name	= INPORT_NAME,
+	.phys	= "isa023c/input0",
+	.id = { 
+ 		.bustype = BUS_ISA,
+        	.vendor  = INPORT_VENDOR,
+        	.product = 0x0001,
+        	.version = 0x0100,
+	},
+};
+
+static irqreturn_t inport_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned char buttons;
+
+	outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+	outb(INPORT_MODE_HOLD | INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
+
+	input_regs(&inport_dev, regs);
+
+	outb(INPORT_REG_X, INPORT_CONTROL_PORT);
+	input_report_rel(&inport_dev, REL_X, inb(INPORT_DATA_PORT));
+
+	outb(INPORT_REG_Y, INPORT_CONTROL_PORT);
+	input_report_rel(&inport_dev, REL_Y, inb(INPORT_DATA_PORT));
+
+	outb(INPORT_REG_BTNS, INPORT_CONTROL_PORT);
+	buttons = inb(INPORT_DATA_PORT);
+
+	input_report_key(&inport_dev, BTN_MIDDLE, buttons & 1);
+	input_report_key(&inport_dev, BTN_LEFT,   buttons & 2);
+	input_report_key(&inport_dev, BTN_RIGHT,  buttons & 4);
+
+	outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+	outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT);
+
+	input_sync(&inport_dev);
+	return IRQ_HANDLED;
+}
+
+static int __init inport_init(void)
+{
+	unsigned char a,b,c;
+
+	if (!request_region(INPORT_BASE, INPORT_EXTENT, "inport")) {
+		printk(KERN_ERR "inport.c: Can't allocate ports at %#x\n", INPORT_BASE);
+		return -EBUSY;
+	}
+
+	a = inb(INPORT_SIGNATURE_PORT);
+	b = inb(INPORT_SIGNATURE_PORT);
+	c = inb(INPORT_SIGNATURE_PORT);
+	if (( a == b ) || ( a != c )) {
+		release_region(INPORT_BASE, INPORT_EXTENT);
+		printk(KERN_ERR "inport.c: Didn't find InPort mouse at %#x\n", INPORT_BASE);
+		return -ENODEV;
+	}
+
+	outb(INPORT_RESET, INPORT_CONTROL_PORT);
+	outb(INPORT_REG_MODE, INPORT_CONTROL_PORT);
+	outb(INPORT_MODE_BASE, INPORT_DATA_PORT);
+
+	input_register_device(&inport_dev);
+
+	printk(KERN_INFO "input: " INPORT_NAME " at %#x irq %d\n", INPORT_BASE, inport_irq);
+
+	return 0;
+}
+
+static void __exit inport_exit(void)
+{
+	input_unregister_device(&inport_dev);
+	release_region(INPORT_BASE, INPORT_EXTENT);
+}
+
+module_init(inport_init);
+module_exit(inport_exit);
diff --git a/drivers/input/mouse/logibm.c b/drivers/input/mouse/logibm.c
new file mode 100644
index 0000000..77eb83e
--- /dev/null
+++ b/drivers/input/mouse/logibm.c
@@ -0,0 +1,183 @@
+/*
+ * $Id: logibm.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	James Banks		Matthew Dillon
+ *	David Giller		Nathan Laredo
+ *	Linus Torvalds		Johan Myreen
+ *	Cliff Matthews		Philip Blundell
+ *	Russell King
+ */
+
+/*
+ * Logitech Bus Mouse 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Logitech busmouse driver");
+MODULE_LICENSE("GPL");
+
+#define	LOGIBM_BASE		0x23c
+#define	LOGIBM_EXTENT		4
+
+#define	LOGIBM_DATA_PORT	LOGIBM_BASE + 0
+#define	LOGIBM_SIGNATURE_PORT	LOGIBM_BASE + 1
+#define	LOGIBM_CONTROL_PORT	LOGIBM_BASE + 2
+#define	LOGIBM_CONFIG_PORT	LOGIBM_BASE + 3
+
+#define	LOGIBM_ENABLE_IRQ	0x00
+#define	LOGIBM_DISABLE_IRQ	0x10
+#define	LOGIBM_READ_X_LOW	0x80
+#define	LOGIBM_READ_X_HIGH	0xa0
+#define	LOGIBM_READ_Y_LOW	0xc0
+#define	LOGIBM_READ_Y_HIGH	0xe0
+
+#define LOGIBM_DEFAULT_MODE	0x90
+#define LOGIBM_CONFIG_BYTE	0x91
+#define LOGIBM_SIGNATURE_BYTE	0xa5
+
+#define LOGIBM_IRQ		5
+
+static int logibm_irq = LOGIBM_IRQ;
+module_param_named(irq, logibm_irq, uint, 0);
+MODULE_PARM_DESC(irq, "IRQ number (5=default)");
+
+__obsolete_setup("logibm_irq=");
+
+static int logibm_used = 0;
+
+static irqreturn_t logibm_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static int logibm_open(struct input_dev *dev)
+{
+	if (logibm_used++)
+		return 0;
+	if (request_irq(logibm_irq, logibm_interrupt, 0, "logibm", NULL)) {
+		logibm_used--;
+		printk(KERN_ERR "logibm.c: Can't allocate irq %d\n", logibm_irq);
+		return -EBUSY;
+	}
+	outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT);
+	return 0;
+}
+
+static void logibm_close(struct input_dev *dev)
+{
+	if (--logibm_used)
+		return;
+	outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT);
+	free_irq(logibm_irq, NULL);
+}
+
+static struct input_dev logibm_dev = {
+	.evbit	= { BIT(EV_KEY) | BIT(EV_REL) },
+	.keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT) },
+	.relbit	= { BIT(REL_X) | BIT(REL_Y) },
+	.open	= logibm_open,
+	.close	= logibm_close,
+	.name	= "Logitech bus mouse",
+	.phys	= "isa023c/input0",
+	.id	= {
+		.bustype = BUS_ISA,
+		.vendor  = 0x0003,
+		.product = 0x0001,
+		.version = 0x0100,
+	},
+};
+
+static irqreturn_t logibm_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	char dx, dy;
+	unsigned char buttons;
+
+	outb(LOGIBM_READ_X_LOW, LOGIBM_CONTROL_PORT);
+	dx = (inb(LOGIBM_DATA_PORT) & 0xf);
+	outb(LOGIBM_READ_X_HIGH, LOGIBM_CONTROL_PORT);
+	dx |= (inb(LOGIBM_DATA_PORT) & 0xf) << 4;
+	outb(LOGIBM_READ_Y_LOW, LOGIBM_CONTROL_PORT);
+	dy = (inb(LOGIBM_DATA_PORT) & 0xf);
+	outb(LOGIBM_READ_Y_HIGH, LOGIBM_CONTROL_PORT);
+	buttons = inb(LOGIBM_DATA_PORT);
+	dy |= (buttons & 0xf) << 4;
+	buttons = ~buttons >> 5;
+
+	input_regs(&logibm_dev, regs);
+	input_report_rel(&logibm_dev, REL_X, dx);
+	input_report_rel(&logibm_dev, REL_Y, dy);
+	input_report_key(&logibm_dev, BTN_RIGHT,  buttons & 1);
+	input_report_key(&logibm_dev, BTN_MIDDLE, buttons & 2);
+	input_report_key(&logibm_dev, BTN_LEFT,   buttons & 4);
+	input_sync(&logibm_dev);
+
+	outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT);
+	return IRQ_HANDLED;
+}
+
+static int __init logibm_init(void)
+{
+	if (!request_region(LOGIBM_BASE, LOGIBM_EXTENT, "logibm")) {
+		printk(KERN_ERR "logibm.c: Can't allocate ports at %#x\n", LOGIBM_BASE);
+		return -EBUSY;
+	}
+
+	outb(LOGIBM_CONFIG_BYTE, LOGIBM_CONFIG_PORT);
+	outb(LOGIBM_SIGNATURE_BYTE, LOGIBM_SIGNATURE_PORT);
+	udelay(100);
+
+	if (inb(LOGIBM_SIGNATURE_PORT) != LOGIBM_SIGNATURE_BYTE) {
+		release_region(LOGIBM_BASE, LOGIBM_EXTENT);
+		printk(KERN_ERR "logibm.c: Didn't find Logitech busmouse at %#x\n", LOGIBM_BASE);
+		return -ENODEV;
+	}
+
+	outb(LOGIBM_DEFAULT_MODE, LOGIBM_CONFIG_PORT);
+	outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT);
+
+	input_register_device(&logibm_dev);
+	
+	printk(KERN_INFO "input: Logitech bus mouse at %#x irq %d\n", LOGIBM_BASE, logibm_irq);
+
+	return 0;
+}
+
+static void __exit logibm_exit(void)
+{
+	input_unregister_device(&logibm_dev);
+	release_region(LOGIBM_BASE, LOGIBM_EXTENT);
+}
+
+module_init(logibm_init);
+module_exit(logibm_exit);
diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c
new file mode 100644
index 0000000..5ab1bd7
--- /dev/null
+++ b/drivers/input/mouse/logips2pp.c
@@ -0,0 +1,397 @@
+/*
+ * Logitech PS/2++ mouse driver
+ *
+ * Copyright (c) 1999-2003 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2003 Eric Wong <eric@yhbt.net>
+ *
+ * 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/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include "psmouse.h"
+#include "logips2pp.h"
+
+/* Logitech mouse types */
+#define PS2PP_KIND_WHEEL	1
+#define PS2PP_KIND_MX		2
+#define PS2PP_KIND_TP3		3
+
+/* Logitech mouse features */
+#define PS2PP_WHEEL		0x01
+#define PS2PP_HWHEEL		0x02
+#define PS2PP_SIDE_BTN		0x04
+#define PS2PP_EXTRA_BTN		0x08
+#define PS2PP_TASK_BTN		0x10
+#define PS2PP_NAV_BTN		0x20
+
+struct ps2pp_info {
+	const int model;
+	unsigned const int kind;
+	unsigned const int features;
+};
+
+/*
+ * Process a PS2++ or PS2T++ packet.
+ */
+
+static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
+{
+	struct input_dev *dev = &psmouse->dev;
+	unsigned char *packet = psmouse->packet;
+
+	if (psmouse->pktcnt < 3)
+		return PSMOUSE_GOOD_DATA;
+
+/*
+ * Full packet accumulated, process it
+ */
+
+	input_regs(dev, regs);
+
+	if ((packet[0] & 0x48) == 0x48 && (packet[1] & 0x02) == 0x02) {
+
+		/* Logitech extended packet */
+		switch ((packet[1] >> 4) | (packet[0] & 0x30)) {
+
+			case 0x0d: /* Mouse extra info */
+
+				input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL,
+					(int) (packet[2] & 8) - (int) (packet[2] & 7));
+				input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1);
+				input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1);
+
+				break;
+
+			case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */
+
+				input_report_key(dev, BTN_SIDE, (packet[2]) & 1);
+				input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1);
+				input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1);
+				input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1);
+				input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1);
+
+				break;
+
+			case 0x0f: /* TouchPad extra info */
+
+				input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL,
+					(int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7));
+				packet[0] = packet[2] | 0x08;
+				break;
+
+#ifdef DEBUG
+			default:
+				printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n",
+					(packet[1] >> 4) | (packet[0] & 0x30));
+#endif
+		}
+	} else {
+		/* Standard PS/2 motion data */
+		input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
+		input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
+	}
+
+	input_report_key(dev, BTN_LEFT,    packet[0]       & 1);
+	input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
+	input_report_key(dev, BTN_RIGHT,  (packet[0] >> 1) & 1);
+
+	input_sync(dev);
+
+	return PSMOUSE_FULL_PACKET;
+
+}
+
+/*
+ * ps2pp_cmd() sends a PS2++ command, sliced into two bit
+ * pieces through the SETRES command. This is needed to send extended
+ * commands to mice on notebooks that try to understand the PS/2 protocol
+ * Ugly.
+ */
+
+static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command)
+{
+	if (psmouse_sliced_command(psmouse, command))
+		return -1;
+
+	if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL))
+		return -1;
+
+	return 0;
+}
+
+/*
+ * SmartScroll / CruiseControl for some newer Logitech mice Defaults to
+ * enabled if we do nothing to it. Of course I put this in because I want it
+ * disabled :P
+ * 1 - enabled (if previously disabled, also default)
+ * 0 - disabled
+ */
+
+static void ps2pp_set_smartscroll(struct psmouse *psmouse, unsigned int smartscroll)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[4];
+
+	if (smartscroll > 1)
+		smartscroll = 1;
+
+	ps2pp_cmd(psmouse, param, 0x32);
+
+	param[0] = 0;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+
+	param[0] = smartscroll;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+}
+
+static ssize_t psmouse_attr_show_smartscroll(struct psmouse *psmouse, char *buf)
+{
+	return sprintf(buf, "%d\n", psmouse->smartscroll ? 1 : 0);
+}
+
+static ssize_t psmouse_attr_set_smartscroll(struct psmouse *psmouse, const char *buf, size_t count)
+{
+	unsigned long value;
+	char *rest;
+
+	value = simple_strtoul(buf, &rest, 10);
+	if (*rest || value > 1)
+		return -EINVAL;
+
+	ps2pp_set_smartscroll(psmouse, value);
+	psmouse->smartscroll = value;
+	return count;
+}
+
+PSMOUSE_DEFINE_ATTR(smartscroll);
+
+/*
+ * Support 800 dpi resolution _only_ if the user wants it (there are good
+ * reasons to not use it even if the mouse supports it, and of course there are
+ * also good reasons to use it, let the user decide).
+ */
+
+static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolution)
+{
+	if (resolution > 400) {
+		struct ps2dev *ps2dev = &psmouse->ps2dev;
+		unsigned char param = 3;
+
+		ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+		ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+		ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+		ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
+		psmouse->resolution = 800;
+	} else
+		psmouse_set_resolution(psmouse, resolution);
+}
+
+static void ps2pp_disconnect(struct psmouse *psmouse)
+{
+	device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll);
+}
+
+static struct ps2pp_info *get_model_info(unsigned char model)
+{
+	static struct ps2pp_info ps2pp_list[] = {
+		{ 12,	0,			PS2PP_SIDE_BTN},
+		{ 13,	0,			0 },
+		{ 15,	PS2PP_KIND_MX,					/* MX1000 */
+				PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
+				PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL },
+		{ 40,	0,			PS2PP_SIDE_BTN },
+		{ 41,	0,			PS2PP_SIDE_BTN },
+		{ 42,	0,			PS2PP_SIDE_BTN },
+		{ 43,	0,			PS2PP_SIDE_BTN },
+		{ 50,	0,			0 },
+		{ 51,	0,			0 },
+		{ 52,	PS2PP_KIND_WHEEL,	PS2PP_SIDE_BTN | PS2PP_WHEEL },
+		{ 53,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 61,	PS2PP_KIND_MX,					/* MX700 */
+				PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
+				PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
+		{ 73,	0,			PS2PP_SIDE_BTN },
+		{ 75,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 76,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 80,	PS2PP_KIND_WHEEL,	PS2PP_SIDE_BTN | PS2PP_WHEEL },
+		{ 81,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 83,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 88,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
+		{ 96,	0,			0 },
+		{ 97,	PS2PP_KIND_TP3,		PS2PP_WHEEL | PS2PP_HWHEEL },
+		{ 100,	PS2PP_KIND_MX,					/* MX510 */
+				PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
+				PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
+		{ 111,  PS2PP_KIND_MX,					/* MX300 */
+				PS2PP_WHEEL | PS2PP_EXTRA_BTN | PS2PP_TASK_BTN },
+		{ 112,	PS2PP_KIND_MX,					/* MX500 */
+				PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
+				PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
+		{ 114,	PS2PP_KIND_MX,					/* MX310 */
+				PS2PP_WHEEL | PS2PP_SIDE_BTN |
+				PS2PP_TASK_BTN | PS2PP_EXTRA_BTN },
+		{ }
+	};
+	int i;
+
+	for (i = 0; ps2pp_list[i].model; i++)
+		if (model == ps2pp_list[i].model)
+			return &ps2pp_list[i];
+
+	printk(KERN_WARNING "logips2pp: Detected unknown logitech mouse model %d\n", model);
+	return NULL;
+}
+
+/*
+ * Set up input device's properties based on the detected mouse model.
+ */
+
+static void ps2pp_set_model_properties(struct psmouse *psmouse, struct ps2pp_info *model_info,
+				       int using_ps2pp)
+{
+	if (model_info->features & PS2PP_SIDE_BTN)
+		set_bit(BTN_SIDE, psmouse->dev.keybit);
+
+	if (model_info->features & PS2PP_EXTRA_BTN)
+		set_bit(BTN_EXTRA, psmouse->dev.keybit);
+
+	if (model_info->features & PS2PP_TASK_BTN)
+		set_bit(BTN_TASK, psmouse->dev.keybit);
+
+	if (model_info->features & PS2PP_NAV_BTN) {
+		set_bit(BTN_FORWARD, psmouse->dev.keybit);
+		set_bit(BTN_BACK, psmouse->dev.keybit);
+	}
+
+	if (model_info->features & PS2PP_WHEEL)
+		set_bit(REL_WHEEL, psmouse->dev.relbit);
+
+	if (model_info->features & PS2PP_HWHEEL)
+		set_bit(REL_HWHEEL, psmouse->dev.relbit);
+
+	switch (model_info->kind) {
+		case PS2PP_KIND_WHEEL:
+			psmouse->name = "Wheel Mouse";
+			break;
+
+		case PS2PP_KIND_MX:
+			psmouse->name = "MX Mouse";
+			break;
+
+		case PS2PP_KIND_TP3:
+			psmouse->name = "TouchPad 3";
+			break;
+
+		default:
+			/*
+			 * Set name to "Mouse" only when using PS2++,
+			 * otherwise let other protocols define suitable
+			 * name
+			 */
+			if (using_ps2pp)
+				psmouse->name = "Mouse";
+			break;
+	}
+}
+
+
+/*
+ * Logitech magic init. Detect whether the mouse is a Logitech one
+ * and its exact model and try turning on extended protocol for ones
+ * that support it.
+ */
+
+int ps2pp_init(struct psmouse *psmouse, int set_properties)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[4];
+	unsigned char model, buttons;
+	struct ps2pp_info *model_info;
+	int use_ps2pp = 0;
+
+	param[0] = 0;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
+	param[1] = 0;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
+
+	if (!param[1])
+		return -1;
+
+	model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78);
+	buttons = param[1];
+
+	if ((model_info = get_model_info(model)) != NULL) {
+
+/*
+ * Do Logitech PS2++ / PS2T++ magic init.
+ */
+		if (model == 97) { /* Touch Pad 3 */
+
+			/* Unprotect RAM */
+			param[0] = 0x11; param[1] = 0x04; param[2] = 0x68;
+			ps2_command(ps2dev, param, 0x30d1);
+			/* Enable features */
+			param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b;
+			ps2_command(ps2dev, param, 0x30d1);
+			/* Enable PS2++ */
+			param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3;
+			ps2_command(ps2dev, param, 0x30d1);
+
+			param[0] = 0;
+			if (!ps2_command(ps2dev, param, 0x13d1) &&
+			    param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) {
+				use_ps2pp = 1;
+			}
+
+		} else {
+
+			param[0] = param[1] = param[2] = 0;
+			ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */
+			ps2pp_cmd(psmouse, param, 0xDB);
+
+			if ((param[0] & 0x78) == 0x48 &&
+			    (param[1] & 0xf3) == 0xc2 &&
+			    (param[2] & 0x03) == ((param[1] >> 2) & 3)) {
+				ps2pp_set_smartscroll(psmouse, psmouse->smartscroll);
+				use_ps2pp = 1;
+			}
+		}
+	}
+
+	if (set_properties) {
+		psmouse->vendor = "Logitech";
+		psmouse->model = model;
+
+		if (use_ps2pp) {
+			psmouse->protocol_handler = ps2pp_process_byte;
+			psmouse->pktsize = 3;
+
+			if (model_info->kind != PS2PP_KIND_TP3) {
+				psmouse->set_resolution = ps2pp_set_resolution;
+				psmouse->disconnect = ps2pp_disconnect;
+
+				device_create_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll);
+			}
+		}
+
+		if (buttons < 3)
+			clear_bit(BTN_MIDDLE, psmouse->dev.keybit);
+		if (buttons < 2)
+			clear_bit(BTN_RIGHT, psmouse->dev.keybit);
+
+		if (model_info)
+			ps2pp_set_model_properties(psmouse, model_info, use_ps2pp);
+	}
+
+	return use_ps2pp ? 0 : -1;
+}
+
diff --git a/drivers/input/mouse/logips2pp.h b/drivers/input/mouse/logips2pp.h
new file mode 100644
index 0000000..64a8ec5
--- /dev/null
+++ b/drivers/input/mouse/logips2pp.h
@@ -0,0 +1,16 @@
+/*
+ * Logitech PS/2++ mouse driver header
+ *
+ * Copyright (c) 2003 Vojtech Pavlik <vojtech@suse.cz>
+ *
+ * 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.
+ */
+
+#ifndef _LOGIPS2PP_H
+#define _LOGIPS2PP_H
+
+int ps2pp_init(struct psmouse *psmouse, int set_properties);
+
+#endif
diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c
new file mode 100644
index 0000000..12dc0ef
--- /dev/null
+++ b/drivers/input/mouse/maplemouse.c
@@ -0,0 +1,134 @@
+/*
+ *	$Id: maplemouse.c,v 1.2 2004/03/22 01:18:15 lethal Exp $
+ * 	SEGA Dreamcast mouse driver
+ *	Based on drivers/usb/usbmouse.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 mouse driver");
+
+struct dc_mouse {
+	struct input_dev dev;
+	int open;
+};
+
+
+static void dc_mouse_callback(struct mapleq *mq)
+{
+	int buttons, relx, rely, relz;
+	struct maple_device *mapledev = mq->dev;
+	struct dc_mouse *mouse = mapledev->private_data;
+	struct input_dev *dev = &mouse->dev;
+	unsigned char *res = mq->recvbuf;
+
+	buttons = ~res[8];
+	relx=*(unsigned short *)(res+12)-512;
+	rely=*(unsigned short *)(res+14)-512;
+	relz=*(unsigned short *)(res+16)-512;
+
+	input_report_key(dev, BTN_LEFT,   buttons&4);
+	input_report_key(dev, BTN_MIDDLE, buttons&9);
+	input_report_key(dev, BTN_RIGHT,  buttons&2);
+	input_report_rel(dev, REL_X,      relx);
+	input_report_rel(dev, REL_Y,      rely);
+	input_report_rel(dev, REL_WHEEL,  relz);
+	input_sync(dev);
+}
+
+
+static int dc_mouse_open(struct input_dev *dev)
+{
+	struct dc_mouse *mouse = dev->private;
+	mouse->open++;
+	return 0;
+}
+
+
+static void dc_mouse_close(struct input_dev *dev)
+{
+	struct dc_mouse *mouse = dev->private;
+	mouse->open--;
+}
+
+
+static int dc_mouse_connect(struct maple_device *dev)
+{
+	unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]);
+	struct dc_mouse *mouse;
+
+	if (!(mouse = kmalloc(sizeof(struct dc_mouse), GFP_KERNEL)))
+		return -1;
+	memset(mouse, 0, sizeof(struct dc_mouse));
+
+	dev->private_data = mouse;
+
+	mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+	mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+	mouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL);
+
+	init_input_dev(&mouse->dev);
+
+	mouse->dev.private = mouse;
+	mouse->dev.open = dc_mouse_open;
+	mouse->dev.close = dc_mouse_close;
+	mouse->dev.event = NULL;
+
+	mouse->dev.name = dev->product_name;
+	mouse->dev.id.bustype = BUS_MAPLE;
+	
+	input_register_device(&mouse->dev);
+
+	maple_getcond_callback(dev, dc_mouse_callback, 1, MAPLE_FUNC_MOUSE);
+
+	printk(KERN_INFO "input: mouse(0x%lx): %s\n", data, mouse->dev.name);
+
+	return 0;
+}
+
+
+static void dc_mouse_disconnect(struct maple_device *dev)
+{
+	struct dc_mouse *mouse = dev->private_data;
+
+	input_unregister_device(&mouse->dev);
+	kfree(mouse);
+}
+
+
+static struct maple_driver dc_mouse_driver = {
+	.function =	MAPLE_FUNC_MOUSE,
+	.name =		"Dreamcast mouse",
+	.connect =	dc_mouse_connect,
+	.disconnect =	dc_mouse_disconnect,
+};
+
+
+static int __init dc_mouse_init(void)
+{
+	maple_register_driver(&dc_mouse_driver);
+	return 0;
+}
+
+
+static void __exit dc_mouse_exit(void)
+{
+	maple_unregister_driver(&dc_mouse_driver);
+}
+
+
+module_init(dc_mouse_init);
+module_exit(dc_mouse_exit);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c
new file mode 100644
index 0000000..0c74918
--- /dev/null
+++ b/drivers/input/mouse/pc110pad.c
@@ -0,0 +1,178 @@
+/*
+ * $Id: pc110pad.c,v 1.12 2001/09/25 10:12:07 vojtech Exp $
+ *
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Alan Cox	Robin O'Leary	
+ */
+
+/*
+ * IBM PC110 touchpad 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/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("IBM PC110 touchpad driver");
+MODULE_LICENSE("GPL");
+
+#define PC110PAD_OFF	0x30
+#define PC110PAD_ON	0x38
+
+static int pc110pad_irq = 10;
+static int pc110pad_io = 0x15e0;
+
+static struct input_dev pc110pad_dev;
+static int pc110pad_data[3];
+static int pc110pad_count;
+static int pc110pad_used;
+
+static char *pc110pad_name = "IBM PC110 TouchPad";
+static char *pc110pad_phys = "isa15e0/input0";
+
+static irqreturn_t pc110pad_interrupt(int irq, void *ptr, struct pt_regs *regs)
+{
+	int value     = inb_p(pc110pad_io);
+	int handshake = inb_p(pc110pad_io + 2);
+
+	outb_p(handshake |  1, pc110pad_io + 2);
+	outb_p(handshake & ~1, pc110pad_io + 2);
+	inb_p(0x64);
+
+	pc110pad_data[pc110pad_count++] = value;
+
+	if (pc110pad_count < 3)
+		return IRQ_HANDLED;
+	
+	input_regs(&pc110pad_dev, regs);
+	input_report_key(&pc110pad_dev, BTN_TOUCH,
+		pc110pad_data[0] & 0x01);
+	input_report_abs(&pc110pad_dev, ABS_X,
+		pc110pad_data[1] | ((pc110pad_data[0] << 3) & 0x80) | ((pc110pad_data[0] << 1) & 0x100));
+	input_report_abs(&pc110pad_dev, ABS_Y,
+		pc110pad_data[2] | ((pc110pad_data[0] << 4) & 0x80));
+	input_sync(&pc110pad_dev);
+
+	pc110pad_count = 0;
+	return IRQ_HANDLED;
+}
+
+static void pc110pad_close(struct input_dev *dev)
+{
+	if (!--pc110pad_used)
+		outb(PC110PAD_OFF, pc110pad_io + 2);
+}
+
+static int pc110pad_open(struct input_dev *dev)
+{
+	if (pc110pad_used++)
+		return 0;
+
+	pc110pad_interrupt(0,NULL,NULL);
+	pc110pad_interrupt(0,NULL,NULL);
+	pc110pad_interrupt(0,NULL,NULL);
+	outb(PC110PAD_ON, pc110pad_io + 2);
+	pc110pad_count = 0;
+
+	return 0;
+}
+
+/*
+ * We try to avoid enabling the hardware if it's not
+ * there, but we don't know how to test. But we do know
+ * that the PC110 is not a PCI system. So if we find any
+ * PCI devices in the machine, we don't have a PC110.
+ */
+static int __init pc110pad_init(void)
+{
+	struct pci_dev *dev;
+
+	dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+	if (dev) {
+		pci_dev_put(dev);
+		return -ENOENT;
+	}
+
+	if (!request_region(pc110pad_io, 4, "pc110pad")) {
+		printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n",
+				pc110pad_io, pc110pad_io + 4);
+		return -EBUSY;
+	}
+
+	outb(PC110PAD_OFF, pc110pad_io + 2);
+
+	if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL))
+	{
+		release_region(pc110pad_io, 4);
+		printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq);
+		return -EBUSY;
+	}
+
+        pc110pad_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+        pc110pad_dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+        pc110pad_dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+
+	pc110pad_dev.absmax[ABS_X] = 0x1ff;
+	pc110pad_dev.absmax[ABS_Y] = 0x0ff;
+        
+	pc110pad_dev.open = pc110pad_open;
+        pc110pad_dev.close = pc110pad_close;
+
+	pc110pad_dev.name = pc110pad_name;
+	pc110pad_dev.phys = pc110pad_phys;
+	pc110pad_dev.id.bustype = BUS_ISA;
+	pc110pad_dev.id.vendor = 0x0003;
+	pc110pad_dev.id.product = 0x0001;
+	pc110pad_dev.id.version = 0x0100;
+
+	input_register_device(&pc110pad_dev);	
+
+	printk(KERN_INFO "input: %s at %#x irq %d\n",
+		pc110pad_name, pc110pad_io, pc110pad_irq);
+	
+	return 0;
+}
+ 
+static void __exit pc110pad_exit(void)
+{
+	input_unregister_device(&pc110pad_dev);	
+
+	outb(PC110PAD_OFF, pc110pad_io + 2);
+
+	free_irq(pc110pad_irq, NULL);
+	release_region(pc110pad_io, 4);
+}
+
+module_init(pc110pad_init);
+module_exit(pc110pad_exit);
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
new file mode 100644
index 0000000..cd85095
--- /dev/null
+++ b/drivers/input/mouse/psmouse-base.c
@@ -0,0 +1,1011 @@
+/*
+ * PS/2 mouse driver
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ * Copyright (c) 2003-2004 Dmitry Torokhov
+ */
+
+/*
+ * 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/libps2.h>
+#include "psmouse.h"
+#include "synaptics.h"
+#include "logips2pp.h"
+#include "alps.h"
+
+#define DRIVER_DESC	"PS/2 mouse driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static unsigned int psmouse_max_proto = -1U;
+static int psmouse_set_maxproto(const char *val, struct kernel_param *kp);
+static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp);
+static char *psmouse_proto_abbrev[] = { NULL, "bare", NULL, NULL, NULL, "imps", "exps", NULL, NULL, NULL };
+#define param_check_proto_abbrev(name, p)	__param_check(name, p, unsigned int)
+#define param_set_proto_abbrev			psmouse_set_maxproto
+#define param_get_proto_abbrev			psmouse_get_maxproto
+module_param_named(proto, psmouse_max_proto, proto_abbrev, 0644);
+MODULE_PARM_DESC(proto, "Highest protocol extension to probe (bare, imps, exps, any). Useful for KVM switches.");
+
+static unsigned int psmouse_resolution = 200;
+module_param_named(resolution, psmouse_resolution, uint, 0644);
+MODULE_PARM_DESC(resolution, "Resolution, in dpi.");
+
+static unsigned int psmouse_rate = 100;
+module_param_named(rate, psmouse_rate, uint, 0644);
+MODULE_PARM_DESC(rate, "Report rate, in reports per second.");
+
+static unsigned int psmouse_smartscroll = 1;
+module_param_named(smartscroll, psmouse_smartscroll, bool, 0644);
+MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled.");
+
+static unsigned int psmouse_resetafter;
+module_param_named(resetafter, psmouse_resetafter, uint, 0644);
+MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never).");
+
+PSMOUSE_DEFINE_ATTR(rate);
+PSMOUSE_DEFINE_ATTR(resolution);
+PSMOUSE_DEFINE_ATTR(resetafter);
+
+__obsolete_setup("psmouse_noext");
+__obsolete_setup("psmouse_resolution=");
+__obsolete_setup("psmouse_smartscroll=");
+__obsolete_setup("psmouse_resetafter=");
+__obsolete_setup("psmouse_rate=");
+
+static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "ThinkPS/2", "GenPS/2", "ImPS/2", "ImExPS/2", "SynPS/2", "AlpsPS/2" };
+
+/*
+ * psmouse_process_byte() analyzes the PS/2 data stream and reports
+ * relevant events to the input module once full packet has arrived.
+ */
+
+static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
+{
+	struct input_dev *dev = &psmouse->dev;
+	unsigned char *packet = psmouse->packet;
+
+	if (psmouse->pktcnt < psmouse->pktsize)
+		return PSMOUSE_GOOD_DATA;
+
+/*
+ * Full packet accumulated, process it
+ */
+
+	input_regs(dev, regs);
+
+/*
+ * Scroll wheel on IntelliMice, scroll buttons on NetMice
+ */
+
+	if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS)
+		input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
+
+/*
+ * Scroll wheel and buttons on IntelliMouse Explorer
+ */
+
+	if (psmouse->type == PSMOUSE_IMEX) {
+		input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7));
+		input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1);
+		input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1);
+	}
+
+/*
+ * Extra buttons on Genius NewNet 3D
+ */
+
+	if (psmouse->type == PSMOUSE_GENPS) {
+		input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1);
+		input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1);
+	}
+
+/*
+ * Extra button on ThinkingMouse
+ */
+	if (psmouse->type == PSMOUSE_THINKPS) {
+		input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1);
+		/* Without this bit of weirdness moving up gives wildly high Y changes. */
+		packet[1] |= (packet[0] & 0x40) << 1;
+	}
+
+/*
+ * Generic PS/2 Mouse
+ */
+
+	input_report_key(dev, BTN_LEFT,    packet[0]       & 1);
+	input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
+	input_report_key(dev, BTN_RIGHT,  (packet[0] >> 1) & 1);
+
+	input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
+	input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
+
+	input_sync(dev);
+
+	return PSMOUSE_FULL_PACKET;
+}
+
+/*
+ * psmouse_interrupt() handles incoming characters, either gathering them into
+ * packets or passing them to the command routine as command output.
+ */
+
+static irqreturn_t psmouse_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+	struct psmouse *psmouse = serio_get_drvdata(serio);
+	psmouse_ret_t rc;
+
+	if (psmouse->state == PSMOUSE_IGNORE)
+		goto out;
+
+	if (flags & (SERIO_PARITY|SERIO_TIMEOUT)) {
+		if (psmouse->state == PSMOUSE_ACTIVATED)
+			printk(KERN_WARNING "psmouse.c: bad data from KBC -%s%s\n",
+				flags & SERIO_TIMEOUT ? " timeout" : "",
+				flags & SERIO_PARITY ? " bad parity" : "");
+		ps2_cmd_aborted(&psmouse->ps2dev);
+		goto out;
+	}
+
+	if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_ACK))
+		if  (ps2_handle_ack(&psmouse->ps2dev, data))
+			goto out;
+
+	if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_CMD))
+		if  (ps2_handle_response(&psmouse->ps2dev, data))
+			goto out;
+
+	if (psmouse->state == PSMOUSE_INITIALIZING)
+		goto out;
+
+	if (psmouse->state == PSMOUSE_ACTIVATED &&
+	    psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) {
+		printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n",
+		       psmouse->name, psmouse->phys, psmouse->pktcnt);
+		psmouse->pktcnt = 0;
+	}
+
+	psmouse->last = jiffies;
+	psmouse->packet[psmouse->pktcnt++] = data;
+
+	if (psmouse->packet[0] == PSMOUSE_RET_BAT) {
+		if (psmouse->pktcnt == 1)
+			goto out;
+
+		if (psmouse->pktcnt == 2) {
+			if (psmouse->packet[1] == PSMOUSE_RET_ID) {
+				psmouse->state = PSMOUSE_IGNORE;
+				serio_reconnect(serio);
+				goto out;
+			}
+			if (psmouse->type == PSMOUSE_SYNAPTICS) {
+				/* neither 0xAA nor 0x00 are valid first bytes
+				 * for a packet in absolute mode
+				 */
+				psmouse->pktcnt = 0;
+				goto out;
+			}
+		}
+	}
+
+	rc = psmouse->protocol_handler(psmouse, regs);
+
+	switch (rc) {
+		case PSMOUSE_BAD_DATA:
+			printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n",
+				psmouse->name, psmouse->phys, psmouse->pktcnt);
+			psmouse->pktcnt = 0;
+
+			if (++psmouse->out_of_sync == psmouse->resetafter) {
+				psmouse->state = PSMOUSE_IGNORE;
+				printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n");
+				serio_reconnect(psmouse->ps2dev.serio);
+			}
+			break;
+
+		case PSMOUSE_FULL_PACKET:
+			psmouse->pktcnt = 0;
+			if (psmouse->out_of_sync) {
+				psmouse->out_of_sync = 0;
+				printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n",
+					psmouse->name, psmouse->phys);
+			}
+			break;
+
+		case PSMOUSE_GOOD_DATA:
+			break;
+	}
+out:
+	return IRQ_HANDLED;
+}
+
+
+/*
+ * psmouse_sliced_command() sends an extended PS/2 command to the mouse
+ * using sliced syntax, understood by advanced devices, such as Logitech
+ * or Synaptics touchpads. The command is encoded as:
+ * 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
+ * is the command.
+ */
+int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command)
+{
+	int i;
+
+	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11))
+		return -1;
+
+	for (i = 6; i >= 0; i -= 2) {
+		unsigned char d = (command >> i) & 3;
+		if (ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES))
+			return -1;
+	}
+
+	return 0;
+}
+
+
+/*
+ * psmouse_reset() resets the mouse into power-on state.
+ */
+int psmouse_reset(struct psmouse *psmouse)
+{
+	unsigned char param[2];
+
+	if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT))
+		return -1;
+
+	if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID)
+		return -1;
+
+	return 0;
+}
+
+
+/*
+ * Genius NetMouse magic init.
+ */
+static int genius_detect(struct psmouse *psmouse, int set_properties)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[4];
+
+	param[0] = 3;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
+	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
+
+	if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55)
+		return -1;
+
+	if (set_properties) {
+		set_bit(BTN_EXTRA, psmouse->dev.keybit);
+		set_bit(BTN_SIDE, psmouse->dev.keybit);
+		set_bit(REL_WHEEL, psmouse->dev.relbit);
+
+		psmouse->vendor = "Genius";
+		psmouse->name = "Wheel Mouse";
+		psmouse->pktsize = 4;
+	}
+
+	return 0;
+}
+
+/*
+ * IntelliMouse magic init.
+ */
+static int intellimouse_detect(struct psmouse *psmouse, int set_properties)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[2];
+
+	param[0] = 200;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	param[0] = 100;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	param[0] =  80;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
+
+	if (param[0] != 3)
+		return -1;
+
+	if (set_properties) {
+		set_bit(REL_WHEEL, psmouse->dev.relbit);
+
+		if (!psmouse->vendor) psmouse->vendor = "Generic";
+		if (!psmouse->name) psmouse->name = "Wheel Mouse";
+		psmouse->pktsize = 4;
+	}
+
+	return 0;
+}
+
+/*
+ * Try IntelliMouse/Explorer magic init.
+ */
+static int im_explorer_detect(struct psmouse *psmouse, int set_properties)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[2];
+
+	intellimouse_detect(psmouse, 0);
+
+	param[0] = 200;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	param[0] = 200;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	param[0] =  80;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
+
+	if (param[0] != 4)
+		return -1;
+
+	if (set_properties) {
+		set_bit(REL_WHEEL, psmouse->dev.relbit);
+		set_bit(BTN_SIDE, psmouse->dev.keybit);
+		set_bit(BTN_EXTRA, psmouse->dev.keybit);
+
+		if (!psmouse->vendor) psmouse->vendor = "Generic";
+		if (!psmouse->name) psmouse->name = "Explorer Mouse";
+		psmouse->pktsize = 4;
+	}
+
+	return 0;
+}
+
+/*
+ * Kensington ThinkingMouse / ExpertMouse magic init.
+ */
+static int thinking_detect(struct psmouse *psmouse, int set_properties)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[2];
+	unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20, 0 };
+	int i;
+
+	param[0] = 10;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+	param[0] = 0;
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	for (i = 0; seq[i]; i++)
+		ps2_command(ps2dev, seq + i, PSMOUSE_CMD_SETRATE);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
+
+	if (param[0] != 2)
+		return -1;
+
+	if (set_properties) {
+		set_bit(BTN_EXTRA, psmouse->dev.keybit);
+
+		psmouse->vendor = "Kensington";
+		psmouse->name = "ThinkingMouse";
+	}
+
+	return 0;
+}
+
+/*
+ * Bare PS/2 protocol "detection". Always succeeds.
+ */
+static int ps2bare_detect(struct psmouse *psmouse, int set_properties)
+{
+	if (!psmouse->vendor) psmouse->vendor = "Generic";
+	if (!psmouse->name) psmouse->name = "Mouse";
+
+	return 0;
+}
+
+/*
+ * psmouse_extensions() probes for any extensions to the basic PS/2 protocol
+ * the mouse may have.
+ */
+
+static int psmouse_extensions(struct psmouse *psmouse,
+			      unsigned int max_proto, int set_properties)
+{
+	int synaptics_hardware = 0;
+
+/*
+ * Try Kensington ThinkingMouse (we try first, because synaptics probe
+ * upsets the thinkingmouse).
+ */
+
+	if (max_proto > PSMOUSE_IMEX && thinking_detect(psmouse, set_properties) == 0)
+		return PSMOUSE_THINKPS;
+
+/*
+ * Try Synaptics TouchPad
+ */
+	if (max_proto > PSMOUSE_PS2 && synaptics_detect(psmouse, set_properties) == 0) {
+		synaptics_hardware = 1;
+
+		if (max_proto > PSMOUSE_IMEX) {
+			if (!set_properties || synaptics_init(psmouse) == 0)
+				return PSMOUSE_SYNAPTICS;
+/*
+ * Some Synaptics touchpads can emulate extended protocols (like IMPS/2).
+ * Unfortunately Logitech/Genius probes confuse some firmware versions so
+ * we'll have to skip them.
+ */
+			max_proto = PSMOUSE_IMEX;
+		}
+/*
+ * Make sure that touchpad is in relative mode, gestures (taps) are enabled
+ */
+		synaptics_reset(psmouse);
+	}
+
+/*
+ * Try ALPS TouchPad
+ */
+	if (max_proto > PSMOUSE_IMEX) {
+		ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+		if (alps_detect(psmouse, set_properties) == 0) {
+			if (!set_properties || alps_init(psmouse) == 0)
+				return PSMOUSE_ALPS;
+/*
+ * Init failed, try basic relative protocols
+ */
+			max_proto = PSMOUSE_IMEX;
+		}
+	}
+
+	if (max_proto > PSMOUSE_IMEX && genius_detect(psmouse, set_properties) == 0)
+		return PSMOUSE_GENPS;
+
+	if (max_proto > PSMOUSE_IMEX && ps2pp_init(psmouse, set_properties) == 0)
+		return PSMOUSE_PS2PP;
+
+/*
+ * Reset to defaults in case the device got confused by extended
+ * protocol probes.
+ */
+	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+
+	if (max_proto >= PSMOUSE_IMEX && im_explorer_detect(psmouse, set_properties) == 0)
+		return PSMOUSE_IMEX;
+
+	if (max_proto >= PSMOUSE_IMPS && intellimouse_detect(psmouse, set_properties) == 0)
+		return PSMOUSE_IMPS;
+
+/*
+ * Okay, all failed, we have a standard mouse here. The number of the buttons
+ * is still a question, though. We assume 3.
+ */
+	ps2bare_detect(psmouse, set_properties);
+
+	if (synaptics_hardware) {
+/*
+ * We detected Synaptics hardware but it did not respond to IMPS/2 probes.
+ * We need to reset the touchpad because if there is a track point on the
+ * pass through port it could get disabled while probing for protocol
+ * extensions.
+ */
+		psmouse_reset(psmouse);
+		ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+	}
+
+	return PSMOUSE_PS2;
+}
+
+/*
+ * psmouse_probe() probes for a PS/2 mouse.
+ */
+
+static int psmouse_probe(struct psmouse *psmouse)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[2];
+
+/*
+ * First, we check if it's a mouse. It should send 0x00 or 0x03
+ * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
+ */
+
+	param[0] = 0xa5;
+	if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID))
+		return -1;
+
+	if (param[0] != 0x00 && param[0] != 0x03 && param[0] != 0x04)
+		return -1;
+
+/*
+ * Then we reset and disable the mouse so that it doesn't generate events.
+ */
+
+	if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS))
+		printk(KERN_WARNING "psmouse.c: Failed to reset mouse on %s\n", ps2dev->serio->phys);
+
+	return 0;
+}
+
+/*
+ * Here we set the mouse resolution.
+ */
+
+void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
+{
+	unsigned char params[] = { 0, 1, 2, 2, 3 };
+
+	if (resolution == 0 || resolution > 200)
+		resolution = 200;
+
+	ps2_command(&psmouse->ps2dev, &params[resolution / 50], PSMOUSE_CMD_SETRES);
+	psmouse->resolution = 25 << params[resolution / 50];
+}
+
+/*
+ * Here we set the mouse report rate.
+ */
+
+static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
+{
+	unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
+	int i = 0;
+
+	while (rates[i] > rate) i++;
+	ps2_command(&psmouse->ps2dev, &rates[i], PSMOUSE_CMD_SETRATE);
+	psmouse->rate = rates[i];
+}
+
+/*
+ * psmouse_initialize() initializes the mouse to a sane state.
+ */
+
+static void psmouse_initialize(struct psmouse *psmouse)
+{
+/*
+ * We set the mouse into streaming mode.
+ */
+
+	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM);
+
+/*
+ * We set the mouse report rate, resolution and scaling.
+ */
+
+	if (psmouse_max_proto != PSMOUSE_PS2) {
+		psmouse->set_rate(psmouse, psmouse->rate);
+		psmouse->set_resolution(psmouse, psmouse->resolution);
+		ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+	}
+}
+
+/*
+ * psmouse_set_state() sets new psmouse state and resets all flags and
+ * counters while holding serio lock so fighting with interrupt handler
+ * is not a concern.
+ */
+
+static void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
+{
+	serio_pause_rx(psmouse->ps2dev.serio);
+	psmouse->state = new_state;
+	psmouse->pktcnt = psmouse->out_of_sync = 0;
+	psmouse->ps2dev.flags = 0;
+	serio_continue_rx(psmouse->ps2dev.serio);
+}
+
+/*
+ * psmouse_activate() enables the mouse so that we get motion reports from it.
+ */
+
+static void psmouse_activate(struct psmouse *psmouse)
+{
+	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE))
+		printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n",
+			psmouse->ps2dev.serio->phys);
+
+	psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+}
+
+
+/*
+ * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion
+ * reports from it unless we explicitely request it.
+ */
+
+static void psmouse_deactivate(struct psmouse *psmouse)
+{
+	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE))
+		printk(KERN_WARNING "psmouse.c: Failed to deactivate mouse on %s\n",
+			psmouse->ps2dev.serio->phys);
+
+	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+}
+
+
+/*
+ * psmouse_cleanup() resets the mouse into power-on state.
+ */
+
+static void psmouse_cleanup(struct serio *serio)
+{
+	struct psmouse *psmouse = serio_get_drvdata(serio);
+
+	psmouse_reset(psmouse);
+}
+
+/*
+ * psmouse_disconnect() closes and frees.
+ */
+
+static void psmouse_disconnect(struct serio *serio)
+{
+	struct psmouse *psmouse, *parent;
+
+	device_remove_file(&serio->dev, &psmouse_attr_rate);
+	device_remove_file(&serio->dev, &psmouse_attr_resolution);
+	device_remove_file(&serio->dev, &psmouse_attr_resetafter);
+
+	psmouse = serio_get_drvdata(serio);
+	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+		parent = serio_get_drvdata(serio->parent);
+		if (parent->pt_deactivate)
+			parent->pt_deactivate(parent);
+	}
+
+	if (psmouse->disconnect)
+		psmouse->disconnect(psmouse);
+
+	psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+
+	input_unregister_device(&psmouse->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(psmouse);
+}
+
+/*
+ * psmouse_connect() is a callback from the serio module when
+ * an unhandled serio port is found.
+ */
+static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct psmouse *psmouse, *parent = NULL;
+	int retval;
+
+	/*
+	 * If this is a pass-through port deactivate parent so the device
+	 * connected to this port can be successfully identified
+	 */
+	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+		parent = serio_get_drvdata(serio->parent);
+		psmouse_deactivate(parent);
+	}
+
+	if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL))) {
+		retval = -ENOMEM;
+		goto out;
+	}
+
+	memset(psmouse, 0, sizeof(struct psmouse));
+
+	ps2_init(&psmouse->ps2dev, serio);
+	sprintf(psmouse->phys, "%s/input0", serio->phys);
+	psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+	psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
+	psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
+	psmouse->dev.private = psmouse;
+	psmouse->dev.dev = &serio->dev;
+	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+	serio_set_drvdata(serio, psmouse);
+
+	retval = serio_open(serio, drv);
+	if (retval) {
+		serio_set_drvdata(serio, NULL);
+		kfree(psmouse);
+		goto out;
+	}
+
+	if (psmouse_probe(psmouse) < 0) {
+		serio_close(serio);
+		serio_set_drvdata(serio, NULL);
+		kfree(psmouse);
+		retval = -ENODEV;
+		goto out;
+	}
+
+	psmouse->rate = psmouse_rate;
+	psmouse->resolution = psmouse_resolution;
+	psmouse->resetafter = psmouse_resetafter;
+	psmouse->smartscroll = psmouse_smartscroll;
+	psmouse->set_rate = psmouse_set_rate;
+	psmouse->set_resolution = psmouse_set_resolution;
+	psmouse->protocol_handler = psmouse_process_byte;
+	psmouse->pktsize = 3;
+
+	psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1);
+
+	sprintf(psmouse->devname, "%s %s %s",
+		psmouse_protocols[psmouse->type], psmouse->vendor, psmouse->name);
+
+	psmouse->dev.name = psmouse->devname;
+	psmouse->dev.phys = psmouse->phys;
+	psmouse->dev.id.bustype = BUS_I8042;
+	psmouse->dev.id.vendor = 0x0002;
+	psmouse->dev.id.product = psmouse->type;
+	psmouse->dev.id.version = psmouse->model;
+
+	input_register_device(&psmouse->dev);
+
+	printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys);
+
+	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+	psmouse_initialize(psmouse);
+
+	if (parent && parent->pt_activate)
+		parent->pt_activate(parent);
+
+	device_create_file(&serio->dev, &psmouse_attr_rate);
+	device_create_file(&serio->dev, &psmouse_attr_resolution);
+	device_create_file(&serio->dev, &psmouse_attr_resetafter);
+
+	psmouse_activate(psmouse);
+
+	retval = 0;
+
+out:
+	/* If this is a pass-through port the parent awaits to be activated */
+	if (parent)
+		psmouse_activate(parent);
+
+	return retval;
+}
+
+
+static int psmouse_reconnect(struct serio *serio)
+{
+	struct psmouse *psmouse = serio_get_drvdata(serio);
+	struct psmouse *parent = NULL;
+	struct serio_driver *drv = serio->drv;
+	int rc = -1;
+
+	if (!drv || !psmouse) {
+		printk(KERN_DEBUG "psmouse: reconnect request, but serio is disconnected, ignoring...\n");
+		return -1;
+	}
+
+	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+		parent = serio_get_drvdata(serio->parent);
+		psmouse_deactivate(parent);
+	}
+
+	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+
+	if (psmouse->reconnect) {
+		if (psmouse->reconnect(psmouse))
+			goto out;
+	} else if (psmouse_probe(psmouse) < 0 ||
+		   psmouse->type != psmouse_extensions(psmouse, psmouse_max_proto, 0))
+		goto out;
+
+	/* ok, the device type (and capabilities) match the old one,
+	 * we can continue using it, complete intialization
+	 */
+	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+
+	psmouse_initialize(psmouse);
+
+	if (parent && parent->pt_activate)
+		parent->pt_activate(parent);
+
+	psmouse_activate(psmouse);
+	rc = 0;
+
+out:
+	/* If this is a pass-through port the parent waits to be activated */
+	if (parent)
+		psmouse_activate(parent);
+
+	return rc;
+}
+
+static struct serio_device_id psmouse_serio_ids[] = {
+	{
+		.type	= SERIO_8042,
+		.proto	= SERIO_ANY,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_PS_PSTHRU,
+		.proto	= SERIO_ANY,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, psmouse_serio_ids);
+
+static struct serio_driver psmouse_drv = {
+	.driver		= {
+		.name	= "psmouse",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= psmouse_serio_ids,
+	.interrupt	= psmouse_interrupt,
+	.connect	= psmouse_connect,
+	.reconnect	= psmouse_reconnect,
+	.disconnect	= psmouse_disconnect,
+	.cleanup	= psmouse_cleanup,
+};
+
+ssize_t psmouse_attr_show_helper(struct device *dev, char *buf,
+				 ssize_t (*handler)(struct psmouse *, char *))
+{
+	struct serio *serio = to_serio_port(dev);
+	int retval;
+
+	retval = serio_pin_driver(serio);
+	if (retval)
+		return retval;
+
+	if (serio->drv != &psmouse_drv) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	retval = handler(serio_get_drvdata(serio), buf);
+
+out:
+	serio_unpin_driver(serio);
+	return retval;
+}
+
+ssize_t psmouse_attr_set_helper(struct device *dev, const char *buf, size_t count,
+				ssize_t (*handler)(struct psmouse *, const char *, size_t))
+{
+	struct serio *serio = to_serio_port(dev);
+	struct psmouse *psmouse = serio_get_drvdata(serio);
+	struct psmouse *parent = NULL;
+	int retval;
+
+	retval = serio_pin_driver(serio);
+	if (retval)
+		return retval;
+
+	if (serio->drv != &psmouse_drv) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
+		parent = serio_get_drvdata(serio->parent);
+		psmouse_deactivate(parent);
+	}
+	psmouse_deactivate(psmouse);
+
+	retval = handler(psmouse, buf, count);
+
+	psmouse_activate(psmouse);
+	if (parent)
+		psmouse_activate(parent);
+
+out:
+	serio_unpin_driver(serio);
+	return retval;
+}
+
+static ssize_t psmouse_attr_show_rate(struct psmouse *psmouse, char *buf)
+{
+	return sprintf(buf, "%d\n", psmouse->rate);
+}
+
+static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, const char *buf, size_t count)
+{
+	unsigned long value;
+	char *rest;
+
+	value = simple_strtoul(buf, &rest, 10);
+	if (*rest)
+		return -EINVAL;
+
+	psmouse->set_rate(psmouse, value);
+	return count;
+}
+
+static ssize_t psmouse_attr_show_resolution(struct psmouse *psmouse, char *buf)
+{
+	return sprintf(buf, "%d\n", psmouse->resolution);
+}
+
+static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, const char *buf, size_t count)
+{
+	unsigned long value;
+	char *rest;
+
+	value = simple_strtoul(buf, &rest, 10);
+	if (*rest)
+		return -EINVAL;
+
+	psmouse->set_resolution(psmouse, value);
+	return count;
+}
+
+static ssize_t psmouse_attr_show_resetafter(struct psmouse *psmouse, char *buf)
+{
+	return sprintf(buf, "%d\n", psmouse->resetafter);
+}
+
+static ssize_t psmouse_attr_set_resetafter(struct psmouse *psmouse, const char *buf, size_t count)
+{
+	unsigned long value;
+	char *rest;
+
+	value = simple_strtoul(buf, &rest, 10);
+	if (*rest)
+		return -EINVAL;
+
+	psmouse->resetafter = value;
+	return count;
+}
+
+static int psmouse_set_maxproto(const char *val, struct kernel_param *kp)
+{
+	int i;
+
+	if (!val)
+		return -EINVAL;
+
+	if (!strncmp(val, "any", 3)) {
+		*((unsigned int *)kp->arg) = -1UL;
+		return 0;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(psmouse_proto_abbrev); i++) {
+		if (!psmouse_proto_abbrev[i])
+			continue;
+
+		if (!strncmp(val, psmouse_proto_abbrev[i], strlen(psmouse_proto_abbrev[i]))) {
+			*((unsigned int *)kp->arg) = i;
+			return 0;
+		}
+	}
+
+	return -EINVAL;					\
+}
+
+static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp)
+{
+	return sprintf(buffer, "%s\n",
+			psmouse_max_proto < ARRAY_SIZE(psmouse_proto_abbrev) ?
+				psmouse_proto_abbrev[psmouse_max_proto] : "any");
+}
+
+static int __init psmouse_init(void)
+{
+	serio_register_driver(&psmouse_drv);
+	return 0;
+}
+
+static void __exit psmouse_exit(void)
+{
+	serio_unregister_driver(&psmouse_drv);
+}
+
+module_init(psmouse_init);
+module_exit(psmouse_exit);
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
new file mode 100644
index 0000000..bda5b06
--- /dev/null
+++ b/drivers/input/mouse/psmouse.h
@@ -0,0 +1,106 @@
+#ifndef _PSMOUSE_H
+#define _PSMOUSE_H
+
+#define PSMOUSE_CMD_SETSCALE11	0x00e6
+#define PSMOUSE_CMD_SETSCALE21	0x00e7
+#define PSMOUSE_CMD_SETRES	0x10e8
+#define PSMOUSE_CMD_GETINFO	0x03e9
+#define PSMOUSE_CMD_SETSTREAM	0x00ea
+#define PSMOUSE_CMD_SETPOLL	0x00f0
+#define PSMOUSE_CMD_POLL	0x03eb
+#define PSMOUSE_CMD_GETID	0x02f2
+#define PSMOUSE_CMD_SETRATE	0x10f3
+#define PSMOUSE_CMD_ENABLE	0x00f4
+#define PSMOUSE_CMD_DISABLE	0x00f5
+#define PSMOUSE_CMD_RESET_DIS	0x00f6
+#define PSMOUSE_CMD_RESET_BAT	0x02ff
+
+#define PSMOUSE_RET_BAT		0xaa
+#define PSMOUSE_RET_ID		0x00
+#define PSMOUSE_RET_ACK		0xfa
+#define PSMOUSE_RET_NAK		0xfe
+
+enum psmouse_state {
+	PSMOUSE_IGNORE,
+	PSMOUSE_INITIALIZING,
+	PSMOUSE_CMD_MODE,
+	PSMOUSE_ACTIVATED,
+};
+
+/* psmouse protocol handler return codes */
+typedef enum {
+	PSMOUSE_BAD_DATA,
+	PSMOUSE_GOOD_DATA,
+	PSMOUSE_FULL_PACKET
+} psmouse_ret_t;
+
+struct psmouse {
+	void *private;
+	struct input_dev dev;
+	struct ps2dev ps2dev;
+	char *vendor;
+	char *name;
+	unsigned char packet[8];
+	unsigned char pktcnt;
+	unsigned char pktsize;
+	unsigned char type;
+	unsigned int model;
+	unsigned long last;
+	unsigned long out_of_sync;
+	enum psmouse_state state;
+	char devname[64];
+	char phys[32];
+
+	unsigned int rate;
+	unsigned int resolution;
+	unsigned int resetafter;
+	unsigned int smartscroll;	/* Logitech only */
+
+	psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs);
+	void (*set_rate)(struct psmouse *psmouse, unsigned int rate);
+	void (*set_resolution)(struct psmouse *psmouse, unsigned int resolution);
+
+	int (*reconnect)(struct psmouse *psmouse);
+	void (*disconnect)(struct psmouse *psmouse);
+
+	void (*pt_activate)(struct psmouse *psmouse);
+	void (*pt_deactivate)(struct psmouse *psmouse);
+};
+
+enum psmouse_type {
+	PSMOUSE_NONE,
+	PSMOUSE_PS2,
+	PSMOUSE_PS2PP,
+	PSMOUSE_THINKPS,
+	PSMOUSE_GENPS,
+	PSMOUSE_IMPS,
+	PSMOUSE_IMEX,
+	PSMOUSE_SYNAPTICS,
+	PSMOUSE_ALPS,
+};
+
+int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
+int psmouse_reset(struct psmouse *psmouse);
+void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution);
+
+ssize_t psmouse_attr_show_helper(struct device *dev, char *buf,
+			ssize_t (*handler)(struct psmouse *, char *));
+ssize_t psmouse_attr_set_helper(struct device *dev, const char *buf, size_t count,
+			ssize_t (*handler)(struct psmouse *, const char *, size_t));
+
+#define PSMOUSE_DEFINE_ATTR(_name)						\
+static ssize_t psmouse_attr_show_##_name(struct psmouse *, char *);		\
+static ssize_t psmouse_attr_set_##_name(struct psmouse *, const char *, size_t);\
+static ssize_t psmouse_do_show_##_name(struct device *d, char *b)		\
+{										\
+	return psmouse_attr_show_helper(d, b, psmouse_attr_show_##_name);	\
+}										\
+static ssize_t psmouse_do_set_##_name(struct device *d, const char *b, size_t s)\
+{										\
+	return psmouse_attr_set_helper(d, b, s, psmouse_attr_set_##_name);	\
+}										\
+static struct device_attribute psmouse_attr_##_name = 				\
+	__ATTR(_name, S_IWUSR | S_IRUGO,					\
+		psmouse_do_show_##_name, psmouse_do_set_##_name);
+
+#endif /* _PSMOUSE_H */
diff --git a/drivers/input/mouse/rpcmouse.c b/drivers/input/mouse/rpcmouse.c
new file mode 100644
index 0000000..7280f68
--- /dev/null
+++ b/drivers/input/mouse/rpcmouse.c
@@ -0,0 +1,107 @@
+/*
+ *  Acorn RiscPC mouse driver for Linux/ARM
+ *
+ *  Copyright (c) 2000-2002 Vojtech Pavlik
+ *  Copyright (C) 1996-2002 Russell King
+ *
+ */
+
+/*
+ * 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 handles the Acorn RiscPCs mouse.  We basically have a couple of
+ * hardware registers that track the sensor count for the X-Y movement and
+ * another register holding the button state.  On every VSYNC interrupt we read
+ * the complete state and then work out if something has changed.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/hardware/iomd.h>
+
+MODULE_AUTHOR("Vojtech Pavlik, Russell King");
+MODULE_DESCRIPTION("Acorn RiscPC mouse driver");
+MODULE_LICENSE("GPL");
+
+static short rpcmouse_lastx, rpcmouse_lasty;
+
+static struct input_dev rpcmouse_dev = {
+	.evbit	= { BIT(EV_KEY) | BIT(EV_REL) },
+	.keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT) },
+	.relbit	= { BIT(REL_X) | BIT(REL_Y) },
+	.name	= "Acorn RiscPC Mouse",
+	.phys	= "rpcmouse/input0",
+	.id	= {
+		.bustype = BUS_HOST,
+		.vendor  = 0x0005,
+		.product = 0x0001,
+		.version = 0x0100,
+	},
+};
+
+static irqreturn_t rpcmouse_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct input_dev *dev = dev_id;
+	short x, y, dx, dy, b;
+
+	x = (short) iomd_readl(IOMD_MOUSEX);
+	y = (short) iomd_readl(IOMD_MOUSEY);
+	b = (short) (__raw_readl(0xe0310000) ^ 0x70);
+
+	dx = x - rpcmouse_lastx;
+	dy = y - rpcmouse_lasty; 
+
+	rpcmouse_lastx = x;
+	rpcmouse_lasty = y;
+
+	input_regs(dev, regs);
+
+	input_report_rel(dev, REL_X, dx);
+	input_report_rel(dev, REL_Y, -dy);
+
+	input_report_key(dev, BTN_LEFT,   b & 0x40);
+	input_report_key(dev, BTN_MIDDLE, b & 0x20);
+	input_report_key(dev, BTN_RIGHT,  b & 0x10);
+
+	input_sync(dev);
+
+	return IRQ_HANDLED;
+}
+
+static int __init rpcmouse_init(void)
+{
+	init_input_dev(&rpcmouse_dev);
+
+	rpcmouse_lastx = (short) iomd_readl(IOMD_MOUSEX);
+	rpcmouse_lasty = (short) iomd_readl(IOMD_MOUSEY);
+
+	if (request_irq(IRQ_VSYNCPULSE, rpcmouse_irq, SA_SHIRQ, "rpcmouse", &rpcmouse_dev)) {
+		printk(KERN_ERR "rpcmouse: unable to allocate VSYNC interrupt\n");
+		return -1;
+	}
+
+	input_register_device(&rpcmouse_dev);
+
+	printk(KERN_INFO "input: Acorn RiscPC mouse\n");
+
+	return 0;
+}
+
+static void __exit rpcmouse_exit(void)
+{
+	input_unregister_device(&rpcmouse_dev);
+	free_irq(IRQ_VSYNCPULSE, &rpcmouse_dev);
+}
+
+module_init(rpcmouse_init);
+module_exit(rpcmouse_exit);
diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c
new file mode 100644
index 0000000..d12b93a
--- /dev/null
+++ b/drivers/input/mouse/sermouse.c
@@ -0,0 +1,370 @@
+/*
+ * $Id: sermouse.c,v 1.17 2002/03/13 10:03:43 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ *  Serial mouse 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/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/config.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC	"Serial mouse driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse",
+					"Logitech M+ Mouse", "Microsoft MZ Mouse", "Logitech MZ+ Mouse",
+					"Logitech MZ++ Mouse"};
+
+struct sermouse {
+	struct input_dev dev;
+	signed char buf[8];
+	unsigned char count;
+	unsigned char type;
+	unsigned long last;
+	char phys[32];
+};
+
+/*
+ * sermouse_process_msc() analyzes the incoming MSC/Sun bytestream and
+ * applies some prediction to the data, resulting in 96 updates per
+ * second, which is as good as a PS/2 or USB mouse.
+ */
+
+static void sermouse_process_msc(struct sermouse *sermouse, signed char data, struct pt_regs *regs)
+{
+	struct input_dev *dev = &sermouse->dev;
+	signed char *buf = sermouse->buf;
+
+	input_regs(dev, regs);
+
+	switch (sermouse->count) {
+
+		case 0:
+			if ((data & 0xf8) != 0x80) return;
+			input_report_key(dev, BTN_LEFT,   !(data & 4));
+			input_report_key(dev, BTN_RIGHT,  !(data & 1));
+			input_report_key(dev, BTN_MIDDLE, !(data & 2));
+			break;
+
+		case 1:
+		case 3:
+			input_report_rel(dev, REL_X, data / 2);
+			input_report_rel(dev, REL_Y, -buf[1]);
+			buf[0] = data - data / 2;
+			break;
+
+		case 2:
+		case 4:
+			input_report_rel(dev, REL_X, buf[0]);
+			input_report_rel(dev, REL_Y, buf[1] - data);
+			buf[1] = data / 2;
+			break;
+	}
+
+	input_sync(dev);
+
+	if (++sermouse->count == (5 - ((sermouse->type == SERIO_SUN) << 1)))
+		sermouse->count = 0;
+}
+
+/*
+ * sermouse_process_ms() anlyzes the incoming MS(Z/+/++) bytestream and
+ * generates events. With prediction it gets 80 updates/sec, assuming
+ * standard 3-byte packets and 1200 bps.
+ */
+
+static void sermouse_process_ms(struct sermouse *sermouse, signed char data, struct pt_regs *regs)
+{
+	struct input_dev *dev = &sermouse->dev;
+	signed char *buf = sermouse->buf;
+
+	if (data & 0x40) sermouse->count = 0;
+
+	input_regs(dev, regs);
+
+	switch (sermouse->count) {
+
+		case 0:
+			buf[1] = data;
+			input_report_key(dev, BTN_LEFT,   (data >> 5) & 1);
+			input_report_key(dev, BTN_RIGHT,  (data >> 4) & 1);
+			break;
+
+		case 1:
+			buf[2] = data;
+			data = (signed char) (((buf[1] << 6) & 0xc0) | (data & 0x3f));
+			input_report_rel(dev, REL_X, data / 2);
+			input_report_rel(dev, REL_Y, buf[4]);
+			buf[3] = data - data / 2;
+			break;
+
+		case 2:
+			/* Guessing the state of the middle button on 3-button MS-protocol mice - ugly. */
+			if ((sermouse->type == SERIO_MS) && !data && !buf[2] && !((buf[0] & 0xf0) ^ buf[1]))
+				input_report_key(dev, BTN_MIDDLE, !test_bit(BTN_MIDDLE, dev->key));
+			buf[0] = buf[1];
+
+			data = (signed char) (((buf[1] << 4) & 0xc0) | (data & 0x3f));
+			input_report_rel(dev, REL_X, buf[3]);
+			input_report_rel(dev, REL_Y, data - buf[4]);
+			buf[4] = data / 2;
+			break;
+
+		case 3:
+
+			switch (sermouse->type) {
+
+				case SERIO_MS:
+					 sermouse->type = SERIO_MP;
+
+				case SERIO_MP:
+					if ((data >> 2) & 3) break;	/* M++ Wireless Extension packet. */
+					input_report_key(dev, BTN_MIDDLE, (data >> 5) & 1);
+					input_report_key(dev, BTN_SIDE,   (data >> 4) & 1);
+					break;
+
+				case SERIO_MZP:
+				case SERIO_MZPP:
+					input_report_key(dev, BTN_SIDE,   (data >> 5) & 1);
+
+				case SERIO_MZ:
+					input_report_key(dev, BTN_MIDDLE, (data >> 4) & 1);
+					input_report_rel(dev, REL_WHEEL,  (data & 8) - (data & 7));
+					break;
+			}
+
+			break;
+
+		case 4:
+		case 6:	/* MZ++ packet type. We can get these bytes for M++ too but we ignore them later. */
+			buf[1] = (data >> 2) & 0x0f;
+			break;
+
+		case 5:
+		case 7: /* Ignore anything besides MZ++ */
+			if (sermouse->type != SERIO_MZPP) break;
+
+			switch (buf[1]) {
+
+				case 1: /* Extra mouse info */
+
+					input_report_key(dev, BTN_SIDE, (data >> 4) & 1);
+					input_report_key(dev, BTN_EXTRA, (data >> 5) & 1);
+					input_report_rel(dev, data & 0x80 ? REL_HWHEEL : REL_WHEEL, (data & 7) - (data & 8));
+
+					break;
+
+				default: /* We don't decode anything else yet. */
+
+					printk(KERN_WARNING
+						"sermouse.c: Received MZ++ packet %x, don't know how to handle.\n", buf[1]);
+					break;
+			}
+
+			break;
+	}
+
+	input_sync(dev);
+
+	sermouse->count++;
+}
+
+/*
+ * sermouse_interrupt() handles incoming characters, either gathering them into
+ * packets or passing them to the command routine as command output.
+ */
+
+static irqreturn_t sermouse_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+	struct sermouse *sermouse = serio_get_drvdata(serio);
+
+	if (time_after(jiffies, sermouse->last + HZ/10)) sermouse->count = 0;
+	sermouse->last = jiffies;
+
+	if (sermouse->type > SERIO_SUN)
+		sermouse_process_ms(sermouse, data, regs);
+	else
+		sermouse_process_msc(sermouse, data, regs);
+	return IRQ_HANDLED;
+}
+
+/*
+ * sermouse_disconnect() cleans up after we don't want talk
+ * to the mouse anymore.
+ */
+
+static void sermouse_disconnect(struct serio *serio)
+{
+	struct sermouse *sermouse = serio_get_drvdata(serio);
+
+	input_unregister_device(&sermouse->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(sermouse);
+}
+
+/*
+ * sermouse_connect() is a callback form the serio module when
+ * an unhandled serio port is found.
+ */
+
+static int sermouse_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct sermouse *sermouse;
+	unsigned char c;
+	int err;
+
+	if (!serio->id.proto || serio->id.proto > SERIO_MZPP)
+		return -ENODEV;
+
+	if (!(sermouse = kmalloc(sizeof(struct sermouse), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(sermouse, 0, sizeof(struct sermouse));
+
+	init_input_dev(&sermouse->dev);
+	sermouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+	sermouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);
+	sermouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
+	sermouse->dev.private = sermouse;
+
+	sermouse->type = serio->id.proto;
+	c = serio->id.extra;
+
+	if (c & 0x01) set_bit(BTN_MIDDLE, sermouse->dev.keybit);
+	if (c & 0x02) set_bit(BTN_SIDE, sermouse->dev.keybit);
+	if (c & 0x04) set_bit(BTN_EXTRA, sermouse->dev.keybit);
+	if (c & 0x10) set_bit(REL_WHEEL, sermouse->dev.relbit);
+	if (c & 0x20) set_bit(REL_HWHEEL, sermouse->dev.relbit);
+
+	sprintf(sermouse->phys, "%s/input0", serio->phys);
+
+	sermouse->dev.name = sermouse_protocols[sermouse->type];
+	sermouse->dev.phys = sermouse->phys;
+	sermouse->dev.id.bustype = BUS_RS232;
+	sermouse->dev.id.vendor = sermouse->type;
+	sermouse->dev.id.product = c;
+	sermouse->dev.id.version = 0x0100;
+	sermouse->dev.dev = &serio->dev;
+
+	serio_set_drvdata(serio, sermouse);
+
+	err = serio_open(serio, drv);
+	if (err) {
+		serio_set_drvdata(serio, NULL);
+		kfree(sermouse);
+		return err;
+	}
+
+	input_register_device(&sermouse->dev);
+
+	printk(KERN_INFO "input: %s on %s\n", sermouse_protocols[sermouse->type], serio->phys);
+
+	return 0;
+}
+
+static struct serio_device_id sermouse_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MSC,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_SUN,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MS,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MP,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MZ,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MZP,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MZPP,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, sermouse_serio_ids);
+
+static struct serio_driver sermouse_drv = {
+	.driver		= {
+		.name	= "sermouse",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= sermouse_serio_ids,
+	.interrupt	= sermouse_interrupt,
+	.connect	= sermouse_connect,
+	.disconnect	= sermouse_disconnect,
+};
+
+static int __init sermouse_init(void)
+{
+	serio_register_driver(&sermouse_drv);
+	return 0;
+}
+
+static void __exit sermouse_exit(void)
+{
+	serio_unregister_driver(&sermouse_drv);
+}
+
+module_init(sermouse_init);
+module_exit(sermouse_exit);
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
new file mode 100644
index 0000000..69832f8
--- /dev/null
+++ b/drivers/input/mouse/synaptics.c
@@ -0,0 +1,700 @@
+/*
+ * Synaptics TouchPad PS/2 mouse driver
+ *
+ *   2003 Dmitry Torokhov <dtor@mail.ru>
+ *     Added support for pass-through port. Special thanks to Peter Berg Larsen
+ *     for explaining various Synaptics quirks.
+ *
+ *   2003 Peter Osterlund <petero2@telia.com>
+ *     Ported to 2.5 input device infrastructure.
+ *
+ *   Copyright (C) 2001 Stefan Gmeiner <riddlebox@freesurf.ch>
+ *     start merging tpconfig and gpm code to a xfree-input module
+ *     adding some changes and extensions (ex. 3rd and 4th button)
+ *
+ *   Copyright (c) 1997 C. Scott Ananian <cananian@alumni.priceton.edu>
+ *   Copyright (c) 1998-2000 Bruce Kalk <kall@compass.com>
+ *     code for the special synaptics commands (from the tpconfig-source)
+ *
+ * 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.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/libps2.h>
+#include "psmouse.h"
+#include "synaptics.h"
+
+/*
+ * The x/y limits are taken from the Synaptics TouchPad interfacing Guide,
+ * section 2.3.2, which says that they should be valid regardless of the
+ * actual size of the sensor.
+ */
+#define XMIN_NOMINAL 1472
+#define XMAX_NOMINAL 5472
+#define YMIN_NOMINAL 1408
+#define YMAX_NOMINAL 4448
+
+/*****************************************************************************
+ *	Synaptics communications functions
+ ****************************************************************************/
+
+/*
+ * Send a command to the synpatics touchpad by special commands
+ */
+static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param)
+{
+	if (psmouse_sliced_command(psmouse, c))
+		return -1;
+	if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
+		return -1;
+	return 0;
+}
+
+/*
+ * Set the synaptics touchpad mode byte by special commands
+ */
+static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode)
+{
+	unsigned char param[1];
+
+	if (psmouse_sliced_command(psmouse, mode))
+		return -1;
+	param[0] = SYN_PS_SET_MODE2;
+	if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE))
+		return -1;
+	return 0;
+}
+
+/*
+ * Read the model-id bytes from the touchpad
+ * see also SYN_MODEL_* macros
+ */
+static int synaptics_model_id(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	unsigned char mi[3];
+
+	if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi))
+		return -1;
+	priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2];
+	return 0;
+}
+
+/*
+ * Read the capability-bits from the touchpad
+ * see also the SYN_CAP_* macros
+ */
+static int synaptics_capability(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	unsigned char cap[3];
+
+	if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap))
+		return -1;
+	priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+	priv->ext_cap = 0;
+	if (!SYN_CAP_VALID(priv->capabilities))
+		return -1;
+
+	/*
+	 * Unless capExtended is set the rest of the flags should be ignored
+	 */
+	if (!SYN_CAP_EXTENDED(priv->capabilities))
+		priv->capabilities = 0;
+
+	if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) {
+		if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) {
+			printk(KERN_ERR "Synaptics claims to have extended capabilities,"
+			       " but I'm not able to read them.");
+		} else {
+			priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+
+			/*
+			 * if nExtBtn is greater than 8 it should be considered
+			 * invalid and treated as 0
+			 */
+			if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8)
+				priv->ext_cap &= 0xff0fff;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Identify Touchpad
+ * See also the SYN_ID_* macros
+ */
+static int synaptics_identify(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	unsigned char id[3];
+
+	if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id))
+		return -1;
+	priv->identity = (id[0]<<16) | (id[1]<<8) | id[2];
+	if (SYN_ID_IS_SYNAPTICS(priv->identity))
+		return 0;
+	return -1;
+}
+
+static void print_ident(struct synaptics_data *priv)
+{
+	printk(KERN_INFO "Synaptics Touchpad, model: %ld\n", SYN_ID_MODEL(priv->identity));
+	printk(KERN_INFO " Firmware: %ld.%ld\n", SYN_ID_MAJOR(priv->identity),
+	       SYN_ID_MINOR(priv->identity));
+	if (SYN_MODEL_ROT180(priv->model_id))
+		printk(KERN_INFO " 180 degree mounted touchpad\n");
+	if (SYN_MODEL_PORTRAIT(priv->model_id))
+		printk(KERN_INFO " portrait touchpad\n");
+	printk(KERN_INFO " Sensor: %ld\n", SYN_MODEL_SENSOR(priv->model_id));
+	if (SYN_MODEL_NEWABS(priv->model_id))
+		printk(KERN_INFO " new absolute packet format\n");
+	if (SYN_MODEL_PEN(priv->model_id))
+		printk(KERN_INFO " pen detection\n");
+
+	if (SYN_CAP_EXTENDED(priv->capabilities)) {
+		printk(KERN_INFO " Touchpad has extended capability bits\n");
+		if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
+			printk(KERN_INFO " -> %d multi-buttons, i.e. besides standard buttons\n",
+			       (int)(SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap)));
+		if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+			printk(KERN_INFO " -> middle button\n");
+		if (SYN_CAP_FOUR_BUTTON(priv->capabilities))
+			printk(KERN_INFO " -> four buttons\n");
+		if (SYN_CAP_MULTIFINGER(priv->capabilities))
+			printk(KERN_INFO " -> multifinger detection\n");
+		if (SYN_CAP_PALMDETECT(priv->capabilities))
+			printk(KERN_INFO " -> palm detection\n");
+		if (SYN_CAP_PASS_THROUGH(priv->capabilities))
+			printk(KERN_INFO " -> pass-through port\n");
+	}
+}
+
+static int synaptics_query_hardware(struct psmouse *psmouse)
+{
+	int retries = 0;
+
+	while ((retries++ < 3) && psmouse_reset(psmouse))
+		printk(KERN_ERR "synaptics reset failed\n");
+
+	if (synaptics_identify(psmouse))
+		return -1;
+	if (synaptics_model_id(psmouse))
+		return -1;
+	if (synaptics_capability(psmouse))
+		return -1;
+
+	return 0;
+}
+
+static int synaptics_set_absolute_mode(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+
+	priv->mode = SYN_BIT_ABSOLUTE_MODE;
+	if (SYN_ID_MAJOR(priv->identity) >= 4)
+		priv->mode |= SYN_BIT_DISABLE_GESTURE;
+	if (SYN_CAP_EXTENDED(priv->capabilities))
+		priv->mode |= SYN_BIT_W_MODE;
+
+	if (synaptics_mode_cmd(psmouse, priv->mode))
+		return -1;
+
+	return 0;
+}
+
+static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate)
+{
+	struct synaptics_data *priv = psmouse->private;
+
+	if (rate >= 80) {
+		priv->mode |= SYN_BIT_HIGH_RATE;
+		psmouse->rate = 80;
+	} else {
+		priv->mode &= ~SYN_BIT_HIGH_RATE;
+		psmouse->rate = 40;
+	}
+
+	synaptics_mode_cmd(psmouse, priv->mode);
+}
+
+/*****************************************************************************
+ *	Synaptics pass-through PS/2 port support
+ ****************************************************************************/
+static int synaptics_pt_write(struct serio *serio, unsigned char c)
+{
+	struct psmouse *parent = serio_get_drvdata(serio->parent);
+	char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
+
+	if (psmouse_sliced_command(parent, c))
+		return -1;
+	if (ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE))
+		return -1;
+	return 0;
+}
+
+static inline int synaptics_is_pt_packet(unsigned char *buf)
+{
+	return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
+}
+
+static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet)
+{
+	struct psmouse *child = serio_get_drvdata(ptport);
+
+	if (child && child->state == PSMOUSE_ACTIVATED) {
+		serio_interrupt(ptport, packet[1], 0, NULL);
+		serio_interrupt(ptport, packet[4], 0, NULL);
+		serio_interrupt(ptport, packet[5], 0, NULL);
+		if (child->type >= PSMOUSE_GENPS)
+			serio_interrupt(ptport, packet[2], 0, NULL);
+	} else
+		serio_interrupt(ptport, packet[1], 0, NULL);
+}
+
+static void synaptics_pt_activate(struct psmouse *psmouse)
+{
+	struct serio *ptport = psmouse->ps2dev.serio->child;
+	struct psmouse *child = serio_get_drvdata(ptport);
+	struct synaptics_data *priv = psmouse->private;
+
+	/* adjust the touchpad to child's choice of protocol */
+	if (child) {
+		if (child->type >= PSMOUSE_GENPS)
+			priv->mode |= SYN_BIT_FOUR_BYTE_CLIENT;
+		else
+			priv->mode &= ~SYN_BIT_FOUR_BYTE_CLIENT;
+
+		if (synaptics_mode_cmd(psmouse, priv->mode))
+			printk(KERN_INFO "synaptics: failed to switch guest protocol\n");
+	}
+}
+
+static void synaptics_pt_create(struct psmouse *psmouse)
+{
+	struct serio *serio;
+
+	serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!serio) {
+		printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n");
+		return;
+	}
+
+	memset(serio, 0, sizeof(struct serio));
+
+	serio->id.type = SERIO_PS_PSTHRU;
+	strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
+	strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
+	serio->write = synaptics_pt_write;
+	serio->parent = psmouse->ps2dev.serio;
+
+	psmouse->pt_activate = synaptics_pt_activate;
+
+	printk(KERN_INFO "serio: %s port at %s\n", serio->name, psmouse->phys);
+	serio_register_port(serio);
+}
+
+/*****************************************************************************
+ *	Functions to interpret the absolute mode packets
+ ****************************************************************************/
+
+static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw)
+{
+	memset(hw, 0, sizeof(struct synaptics_hw_state));
+
+	if (SYN_MODEL_NEWABS(priv->model_id)) {
+		hw->x = (((buf[3] & 0x10) << 8) |
+			 ((buf[1] & 0x0f) << 8) |
+			 buf[4]);
+		hw->y = (((buf[3] & 0x20) << 7) |
+			 ((buf[1] & 0xf0) << 4) |
+			 buf[5]);
+
+		hw->z = buf[2];
+		hw->w = (((buf[0] & 0x30) >> 2) |
+			 ((buf[0] & 0x04) >> 1) |
+			 ((buf[3] & 0x04) >> 2));
+
+		hw->left  = (buf[0] & 0x01) ? 1 : 0;
+		hw->right = (buf[0] & 0x02) ? 1 : 0;
+
+		if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
+			hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
+			if (hw->w == 2)
+				hw->scroll = (signed char)(buf[1]);
+		}
+
+		if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+			hw->up   = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
+			hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
+		}
+
+		if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) &&
+		    ((buf[0] ^ buf[3]) & 0x02)) {
+			switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
+			default:
+				/*
+				 * if nExtBtn is greater than 8 it should be
+				 * considered invalid and treated as 0
+				 */
+				break;
+			case 8:
+				hw->ext_buttons |= ((buf[5] & 0x08)) ? 0x80 : 0;
+				hw->ext_buttons |= ((buf[4] & 0x08)) ? 0x40 : 0;
+			case 6:
+				hw->ext_buttons |= ((buf[5] & 0x04)) ? 0x20 : 0;
+				hw->ext_buttons |= ((buf[4] & 0x04)) ? 0x10 : 0;
+			case 4:
+				hw->ext_buttons |= ((buf[5] & 0x02)) ? 0x08 : 0;
+				hw->ext_buttons |= ((buf[4] & 0x02)) ? 0x04 : 0;
+			case 2:
+				hw->ext_buttons |= ((buf[5] & 0x01)) ? 0x02 : 0;
+				hw->ext_buttons |= ((buf[4] & 0x01)) ? 0x01 : 0;
+			}
+		}
+	} else {
+		hw->x = (((buf[1] & 0x1f) << 8) | buf[2]);
+		hw->y = (((buf[4] & 0x1f) << 8) | buf[5]);
+
+		hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F));
+		hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1));
+
+		hw->left  = (buf[0] & 0x01) ? 1 : 0;
+		hw->right = (buf[0] & 0x02) ? 1 : 0;
+	}
+}
+
+/*
+ *  called for each full received packet from the touchpad
+ */
+static void synaptics_process_packet(struct psmouse *psmouse)
+{
+	struct input_dev *dev = &psmouse->dev;
+	struct synaptics_data *priv = psmouse->private;
+	struct synaptics_hw_state hw;
+	int num_fingers;
+	int finger_width;
+	int i;
+
+	synaptics_parse_hw_state(psmouse->packet, priv, &hw);
+
+	if (hw.scroll) {
+		priv->scroll += hw.scroll;
+
+		while (priv->scroll >= 4) {
+			input_report_key(dev, BTN_BACK, !hw.down);
+			input_sync(dev);
+			input_report_key(dev, BTN_BACK, hw.down);
+			input_sync(dev);
+			priv->scroll -= 4;
+		}
+		while (priv->scroll <= -4) {
+			input_report_key(dev, BTN_FORWARD, !hw.up);
+			input_sync(dev);
+			input_report_key(dev, BTN_FORWARD, hw.up);
+			input_sync(dev);
+			priv->scroll += 4;
+		}
+		return;
+	}
+
+	if (hw.z > 0) {
+		num_fingers = 1;
+		finger_width = 5;
+		if (SYN_CAP_EXTENDED(priv->capabilities)) {
+			switch (hw.w) {
+			case 0 ... 1:
+				if (SYN_CAP_MULTIFINGER(priv->capabilities))
+					num_fingers = hw.w + 2;
+				break;
+			case 2:
+				if (SYN_MODEL_PEN(priv->model_id))
+					;   /* Nothing, treat a pen as a single finger */
+				break;
+			case 4 ... 15:
+				if (SYN_CAP_PALMDETECT(priv->capabilities))
+					finger_width = hw.w;
+				break;
+			}
+		}
+	} else {
+		num_fingers = 0;
+		finger_width = 0;
+	}
+
+	/* Post events
+	 * BTN_TOUCH has to be first as mousedev relies on it when doing
+	 * absolute -> relative conversion
+	 */
+	if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1);
+	if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0);
+
+	if (hw.z > 0) {
+		input_report_abs(dev, ABS_X, hw.x);
+		input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y);
+	}
+	input_report_abs(dev, ABS_PRESSURE, hw.z);
+
+	input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
+	input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
+	input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
+	input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
+
+	input_report_key(dev, BTN_LEFT, hw.left);
+	input_report_key(dev, BTN_RIGHT, hw.right);
+
+	if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+		input_report_key(dev, BTN_MIDDLE, hw.middle);
+
+	if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+		input_report_key(dev, BTN_FORWARD, hw.up);
+		input_report_key(dev, BTN_BACK, hw.down);
+	}
+
+	for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
+		input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i));
+
+	input_sync(dev);
+}
+
+static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned char pkt_type)
+{
+	static unsigned char newabs_mask[]	= { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
+	static unsigned char newabs_rel_mask[]	= { 0xC0, 0x00, 0x00, 0xC0, 0x00 };
+	static unsigned char newabs_rslt[]	= { 0x80, 0x00, 0x00, 0xC0, 0x00 };
+	static unsigned char oldabs_mask[]	= { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
+	static unsigned char oldabs_rslt[]	= { 0xC0, 0x00, 0x00, 0x80, 0x00 };
+
+	if (idx < 0 || idx > 4)
+		return 0;
+
+	switch (pkt_type) {
+		case SYN_NEWABS:
+		case SYN_NEWABS_RELAXED:
+			return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx];
+
+		case SYN_NEWABS_STRICT:
+			return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx];
+
+		case SYN_OLDABS:
+			return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx];
+
+		default:
+			printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type);
+			return 0;
+	}
+}
+
+static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse)
+{
+	int i;
+
+	for (i = 0; i < 5; i++)
+		if (!synaptics_validate_byte(psmouse->packet, i, SYN_NEWABS_STRICT)) {
+			printk(KERN_INFO "synaptics: using relaxed packet validation\n");
+			return SYN_NEWABS_RELAXED;
+		}
+
+	return SYN_NEWABS_STRICT;
+}
+
+static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
+{
+	struct input_dev *dev = &psmouse->dev;
+	struct synaptics_data *priv = psmouse->private;
+
+	input_regs(dev, regs);
+
+	if (psmouse->pktcnt >= 6) { /* Full packet received */
+		if (unlikely(priv->pkt_type == SYN_NEWABS))
+			priv->pkt_type = synaptics_detect_pkt_type(psmouse);
+
+		if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) {
+			if (psmouse->ps2dev.serio->child)
+				synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet);
+		} else
+			synaptics_process_packet(psmouse);
+
+		return PSMOUSE_FULL_PACKET;
+	}
+
+	return synaptics_validate_byte(psmouse->packet, psmouse->pktcnt - 1, priv->pkt_type) ?
+		PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
+}
+
+/*****************************************************************************
+ *	Driver initialization/cleanup functions
+ ****************************************************************************/
+static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
+{
+	int i;
+
+	set_bit(EV_ABS, dev->evbit);
+	input_set_abs_params(dev, ABS_X, XMIN_NOMINAL, XMAX_NOMINAL, 0, 0);
+	input_set_abs_params(dev, ABS_Y, YMIN_NOMINAL, YMAX_NOMINAL, 0, 0);
+	input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
+	set_bit(ABS_TOOL_WIDTH, dev->absbit);
+
+	set_bit(EV_KEY, dev->evbit);
+	set_bit(BTN_TOUCH, dev->keybit);
+	set_bit(BTN_TOOL_FINGER, dev->keybit);
+	set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+	set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
+
+	set_bit(BTN_LEFT, dev->keybit);
+	set_bit(BTN_RIGHT, dev->keybit);
+
+	if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+		set_bit(BTN_MIDDLE, dev->keybit);
+
+	if (SYN_CAP_FOUR_BUTTON(priv->capabilities) ||
+	    SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
+		set_bit(BTN_FORWARD, dev->keybit);
+		set_bit(BTN_BACK, dev->keybit);
+	}
+
+	for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
+		set_bit(BTN_0 + i, dev->keybit);
+
+	clear_bit(EV_REL, dev->evbit);
+	clear_bit(REL_X, dev->relbit);
+	clear_bit(REL_Y, dev->relbit);
+}
+
+void synaptics_reset(struct psmouse *psmouse)
+{
+	/* reset touchpad back to relative mode, gestures enabled */
+	synaptics_mode_cmd(psmouse, 0);
+}
+
+static void synaptics_disconnect(struct psmouse *psmouse)
+{
+	synaptics_reset(psmouse);
+	kfree(psmouse->private);
+	psmouse->private = NULL;
+}
+
+static int synaptics_reconnect(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	struct synaptics_data old_priv = *priv;
+
+	if (synaptics_detect(psmouse, 0))
+		return -1;
+
+	if (synaptics_query_hardware(psmouse)) {
+		printk(KERN_ERR "Unable to query Synaptics hardware.\n");
+		return -1;
+	}
+
+	if (old_priv.identity != priv->identity ||
+	    old_priv.model_id != priv->model_id ||
+	    old_priv.capabilities != priv->capabilities ||
+	    old_priv.ext_cap != priv->ext_cap)
+		return -1;
+
+	if (synaptics_set_absolute_mode(psmouse)) {
+		printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+int synaptics_detect(struct psmouse *psmouse, int set_properties)
+{
+	struct ps2dev *ps2dev = &psmouse->ps2dev;
+	unsigned char param[4];
+
+	param[0] = 0;
+
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
+	ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
+
+	if (param[1] != 0x47)
+		return -1;
+
+	if (set_properties) {
+		psmouse->vendor = "Synaptics";
+		psmouse->name = "TouchPad";
+	}
+
+	return 0;
+}
+
+#if defined(__i386__)
+#include <linux/dmi.h>
+static struct dmi_system_id toshiba_dmi_table[] = {
+	{
+		.ident = "Toshiba Satellite",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+			DMI_MATCH(DMI_PRODUCT_NAME , "Satellite"),
+		},
+	},
+	{ }
+};
+#endif
+
+int synaptics_init(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv;
+
+	psmouse->private = priv = kmalloc(sizeof(struct synaptics_data), GFP_KERNEL);
+	if (!priv)
+		return -1;
+	memset(priv, 0, sizeof(struct synaptics_data));
+
+	if (synaptics_query_hardware(psmouse)) {
+		printk(KERN_ERR "Unable to query Synaptics hardware.\n");
+		goto init_fail;
+	}
+
+	if (synaptics_set_absolute_mode(psmouse)) {
+		printk(KERN_ERR "Unable to initialize Synaptics hardware.\n");
+		goto init_fail;
+	}
+
+	priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
+
+	print_ident(priv);
+	set_input_params(&psmouse->dev, priv);
+
+	psmouse->protocol_handler = synaptics_process_byte;
+	psmouse->set_rate = synaptics_set_rate;
+	psmouse->disconnect = synaptics_disconnect;
+	psmouse->reconnect = synaptics_reconnect;
+	psmouse->pktsize = 6;
+
+	if (SYN_CAP_PASS_THROUGH(priv->capabilities))
+		synaptics_pt_create(psmouse);
+
+#if defined(__i386__)
+	/*
+	 * Toshiba's KBC seems to have trouble handling data from
+	 * Synaptics as full rate, switch to lower rate which is roughly
+	 * thye same as rate of standard PS/2 mouse.
+	 */
+	if (psmouse->rate >= 80 && dmi_check_system(toshiba_dmi_table)) {
+		printk(KERN_INFO "synaptics: Toshiba Satellite detected, limiting rate to 40pps.\n");
+		psmouse->rate = 40;
+	}
+#endif
+
+	return 0;
+
+ init_fail:
+	kfree(priv);
+	return -1;
+}
+
+
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
new file mode 100644
index 0000000..68fff1d
--- /dev/null
+++ b/drivers/input/mouse/synaptics.h
@@ -0,0 +1,110 @@
+/*
+ * Synaptics TouchPad PS/2 mouse driver
+ *
+ * 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.
+ */
+
+#ifndef _SYNAPTICS_H
+#define _SYNAPTICS_H
+
+extern int synaptics_detect(struct psmouse *psmouse, int set_properties);
+extern int synaptics_init(struct psmouse *psmouse);
+extern void synaptics_reset(struct psmouse *psmouse);
+
+/* synaptics queries */
+#define SYN_QUE_IDENTIFY		0x00
+#define SYN_QUE_MODES			0x01
+#define SYN_QUE_CAPABILITIES		0x02
+#define SYN_QUE_MODEL			0x03
+#define SYN_QUE_SERIAL_NUMBER_PREFIX	0x06
+#define SYN_QUE_SERIAL_NUMBER_SUFFIX	0x07
+#define SYN_QUE_RESOLUTION		0x08
+#define SYN_QUE_EXT_CAPAB		0x09
+
+/* synatics modes */
+#define SYN_BIT_ABSOLUTE_MODE		(1 << 7)
+#define SYN_BIT_HIGH_RATE		(1 << 6)
+#define SYN_BIT_SLEEP_MODE		(1 << 3)
+#define SYN_BIT_DISABLE_GESTURE		(1 << 2)
+#define SYN_BIT_FOUR_BYTE_CLIENT	(1 << 1)
+#define SYN_BIT_W_MODE			(1 << 0)
+
+/* synaptics model ID bits */
+#define SYN_MODEL_ROT180(m)		((m) & (1 << 23))
+#define SYN_MODEL_PORTRAIT(m)		((m) & (1 << 22))
+#define SYN_MODEL_SENSOR(m)		(((m) >> 16) & 0x3f)
+#define SYN_MODEL_HARDWARE(m)		(((m) >> 9) & 0x7f)
+#define SYN_MODEL_NEWABS(m)		((m) & (1 << 7))
+#define SYN_MODEL_PEN(m)		((m) & (1 << 6))
+#define SYN_MODEL_SIMPLIC(m)		((m) & (1 << 5))
+#define SYN_MODEL_GEOMETRY(m)		((m) & 0x0f)
+
+/* synaptics capability bits */
+#define SYN_CAP_EXTENDED(c)		((c) & (1 << 23))
+#define SYN_CAP_MIDDLE_BUTTON(c)	((c) & (1 << 18))
+#define SYN_CAP_PASS_THROUGH(c)		((c) & (1 << 7))
+#define SYN_CAP_SLEEP(c)		((c) & (1 << 4))
+#define SYN_CAP_FOUR_BUTTON(c)		((c) & (1 << 3))
+#define SYN_CAP_MULTIFINGER(c)		((c) & (1 << 1))
+#define SYN_CAP_PALMDETECT(c)		((c) & (1 << 0))
+#define SYN_CAP_VALID(c)		((((c) & 0x00ff00) >> 8) == 0x47)
+#define SYN_EXT_CAP_REQUESTS(c)		(((c) & 0x700000) >> 20)
+#define SYN_CAP_MULTI_BUTTON_NO(ec)	(((ec) & 0x00f000) >> 12)
+
+/* synaptics modes query bits */
+#define SYN_MODE_ABSOLUTE(m)		((m) & (1 << 7))
+#define SYN_MODE_RATE(m)		((m) & (1 << 6))
+#define SYN_MODE_BAUD_SLEEP(m)		((m) & (1 << 3))
+#define SYN_MODE_DISABLE_GESTURE(m)	((m) & (1 << 2))
+#define SYN_MODE_PACKSIZE(m)		((m) & (1 << 1))
+#define SYN_MODE_WMODE(m)		((m) & (1 << 0))
+
+/* synaptics identify query bits */
+#define SYN_ID_MODEL(i) 		(((i) >> 4) & 0x0f)
+#define SYN_ID_MAJOR(i) 		((i) & 0x0f)
+#define SYN_ID_MINOR(i) 		(((i) >> 16) & 0xff)
+#define SYN_ID_IS_SYNAPTICS(i)		((((i) >> 8) & 0xff) == 0x47)
+
+/* synaptics special commands */
+#define SYN_PS_SET_MODE2		0x14
+#define SYN_PS_CLIENT_CMD		0x28
+
+/* synaptics packet types */
+#define SYN_NEWABS			0
+#define SYN_NEWABS_STRICT		1
+#define SYN_NEWABS_RELAXED		2
+#define SYN_OLDABS			3
+
+/*
+ * A structure to describe the state of the touchpad hardware (buttons and pad)
+ */
+
+struct synaptics_hw_state {
+	int x;
+	int y;
+	int z;
+	int w;
+	unsigned int left:1;
+	unsigned int right:1;
+	unsigned int middle:1;
+	unsigned int up:1;
+	unsigned int down:1;
+	unsigned char ext_buttons;
+	signed char scroll;
+};
+
+struct synaptics_data {
+	/* Data read from the touchpad */
+	unsigned long int model_id;		/* Model-ID */
+	unsigned long int capabilities; 	/* Capabilities */
+	unsigned long int ext_cap; 		/* Extended Capabilities */
+	unsigned long int identity;		/* Identification */
+
+	unsigned char pkt_type;			/* packet type - old, new, etc */
+	unsigned char mode;			/* current mode byte */
+	int scroll;
+};
+
+#endif /* _SYNAPTICS_H */
diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c
new file mode 100644
index 0000000..b2cb101
--- /dev/null
+++ b/drivers/input/mouse/vsxxxaa.c
@@ -0,0 +1,591 @@
+/*
+ * Driver for	DEC VSXXX-AA mouse (hockey-puck mouse, ball or two rollers)
+ * 		DEC VSXXX-GA mouse (rectangular mouse, with ball)
+ * 		DEC VSXXX-AB tablet (digitizer with hair cross or stylus)
+ *
+ * Copyright (C) 2003-2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
+ *
+ * The packet format was initially taken from a patch to GPM which is (C) 2001
+ * by	Karsten Merker <merker@linuxtag.org>
+ * and	Maciej W. Rozycki <macro@ds2.pg.gda.pl>
+ * Later on, I had access to the device's documentation (referenced below).
+ */
+
+/*
+ * 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
+ */
+
+/*
+ * Building an adaptor to DE9 / DB25 RS232
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * DISCLAIMER: Use this description AT YOUR OWN RISK! I'll not pay for
+ * anything if you break your mouse, your computer or whatever!
+ *
+ * In theory, this mouse is a simple RS232 device. In practice, it has got
+ * a quite uncommon plug and the requirement to additionally get a power
+ * supply at +5V and -12V.
+ *
+ * If you look at the socket/jack (_not_ at the plug), we use this pin
+ * numbering:
+ *    _______
+ *   / 7 6 5 \
+ *  | 4 --- 3 |
+ *   \  2 1  /
+ *    -------
+ *
+ *	DEC socket	DE9	DB25	Note
+ *	1 (GND)		5	7	-
+ *	2 (RxD)		2	3	-
+ *	3 (TxD)		3	2	-
+ *	4 (-12V)	-	-	Somewhere from the PSU. At ATX, it's
+ *					the thin blue wire at pin 12 of the
+ *					ATX power connector. Only required for
+ *					VSXXX-AA/-GA mice.
+ *	5 (+5V)		-	-	PSU (red wires of ATX power connector
+ *					on pin 4, 6, 19 or 20) or HDD power
+ *					connector (also red wire).
+ *	6 (+12V)	-	-	HDD power connector, yellow wire. Only
+ *					required for VSXXX-AB digitizer.
+ *	7 (dev. avail.)	-	-	The mouse shorts this one to pin 1.
+ *					This way, the host computer can detect
+ *					the mouse. To use it with the adaptor,
+ *					simply don't connect this pin.
+ *
+ * So to get a working adaptor, you need to connect the mouse with three
+ * wires to a RS232 port and two or three additional wires for +5V, +12V and
+ * -12V to the PSU.
+ *
+ * Flow specification for the link is 4800, 8o1.
+ *
+ * The mice and tablet are described in "VCB02 Video Subsystem - Technical
+ * Manual", DEC EK-104AA-TM-001. You'll find it at MANX, a search engine
+ * specific for DEC documentation. Try
+ * http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/config.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Driver for DEC VSXXX-AA and -GA mice and VSXXX-AB tablet"
+
+MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_LICENSE ("GPL");
+
+#undef VSXXXAA_DEBUG
+#ifdef VSXXXAA_DEBUG
+#define DBG(x...) printk (x)
+#else
+#define DBG(x...) do {} while (0)
+#endif
+
+#define VSXXXAA_INTRO_MASK	0x80
+#define VSXXXAA_INTRO_HEAD	0x80
+#define IS_HDR_BYTE(x)		(((x) & VSXXXAA_INTRO_MASK)	\
+					== VSXXXAA_INTRO_HEAD)
+
+#define VSXXXAA_PACKET_MASK	0xe0
+#define VSXXXAA_PACKET_REL	0x80
+#define VSXXXAA_PACKET_ABS	0xc0
+#define VSXXXAA_PACKET_POR	0xa0
+#define MATCH_PACKET_TYPE(data, type)	(((data) & VSXXXAA_PACKET_MASK) == (type))
+
+
+
+struct vsxxxaa {
+	struct input_dev dev;
+	struct serio *serio;
+#define BUFLEN 15 /* At least 5 is needed for a full tablet packet */
+	unsigned char buf[BUFLEN];
+	unsigned char count;
+	unsigned char version;
+	unsigned char country;
+	unsigned char type;
+	char name[64];
+	char phys[32];
+};
+
+static void
+vsxxxaa_drop_bytes (struct vsxxxaa *mouse, int num)
+{
+	if (num >= mouse->count)
+		mouse->count = 0;
+	else {
+		memmove (mouse->buf, mouse->buf + num - 1, BUFLEN - num);
+		mouse->count -= num;
+	}
+}
+
+static void
+vsxxxaa_queue_byte (struct vsxxxaa *mouse, unsigned char byte)
+{
+	if (mouse->count == BUFLEN) {
+		printk (KERN_ERR "%s on %s: Dropping a byte of full buffer.\n",
+				mouse->name, mouse->phys);
+		vsxxxaa_drop_bytes (mouse, 1);
+	}
+	DBG (KERN_INFO "Queueing byte 0x%02x\n", byte);
+
+	mouse->buf[mouse->count++] = byte;
+}
+
+static void
+vsxxxaa_detection_done (struct vsxxxaa *mouse)
+{
+	switch (mouse->type) {
+		case 0x02:
+			sprintf (mouse->name, "DEC VSXXX-AA/-GA mouse");
+			break;
+
+		case 0x04:
+			sprintf (mouse->name, "DEC VSXXX-AB digitizer");
+			break;
+
+		default:
+			sprintf (mouse->name, "unknown DEC pointer device "
+					"(type = 0x%02x)", mouse->type);
+			break;
+	}
+
+	printk (KERN_INFO "Found %s version 0x%02x from country 0x%02x "
+			"on port %s\n", mouse->name, mouse->version,
+			mouse->country, mouse->phys);
+}
+
+/*
+ * Returns number of bytes to be dropped, 0 if packet is okay.
+ */
+static int
+vsxxxaa_check_packet (struct vsxxxaa *mouse, int packet_len)
+{
+	int i;
+
+	/* First byte must be a header byte */
+	if (!IS_HDR_BYTE (mouse->buf[0])) {
+		DBG ("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]);
+		return 1;
+	}
+
+	/* Check all following bytes */
+	if (packet_len > 1) {
+		for (i = 1; i < packet_len; i++) {
+			if (IS_HDR_BYTE (mouse->buf[i])) {
+				printk (KERN_ERR "Need to drop %d bytes "
+						"of a broken packet.\n",
+						i - 1);
+				DBG (KERN_INFO "check: len=%d, b[%d]=0x%02x\n",
+						packet_len, i, mouse->buf[i]);
+				return i - 1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static __inline__ int
+vsxxxaa_smells_like_packet (struct vsxxxaa *mouse, unsigned char type, size_t len)
+{
+	return (mouse->count >= len) && MATCH_PACKET_TYPE (mouse->buf[0], type);
+}
+
+static void
+vsxxxaa_handle_REL_packet (struct vsxxxaa *mouse, struct pt_regs *regs)
+{
+	struct input_dev *dev = &mouse->dev;
+	unsigned char *buf = mouse->buf;
+	int left, middle, right;
+	int dx, dy;
+
+	/*
+	 * Check for normal stream packets. This is three bytes,
+	 * with the first byte's 3 MSB set to 100.
+	 *
+	 * [0]:	1	0	0	SignX	SignY	Left	Middle	Right
+	 * [1]: 0	dx	dx	dx	dx	dx	dx	dx
+	 * [2]:	0	dy	dy	dy	dy	dy	dy	dy
+	 */
+
+	/*
+	 * Low 7 bit of byte 1 are abs(dx), bit 7 is
+	 * 0, bit 4 of byte 0 is direction.
+	 */
+	dx = buf[1] & 0x7f;
+	dx *= ((buf[0] >> 4) & 0x01)? 1: -1;
+
+	/*
+	 * Low 7 bit of byte 2 are abs(dy), bit 7 is
+	 * 0, bit 3 of byte 0 is direction.
+	 */
+	dy = buf[2] & 0x7f;
+	dy *= ((buf[0] >> 3) & 0x01)? -1: 1;
+
+	/*
+	 * Get button state. It's the low three bits
+	 * (for three buttons) of byte 0.
+	 */
+	left	= (buf[0] & 0x04)? 1: 0;
+	middle	= (buf[0] & 0x02)? 1: 0;
+	right	= (buf[0] & 0x01)? 1: 0;
+
+	vsxxxaa_drop_bytes (mouse, 3);
+
+	DBG (KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n",
+			mouse->name, mouse->phys, dx, dy,
+			left? "L": "l", middle? "M": "m", right? "R": "r");
+
+	/*
+	 * Report what we've found so far...
+	 */
+	input_regs (dev, regs);
+	input_report_key (dev, BTN_LEFT, left);
+	input_report_key (dev, BTN_MIDDLE, middle);
+	input_report_key (dev, BTN_RIGHT, right);
+	input_report_key (dev, BTN_TOUCH, 0);
+	input_report_rel (dev, REL_X, dx);
+	input_report_rel (dev, REL_Y, dy);
+	input_sync (dev);
+}
+
+static void
+vsxxxaa_handle_ABS_packet (struct vsxxxaa *mouse, struct pt_regs *regs)
+{
+	struct input_dev *dev = &mouse->dev;
+	unsigned char *buf = mouse->buf;
+	int left, middle, right, touch;
+	int x, y;
+
+	/*
+	 * Tablet position / button packet
+	 *
+	 * [0]:	1	1	0	B4	B3	B2	B1	Pr
+	 * [1]:	0	0	X5	X4	X3	X2	X1	X0
+	 * [2]:	0	0	X11	X10	X9	X8	X7	X6
+	 * [3]:	0	0	Y5	Y4	Y3	Y2	Y1	Y0
+	 * [4]:	0	0	Y11	Y10	Y9	Y8	Y7	Y6
+	 */
+
+	/*
+	 * Get X/Y position. Y axis needs to be inverted since VSXXX-AB
+	 * counts down->top while monitor counts top->bottom.
+	 */
+	x = ((buf[2] & 0x3f) << 6) | (buf[1] & 0x3f);
+	y = ((buf[4] & 0x3f) << 6) | (buf[3] & 0x3f);
+	y = 1023 - y;
+
+	/*
+	 * Get button state. It's bits <4..1> of byte 0.
+	 */
+	left	= (buf[0] & 0x02)? 1: 0;
+	middle	= (buf[0] & 0x04)? 1: 0;
+	right	= (buf[0] & 0x08)? 1: 0;
+	touch	= (buf[0] & 0x10)? 1: 0;
+
+	vsxxxaa_drop_bytes (mouse, 5);
+
+	DBG (KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n",
+			mouse->name, mouse->phys, x, y,
+			left? "L": "l", middle? "M": "m",
+			right? "R": "r", touch? "T": "t");
+
+	/*
+	 * Report what we've found so far...
+	 */
+	input_regs (dev, regs);
+	input_report_key (dev, BTN_LEFT, left);
+	input_report_key (dev, BTN_MIDDLE, middle);
+	input_report_key (dev, BTN_RIGHT, right);
+	input_report_key (dev, BTN_TOUCH, touch);
+	input_report_abs (dev, ABS_X, x);
+	input_report_abs (dev, ABS_Y, y);
+	input_sync (dev);
+}
+
+static void
+vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse, struct pt_regs *regs)
+{
+	struct input_dev *dev = &mouse->dev;
+	unsigned char *buf = mouse->buf;
+	int left, middle, right;
+	unsigned char error;
+
+	/*
+	 * Check for Power-On-Reset packets. These are sent out
+	 * after plugging the mouse in, or when explicitely
+	 * requested by sending 'T'.
+	 *
+	 * [0]:	1	0	1	0	R3	R2	R1	R0
+	 * [1]:	0	M2	M1	M0	D3	D2	D1	D0
+	 * [2]:	0	E6	E5	E4	E3	E2	E1	E0
+	 * [3]:	0	0	0	0	0	Left	Middle	Right
+	 *
+	 * M: manufacturer location code
+	 * R: revision code
+	 * E: Error code. If it's in the range of 0x00..0x1f, only some
+	 *    minor problem occured. Errors >= 0x20 are considered bad
+	 *    and the device may not work properly...
+	 * D: <0010> == mouse, <0100> == tablet
+	 */
+
+	mouse->version = buf[0] & 0x0f;
+	mouse->country = (buf[1] >> 4) & 0x07;
+	mouse->type = buf[1] & 0x0f;
+	error = buf[2] & 0x7f;
+
+	/*
+	 * Get button state. It's the low three bits
+	 * (for three buttons) of byte 0. Maybe even the bit <3>
+	 * has some meaning if a tablet is attached.
+	 */
+	left	= (buf[0] & 0x04)? 1: 0;
+	middle	= (buf[0] & 0x02)? 1: 0;
+	right	= (buf[0] & 0x01)? 1: 0;
+
+	vsxxxaa_drop_bytes (mouse, 4);
+	vsxxxaa_detection_done (mouse);
+
+	if (error <= 0x1f) {
+		/* No (serious) error. Report buttons */
+		input_regs (dev, regs);
+		input_report_key (dev, BTN_LEFT, left);
+		input_report_key (dev, BTN_MIDDLE, middle);
+		input_report_key (dev, BTN_RIGHT, right);
+		input_report_key (dev, BTN_TOUCH, 0);
+		input_sync (dev);
+
+		if (error != 0)
+			printk (KERN_INFO "Your %s on %s reports error=0x%02x\n",
+					mouse->name, mouse->phys, error);
+
+	}
+
+	/*
+	 * If the mouse was hot-plugged, we need to force differential mode
+	 * now... However, give it a second to recover from it's reset.
+	 */
+	printk (KERN_NOTICE "%s on %s: Forceing standard packet format, "
+			"incremental streaming mode and 72 samples/sec\n",
+			mouse->name, mouse->phys);
+	mouse->serio->write (mouse->serio, 'S');	/* Standard format */
+	mdelay (50);
+	mouse->serio->write (mouse->serio, 'R');	/* Incremental */
+	mdelay (50);
+	mouse->serio->write (mouse->serio, 'L');	/* 72 samples/sec */
+}
+
+static void
+vsxxxaa_parse_buffer (struct vsxxxaa *mouse, struct pt_regs *regs)
+{
+	unsigned char *buf = mouse->buf;
+	int stray_bytes;
+
+	/*
+	 * Parse buffer to death...
+	 */
+	do {
+		/*
+		 * Out of sync? Throw away what we don't understand. Each
+		 * packet starts with a byte whose bit 7 is set. Unhandled
+		 * packets (ie. which we don't know about or simply b0rk3d
+		 * data...) will get shifted out of the buffer after some
+		 * activity on the mouse.
+		 */
+		while (mouse->count > 0 && !IS_HDR_BYTE(buf[0])) {
+			printk (KERN_ERR "%s on %s: Dropping a byte to regain "
+					"sync with mouse data stream...\n",
+					mouse->name, mouse->phys);
+			vsxxxaa_drop_bytes (mouse, 1);
+		}
+
+		/*
+		 * Check for packets we know about.
+		 */
+
+		if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_REL, 3)) {
+			/* Check for broken packet */
+			stray_bytes = vsxxxaa_check_packet (mouse, 3);
+			if (stray_bytes > 0) {
+				printk (KERN_ERR "Dropping %d bytes now...\n",
+						stray_bytes);
+				vsxxxaa_drop_bytes (mouse, stray_bytes);
+				continue;
+			}
+
+			vsxxxaa_handle_REL_packet (mouse, regs);
+			continue; /* More to parse? */
+		}
+
+		if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_ABS, 5)) {
+			/* Check for broken packet */
+			stray_bytes = vsxxxaa_check_packet (mouse, 5);
+			if (stray_bytes > 0) {
+				printk (KERN_ERR "Dropping %d bytes now...\n",
+						stray_bytes);
+				vsxxxaa_drop_bytes (mouse, stray_bytes);
+				continue;
+			}
+
+			vsxxxaa_handle_ABS_packet (mouse, regs);
+			continue; /* More to parse? */
+		}
+
+		if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_POR, 4)) {
+			/* Check for broken packet */
+			stray_bytes = vsxxxaa_check_packet (mouse, 4);
+			if (stray_bytes > 0) {
+				printk (KERN_ERR "Dropping %d bytes now...\n",
+						stray_bytes);
+				vsxxxaa_drop_bytes (mouse, stray_bytes);
+				continue;
+			}
+
+			vsxxxaa_handle_POR_packet (mouse, regs);
+			continue; /* More to parse? */
+		}
+
+		break; /* No REL, ABS or POR packet found */
+	} while (1);
+}
+
+static irqreturn_t
+vsxxxaa_interrupt (struct serio *serio, unsigned char data, unsigned int flags,
+		struct pt_regs *regs)
+{
+	struct vsxxxaa *mouse = serio_get_drvdata (serio);
+
+	vsxxxaa_queue_byte (mouse, data);
+	vsxxxaa_parse_buffer (mouse, regs);
+
+	return IRQ_HANDLED;
+}
+
+static void
+vsxxxaa_disconnect (struct serio *serio)
+{
+	struct vsxxxaa *mouse = serio_get_drvdata (serio);
+
+	input_unregister_device (&mouse->dev);
+	serio_close (serio);
+	serio_set_drvdata (serio, NULL);
+	kfree (mouse);
+}
+
+static int
+vsxxxaa_connect (struct serio *serio, struct serio_driver *drv)
+{
+	struct vsxxxaa *mouse;
+	int err;
+
+	if (!(mouse = kmalloc (sizeof (struct vsxxxaa), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset (mouse, 0, sizeof (struct vsxxxaa));
+
+	init_input_dev (&mouse->dev);
+	set_bit (EV_KEY, mouse->dev.evbit);		/* We have buttons */
+	set_bit (EV_REL, mouse->dev.evbit);
+	set_bit (EV_ABS, mouse->dev.evbit);
+	set_bit (BTN_LEFT, mouse->dev.keybit);		/* We have 3 buttons */
+	set_bit (BTN_MIDDLE, mouse->dev.keybit);
+	set_bit (BTN_RIGHT, mouse->dev.keybit);
+	set_bit (BTN_TOUCH, mouse->dev.keybit);		/* ...and Tablet */
+	set_bit (REL_X, mouse->dev.relbit);
+	set_bit (REL_Y, mouse->dev.relbit);
+	set_bit (ABS_X, mouse->dev.absbit);
+	set_bit (ABS_Y, mouse->dev.absbit);
+
+	mouse->dev.absmin[ABS_X] = 0;
+	mouse->dev.absmax[ABS_X] = 1023;
+	mouse->dev.absmin[ABS_Y] = 0;
+	mouse->dev.absmax[ABS_Y] = 1023;
+
+	mouse->dev.private = mouse;
+
+	sprintf (mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer");
+	sprintf (mouse->phys, "%s/input0", serio->phys);
+	mouse->dev.name = mouse->name;
+	mouse->dev.phys = mouse->phys;
+	mouse->dev.id.bustype = BUS_RS232;
+	mouse->dev.dev = &serio->dev;
+	mouse->serio = serio;
+
+	serio_set_drvdata (serio, mouse);
+
+	err = serio_open (serio, drv);
+	if (err) {
+		serio_set_drvdata (serio, NULL);
+		kfree (mouse);
+		return err;
+	}
+
+	/*
+	 * Request selftest. Standard packet format and differential
+	 * mode will be requested after the device ID'ed successfully.
+	 */
+	mouse->serio->write (mouse->serio, 'T'); /* Test */
+
+	input_register_device (&mouse->dev);
+
+	printk (KERN_INFO "input: %s on %s\n", mouse->name, mouse->phys);
+
+	return 0;
+}
+
+static struct serio_device_id vsxxaa_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_VSXXXAA,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, vsxxaa_serio_ids);
+
+static struct serio_driver vsxxxaa_drv = {
+	.driver		= {
+		.name	= "vsxxxaa",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= vsxxaa_serio_ids,
+	.connect	= vsxxxaa_connect,
+	.interrupt	= vsxxxaa_interrupt,
+	.disconnect	= vsxxxaa_disconnect,
+};
+
+static int __init
+vsxxxaa_init (void)
+{
+	serio_register_driver(&vsxxxaa_drv);
+	return 0;
+}
+
+static void __exit
+vsxxxaa_exit (void)
+{
+	serio_unregister_driver(&vsxxxaa_drv);
+}
+
+module_init (vsxxxaa_init);
+module_exit (vsxxxaa_exit);
+
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
new file mode 100644
index 0000000..564974c
--- /dev/null
+++ b/drivers/input/mousedev.c
@@ -0,0 +1,758 @@
+/*
+ * Input driver to ExplorerPS/2 device driver module.
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ * Copyright (c) 2004      Dmitry Torokhov
+ *
+ * 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.
+ */
+
+#define MOUSEDEV_MINOR_BASE 	32
+#define MOUSEDEV_MINORS		32
+#define MOUSEDEV_MIX		31
+
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/config.h>
+#include <linux/smp_lock.h>
+#include <linux/random.h>
+#include <linux/major.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
+#include <linux/miscdevice.h>
+#endif
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Mouse (ExplorerPS/2) device interfaces");
+MODULE_LICENSE("GPL");
+
+#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X
+#define CONFIG_INPUT_MOUSEDEV_SCREEN_X	1024
+#endif
+#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y
+#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y	768
+#endif
+
+static int xres = CONFIG_INPUT_MOUSEDEV_SCREEN_X;
+module_param(xres, uint, 0);
+MODULE_PARM_DESC(xres, "Horizontal screen resolution");
+
+static int yres = CONFIG_INPUT_MOUSEDEV_SCREEN_Y;
+module_param(yres, uint, 0);
+MODULE_PARM_DESC(yres, "Vertical screen resolution");
+
+static unsigned tap_time = 200;
+module_param(tap_time, uint, 0);
+MODULE_PARM_DESC(tap_time, "Tap time for touchpads in absolute mode (msecs)");
+
+struct mousedev_hw_data {
+	int dx, dy, dz;
+	int x, y;
+	int abs_event;
+	unsigned long buttons;
+};
+
+struct mousedev {
+	int exist;
+	int open;
+	int minor;
+	char name[16];
+	wait_queue_head_t wait;
+	struct list_head list;
+	struct input_handle handle;
+
+	struct mousedev_hw_data packet;
+	unsigned int pkt_count;
+	int old_x[4], old_y[4];
+	int frac_dx, frac_dy;
+	unsigned long touch;
+};
+
+enum mousedev_emul {
+	MOUSEDEV_EMUL_PS2,
+	MOUSEDEV_EMUL_IMPS,
+	MOUSEDEV_EMUL_EXPS
+};
+
+struct mousedev_motion {
+	int dx, dy, dz;
+	unsigned long buttons;
+};
+
+#define PACKET_QUEUE_LEN	16
+struct mousedev_list {
+	struct fasync_struct *fasync;
+	struct mousedev *mousedev;
+	struct list_head node;
+
+	struct mousedev_motion packets[PACKET_QUEUE_LEN];
+	unsigned int head, tail;
+	spinlock_t packet_lock;
+	int pos_x, pos_y;
+
+	signed char ps2[6];
+	unsigned char ready, buffer, bufsiz;
+	unsigned char imexseq, impsseq;
+	enum mousedev_emul mode;
+};
+
+#define MOUSEDEV_SEQ_LEN	6
+
+static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
+static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 };
+
+static struct input_handler mousedev_handler;
+
+static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
+static struct mousedev mousedev_mix;
+
+#define fx(i)  (mousedev->old_x[(mousedev->pkt_count - (i)) & 03])
+#define fy(i)  (mousedev->old_y[(mousedev->pkt_count - (i)) & 03])
+
+static void mousedev_touchpad_event(struct input_dev *dev, struct mousedev *mousedev, unsigned int code, int value)
+{
+	int size, tmp;
+	enum { FRACTION_DENOM = 128 };
+
+	if (mousedev->touch) {
+		size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
+		if (size == 0) size = 256 * 2;
+		switch (code) {
+			case ABS_X:
+				fx(0) = value;
+				if (mousedev->pkt_count >= 2) {
+					tmp = ((value - fx(2)) * (256 * FRACTION_DENOM)) / size;
+					tmp += mousedev->frac_dx;
+					mousedev->packet.dx = tmp / FRACTION_DENOM;
+					mousedev->frac_dx = tmp - mousedev->packet.dx * FRACTION_DENOM;
+				}
+				break;
+
+			case ABS_Y:
+				fy(0) = value;
+				if (mousedev->pkt_count >= 2) {
+					tmp = -((value - fy(2)) * (256 * FRACTION_DENOM)) / size;
+					tmp += mousedev->frac_dy;
+					mousedev->packet.dy = tmp / FRACTION_DENOM;
+					mousedev->frac_dy = tmp - mousedev->packet.dy * FRACTION_DENOM;
+				}
+				break;
+		}
+	}
+}
+
+static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev, unsigned int code, int value)
+{
+	int size;
+
+	switch (code) {
+		case ABS_X:
+			size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
+			if (size == 0) size = xres;
+			if (value > dev->absmax[ABS_X]) value = dev->absmax[ABS_X];
+			if (value < dev->absmin[ABS_X]) value = dev->absmin[ABS_X];
+			mousedev->packet.x = ((value - dev->absmin[ABS_X]) * xres) / size;
+			mousedev->packet.abs_event = 1;
+			break;
+
+		case ABS_Y:
+			size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y];
+			if (size == 0) size = yres;
+			if (value > dev->absmax[ABS_Y]) value = dev->absmax[ABS_Y];
+			if (value < dev->absmin[ABS_Y]) value = dev->absmin[ABS_Y];
+			mousedev->packet.y = yres - ((value - dev->absmin[ABS_Y]) * yres) / size;
+			mousedev->packet.abs_event = 1;
+			break;
+	}
+}
+
+static void mousedev_rel_event(struct mousedev *mousedev, unsigned int code, int value)
+{
+	switch (code) {
+		case REL_X:	mousedev->packet.dx += value; break;
+		case REL_Y:	mousedev->packet.dy -= value; break;
+		case REL_WHEEL:	mousedev->packet.dz -= value; break;
+	}
+}
+
+static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int value)
+{
+	int index;
+
+	switch (code) {
+		case BTN_TOUCH:
+		case BTN_0:
+		case BTN_FORWARD:
+		case BTN_LEFT:		index = 0; break;
+		case BTN_STYLUS:
+		case BTN_1:
+		case BTN_RIGHT:		index = 1; break;
+		case BTN_2:
+		case BTN_STYLUS2:
+		case BTN_MIDDLE:	index = 2; break;
+		case BTN_3:
+		case BTN_BACK:
+		case BTN_SIDE:		index = 3; break;
+		case BTN_4:
+		case BTN_EXTRA:		index = 4; break;
+		default: 		return;
+	}
+
+	if (value) {
+		set_bit(index, &mousedev->packet.buttons);
+		set_bit(index, &mousedev_mix.packet.buttons);
+	} else {
+		clear_bit(index, &mousedev->packet.buttons);
+		clear_bit(index, &mousedev_mix.packet.buttons);
+	}
+}
+
+static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_hw_data *packet)
+{
+	struct mousedev_list *list;
+	struct mousedev_motion *p;
+	unsigned long flags;
+
+	list_for_each_entry(list, &mousedev->list, node) {
+		spin_lock_irqsave(&list->packet_lock, flags);
+
+		p = &list->packets[list->head];
+		if (list->ready && p->buttons != packet->buttons) {
+			unsigned int new_head = (list->head + 1) % PACKET_QUEUE_LEN;
+			if (new_head != list->tail) {
+				p = &list->packets[list->head = new_head];
+				memset(p, 0, sizeof(struct mousedev_motion));
+			}
+		}
+
+		if (packet->abs_event) {
+			p->dx += packet->x - list->pos_x;
+			p->dy += packet->y - list->pos_y;
+			list->pos_x = packet->x;
+			list->pos_y = packet->y;
+		}
+
+		list->pos_x += packet->dx;
+		list->pos_x = list->pos_x < 0 ? 0 : (list->pos_x >= xres ? xres : list->pos_x);
+		list->pos_y += packet->dy;
+		list->pos_y = list->pos_y < 0 ? 0 : (list->pos_y >= yres ? yres : list->pos_y);
+
+		p->dx += packet->dx;
+		p->dy += packet->dy;
+		p->dz += packet->dz;
+		p->buttons = mousedev->packet.buttons;
+
+		list->ready = 1;
+
+		spin_unlock_irqrestore(&list->packet_lock, flags);
+		kill_fasync(&list->fasync, SIGIO, POLL_IN);
+	}
+
+	wake_up_interruptible(&mousedev->wait);
+}
+
+static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
+{
+	if (!value) {
+		if (mousedev->touch &&
+		    time_before(jiffies, mousedev->touch + msecs_to_jiffies(tap_time))) {
+			/*
+			 * Toggle left button to emulate tap.
+			 * We rely on the fact that mousedev_mix always has 0
+			 * motion packet so we won't mess current position.
+			 */
+			set_bit(0, &mousedev->packet.buttons);
+			set_bit(0, &mousedev_mix.packet.buttons);
+			mousedev_notify_readers(mousedev, &mousedev_mix.packet);
+			mousedev_notify_readers(&mousedev_mix, &mousedev_mix.packet);
+			clear_bit(0, &mousedev->packet.buttons);
+			clear_bit(0, &mousedev_mix.packet.buttons);
+		}
+		mousedev->touch = mousedev->pkt_count = 0;
+		mousedev->frac_dx = 0;
+		mousedev->frac_dy = 0;
+	}
+	else
+		if (!mousedev->touch)
+			mousedev->touch = jiffies;
+}
+
+static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
+{
+	struct mousedev *mousedev = handle->private;
+
+	switch (type) {
+		case EV_ABS:
+			/* Ignore joysticks */
+			if (test_bit(BTN_TRIGGER, handle->dev->keybit))
+				return;
+
+			if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
+				mousedev_touchpad_event(handle->dev, mousedev, code, value);
+			else
+				mousedev_abs_event(handle->dev, mousedev, code, value);
+
+			break;
+
+		case EV_REL:
+			mousedev_rel_event(mousedev, code, value);
+			break;
+
+		case EV_KEY:
+			if (value != 2) {
+				if (code == BTN_TOUCH && test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
+					mousedev_touchpad_touch(mousedev, value);
+				else
+					mousedev_key_event(mousedev, code, value);
+			}
+			break;
+
+		case EV_SYN:
+			if (code == SYN_REPORT) {
+				if (mousedev->touch) {
+					mousedev->pkt_count++;
+					/* Input system eats duplicate events, but we need all of them
+					 * to do correct averaging so apply present one forward
+			 		 */
+					fx(0) = fx(1);
+					fy(0) = fy(1);
+				}
+
+				mousedev_notify_readers(mousedev, &mousedev->packet);
+				mousedev_notify_readers(&mousedev_mix, &mousedev->packet);
+
+				mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0;
+				mousedev->packet.abs_event = 0;
+			}
+			break;
+	}
+}
+
+static int mousedev_fasync(int fd, struct file *file, int on)
+{
+	int retval;
+	struct mousedev_list *list = file->private_data;
+	retval = fasync_helper(fd, file, on, &list->fasync);
+	return retval < 0 ? retval : 0;
+}
+
+static void mousedev_free(struct mousedev *mousedev)
+{
+	mousedev_table[mousedev->minor] = NULL;
+	kfree(mousedev);
+}
+
+static int mixdev_release(void)
+{
+	struct input_handle *handle;
+
+	list_for_each_entry(handle, &mousedev_handler.h_list, h_node) {
+		struct mousedev *mousedev = handle->private;
+
+		if (!mousedev->open) {
+			if (mousedev->exist)
+				input_close_device(&mousedev->handle);
+			else
+				mousedev_free(mousedev);
+		}
+	}
+
+	return 0;
+}
+
+static int mousedev_release(struct inode * inode, struct file * file)
+{
+	struct mousedev_list *list = file->private_data;
+
+	mousedev_fasync(-1, file, 0);
+
+	list_del(&list->node);
+
+	if (!--list->mousedev->open) {
+		if (list->mousedev->minor == MOUSEDEV_MIX)
+			return mixdev_release();
+
+		if (!mousedev_mix.open) {
+			if (list->mousedev->exist)
+				input_close_device(&list->mousedev->handle);
+			else
+				mousedev_free(list->mousedev);
+		}
+	}
+
+	kfree(list);
+	return 0;
+}
+
+static int mousedev_open(struct inode * inode, struct file * file)
+{
+	struct mousedev_list *list;
+	struct input_handle *handle;
+	struct mousedev *mousedev;
+	int i;
+
+#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
+	if (imajor(inode) == MISC_MAJOR)
+		i = MOUSEDEV_MIX;
+	else
+#endif
+		i = iminor(inode) - MOUSEDEV_MINOR_BASE;
+
+	if (i >= MOUSEDEV_MINORS || !mousedev_table[i])
+		return -ENODEV;
+
+	if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL)))
+		return -ENOMEM;
+	memset(list, 0, sizeof(struct mousedev_list));
+
+	spin_lock_init(&list->packet_lock);
+	list->pos_x = xres / 2;
+	list->pos_y = yres / 2;
+	list->mousedev = mousedev_table[i];
+	list_add_tail(&list->node, &mousedev_table[i]->list);
+	file->private_data = list;
+
+	if (!list->mousedev->open++) {
+		if (list->mousedev->minor == MOUSEDEV_MIX) {
+			list_for_each_entry(handle, &mousedev_handler.h_list, h_node) {
+				mousedev = handle->private;
+				if (!mousedev->open && mousedev->exist)
+					input_open_device(handle);
+			}
+		} else
+			if (!mousedev_mix.open && list->mousedev->exist)
+				input_open_device(&list->mousedev->handle);
+	}
+
+	return 0;
+}
+
+static inline int mousedev_limit_delta(int delta, int limit)
+{
+	return delta > limit ? limit : (delta < -limit ? -limit : delta);
+}
+
+static void mousedev_packet(struct mousedev_list *list, signed char *ps2_data)
+{
+	struct mousedev_motion *p;
+	unsigned long flags;
+
+	spin_lock_irqsave(&list->packet_lock, flags);
+	p = &list->packets[list->tail];
+
+	ps2_data[0] = 0x08 | ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
+	ps2_data[1] = mousedev_limit_delta(p->dx, 127);
+	ps2_data[2] = mousedev_limit_delta(p->dy, 127);
+	p->dx -= ps2_data[1];
+	p->dy -= ps2_data[2];
+
+	switch (list->mode) {
+		case MOUSEDEV_EMUL_EXPS:
+			ps2_data[3] = mousedev_limit_delta(p->dz, 7);
+			p->dz -= ps2_data[3];
+			ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1);
+			list->bufsiz = 4;
+			break;
+
+		case MOUSEDEV_EMUL_IMPS:
+			ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
+			ps2_data[3] = mousedev_limit_delta(p->dz, 127);
+			p->dz -= ps2_data[3];
+			list->bufsiz = 4;
+			break;
+
+		case MOUSEDEV_EMUL_PS2:
+		default:
+			ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
+			p->dz = 0;
+			list->bufsiz = 3;
+			break;
+	}
+
+	if (!p->dx && !p->dy && !p->dz) {
+		if (list->tail == list->head)
+			list->ready = 0;
+		else
+			list->tail = (list->tail + 1) % PACKET_QUEUE_LEN;
+	}
+
+	spin_unlock_irqrestore(&list->packet_lock, flags);
+}
+
+
+static ssize_t mousedev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
+{
+	struct mousedev_list *list = file->private_data;
+	unsigned char c;
+	unsigned int i;
+
+	for (i = 0; i < count; i++) {
+
+		if (get_user(c, buffer + i))
+			return -EFAULT;
+
+		if (c == mousedev_imex_seq[list->imexseq]) {
+			if (++list->imexseq == MOUSEDEV_SEQ_LEN) {
+				list->imexseq = 0;
+				list->mode = MOUSEDEV_EMUL_EXPS;
+			}
+		} else list->imexseq = 0;
+
+		if (c == mousedev_imps_seq[list->impsseq]) {
+			if (++list->impsseq == MOUSEDEV_SEQ_LEN) {
+				list->impsseq = 0;
+				list->mode = MOUSEDEV_EMUL_IMPS;
+			}
+		} else list->impsseq = 0;
+
+		list->ps2[0] = 0xfa;
+
+		switch (c) {
+
+			case 0xeb: /* Poll */
+				mousedev_packet(list, &list->ps2[1]);
+				list->bufsiz++; /* account for leading ACK */
+				break;
+
+			case 0xf2: /* Get ID */
+				switch (list->mode) {
+					case MOUSEDEV_EMUL_PS2:  list->ps2[1] = 0; break;
+					case MOUSEDEV_EMUL_IMPS: list->ps2[1] = 3; break;
+					case MOUSEDEV_EMUL_EXPS: list->ps2[1] = 4; break;
+				}
+				list->bufsiz = 2;
+				break;
+
+			case 0xe9: /* Get info */
+				list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200;
+				list->bufsiz = 4;
+				break;
+
+			case 0xff: /* Reset */
+				list->impsseq = list->imexseq = 0;
+				list->mode = MOUSEDEV_EMUL_PS2;
+				list->ps2[1] = 0xaa; list->ps2[2] = 0x00;
+				list->bufsiz = 3;
+				break;
+
+			default:
+				list->bufsiz = 1;
+				break;
+		}
+
+		list->buffer = list->bufsiz;
+	}
+
+	kill_fasync(&list->fasync, SIGIO, POLL_IN);
+
+	wake_up_interruptible(&list->mousedev->wait);
+
+	return count;
+}
+
+static ssize_t mousedev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
+{
+	struct mousedev_list *list = file->private_data;
+	int retval = 0;
+
+	if (!list->ready && !list->buffer && (file->f_flags & O_NONBLOCK))
+		return -EAGAIN;
+
+	retval = wait_event_interruptible(list->mousedev->wait,
+					  !list->mousedev->exist || list->ready || list->buffer);
+
+	if (retval)
+		return retval;
+
+	if (!list->mousedev->exist)
+		return -ENODEV;
+
+	if (!list->buffer && list->ready) {
+		mousedev_packet(list, list->ps2);
+		list->buffer = list->bufsiz;
+	}
+
+	if (count > list->buffer)
+		count = list->buffer;
+
+	list->buffer -= count;
+
+	if (copy_to_user(buffer, list->ps2 + list->bufsiz - list->buffer - count, count))
+		return -EFAULT;
+
+	return count;
+}
+
+/* No kernel lock - fine */
+static unsigned int mousedev_poll(struct file *file, poll_table *wait)
+{
+	struct mousedev_list *list = file->private_data;
+	poll_wait(file, &list->mousedev->wait, wait);
+	return ((list->ready || list->buffer) ? (POLLIN | POLLRDNORM) : 0) |
+		(list->mousedev->exist ? 0 : (POLLHUP | POLLERR));
+}
+
+static struct file_operations mousedev_fops = {
+	.owner =	THIS_MODULE,
+	.read =		mousedev_read,
+	.write =	mousedev_write,
+	.poll =		mousedev_poll,
+	.open =		mousedev_open,
+	.release =	mousedev_release,
+	.fasync =	mousedev_fasync,
+};
+
+static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
+{
+	struct mousedev *mousedev;
+	int minor = 0;
+
+	for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++);
+	if (minor == MOUSEDEV_MINORS) {
+		printk(KERN_ERR "mousedev: no more free mousedev devices\n");
+		return NULL;
+	}
+
+	if (!(mousedev = kmalloc(sizeof(struct mousedev), GFP_KERNEL)))
+		return NULL;
+	memset(mousedev, 0, sizeof(struct mousedev));
+
+	INIT_LIST_HEAD(&mousedev->list);
+	init_waitqueue_head(&mousedev->wait);
+
+	mousedev->minor = minor;
+	mousedev->exist = 1;
+	mousedev->handle.dev = dev;
+	mousedev->handle.name = mousedev->name;
+	mousedev->handle.handler = handler;
+	mousedev->handle.private = mousedev;
+	sprintf(mousedev->name, "mouse%d", minor);
+
+	if (mousedev_mix.open)
+		input_open_device(&mousedev->handle);
+
+	mousedev_table[minor] = mousedev;
+
+	devfs_mk_cdev(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor),
+			S_IFCHR|S_IRUGO|S_IWUSR, "input/mouse%d", minor);
+	class_simple_device_add(input_class,
+				MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor),
+				dev->dev, "mouse%d", minor);
+
+	return &mousedev->handle;
+}
+
+static void mousedev_disconnect(struct input_handle *handle)
+{
+	struct mousedev *mousedev = handle->private;
+	struct mousedev_list *list;
+
+	class_simple_device_remove(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + mousedev->minor));
+	devfs_remove("input/mouse%d", mousedev->minor);
+	mousedev->exist = 0;
+
+	if (mousedev->open) {
+		input_close_device(handle);
+		wake_up_interruptible(&mousedev->wait);
+		list_for_each_entry(list, &mousedev->list, node)
+			kill_fasync(&list->fasync, SIGIO, POLL_HUP);
+	} else {
+		if (mousedev_mix.open)
+			input_close_device(handle);
+		mousedev_free(mousedev);
+	}
+}
+
+static struct input_device_id mousedev_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
+		.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
+		.keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) },
+		.relbit = { BIT(REL_X) | BIT(REL_Y) },
+	},	/* A mouse like device, at least one button, two relative axes */
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
+		.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
+		.relbit = { BIT(REL_WHEEL) },
+	},	/* A separate scrollwheel */
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
+		.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
+		.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
+		.absbit = { BIT(ABS_X) | BIT(ABS_Y) },
+	},	/* A tablet like device, at least touch detection, two absolute axes */
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
+		.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
+		.keybit = { [LONG(BTN_TOOL_FINGER)] = BIT(BTN_TOOL_FINGER) },
+		.absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_TOOL_WIDTH) },
+	},	/* A touchpad */
+
+	{ }, 	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(input, mousedev_ids);
+
+static struct input_handler mousedev_handler = {
+	.event =	mousedev_event,
+	.connect =	mousedev_connect,
+	.disconnect =	mousedev_disconnect,
+	.fops =		&mousedev_fops,
+	.minor =	MOUSEDEV_MINOR_BASE,
+	.name =		"mousedev",
+	.id_table =	mousedev_ids,
+};
+
+#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
+static struct miscdevice psaux_mouse = {
+	PSMOUSE_MINOR, "psaux", &mousedev_fops
+};
+static int psaux_registered;
+#endif
+
+static int __init mousedev_init(void)
+{
+	input_register_handler(&mousedev_handler);
+
+	memset(&mousedev_mix, 0, sizeof(struct mousedev));
+	INIT_LIST_HEAD(&mousedev_mix.list);
+	init_waitqueue_head(&mousedev_mix.wait);
+	mousedev_table[MOUSEDEV_MIX] = &mousedev_mix;
+	mousedev_mix.exist = 1;
+	mousedev_mix.minor = MOUSEDEV_MIX;
+
+	devfs_mk_cdev(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX),
+			S_IFCHR|S_IRUGO|S_IWUSR, "input/mice");
+	class_simple_device_add(input_class, MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX),
+				NULL, "mice");
+
+#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
+	if (!(psaux_registered = !misc_register(&psaux_mouse)))
+		printk(KERN_WARNING "mice: could not misc_register the device\n");
+#endif
+
+	printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
+
+	return 0;
+}
+
+static void __exit mousedev_exit(void)
+{
+#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
+	if (psaux_registered)
+		misc_deregister(&psaux_mouse);
+#endif
+	devfs_remove("input/mice");
+	class_simple_device_remove(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX));
+	input_unregister_handler(&mousedev_handler);
+}
+
+module_init(mousedev_init);
+module_exit(mousedev_exit);
diff --git a/drivers/input/power.c b/drivers/input/power.c
new file mode 100644
index 0000000..bfc5c63
--- /dev/null
+++ b/drivers/input/power.c
@@ -0,0 +1,169 @@
+/*
+ * $Id: power.c,v 1.10 2001/09/25 09:17:15 vojtech Exp $
+ *
+ *  Copyright (c) 2001 "Crazy" James Simmons
+ *
+ *  Input driver Power Management.
+ *
+ *  Sponsored by Transvirtual Technology.
+ */
+
+/*
+ * 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 by
+ * e-mail - mail your message to <jsimmons@transvirtual.com>.
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+
+static struct input_handler power_handler;
+
+/*
+ * Power management can't be done in a interrupt context. So we have to
+ * use keventd.
+ */
+static int suspend_button_pushed = 0;
+static void suspend_button_task_handler(void *data)
+{
+        udelay(200); /* debounce */
+        suspend_button_pushed = 0;
+}
+
+static DECLARE_WORK(suspend_button_task, suspend_button_task_handler, NULL);
+
+static void power_event(struct input_handle *handle, unsigned int type,
+		        unsigned int code, int down)
+{
+	struct input_dev *dev = handle->dev;
+
+	printk("Entering power_event\n");
+
+	if (type == EV_PWR) {
+		switch (code) {
+			case KEY_SUSPEND:
+				printk("Powering down entire device\n");
+
+				if (!suspend_button_pushed) {
+                			suspend_button_pushed = 1;
+                        		schedule_work(&suspend_button_task);
+                		}
+				break;
+			case KEY_POWER:
+				/* Hum power down the machine. */
+				break;
+			default:
+				return;
+		}
+	}
+
+	if (type == EV_KEY) {
+		switch (code) {
+			case KEY_SUSPEND:
+				printk("Powering down input device\n");
+				/* This is risky. See pm.h for details. */
+				if (dev->state != PM_RESUME)
+					dev->state = PM_RESUME;
+				else
+					dev->state = PM_SUSPEND;
+				pm_send(dev->pm_dev, dev->state, dev);
+				break;
+			case KEY_POWER:
+				/* Turn the input device off completely ? */
+				break;
+			default:
+				return;
+		}
+	}
+	return;
+}
+
+static struct input_handle *power_connect(struct input_handler *handler,
+					  struct input_dev *dev,
+					  struct input_device_id *id)
+{
+	struct input_handle *handle;
+
+	if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL)))
+		return NULL;
+	memset(handle, 0, sizeof(struct input_handle));
+
+	handle->dev = dev;
+	handle->handler = handler;
+
+	input_open_device(handle);
+
+	printk(KERN_INFO "power.c: Adding power management to input layer\n");
+	return handle;
+}
+
+static void power_disconnect(struct input_handle *handle)
+{
+	input_close_device(handle);
+	kfree(handle);
+}
+
+static struct input_device_id power_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
+		.evbit = { BIT(EV_KEY) },
+		.keybit = { [LONG(KEY_SUSPEND)] = BIT(KEY_SUSPEND) }
+	},
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
+		.evbit = { BIT(EV_KEY) },
+		.keybit = { [LONG(KEY_POWER)] = BIT(KEY_POWER) }
+	},
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+		.evbit = { BIT(EV_PWR) },
+	},
+	{ }, 	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(input, power_ids);
+
+static struct input_handler power_handler = {
+	.event =	power_event,
+	.connect =	power_connect,
+	.disconnect =	power_disconnect,
+	.name =		"power",
+	.id_table =	power_ids,
+};
+
+static int __init power_init(void)
+{
+	input_register_handler(&power_handler);
+	return 0;
+}
+
+static void __exit power_exit(void)
+{
+	input_unregister_handler(&power_handler);
+}
+
+module_init(power_init);
+module_exit(power_exit);
+
+MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>");
+MODULE_DESCRIPTION("Input Power Management driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
new file mode 100644
index 0000000..b371073
--- /dev/null
+++ b/drivers/input/serio/Kconfig
@@ -0,0 +1,183 @@
+#
+# Input core configuration
+#
+config SERIO
+	tristate "Serial I/O support" if EMBEDDED || !X86
+	default y
+	---help---
+	  Say Yes here if you have any input device that uses serial I/O to
+	  communicate with the system. This includes the
+	  		* standard AT keyboard and PS/2 mouse *
+	  as well as serial mice, Sun keyboards, some joysticks and 6dof
+	  devices and more.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called serio.
+
+if SERIO
+
+config SERIO_I8042
+	tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86
+	default y
+	depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && !M68K
+	---help---
+	  i8042 is the chip over which the standard AT keyboard and PS/2
+	  mouse are connected to the computer. If you use these devices,
+	  you'll need to say Y here.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called i8042.
+
+config SERIO_SERPORT
+	tristate "Serial port line discipline"
+	default y
+	---help---
+	  Say Y here if you plan to use an input device (mouse, joystick,
+	  tablet, 6dof) that communicates over the RS232 serial (COM) port.
+
+	  More information is available: <file:Documentation/input/input.txt>
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called serport.
+
+config SERIO_CT82C710
+	tristate "ct82c710 Aux port controller"
+	depends on X86
+	---help---
+	  Say Y here if you have a Texas Instruments TravelMate notebook
+	  equipped with the ct82c710 chip and want to use a mouse connected
+	  to the "QuickPort".
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ct82c710.
+
+config SERIO_Q40KBD
+	tristate "Q40 keyboard controller"
+	depends on Q40
+
+config SERIO_PARKBD
+	tristate "Parallel port keyboard adapter"
+	depends on PARPORT
+	---help---
+	  Say Y here if you built a simple parallel port adapter to attach
+	  an additional AT keyboard, XT keyboard or PS/2 mouse.
+
+	  More information is available: <file:Documentation/input/input.txt>
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called parkbd.
+
+config SERIO_RPCKBD
+	tristate "Acorn RiscPC keyboard controller"
+	depends on ARCH_ACORN || ARCH_CLPS7500
+	default y
+	help
+	  Say Y here if you have the Acorn RiscPC and want to use an AT
+	  keyboard connected to its keyboard controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rpckbd.
+
+config SERIO_AMBAKMI
+	tristate "AMBA KMI keyboard controller"
+	depends on ARM_AMBA
+
+config SERIO_SA1111
+	tristate "Intel SA1111 keyboard controller"
+	depends on SA1111
+
+config SERIO_GSCPS2
+	tristate "HP GSC PS/2 keyboard and PS/2 mouse controller"
+	depends on GSC
+	default y
+	help
+	  This driver provides support for the PS/2 ports on PA-RISC machines
+	  over which HP PS/2 keyboards and PS/2 mice may be connected.
+	  If you use these devices, you'll need to say Y here.
+
+	  It's safe to enable this driver, so if unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gscps2.
+
+config HP_SDC
+	tristate "HP System Device Controller i8042 Support"
+	depends on GSC && SERIO
+	default y
+	---help---
+	  This option enables supports for the the "System Device
+	  Controller", an i8042 carrying microcode to manage a
+	  few miscellanous devices on some Hewlett Packard systems.
+	  The SDC itself contains a 10ms resolution timer/clock capable
+	  of delivering interrupts on a periodic and one-shot basis.
+	  The SDC may also be connected to a battery-backed real-time
+	  clock, a basic audio waveform generator, and an HP-HIL Master
+	  Link Controller serving up to seven input devices.
+
+	  By itself this option is rather useless, but enabling it will
+	  enable selection of drivers for the abovementioned devices.
+	  It is, however, incompatible with the old, reliable HIL keyboard
+	  driver, and the new HIL driver is experimental, so if you plan
+	  to use a HIL keyboard as your primary keyboard, you may wish
+	  to keep using that driver until the new HIL drivers have had
+	  more testing.
+
+config HIL_MLC
+	tristate "HIL MLC Support (needed for HIL input devices)"
+	depends on HP_SDC
+
+config SERIO_PCIPS2
+	tristate "PCI PS/2 keyboard and PS/2 mouse controller"
+	depends on PCI
+	help
+	  Say Y here if you have a Mobility Docking station with PS/2
+	  keyboard and mice ports.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pcips2.
+
+config SERIO_MACEPS2
+	tristate "SGI O2 MACE PS/2 controller"
+	depends on SGI_IP32
+	help
+	  Say Y here if you have SGI O2 workstation and want to use its
+	  PS/2 ports.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called maceps2.
+
+config SERIO_LIBPS2
+	tristate "PS/2 driver library" if EMBEDDED
+	help
+	  Say Y here if you are using a driver for device connected
+	  to a PS/2 port, such as PS/2 mouse or standard AT keyboard.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called libps2.
+
+config SERIO_RAW
+	tristate "Raw access to serio ports"
+	help
+	  Say Y here if you want to have raw access to serio ports, such as
+	  AUX ports on i8042 keyboard controller. Each serio port that is
+	  bound to this driver will be accessible via a char device with
+	  major 10 and dynamically allocated minor. The driver will try
+	  allocating minor 1 (that historically corresponds to /dev/psaux)
+	  first. To bind this driver to a serio port use sysfs interface:
+
+	      echo -n "serio_raw" > /sys/bus/serio/devices/serioX/driver
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called serio_raw.
+
+endif
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
new file mode 100644
index 0000000..678a859
--- /dev/null
+++ b/drivers/input/serio/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile for the input core drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_SERIO)		+= serio.o
+obj-$(CONFIG_SERIO_I8042)	+= i8042.o
+obj-$(CONFIG_SERIO_PARKBD)	+= parkbd.o
+obj-$(CONFIG_SERIO_SERPORT)	+= serport.o
+obj-$(CONFIG_SERIO_CT82C710)	+= ct82c710.o
+obj-$(CONFIG_SERIO_RPCKBD)	+= rpckbd.o
+obj-$(CONFIG_SERIO_SA1111)	+= sa1111ps2.o
+obj-$(CONFIG_SERIO_AMBAKMI)	+= ambakmi.o
+obj-$(CONFIG_SERIO_Q40KBD)	+= q40kbd.o
+obj-$(CONFIG_SERIO_98KBD)	+= 98kbd-io.o
+obj-$(CONFIG_SERIO_GSCPS2)	+= gscps2.o
+obj-$(CONFIG_HP_SDC)		+= hp_sdc.o
+obj-$(CONFIG_HIL_MLC)		+= hp_sdc_mlc.o hil_mlc.o
+obj-$(CONFIG_SERIO_PCIPS2)	+= pcips2.o
+obj-$(CONFIG_SERIO_MACEPS2)	+= maceps2.o
+obj-$(CONFIG_SERIO_LIBPS2)	+= libps2.o
+obj-$(CONFIG_SERIO_RAW)		+= serio_raw.o
diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c
new file mode 100644
index 0000000..9b1ab5e
--- /dev/null
+++ b/drivers/input/serio/ambakmi.c
@@ -0,0 +1,231 @@
+/*
+ *  linux/drivers/input/serio/ambakmi.c
+ *
+ *  Copyright (C) 2000-2003 Deep Blue Solutions Ltd.
+ *  Copyright (C) 2002 Russell King.
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/hardware/amba.h>
+#include <asm/hardware/amba_kmi.h>
+#include <asm/hardware/clock.h>
+
+#define KMI_BASE	(kmi->base)
+
+struct amba_kmi_port {
+	struct serio		*io;
+	struct clk		*clk;
+	void __iomem		*base;
+	unsigned int		irq;
+	unsigned int		divisor;
+	unsigned int		open;
+};
+
+static irqreturn_t amba_kmi_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct amba_kmi_port *kmi = dev_id;
+	unsigned int status = readb(KMIIR);
+	int handled = IRQ_NONE;
+
+	while (status & KMIIR_RXINTR) {
+		serio_interrupt(kmi->io, readb(KMIDATA), 0, regs);
+		status = readb(KMIIR);
+		handled = IRQ_HANDLED;
+	}
+
+	return handled;
+}
+
+static int amba_kmi_write(struct serio *io, unsigned char val)
+{
+	struct amba_kmi_port *kmi = io->port_data;
+	unsigned int timeleft = 10000; /* timeout in 100ms */
+
+	while ((readb(KMISTAT) & KMISTAT_TXEMPTY) == 0 && timeleft--)
+		udelay(10);
+
+	if (timeleft)
+		writeb(val, KMIDATA);
+
+	return timeleft ? 0 : SERIO_TIMEOUT;
+}
+
+static int amba_kmi_open(struct serio *io)
+{
+	struct amba_kmi_port *kmi = io->port_data;
+	unsigned int divisor;
+	int ret;
+
+	ret = clk_use(kmi->clk);
+	if (ret)
+		goto out;
+
+	ret = clk_enable(kmi->clk);
+	if (ret)
+		goto clk_unuse;
+
+	divisor = clk_get_rate(kmi->clk) / 8000000 - 1;
+	writeb(divisor, KMICLKDIV);
+	writeb(KMICR_EN, KMICR);
+
+	ret = request_irq(kmi->irq, amba_kmi_int, 0, "kmi-pl050", kmi);
+	if (ret) {
+		printk(KERN_ERR "kmi: failed to claim IRQ%d\n", kmi->irq);
+		writeb(0, KMICR);
+		goto clk_disable;
+	}
+
+	writeb(KMICR_EN | KMICR_RXINTREN, KMICR);
+
+	return 0;
+
+ clk_disable:
+	clk_disable(kmi->clk);
+ clk_unuse:
+	clk_unuse(kmi->clk);
+ out:
+	return ret;
+}
+
+static void amba_kmi_close(struct serio *io)
+{
+	struct amba_kmi_port *kmi = io->port_data;
+
+	writeb(0, KMICR);
+
+	free_irq(kmi->irq, kmi);
+	clk_disable(kmi->clk);
+	clk_unuse(kmi->clk);
+}
+
+static int amba_kmi_probe(struct amba_device *dev, void *id)
+{
+	struct amba_kmi_port *kmi;
+	struct serio *io;
+	int ret;
+
+	ret = amba_request_regions(dev, NULL);
+	if (ret)
+		return ret;
+
+	kmi = kmalloc(sizeof(struct amba_kmi_port), GFP_KERNEL);
+	io = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!kmi || !io) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	memset(kmi, 0, sizeof(struct amba_kmi_port));
+	memset(io, 0, sizeof(struct serio));
+
+	io->id.type	= SERIO_8042;
+	io->write	= amba_kmi_write;
+	io->open	= amba_kmi_open;
+	io->close	= amba_kmi_close;
+	strlcpy(io->name, dev->dev.bus_id, sizeof(io->name));
+	strlcpy(io->phys, dev->dev.bus_id, sizeof(io->phys));
+	io->port_data	= kmi;
+	io->dev.parent	= &dev->dev;
+
+	kmi->io 	= io;
+	kmi->base	= ioremap(dev->res.start, KMI_SIZE);
+	if (!kmi->base) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	kmi->clk = clk_get(&dev->dev, "KMIREFCLK");
+	if (IS_ERR(kmi->clk)) {
+		ret = PTR_ERR(kmi->clk);
+		goto unmap;
+	}
+
+	kmi->irq = dev->irq[0];
+	amba_set_drvdata(dev, kmi);
+
+	serio_register_port(kmi->io);
+	return 0;
+
+ unmap:
+	iounmap(kmi->base);
+ out:
+	kfree(kmi);
+	kfree(io);
+	amba_release_regions(dev);
+	return ret;
+}
+
+static int amba_kmi_remove(struct amba_device *dev)
+{
+	struct amba_kmi_port *kmi = amba_get_drvdata(dev);
+
+	amba_set_drvdata(dev, NULL);
+
+	serio_unregister_port(kmi->io);
+	clk_put(kmi->clk);
+	iounmap(kmi->base);
+	kfree(kmi);
+	amba_release_regions(dev);
+	return 0;
+}
+
+static int amba_kmi_resume(struct amba_device *dev)
+{
+	struct amba_kmi_port *kmi = amba_get_drvdata(dev);
+
+	/* kick the serio layer to rescan this port */
+	serio_reconnect(kmi->io);
+
+	return 0;
+}
+
+static struct amba_id amba_kmi_idtable[] = {
+	{
+		.id	= 0x00041050,
+		.mask	= 0x000fffff,
+	},
+	{ 0, 0 }
+};
+
+static struct amba_driver ambakmi_driver = {
+	.drv		= {
+		.name	= "kmi-pl050",
+	},
+	.id_table	= amba_kmi_idtable,
+	.probe		= amba_kmi_probe,
+	.remove		= amba_kmi_remove,
+	.resume		= amba_kmi_resume,
+};
+
+static int __init amba_kmi_init(void)
+{
+	return amba_driver_register(&ambakmi_driver);
+}
+
+static void __exit amba_kmi_exit(void)
+{
+	amba_driver_unregister(&ambakmi_driver);
+}
+
+module_init(amba_kmi_init);
+module_exit(amba_kmi_exit);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("AMBA KMI controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c
new file mode 100644
index 0000000..dd0f5bd
--- /dev/null
+++ b/drivers/input/serio/ct82c710.c
@@ -0,0 +1,225 @@
+/*
+ * $Id: ct82c710.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ *  82C710 C&T mouse port chip 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/module.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+
+#include <asm/io.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("82C710 C&T mouse port chip driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * ct82c710 interface
+ */
+
+#define CT82C710_DEV_IDLE     0x01		/* Device Idle */
+#define CT82C710_RX_FULL      0x02		/* Device Char received */
+#define CT82C710_TX_IDLE      0x04		/* Device XMIT Idle */
+#define CT82C710_RESET        0x08		/* Device Reset */
+#define CT82C710_INTS_ON      0x10		/* Device Interrupt On */
+#define CT82C710_ERROR_FLAG   0x20		/* Device Error */
+#define CT82C710_CLEAR        0x40		/* Device Clear */
+#define CT82C710_ENABLE       0x80		/* Device Enable */
+
+#define CT82C710_IRQ          12
+
+#define CT82C710_DATA         ct82c710_iores.start
+#define CT82C710_STATUS       (ct82c710_iores.start + 1)
+
+static struct serio *ct82c710_port;
+static struct platform_device *ct82c710_device;
+static struct resource ct82c710_iores;
+
+/*
+ * Interrupt handler for the 82C710 mouse port. A character
+ * is waiting in the 82C710.
+ */
+
+static irqreturn_t ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs)
+{
+	return serio_interrupt(ct82c710_port, inb(CT82C710_DATA), 0, regs);
+}
+
+/*
+ * Wait for device to send output char and flush any input char.
+ */
+
+static int ct82c170_wait(void)
+{
+	int timeout = 60000;
+
+	while ((inb(CT82C710_STATUS) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE))
+		       != (CT82C710_DEV_IDLE | CT82C710_TX_IDLE) && timeout) {
+
+		if (inb_p(CT82C710_STATUS) & CT82C710_RX_FULL) inb_p(CT82C710_DATA);
+
+		udelay(1);
+		timeout--;
+	}
+
+	return !timeout;
+}
+
+static void ct82c710_close(struct serio *serio)
+{
+	if (ct82c170_wait())
+		printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
+
+	outb_p(inb_p(CT82C710_STATUS) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), CT82C710_STATUS);
+
+	if (ct82c170_wait())
+		printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
+
+	free_irq(CT82C710_IRQ, NULL);
+}
+
+static int ct82c710_open(struct serio *serio)
+{
+	unsigned char status;
+
+	if (request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL))
+		return -1;
+
+	status = inb_p(CT82C710_STATUS);
+
+	status |= (CT82C710_ENABLE | CT82C710_RESET);
+	outb_p(status, CT82C710_STATUS);
+
+	status &= ~(CT82C710_RESET);
+	outb_p(status, CT82C710_STATUS);
+
+	status |= CT82C710_INTS_ON;
+	outb_p(status, CT82C710_STATUS);	/* Enable interrupts */
+
+	while (ct82c170_wait()) {
+		printk(KERN_ERR "ct82c710: Device busy in open()\n");
+		status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON);
+		outb_p(status, CT82C710_STATUS);
+		free_irq(CT82C710_IRQ, NULL);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Write to the 82C710 mouse device.
+ */
+
+static int ct82c710_write(struct serio *port, unsigned char c)
+{
+	if (ct82c170_wait()) return -1;
+	outb_p(c, CT82C710_DATA);
+	return 0;
+}
+
+/*
+ * See if we can find a 82C710 device. Read mouse address.
+ */
+
+static int __init ct82c710_probe(void)
+{
+	outb_p(0x55, 0x2fa);				/* Any value except 9, ff or 36 */
+	outb_p(0xaa, 0x3fa);				/* Inverse of 55 */
+	outb_p(0x36, 0x3fa);				/* Address the chip */
+	outb_p(0xe4, 0x3fa);				/* 390/4; 390 = config address */
+	outb_p(0x1b, 0x2fa);				/* Inverse of e4 */
+	outb_p(0x0f, 0x390);				/* Write index */
+	if (inb_p(0x391) != 0xe4)			/* Config address found? */
+		return -1;				/* No: no 82C710 here */
+
+	outb_p(0x0d, 0x390);				/* Write index */
+	ct82c710_iores.start = inb_p(0x391) << 2;	/* Get mouse I/O address */
+	ct82c710_iores.end = ct82c710_iores.start + 1;
+	ct82c710_iores.flags = IORESOURCE_IO;
+	outb_p(0x0f, 0x390);
+	outb_p(0x0f, 0x391);				/* Close config mode */
+
+	return 0;
+}
+
+static struct serio * __init ct82c710_allocate_port(void)
+{
+	struct serio *serio;
+
+	serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (serio) {
+		memset(serio, 0, sizeof(struct serio));
+		serio->id.type = SERIO_8042;
+		serio->open = ct82c710_open;
+		serio->close = ct82c710_close;
+		serio->write = ct82c710_write;
+		serio->dev.parent = &ct82c710_device->dev;
+		strlcpy(serio->name, "C&T 82c710 mouse port", sizeof(serio->name));
+		snprintf(serio->phys, sizeof(serio->phys), "isa%04lx/serio0", CT82C710_DATA);
+	}
+
+	return serio;
+}
+
+static int __init ct82c710_init(void)
+{
+	if (ct82c710_probe())
+		return -ENODEV;
+
+	ct82c710_device = platform_device_register_simple("ct82c710", -1, &ct82c710_iores, 1);
+	if (IS_ERR(ct82c710_device))
+		return PTR_ERR(ct82c710_device);
+
+	if (!(ct82c710_port = ct82c710_allocate_port())) {
+		platform_device_unregister(ct82c710_device);
+		return -ENOMEM;
+	}
+
+	serio_register_port(ct82c710_port);
+
+	printk(KERN_INFO "serio: C&T 82c710 mouse port at %#lx irq %d\n",
+		CT82C710_DATA, CT82C710_IRQ);
+
+	return 0;
+}
+
+static void __exit ct82c710_exit(void)
+{
+	serio_unregister_port(ct82c710_port);
+	platform_device_unregister(ct82c710_device);
+}
+
+module_init(ct82c710_init);
+module_exit(ct82c710_exit);
diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c
new file mode 100644
index 0000000..897e4c1
--- /dev/null
+++ b/drivers/input/serio/gscps2.c
@@ -0,0 +1,467 @@
+/*
+ * drivers/input/serio/gscps2.c
+ *
+ * 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>
+ *
+ * Pieces of code based on linux-2.4's hp_mouse.c & hp_keyb.c
+ * 	Copyright (c) 1999 Alex deVries <alex@onefishtwo.ca>
+ *	Copyright (c) 1999-2000 Philipp Rumpf <prumpf@tux.org>
+ *	Copyright (c) 2000 Xavier Debacker <debackex@esiee.fr>
+ *	Copyright (c) 2000-2001 Thomas Marteau <marteaut@esiee.fr>
+ *
+ * HP GSC PS/2 port driver, found in PA/RISC Workstations
+ *
+ * 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.
+ *
+ * TODO:
+ * - Dino testing (did HP ever shipped a machine on which this port
+ *                 was usable/enabled ?)
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/pci_ids.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/parisc-device.h>
+
+MODULE_AUTHOR("Laurent Canet <canetl@esiee.fr>, Thibaut Varene <varenet@parisc-linux.org>, Helge Deller <deller@gmx.de>");
+MODULE_DESCRIPTION("HP GSC PS2 port driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl);
+
+#define PFX "gscps2.c: "
+
+/*
+ * Driver constants
+ */
+
+/* various constants */
+#define ENABLE			1
+#define DISABLE			0
+
+#define GSC_DINO_OFFSET		0x0800	/* offset for DINO controller versus LASI one */
+
+/* PS/2 IO port offsets */
+#define GSC_ID			0x00	/* device ID offset (see: GSC_ID_XXX) */
+#define GSC_RESET		0x00	/* reset port offset */
+#define GSC_RCVDATA		0x04	/* receive port offset */
+#define GSC_XMTDATA		0x04	/* transmit port offset */
+#define GSC_CONTROL		0x08	/* see: Control register bits */
+#define GSC_STATUS		0x0C	/* see: Status register bits */
+
+/* Control register bits */
+#define GSC_CTRL_ENBL		0x01	/* enable interface */
+#define GSC_CTRL_LPBXR		0x02	/* loopback operation */
+#define GSC_CTRL_DIAG		0x20	/* directly control clock/data line */
+#define GSC_CTRL_DATDIR		0x40	/* data line direct control */
+#define GSC_CTRL_CLKDIR		0x80	/* clock line direct control */
+
+/* Status register bits */
+#define GSC_STAT_RBNE		0x01	/* Receive Buffer Not Empty */
+#define GSC_STAT_TBNE		0x02	/* Transmit Buffer Not Empty */
+#define GSC_STAT_TERR		0x04	/* Timeout Error */
+#define GSC_STAT_PERR		0x08	/* Parity Error */
+#define GSC_STAT_CMPINTR	0x10	/* Composite Interrupt = irq on any port */
+#define GSC_STAT_DATSHD		0x40	/* Data Line Shadow */
+#define GSC_STAT_CLKSHD		0x80	/* Clock Line Shadow */
+
+/* IDs returned by GSC_ID port register */
+#define GSC_ID_KEYBOARD		0	/* device ID values */
+#define GSC_ID_MOUSE		1
+
+
+static irqreturn_t gscps2_interrupt(int irq, void *dev, struct pt_regs *regs);
+
+#define BUFFER_SIZE 0x0f
+
+/* GSC PS/2 port device struct */
+struct gscps2port {
+	struct list_head node;
+	struct parisc_device *padev;
+	struct serio *port;
+	spinlock_t lock;
+	char *addr;
+	u8 act, append; /* position in buffer[] */
+	struct {
+		u8 data;
+		u8 str;
+	} buffer[BUFFER_SIZE+1];
+	int id;
+};
+
+/*
+ * Various HW level routines
+ */
+
+#define gscps2_readb_input(x)		readb((x)+GSC_RCVDATA)
+#define gscps2_readb_control(x)		readb((x)+GSC_CONTROL)
+#define gscps2_readb_status(x)		readb((x)+GSC_STATUS)
+#define gscps2_writeb_control(x, y)	writeb((x), (y)+GSC_CONTROL)
+
+
+/*
+ * wait_TBE() - wait for Transmit Buffer Empty
+ */
+
+static int wait_TBE(char *addr)
+{
+	int timeout = 25000; /* device is expected to react within 250 msec */
+	while (gscps2_readb_status(addr) & GSC_STAT_TBNE) {
+		if (!--timeout)
+			return 0;	/* This should not happen */
+		udelay(10);
+	}
+	return 1;
+}
+
+
+/*
+ * gscps2_flush() - flush the receive buffer
+ */
+
+static void gscps2_flush(struct gscps2port *ps2port)
+{
+	while (gscps2_readb_status(ps2port->addr) & GSC_STAT_RBNE)
+		gscps2_readb_input(ps2port->addr);
+	ps2port->act = ps2port->append = 0;
+}
+
+/*
+ * gscps2_writeb_output() - write a byte to the port
+ *
+ * returns 1 on sucess, 0 on error
+ */
+
+static inline int gscps2_writeb_output(struct gscps2port *ps2port, u8 data)
+{
+	unsigned long flags;
+	char *addr = ps2port->addr;
+
+	if (!wait_TBE(addr)) {
+		printk(KERN_DEBUG PFX "timeout - could not write byte %#x\n", data);
+		return 0;
+	}
+
+	while (gscps2_readb_status(ps2port->addr) & GSC_STAT_RBNE)
+		/* wait */;
+
+	spin_lock_irqsave(&ps2port->lock, flags);
+	writeb(data, addr+GSC_XMTDATA);
+	spin_unlock_irqrestore(&ps2port->lock, flags);
+
+	/* this is ugly, but due to timing of the port it seems to be necessary. */
+	mdelay(6);
+
+	/* make sure any received data is returned as fast as possible */
+	/* this is important e.g. when we set the LEDs on the keyboard */
+	gscps2_interrupt(0, NULL, NULL);
+
+	return 1;
+}
+
+
+/*
+ * gscps2_enable() - enables or disables the port
+ */
+
+static void gscps2_enable(struct gscps2port *ps2port, int enable)
+{
+	unsigned long flags;
+	u8 data;
+
+	/* now enable/disable the port */
+	spin_lock_irqsave(&ps2port->lock, flags);
+	gscps2_flush(ps2port);
+	data = gscps2_readb_control(ps2port->addr);
+	if (enable)
+		data |= GSC_CTRL_ENBL;
+	else
+		data &= ~GSC_CTRL_ENBL;
+	gscps2_writeb_control(data, ps2port->addr);
+	spin_unlock_irqrestore(&ps2port->lock, flags);
+	wait_TBE(ps2port->addr);
+	gscps2_flush(ps2port);
+}
+
+/*
+ * gscps2_reset() - resets the PS/2 port
+ */
+
+static void gscps2_reset(struct gscps2port *ps2port)
+{
+	char *addr = ps2port->addr;
+	unsigned long flags;
+
+	/* reset the interface */
+	spin_lock_irqsave(&ps2port->lock, flags);
+	gscps2_flush(ps2port);
+	writeb(0xff, addr+GSC_RESET);
+	gscps2_flush(ps2port);
+	spin_unlock_irqrestore(&ps2port->lock, flags);
+
+	/* enable it */
+	gscps2_enable(ps2port, ENABLE);
+}
+
+static LIST_HEAD(ps2port_list);
+
+/**
+ * gscps2_interrupt() - Interruption service routine
+ *
+ * This function reads received PS/2 bytes and processes them on
+ * all interfaces.
+ * The problematic part here is, that the keyboard and mouse PS/2 port
+ * share the same interrupt and it's not possible to send data if any
+ * one of them holds input data. To solve this problem we try to receive
+ * the data as fast as possible and handle the reporting to the upper layer
+ * later.
+ */
+
+static irqreturn_t gscps2_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+	struct gscps2port *ps2port;
+
+	list_for_each_entry(ps2port, &ps2port_list, node) {
+
+	  unsigned long flags;
+	  spin_lock_irqsave(&ps2port->lock, flags);
+
+	  while ( (ps2port->buffer[ps2port->append].str =
+		   gscps2_readb_status(ps2port->addr)) & GSC_STAT_RBNE ) {
+		ps2port->buffer[ps2port->append].data =
+				gscps2_readb_input(ps2port->addr);
+		ps2port->append = ((ps2port->append+1) & BUFFER_SIZE);
+	  }
+
+	  spin_unlock_irqrestore(&ps2port->lock, flags);
+
+	} /* list_for_each_entry */
+
+	/* all data was read from the ports - now report the data to upper layer */
+
+	list_for_each_entry(ps2port, &ps2port_list, node) {
+
+	  while (ps2port->act != ps2port->append) {
+
+	    unsigned int rxflags;
+	    u8 data, status;
+
+	    /* Did new data arrived while we read existing data ?
+	       If yes, exit now and let the new irq handler start over again */
+	    if (gscps2_readb_status(ps2port->addr) & GSC_STAT_CMPINTR)
+		return IRQ_HANDLED;
+
+	    status = ps2port->buffer[ps2port->act].str;
+	    data   = ps2port->buffer[ps2port->act].data;
+
+	    ps2port->act = ((ps2port->act+1) & BUFFER_SIZE);
+	    rxflags =	((status & GSC_STAT_TERR) ? SERIO_TIMEOUT : 0 ) |
+			((status & GSC_STAT_PERR) ? SERIO_PARITY  : 0 );
+
+	    serio_interrupt(ps2port->port, data, rxflags, regs);
+
+	  } /* while() */
+
+	} /* list_for_each_entry */
+
+	return IRQ_HANDLED;
+}
+
+
+/*
+ * gscps2_write() - send a byte out through the aux interface.
+ */
+
+static int gscps2_write(struct serio *port, unsigned char data)
+{
+	struct gscps2port *ps2port = port->port_data;
+
+	if (!gscps2_writeb_output(ps2port, data)) {
+		printk(KERN_DEBUG PFX "sending byte %#x failed.\n", data);
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * gscps2_open() is called when a port is opened by the higher layer.
+ * It resets and enables the port.
+ */
+
+static int gscps2_open(struct serio *port)
+{
+	struct gscps2port *ps2port = port->port_data;
+
+	gscps2_reset(ps2port);
+
+	gscps2_interrupt(0, NULL, NULL);
+
+	return 0;
+}
+
+/*
+ * gscps2_close() disables the port
+ */
+
+static void gscps2_close(struct serio *port)
+{
+	struct gscps2port *ps2port = port->port_data;
+	gscps2_enable(ps2port, DISABLE);
+}
+
+/**
+ * gscps2_probe() - Probes PS2 devices
+ * @return: success/error report
+ */
+
+static int __init gscps2_probe(struct parisc_device *dev)
+{
+	struct gscps2port *ps2port;
+	struct serio *serio;
+	unsigned long hpa = dev->hpa;
+	int ret;
+
+	if (!dev->irq)
+		return -ENODEV;
+
+	/* Offset for DINO PS/2. Works with LASI even */
+	if (dev->id.sversion == 0x96)
+		hpa += GSC_DINO_OFFSET;
+
+	ps2port = kmalloc(sizeof(struct gscps2port), GFP_KERNEL);
+	serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!ps2port || !serio) {
+		ret = -ENOMEM;
+		goto fail_nomem;
+	}
+
+	dev_set_drvdata(&dev->dev, ps2port);
+
+	memset(ps2port, 0, sizeof(struct gscps2port));
+	memset(serio, 0, sizeof(struct serio));
+	ps2port->port = serio;
+	ps2port->padev = dev;
+	ps2port->addr = ioremap(hpa, GSC_STATUS + 4);
+	spin_lock_init(&ps2port->lock);
+
+	gscps2_reset(ps2port);
+	ps2port->id = readb(ps2port->addr + GSC_ID) & 0x0f;
+
+	snprintf(serio->name, sizeof(serio->name), "GSC PS/2 %s",
+		 (ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse");
+	strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys));
+	serio->id.type		= SERIO_8042;
+	serio->write		= gscps2_write;
+	serio->open		= gscps2_open;
+	serio->close		= gscps2_close;
+	serio->port_data	= ps2port;
+	serio->dev.parent	= &dev->dev;
+
+	list_add_tail(&ps2port->node, &ps2port_list);
+
+	ret = -EBUSY;
+	if (request_irq(dev->irq, gscps2_interrupt, SA_SHIRQ, ps2port->port->name, ps2port))
+		goto fail_miserably;
+
+	if (ps2port->id != GSC_ID_KEYBOARD && ps2port->id != GSC_ID_MOUSE) {
+		printk(KERN_WARNING PFX "Unsupported PS/2 port at 0x%08lx (id=%d) ignored\n",
+				hpa, ps2port->id);
+		ret = -ENODEV;
+		goto fail;
+	}
+
+#if 0
+	if (!request_mem_region(hpa, GSC_STATUS + 4, ps2port->port.name))
+		goto fail;
+#endif
+
+	printk(KERN_INFO "serio: %s port at 0x%p irq %d @ %s\n",
+		ps2port->port->name,
+		ps2port->addr,
+		ps2port->padev->irq,
+		ps2port->port->phys);
+
+	serio_register_port(ps2port->port);
+
+	return 0;
+
+fail:
+	free_irq(dev->irq, ps2port);
+
+fail_miserably:
+	list_del(&ps2port->node);
+	iounmap(ps2port->addr);
+	release_mem_region(dev->hpa, GSC_STATUS + 4);
+
+fail_nomem:
+	kfree(ps2port);
+	kfree(serio);
+	return ret;
+}
+
+/**
+ * gscps2_remove() - Removes PS2 devices
+ * @return: success/error report
+ */
+
+static int __devexit gscps2_remove(struct parisc_device *dev)
+{
+	struct gscps2port *ps2port = dev_get_drvdata(&dev->dev);
+
+	serio_unregister_port(ps2port->port);
+	free_irq(dev->irq, ps2port);
+	gscps2_flush(ps2port);
+	list_del(&ps2port->node);
+	iounmap(ps2port->addr);
+#if 0
+	release_mem_region(dev->hpa, GSC_STATUS + 4);
+#endif
+	dev_set_drvdata(&dev->dev, NULL);
+	kfree(ps2port);
+	return 0;
+}
+
+
+static struct parisc_device_id gscps2_device_tbl[] = {
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00084 }, /* LASI PS/2 */
+#ifdef DINO_TESTED
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00096 }, /* DINO PS/2 */
+#endif
+	{ 0, }	/* 0 terminated list */
+};
+
+static struct parisc_driver parisc_ps2_driver = {
+	.name		= "GSC PS2",
+	.id_table	= gscps2_device_tbl,
+	.probe		= gscps2_probe,
+	.remove		= gscps2_remove,
+};
+
+static int __init gscps2_init(void)
+{
+	register_parisc_driver(&parisc_ps2_driver);
+	return 0;
+}
+
+static void __exit gscps2_exit(void)
+{
+	unregister_parisc_driver(&parisc_ps2_driver);
+}
+
+
+module_init(gscps2_init);
+module_exit(gscps2_exit);
+
diff --git a/drivers/input/serio/hil_mlc.c b/drivers/input/serio/hil_mlc.c
new file mode 100644
index 0000000..c243cb6f
--- /dev/null
+++ b/drivers/input/serio/hil_mlc.c
@@ -0,0 +1,949 @@
+/*
+ * HIL MLC state machine and serio interface driver
+ *
+ * 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
+ *
+ *
+ *	Driver theory of operation:
+ *
+ *	Some access methods and an ISR is defined by the sub-driver 
+ *	(e.g. hp_sdc_mlc.c).  These methods are expected to provide a 
+ *	few bits of logic in addition to raw access to the HIL MLC, 
+ *	specifically, the ISR, which is entirely registered by the 
+ *	sub-driver and invoked directly, must check for record 
+ *	termination or packet match, at which point a semaphore must
+ *	be cleared and then the hil_mlcs_tasklet must be scheduled.
+ *
+ *	The hil_mlcs_tasklet processes the state machine for all MLCs
+ *	each time it runs, checking each MLC's progress at the current
+ *	node in the state machine, and moving the MLC to subsequent nodes
+ *	in the state machine when appropriate.  It will reschedule
+ *	itself if output is pending.  (This rescheduling should be replaced
+ *	at some point with a sub-driver-specific mechanism.)
+ *
+ *	A timer task prods the tasklet once per second to prevent 
+ *	hangups when attached devices do not return expected data
+ *	and to initiate probes of the loop for new devices.
+ */
+
+#include <linux/hil_mlc.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION("HIL MLC serio");
+MODULE_LICENSE("Dual BSD/GPL");
+
+EXPORT_SYMBOL(hil_mlc_register);
+EXPORT_SYMBOL(hil_mlc_unregister);
+
+#define PREFIX "HIL MLC: "
+
+static LIST_HEAD(hil_mlcs);
+static DEFINE_RWLOCK(hil_mlcs_lock);
+static struct timer_list	hil_mlcs_kicker;
+static int			hil_mlcs_probe;
+
+static void hil_mlcs_process(unsigned long unused);
+DECLARE_TASKLET_DISABLED(hil_mlcs_tasklet, hil_mlcs_process, 0);
+
+
+/* #define HIL_MLC_DEBUG */
+
+/********************** Device info/instance management **********************/
+
+static void hil_mlc_clear_di_map (hil_mlc *mlc, int val) {
+	int j;
+	for (j = val; j < 7 ; j++) {
+		mlc->di_map[j] = -1;
+	}
+}
+
+static void hil_mlc_clear_di_scratch (hil_mlc *mlc) {
+	memset(&(mlc->di_scratch), 0, sizeof(mlc->di_scratch));
+}
+
+static void hil_mlc_copy_di_scratch (hil_mlc *mlc, int idx) {
+	memcpy(&(mlc->di[idx]), &(mlc->di_scratch), sizeof(mlc->di_scratch));
+}
+
+static int hil_mlc_match_di_scratch (hil_mlc *mlc) {
+	int idx;
+
+	for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
+		int j, found;
+
+		/* In-use slots are not eligible. */
+		found = 0;
+		for (j = 0; j < 7 ; j++) {
+			if (mlc->di_map[j] == idx) found++;
+		}
+		if (found) continue;
+		if (!memcmp(mlc->di + idx, 
+			    &(mlc->di_scratch), 
+			    sizeof(mlc->di_scratch))) break;
+	}
+	return((idx >= HIL_MLC_DEVMEM) ? -1 : idx);
+}
+
+static int hil_mlc_find_free_di(hil_mlc *mlc) {
+	int idx;
+	/* TODO: Pick all-zero slots first, failing that, 
+	 * randomize the slot picked among those eligible. 
+	 */
+	for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
+		int j, found;
+		found = 0;
+		for (j = 0; j < 7 ; j++) {
+			if (mlc->di_map[j] == idx) found++;
+		}
+		if (!found) break;
+	}
+	return(idx); /* Note: It is guaranteed at least one above will match */
+}
+
+static inline void hil_mlc_clean_serio_map(hil_mlc *mlc) {
+	int idx;
+	for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
+		int j, found;
+		found = 0;
+		for (j = 0; j < 7 ; j++) {
+			if (mlc->di_map[j] == idx) found++;
+		}
+		if (!found) mlc->serio_map[idx].di_revmap = -1;
+	}
+}
+
+static void hil_mlc_send_polls(hil_mlc *mlc) {
+	int did, i, cnt;
+	struct serio *serio;
+	struct serio_driver *drv;
+
+	i = cnt = 0;
+	did = (mlc->ipacket[0] & HIL_PKT_ADDR_MASK) >> 8;
+	serio = did ? mlc->serio[mlc->di_map[did - 1]] : NULL;
+	drv = (serio != NULL) ? serio->drv : NULL;
+
+	while (mlc->icount < 15 - i) {
+		hil_packet p;
+		p = mlc->ipacket[i];
+		if (did != (p & HIL_PKT_ADDR_MASK) >> 8) {
+			if (drv == NULL || drv->interrupt == NULL) goto skip;
+
+			drv->interrupt(serio, 0, 0, NULL);
+			drv->interrupt(serio, HIL_ERR_INT >> 16, 0, NULL);
+			drv->interrupt(serio, HIL_PKT_CMD >> 8,  0, NULL);
+			drv->interrupt(serio, HIL_CMD_POL + cnt, 0, NULL);
+		skip:
+			did = (p & HIL_PKT_ADDR_MASK) >> 8;
+			serio = did ? mlc->serio[mlc->di_map[did-1]] : NULL;
+			drv = (serio != NULL) ? serio->drv : NULL;
+			cnt = 0;
+		}
+		cnt++; i++;
+		if (drv == NULL || drv->interrupt == NULL) continue;
+		drv->interrupt(serio, (p >> 24), 0, NULL);
+		drv->interrupt(serio, (p >> 16) & 0xff, 0, NULL);
+		drv->interrupt(serio, (p >> 8) & ~HIL_PKT_ADDR_MASK, 0, NULL);
+		drv->interrupt(serio, p & 0xff, 0, NULL);
+	}
+}
+
+/*************************** State engine *********************************/
+
+#define HILSEN_SCHED	0x000100	/* Schedule the tasklet		*/
+#define HILSEN_BREAK	0x000200	/* Wait until next pass		*/
+#define HILSEN_UP	0x000400	/* relative node#, decrement	*/
+#define HILSEN_DOWN	0x000800	/* relative node#, increment	*/
+#define HILSEN_FOLLOW	0x001000	/* use retval as next node#	*/
+
+#define HILSEN_MASK	0x0000ff
+#define HILSEN_START	0
+#define HILSEN_RESTART	1
+#define HILSEN_DHR	9
+#define HILSEN_DHR2	10
+#define HILSEN_IFC	14
+#define HILSEN_HEAL0	16
+#define HILSEN_HEAL	18
+#define HILSEN_ACF      21
+#define HILSEN_ACF2	22
+#define HILSEN_DISC0	25
+#define HILSEN_DISC	27
+#define HILSEN_MATCH	40
+#define HILSEN_OPERATE	41
+#define HILSEN_PROBE	44
+#define HILSEN_DSR	52
+#define HILSEN_REPOLL	55
+#define HILSEN_IFCACF	58
+#define HILSEN_END	60
+
+#define HILSEN_NEXT	(HILSEN_DOWN | 1)
+#define HILSEN_SAME	(HILSEN_DOWN | 0)
+#define HILSEN_LAST	(HILSEN_UP | 1)
+
+#define HILSEN_DOZE	(HILSEN_SAME | HILSEN_SCHED | HILSEN_BREAK)
+#define HILSEN_SLEEP	(HILSEN_SAME | HILSEN_BREAK)
+
+static int hilse_match(hil_mlc *mlc, int unused) {
+	int rc;
+	rc = hil_mlc_match_di_scratch(mlc);
+	if (rc == -1) {
+		rc = hil_mlc_find_free_di(mlc);
+		if (rc == -1) goto err;
+#ifdef HIL_MLC_DEBUG
+		printk(KERN_DEBUG PREFIX "new in slot %i\n", rc);
+#endif
+		hil_mlc_copy_di_scratch(mlc, rc);
+		mlc->di_map[mlc->ddi] = rc;
+		mlc->serio_map[rc].di_revmap = mlc->ddi;
+		hil_mlc_clean_serio_map(mlc);
+		serio_rescan(mlc->serio[rc]);
+		return -1;
+	}
+	mlc->di_map[mlc->ddi] = rc;
+#ifdef HIL_MLC_DEBUG
+	printk(KERN_DEBUG PREFIX "same in slot %i\n", rc);
+#endif
+	mlc->serio_map[rc].di_revmap = mlc->ddi;
+	hil_mlc_clean_serio_map(mlc);
+	return 0;
+ err:
+	printk(KERN_ERR PREFIX "Residual device slots exhausted, close some serios!\n");
+	return 1;
+}
+
+/* An LCV used to prevent runaway loops, forces 5 second sleep when reset. */
+static int hilse_init_lcv(hil_mlc *mlc, int unused) {
+	struct timeval tv;
+
+	do_gettimeofday(&tv);
+
+	if(mlc->lcv == 0) goto restart;  /* First init, no need to dally */
+	if(tv.tv_sec - mlc->lcv_tv.tv_sec < 5) return -1;
+ restart:
+	mlc->lcv_tv = tv;
+	mlc->lcv = 0;
+	return 0;
+}
+
+static int hilse_inc_lcv(hil_mlc *mlc, int lim) {
+	if (mlc->lcv++ >= lim) return -1;
+	return 0;
+}
+
+#if 0
+static int hilse_set_lcv(hil_mlc *mlc, int val) {
+	mlc->lcv = val;
+	return 0;
+}
+#endif
+
+/* Management of the discovered device index (zero based, -1 means no devs) */
+static int hilse_set_ddi(hil_mlc *mlc, int val) {
+	mlc->ddi = val;
+	hil_mlc_clear_di_map(mlc, val + 1);
+	return 0;
+}
+
+static int hilse_dec_ddi(hil_mlc *mlc, int unused) {
+	mlc->ddi--;
+	if (mlc->ddi <= -1) { 
+		mlc->ddi = -1;
+		hil_mlc_clear_di_map(mlc, 0);
+		return -1;
+	}
+	hil_mlc_clear_di_map(mlc, mlc->ddi + 1);
+	return 0;
+}
+
+static int hilse_inc_ddi(hil_mlc *mlc, int unused) {
+	if (mlc->ddi >= 6) {
+		BUG();
+		return -1;
+	}
+	mlc->ddi++;
+	return 0;
+}
+
+static int hilse_take_idd(hil_mlc *mlc, int unused) {
+	int i;
+
+	/* Help the state engine: 
+	 * Is this a real IDD response or just an echo? 
+	 *
+	 * Real IDD response does not start with a command. 
+	 */
+	if (mlc->ipacket[0] & HIL_PKT_CMD) goto bail;
+	/* Should have the command echoed further down. */
+	for (i = 1; i < 16; i++) {
+		if (((mlc->ipacket[i] & HIL_PKT_ADDR_MASK) == 
+		     (mlc->ipacket[0] & HIL_PKT_ADDR_MASK)) &&
+		    (mlc->ipacket[i] & HIL_PKT_CMD) && 
+		    ((mlc->ipacket[i] & HIL_PKT_DATA_MASK) == HIL_CMD_IDD))
+			break;
+	}
+	if (i > 15) goto bail;
+	/* And the rest of the packets should still be clear. */
+	while (++i < 16) {
+		if (mlc->ipacket[i]) break;
+	}
+	if (i < 16) goto bail;
+	for (i = 0; i < 16; i++) {
+		mlc->di_scratch.idd[i] = 
+			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
+	}
+	/* Next step is to see if RSC supported */
+	if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_RSC) 
+		return HILSEN_NEXT;
+	if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) 
+		return HILSEN_DOWN | 4;
+	return 0;
+ bail:
+	mlc->ddi--;
+	return -1; /* This should send us off to ACF */
+}
+
+static int hilse_take_rsc(hil_mlc *mlc, int unused) {
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		mlc->di_scratch.rsc[i] = 
+			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
+	}
+	/* Next step is to see if EXD supported (IDD has already been read) */
+	if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) 
+		return HILSEN_NEXT;
+	return 0;
+}
+
+static int hilse_take_exd(hil_mlc *mlc, int unused) {
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		mlc->di_scratch.exd[i] = 
+			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
+	}
+	/* Next step is to see if RNM supported. */
+	if (mlc->di_scratch.exd[0] & HIL_EXD_HEADER_RNM) 
+		return HILSEN_NEXT;
+	return 0;
+}
+
+static int hilse_take_rnm(hil_mlc *mlc, int unused) {
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		mlc->di_scratch.rnm[i] = 
+			mlc->ipacket[i] & HIL_PKT_DATA_MASK;
+	}
+	do {
+	  char nam[17];
+	  snprintf(nam, 16, "%s", mlc->di_scratch.rnm);
+	  nam[16] = '\0';
+	  printk(KERN_INFO PREFIX "Device name gotten: %s\n", nam);
+	} while (0);
+	return 0;
+}
+
+static int hilse_operate(hil_mlc *mlc, int repoll) { 
+
+	if (mlc->opercnt == 0) hil_mlcs_probe = 0;
+	mlc->opercnt = 1;
+
+	hil_mlc_send_polls(mlc);
+
+	if (!hil_mlcs_probe) return 0;
+	hil_mlcs_probe = 0;
+	mlc->opercnt = 0;
+	return 1;
+}
+
+#define FUNC(funct, funct_arg, zero_rc, neg_rc, pos_rc) \
+{ HILSE_FUNC,		{ func: &funct }, funct_arg, zero_rc, neg_rc, pos_rc },
+#define OUT(pack) \
+{ HILSE_OUT,		{ packet: pack }, 0, HILSEN_NEXT, HILSEN_DOZE, 0 },
+#define CTS \
+{ HILSE_CTS,		{ packet: 0    }, 0, HILSEN_NEXT | HILSEN_SCHED | HILSEN_BREAK, HILSEN_DOZE, 0 },
+#define EXPECT(comp, to, got, got_wrong, timed_out) \
+{ HILSE_EXPECT,		{ packet: comp }, to, got, got_wrong, timed_out },
+#define EXPECT_LAST(comp, to, got, got_wrong, timed_out) \
+{ HILSE_EXPECT_LAST,	{ packet: comp }, to, got, got_wrong, timed_out },
+#define EXPECT_DISC(comp, to, got, got_wrong, timed_out) \
+{ HILSE_EXPECT_DISC,	{ packet: comp }, to, got, got_wrong, timed_out },
+#define IN(to, got, got_error, timed_out) \
+{ HILSE_IN,		{ packet: 0    }, to, got, got_error, timed_out },
+#define OUT_DISC(pack) \
+{ HILSE_OUT_DISC,	{ packet: pack }, 0, 0, 0, 0 },
+#define OUT_LAST(pack) \
+{ HILSE_OUT_LAST,	{ packet: pack }, 0, 0, 0, 0 },
+
+struct hilse_node hil_mlc_se[HILSEN_END] = {
+
+	/* 0  HILSEN_START */
+	FUNC(hilse_init_lcv, 0,	HILSEN_NEXT,	HILSEN_SLEEP,	0)
+
+	/* 1  HILSEN_RESTART */
+	FUNC(hilse_inc_lcv, 10,	HILSEN_NEXT,	HILSEN_START,  0)
+	OUT(HIL_CTRL_ONLY)			/* Disable APE */
+	CTS
+
+#define TEST_PACKET(x) \
+(HIL_PKT_CMD | (x << HIL_PKT_ADDR_SHIFT) | x << 4 | x)
+
+	OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0x5))
+	EXPECT(HIL_ERR_INT | TEST_PACKET(0x5),
+	       2000,		HILSEN_NEXT,	HILSEN_RESTART,	HILSEN_RESTART)
+	OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0xa))
+	EXPECT(HIL_ERR_INT | TEST_PACKET(0xa),
+	       2000,		HILSEN_NEXT,	HILSEN_RESTART,	HILSEN_RESTART)
+	OUT(HIL_CTRL_ONLY | 0)			/* Disable test mode */
+	
+	/* 9  HILSEN_DHR */
+	FUNC(hilse_init_lcv, 0,	HILSEN_NEXT,	HILSEN_SLEEP,	0)
+
+	/* 10 HILSEN_DHR2 */
+	FUNC(hilse_inc_lcv, 10,	HILSEN_NEXT,	HILSEN_START,	0)
+	FUNC(hilse_set_ddi, -1,	HILSEN_NEXT,	0,		0)
+	OUT(HIL_PKT_CMD | HIL_CMD_DHR)
+	IN(300000,		HILSEN_DHR2,	HILSEN_DHR2,	HILSEN_NEXT)
+
+	/* 14 HILSEN_IFC */
+  	OUT(HIL_PKT_CMD | HIL_CMD_IFC)
+	EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT,
+	       20000,		HILSEN_DISC,	HILSEN_DHR2,	HILSEN_NEXT )
+
+	/* If devices are there, they weren't in PUP or other loopback mode.
+	 * We're more concerned at this point with restoring operation
+	 * to devices than discovering new ones, so we try to salvage
+	 * the loop configuration by closing off the loop.
+	 */
+
+	/* 16 HILSEN_HEAL0 */
+	FUNC(hilse_dec_ddi, 0,	HILSEN_NEXT,	HILSEN_ACF,	0)
+	FUNC(hilse_inc_ddi, 0,	HILSEN_NEXT,	0,		0)
+
+	/* 18 HILSEN_HEAL */
+	OUT_LAST(HIL_CMD_ELB)
+	EXPECT_LAST(HIL_CMD_ELB | HIL_ERR_INT, 
+		    20000,	HILSEN_REPOLL,	HILSEN_DSR,	HILSEN_NEXT)
+	FUNC(hilse_dec_ddi, 0,	HILSEN_HEAL,	HILSEN_NEXT,	0)
+
+	/* 21 HILSEN_ACF */
+	FUNC(hilse_init_lcv, 0,	HILSEN_NEXT,	HILSEN_DOZE,	0)
+
+	/* 22 HILSEN_ACF2 */
+	FUNC(hilse_inc_lcv, 10,	HILSEN_NEXT,	HILSEN_START,	0)
+	OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1)
+	IN(20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_NEXT)
+
+	/* 25 HILSEN_DISC0 */
+	OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB)
+	EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_ELB | HIL_ERR_INT,
+	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
+
+	/* Only enter here if response just received */
+	/* 27 HILSEN_DISC */
+	OUT_DISC(HIL_PKT_CMD | HIL_CMD_IDD)
+	EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_IDD | HIL_ERR_INT,
+	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_START)
+	FUNC(hilse_inc_ddi,  0,	HILSEN_NEXT,	HILSEN_START,	0)
+	FUNC(hilse_take_idd, 0,	HILSEN_MATCH,	HILSEN_IFCACF,	HILSEN_FOLLOW)
+	OUT_LAST(HIL_PKT_CMD | HIL_CMD_RSC)
+	EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RSC | HIL_ERR_INT,
+	       30000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
+	FUNC(hilse_take_rsc, 0,	HILSEN_MATCH,	0,		HILSEN_FOLLOW)
+	OUT_LAST(HIL_PKT_CMD | HIL_CMD_EXD)
+	EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_EXD | HIL_ERR_INT,
+	       30000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
+	FUNC(hilse_take_exd, 0,	HILSEN_MATCH,	0,		HILSEN_FOLLOW)
+	OUT_LAST(HIL_PKT_CMD | HIL_CMD_RNM)
+	EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RNM | HIL_ERR_INT,
+	       30000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_DSR)
+	FUNC(hilse_take_rnm, 0, HILSEN_MATCH,	0,		0)
+
+	/* 40 HILSEN_MATCH */
+	FUNC(hilse_match, 0,	HILSEN_NEXT,	HILSEN_NEXT,	/* TODO */ 0)
+
+	/* 41 HILSEN_OPERATE */
+	OUT(HIL_PKT_CMD | HIL_CMD_POL)
+	EXPECT(HIL_PKT_CMD | HIL_CMD_POL | HIL_ERR_INT,
+	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_NEXT)
+	FUNC(hilse_operate, 0,	HILSEN_OPERATE,	HILSEN_IFC,	HILSEN_NEXT)
+
+	/* 44 HILSEN_PROBE */
+	OUT_LAST(HIL_PKT_CMD | HIL_CMD_EPT)
+	IN(10000, 		HILSEN_DISC,	HILSEN_DSR,	HILSEN_NEXT)
+	OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB)
+	IN(10000,		HILSEN_DISC,	HILSEN_DSR,	HILSEN_NEXT)
+	OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1)
+	IN(10000,		HILSEN_DISC0,	HILSEN_DSR,	HILSEN_NEXT)
+	OUT_LAST(HIL_PKT_CMD | HIL_CMD_ELB)
+	IN(10000,		HILSEN_OPERATE,	HILSEN_DSR,	HILSEN_DSR)
+
+	/* 52 HILSEN_DSR */
+	FUNC(hilse_set_ddi, -1,	HILSEN_NEXT,	0,		0)
+	OUT(HIL_PKT_CMD | HIL_CMD_DSR)
+	IN(20000, 		HILSEN_DHR,	HILSEN_DHR,	HILSEN_IFC)
+
+	/* 55 HILSEN_REPOLL */
+	OUT(HIL_PKT_CMD | HIL_CMD_RPL)
+	EXPECT(HIL_PKT_CMD | HIL_CMD_RPL | HIL_ERR_INT,
+	       20000,		HILSEN_NEXT,	HILSEN_DSR,	HILSEN_NEXT)
+	FUNC(hilse_operate, 1,	HILSEN_OPERATE,	HILSEN_IFC,	HILSEN_PROBE)
+
+	/* 58 HILSEN_IFCACF */
+  	OUT(HIL_PKT_CMD | HIL_CMD_IFC)
+	EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT,
+	       20000,		HILSEN_ACF2,	HILSEN_DHR2,	HILSEN_HEAL)
+
+	/* 60 HILSEN_END */
+};
+
+static inline void hilse_setup_input(hil_mlc *mlc, struct hilse_node *node) {
+
+	switch (node->act) {
+	case HILSE_EXPECT_DISC:
+		mlc->imatch = node->object.packet;
+		mlc->imatch |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT);
+		break;
+	case HILSE_EXPECT_LAST:
+		mlc->imatch = node->object.packet;
+		mlc->imatch |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT);
+		break;
+	case HILSE_EXPECT:
+		mlc->imatch = node->object.packet;
+		break;
+	case HILSE_IN:
+		mlc->imatch = 0;
+		break;
+	default:
+		BUG();
+	}
+	mlc->istarted = 1;
+	mlc->intimeout = node->arg;
+	do_gettimeofday(&(mlc->instart));
+	mlc->icount = 15;
+	memset(mlc->ipacket, 0, 16 * sizeof(hil_packet));
+	if (down_trylock(&(mlc->isem))) BUG();
+
+	return;
+}
+
+#ifdef HIL_MLC_DEBUG
+static int doze = 0;
+static int seidx; /* For debug */
+static int kick = 1;
+#endif
+
+static int hilse_donode (hil_mlc *mlc) {
+	struct hilse_node *node;
+	int nextidx = 0;
+	int sched_long = 0;
+	unsigned long flags;
+
+#ifdef HIL_MLC_DEBUG
+	if (mlc->seidx && (mlc->seidx != seidx)  && mlc->seidx != 41 && mlc->seidx != 42 && mlc->seidx != 43) {
+	  printk(KERN_DEBUG PREFIX "z%i \n%s {%i}", doze, kick ? "K" : "", mlc->seidx);
+		doze = 0;
+	}
+	kick = 0;
+
+	seidx = mlc->seidx;
+#endif
+	node = hil_mlc_se + mlc->seidx;
+
+	switch (node->act) {
+		int rc;
+		hil_packet pack;
+
+	case HILSE_FUNC:
+		if (node->object.func == NULL) break;
+		rc = node->object.func(mlc, node->arg);
+		nextidx = (rc > 0) ? node->ugly : 
+			((rc < 0) ? node->bad : node->good);
+		if (nextidx == HILSEN_FOLLOW) nextidx = rc;
+		break;
+	case HILSE_EXPECT_LAST:
+	case HILSE_EXPECT_DISC:
+	case HILSE_EXPECT:
+	case HILSE_IN:
+		/* Already set up from previous HILSE_OUT_* */
+		write_lock_irqsave(&(mlc->lock), flags);
+		rc = mlc->in(mlc, node->arg);
+		if (rc == 2)  {
+			nextidx = HILSEN_DOZE;
+			sched_long = 1;
+			write_unlock_irqrestore(&(mlc->lock), flags);
+			break;
+		}
+		if (rc == 1)		nextidx = node->ugly;
+		else if (rc == 0)	nextidx = node->good;
+		else			nextidx = node->bad;
+		mlc->istarted = 0;
+		write_unlock_irqrestore(&(mlc->lock), flags);
+		break;
+	case HILSE_OUT_LAST:
+		write_lock_irqsave(&(mlc->lock), flags);
+		pack = node->object.packet;
+		pack |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT);
+		goto out;
+	case HILSE_OUT_DISC:
+		write_lock_irqsave(&(mlc->lock), flags);
+		pack = node->object.packet;
+		pack |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT);
+		goto out;
+	case HILSE_OUT:
+		write_lock_irqsave(&(mlc->lock), flags);
+		pack = node->object.packet;
+	out:
+		if (mlc->istarted) goto out2;
+		/* Prepare to receive input */
+		if ((node + 1)->act & HILSE_IN)
+			hilse_setup_input(mlc, node + 1);
+
+	out2:
+		write_unlock_irqrestore(&(mlc->lock), flags);
+
+		if (down_trylock(&mlc->osem)) {
+			nextidx = HILSEN_DOZE;
+			break;
+		}
+		up(&mlc->osem);
+
+		write_lock_irqsave(&(mlc->lock), flags);
+		if (!(mlc->ostarted)) {
+			mlc->ostarted = 1;
+			mlc->opacket = pack;
+			mlc->out(mlc);
+			nextidx = HILSEN_DOZE;
+			write_unlock_irqrestore(&(mlc->lock), flags);
+			break;
+		}
+		mlc->ostarted = 0;
+		do_gettimeofday(&(mlc->instart));
+		write_unlock_irqrestore(&(mlc->lock), flags);
+		nextidx = HILSEN_NEXT;
+		break;
+	case HILSE_CTS:
+		nextidx = mlc->cts(mlc) ? node->bad : node->good;
+		break;
+	default:
+		BUG();
+		nextidx = 0;
+		break;
+	}
+
+#ifdef HIL_MLC_DEBUG
+	if (nextidx == HILSEN_DOZE) doze++;
+#endif
+
+	while (nextidx & HILSEN_SCHED) {
+		struct timeval tv;
+
+		if (!sched_long) goto sched;
+
+		do_gettimeofday(&tv);
+		tv.tv_usec += 1000000 * (tv.tv_sec - mlc->instart.tv_sec);
+		tv.tv_usec -= mlc->instart.tv_usec;
+		if (tv.tv_usec >= mlc->intimeout) goto sched;
+		tv.tv_usec = (mlc->intimeout - tv.tv_usec) * HZ / 1000000;
+		if (!tv.tv_usec) goto sched;
+		mod_timer(&hil_mlcs_kicker, jiffies + tv.tv_usec);
+		break;
+	sched:
+		tasklet_schedule(&hil_mlcs_tasklet);
+		break;
+	} 
+	if (nextidx & HILSEN_DOWN) mlc->seidx += nextidx & HILSEN_MASK;
+	else if (nextidx & HILSEN_UP) mlc->seidx -= nextidx & HILSEN_MASK;
+	else mlc->seidx = nextidx & HILSEN_MASK;
+
+	if (nextidx & HILSEN_BREAK)	return 1;
+	return 0;
+}
+
+/******************** tasklet context functions **************************/
+static void hil_mlcs_process(unsigned long unused) {
+	struct list_head *tmp;
+
+	read_lock(&hil_mlcs_lock);
+	list_for_each(tmp, &hil_mlcs) {
+		struct hil_mlc *mlc = list_entry(tmp, hil_mlc, list);
+		while (hilse_donode(mlc) == 0) {
+#ifdef HIL_MLC_DEBUG
+		  if (mlc->seidx != 41 && 
+		      mlc->seidx != 42 && 
+		      mlc->seidx != 43) 
+		    printk(KERN_DEBUG PREFIX " + ");
+#endif
+		};
+	}
+	read_unlock(&hil_mlcs_lock);
+}
+
+/************************* Keepalive timer task *********************/
+
+void hil_mlcs_timer (unsigned long data) {
+	hil_mlcs_probe = 1;
+	tasklet_schedule(&hil_mlcs_tasklet);
+	/* Re-insert the periodic task. */
+	if (!timer_pending(&hil_mlcs_kicker))
+		mod_timer(&hil_mlcs_kicker, jiffies + HZ);
+}
+
+/******************** user/kernel context functions **********************/
+
+static int hil_mlc_serio_write(struct serio *serio, unsigned char c) {
+	struct hil_mlc_serio_map *map;
+	struct hil_mlc *mlc;
+	struct serio_driver *drv;
+	uint8_t *idx, *last;
+
+	map = serio->port_data;
+	if (map == NULL) {
+		BUG();
+		return -EIO;
+	}
+	mlc = map->mlc;
+	if (mlc == NULL) {
+		BUG();
+		return -EIO;
+	}
+	mlc->serio_opacket[map->didx] |= 
+		((hil_packet)c) << (8 * (3 - mlc->serio_oidx[map->didx]));
+
+	if (mlc->serio_oidx[map->didx] >= 3) {
+		/* for now only commands */
+		if (!(mlc->serio_opacket[map->didx] & HIL_PKT_CMD)) 
+			return -EIO;
+		switch (mlc->serio_opacket[map->didx] & HIL_PKT_DATA_MASK) {
+		case HIL_CMD_IDD:
+			idx = mlc->di[map->didx].idd;
+			goto emu;
+		case HIL_CMD_RSC:
+			idx = mlc->di[map->didx].rsc;
+			goto emu;
+		case HIL_CMD_EXD:
+			idx = mlc->di[map->didx].exd;
+			goto emu;
+		case HIL_CMD_RNM:
+			idx = mlc->di[map->didx].rnm;
+			goto emu;
+		default:
+			break;
+		}
+		mlc->serio_oidx[map->didx] = 0;
+		mlc->serio_opacket[map->didx] = 0;
+	}
+
+	mlc->serio_oidx[map->didx]++;
+	return -EIO;
+ emu:
+	drv = serio->drv;
+	if (drv == NULL) {
+		BUG();
+		return -EIO;
+	}
+	last = idx + 15;
+	while ((last != idx) && (*last == 0)) last--;
+
+	while (idx != last) {
+		drv->interrupt(serio, 0, 0, NULL);
+		drv->interrupt(serio, HIL_ERR_INT >> 16, 0, NULL);
+		drv->interrupt(serio, 0, 0, NULL);
+		drv->interrupt(serio, *idx, 0, NULL);
+		idx++;
+	}
+	drv->interrupt(serio, 0, 0, NULL);
+	drv->interrupt(serio, HIL_ERR_INT >> 16, 0, NULL);
+	drv->interrupt(serio, HIL_PKT_CMD >> 8, 0, NULL);
+	drv->interrupt(serio, *idx, 0, NULL);
+	
+	mlc->serio_oidx[map->didx] = 0;
+	mlc->serio_opacket[map->didx] = 0;
+
+	return 0;
+}
+
+static int hil_mlc_serio_open(struct serio *serio) {
+	struct hil_mlc_serio_map *map;
+	struct hil_mlc *mlc;
+
+	if (serio->private != NULL) return -EBUSY;
+
+	map = serio->port_data;
+	if (map == NULL) {
+		BUG();
+		return -ENODEV;
+	}
+	mlc = map->mlc;
+	if (mlc == NULL) {
+		BUG();
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void hil_mlc_serio_close(struct serio *serio) {
+	struct hil_mlc_serio_map *map;
+	struct hil_mlc *mlc;
+
+	map = serio->port_data;
+	if (map == NULL) {
+		BUG();
+		return;
+	}
+	mlc = map->mlc;
+	if (mlc == NULL) {
+		BUG();
+		return;
+	}
+
+	serio->private = NULL;
+	serio->drv = NULL;
+	/* TODO wake up interruptable */
+}
+
+int hil_mlc_register(hil_mlc *mlc) {
+	int i;
+        unsigned long flags;
+
+	if (mlc == NULL) {
+		return -EINVAL;
+	}
+
+	mlc->istarted = 0;
+        mlc->ostarted = 0;
+
+        rwlock_init(&mlc->lock);
+        init_MUTEX(&(mlc->osem));
+
+        init_MUTEX(&(mlc->isem));
+        mlc->icount = -1;
+        mlc->imatch = 0;
+
+	mlc->opercnt = 0;
+
+        init_MUTEX_LOCKED(&(mlc->csem));
+
+	hil_mlc_clear_di_scratch(mlc);
+	hil_mlc_clear_di_map(mlc, 0);
+	for (i = 0; i < HIL_MLC_DEVMEM; i++) {
+		struct serio *mlc_serio;
+		hil_mlc_copy_di_scratch(mlc, i);
+		mlc_serio = kmalloc(sizeof(*mlc_serio), GFP_KERNEL);
+		mlc->serio[i] = mlc_serio;
+		memset(mlc_serio, 0, sizeof(*mlc_serio));
+		mlc_serio->type			= SERIO_HIL | SERIO_HIL_MLC;
+		mlc_serio->write		= hil_mlc_serio_write;
+		mlc_serio->open			= hil_mlc_serio_open;
+		mlc_serio->close		= hil_mlc_serio_close;
+		mlc_serio->port_data		= &(mlc->serio_map[i]);
+		mlc->serio_map[i].mlc		= mlc;
+		mlc->serio_map[i].didx		= i;
+		mlc->serio_map[i].di_revmap	= -1;
+		mlc->serio_opacket[i]		= 0;
+		mlc->serio_oidx[i]		= 0;
+		serio_register_port(mlc_serio);
+	}
+
+	mlc->tasklet = &hil_mlcs_tasklet;
+
+	write_lock_irqsave(&hil_mlcs_lock, flags);
+	list_add_tail(&mlc->list, &hil_mlcs);
+	mlc->seidx = HILSEN_START;
+	write_unlock_irqrestore(&hil_mlcs_lock, flags);
+
+	tasklet_schedule(&hil_mlcs_tasklet);
+	return 0;
+}
+
+int hil_mlc_unregister(hil_mlc *mlc) {
+	struct list_head *tmp;
+        unsigned long flags;
+	int i;
+
+	if (mlc == NULL)
+		return -EINVAL;
+
+	write_lock_irqsave(&hil_mlcs_lock, flags);
+	list_for_each(tmp, &hil_mlcs) {
+		if (list_entry(tmp, hil_mlc, list) == mlc)
+			goto found;
+	}
+
+	/* not found in list */
+	write_unlock_irqrestore(&hil_mlcs_lock, flags);
+	tasklet_schedule(&hil_mlcs_tasklet);
+	return -ENODEV;
+
+ found:
+	list_del(tmp);
+        write_unlock_irqrestore(&hil_mlcs_lock, flags);
+
+	for (i = 0; i < HIL_MLC_DEVMEM; i++) {
+		serio_unregister_port(mlc->serio[i]);
+		mlc->serio[i] = NULL;
+	}
+
+	tasklet_schedule(&hil_mlcs_tasklet);
+	return 0;
+}
+
+/**************************** Module interface *************************/
+
+static int __init hil_mlc_init(void)
+{
+	init_timer(&hil_mlcs_kicker);
+	hil_mlcs_kicker.expires = jiffies + HZ;
+	hil_mlcs_kicker.function = &hil_mlcs_timer;
+	add_timer(&hil_mlcs_kicker);
+
+	tasklet_enable(&hil_mlcs_tasklet);
+
+	return 0;
+}
+                
+static void __exit hil_mlc_exit(void)
+{
+	del_timer(&hil_mlcs_kicker);
+
+	tasklet_disable(&hil_mlcs_tasklet);
+	tasklet_kill(&hil_mlcs_tasklet);
+}
+                        
+module_init(hil_mlc_init);
+module_exit(hil_mlc_exit);
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
new file mode 100644
index 0000000..7629452
--- /dev/null
+++ b/drivers/input/serio/hp_sdc.c
@@ -0,0 +1,1054 @@
+/*
+ * HP i8042-based System Device Controller driver.
+ *
+ * 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:
+ * System Device Controller Microprocessor Firmware Theory of Operation
+ *      for Part Number 1820-4784 Revision B.  Dwg No. A-1820-4784-2
+ * Helge Deller's original hilkbd.c port for PA-RISC.
+ *
+ *
+ * Driver theory of operation:
+ *
+ * hp_sdc_put does all writing to the SDC.  ISR can run on a different 
+ * CPU than hp_sdc_put, but only one CPU runs hp_sdc_put at a time 
+ * (it cannot really benefit from SMP anyway.)  A tasket fit this perfectly.
+ *
+ * All data coming back from the SDC is sent via interrupt and can be read 
+ * fully in the ISR, so there are no latency/throughput problems there.  
+ * The problem is with output, due to the slow clock speed of the SDC 
+ * compared to the CPU.  This should not be too horrible most of the time, 
+ * but if used with HIL devices that support the multibyte transfer command, 
+ * keeping outbound throughput flowing at the 6500KBps that the HIL is 
+ * capable of is more than can be done at HZ=100.
+ *
+ * Busy polling for IBF clear wastes CPU cycles and bus cycles.  hp_sdc.ibf 
+ * is set to 0 when the IBF flag in the status register has cleared.  ISR 
+ * may do this, and may also access the parts of queued transactions related 
+ * to reading data back from the SDC, but otherwise will not touch the 
+ * hp_sdc state. Whenever a register is written hp_sdc.ibf is set to 1.
+ *
+ * The i8042 write index and the values in the 4-byte input buffer
+ * starting at 0x70 are kept track of in hp_sdc.wi, and .r7[], respectively,
+ * to minimize the amount of IO needed to the SDC.  However these values 
+ * do not need to be locked since they are only ever accessed by hp_sdc_put.
+ *
+ * A timer task schedules the tasklet once per second just to make
+ * sure it doesn't freeze up and to allow for bad reads to time out.
+ */
+
+#include <linux/hp_sdc.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/slab.h>
+#include <linux/hil.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+/* Machine-specific abstraction */
+
+#if defined(__hppa__)
+# include <asm/parisc-device.h>
+# define sdc_readb(p)		gsc_readb(p)
+# define sdc_writeb(v,p)	gsc_writeb((v),(p))
+#elif defined(__mc68000__)
+# include <asm/uaccess.h>
+# define sdc_readb(p)		in_8(p)
+# define sdc_writeb(v,p)	out_8((p),(v))
+#else
+# error "HIL is not supported on this platform"
+#endif
+
+#define PREFIX "HP SDC: "
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION("HP i8042-based SDC Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+EXPORT_SYMBOL(hp_sdc_request_timer_irq);
+EXPORT_SYMBOL(hp_sdc_request_hil_irq);
+EXPORT_SYMBOL(hp_sdc_request_cooked_irq);
+
+EXPORT_SYMBOL(hp_sdc_release_timer_irq);
+EXPORT_SYMBOL(hp_sdc_release_hil_irq);
+EXPORT_SYMBOL(hp_sdc_release_cooked_irq);
+
+EXPORT_SYMBOL(hp_sdc_enqueue_transaction);
+EXPORT_SYMBOL(hp_sdc_dequeue_transaction);
+
+static hp_i8042_sdc	hp_sdc;	/* All driver state is kept in here. */
+
+/*************** primitives for use in any context *********************/
+static inline uint8_t hp_sdc_status_in8 (void) {
+	uint8_t status;
+	unsigned long flags;
+
+	write_lock_irqsave(&hp_sdc.ibf_lock, flags);
+	status = sdc_readb(hp_sdc.status_io);
+	if (!(status & HP_SDC_STATUS_IBF)) hp_sdc.ibf = 0;
+	write_unlock_irqrestore(&hp_sdc.ibf_lock, flags);
+
+	return status;
+}
+
+static inline uint8_t hp_sdc_data_in8 (void) {
+	return sdc_readb(hp_sdc.data_io); 
+}
+
+static inline void hp_sdc_status_out8 (uint8_t val) {
+	unsigned long flags;
+
+	write_lock_irqsave(&hp_sdc.ibf_lock, flags);
+	hp_sdc.ibf = 1;
+	if ((val & 0xf0) == 0xe0) hp_sdc.wi = 0xff;
+	sdc_writeb(val, hp_sdc.status_io);
+	write_unlock_irqrestore(&hp_sdc.ibf_lock, flags);
+}
+
+static inline void hp_sdc_data_out8 (uint8_t val) {
+	unsigned long flags;
+
+	write_lock_irqsave(&hp_sdc.ibf_lock, flags);
+	hp_sdc.ibf = 1;
+	sdc_writeb(val, hp_sdc.data_io);
+	write_unlock_irqrestore(&hp_sdc.ibf_lock, flags);
+}
+
+/*	Care must be taken to only invoke hp_sdc_spin_ibf when 
+ *	absolutely needed, or in rarely invoked subroutines.  
+ *	Not only does it waste CPU cycles, it also wastes bus cycles. 
+ */
+static inline void hp_sdc_spin_ibf(void) {
+	unsigned long flags;
+	rwlock_t *lock;
+
+	lock = &hp_sdc.ibf_lock;
+
+	read_lock_irqsave(lock, flags);
+	if (!hp_sdc.ibf) {
+		read_unlock_irqrestore(lock, flags);
+		return;
+	}
+	read_unlock(lock);
+	write_lock(lock);
+	while (sdc_readb(hp_sdc.status_io) & HP_SDC_STATUS_IBF) {};
+	hp_sdc.ibf = 0;
+	write_unlock_irqrestore(lock, flags);
+}
+
+
+/************************ Interrupt context functions ************************/
+static void hp_sdc_take (int irq, void *dev_id, uint8_t status, uint8_t data) {
+	hp_sdc_transaction *curr;
+
+	read_lock(&hp_sdc.rtq_lock);
+	if (hp_sdc.rcurr < 0) {
+	  	read_unlock(&hp_sdc.rtq_lock);
+		return;
+	}
+	curr = hp_sdc.tq[hp_sdc.rcurr];
+	read_unlock(&hp_sdc.rtq_lock);
+
+	curr->seq[curr->idx++] = status;
+	curr->seq[curr->idx++] = data;
+	hp_sdc.rqty -= 2;
+	do_gettimeofday(&hp_sdc.rtv);
+
+	if (hp_sdc.rqty <= 0) {
+		/* All data has been gathered. */
+		if(curr->seq[curr->actidx] & HP_SDC_ACT_SEMAPHORE) {
+			if (curr->act.semaphore) up(curr->act.semaphore);
+		}
+		if(curr->seq[curr->actidx] & HP_SDC_ACT_CALLBACK) {
+			if (curr->act.irqhook)
+				curr->act.irqhook(irq, dev_id, status, data);
+		}
+		curr->actidx = curr->idx;
+		curr->idx++;
+		/* Return control of this transaction */
+		write_lock(&hp_sdc.rtq_lock);
+		hp_sdc.rcurr = -1; 
+		hp_sdc.rqty = 0;
+		write_unlock(&hp_sdc.rtq_lock);
+		tasklet_schedule(&hp_sdc.task);
+	}
+}
+
+static irqreturn_t hp_sdc_isr(int irq, void *dev_id, struct pt_regs * regs) {
+	uint8_t status, data;
+
+	status = hp_sdc_status_in8();
+	/* Read data unconditionally to advance i8042. */
+	data =   hp_sdc_data_in8();
+
+	/* For now we are ignoring these until we get the SDC to behave. */
+	if (((status & 0xf1) == 0x51) && data == 0x82) {
+	  return IRQ_HANDLED;
+	}
+
+	switch(status & HP_SDC_STATUS_IRQMASK) {
+	      case 0: /* This case is not documented. */
+		break;
+	      case HP_SDC_STATUS_USERTIMER:
+	      case HP_SDC_STATUS_PERIODIC:
+	      case HP_SDC_STATUS_TIMER:
+		read_lock(&hp_sdc.hook_lock);
+	      	if (hp_sdc.timer != NULL)
+			hp_sdc.timer(irq, dev_id, status, data);
+		read_unlock(&hp_sdc.hook_lock);
+		break;
+	      case HP_SDC_STATUS_REG:
+		hp_sdc_take(irq, dev_id, status, data);
+		break;
+	      case HP_SDC_STATUS_HILCMD:
+	      case HP_SDC_STATUS_HILDATA:
+		read_lock(&hp_sdc.hook_lock);
+		if (hp_sdc.hil != NULL)
+			hp_sdc.hil(irq, dev_id, status, data);
+		read_unlock(&hp_sdc.hook_lock);
+		break;
+	      case HP_SDC_STATUS_PUP:
+		read_lock(&hp_sdc.hook_lock);
+		if (hp_sdc.pup != NULL)
+			hp_sdc.pup(irq, dev_id, status, data);
+		else printk(KERN_INFO PREFIX "HP SDC reports successful PUP.\n");
+		read_unlock(&hp_sdc.hook_lock);
+		break;
+	      default:
+		read_lock(&hp_sdc.hook_lock);
+		if (hp_sdc.cooked != NULL)
+			hp_sdc.cooked(irq, dev_id, status, data);
+		read_unlock(&hp_sdc.hook_lock);
+		break;
+	}
+	return IRQ_HANDLED;
+}
+
+
+static irqreturn_t hp_sdc_nmisr(int irq, void *dev_id, struct pt_regs * regs) {
+	int status;
+	
+	status = hp_sdc_status_in8();
+	printk(KERN_WARNING PREFIX "NMI !\n");
+
+#if 0	
+	if (status & HP_SDC_NMISTATUS_FHS) {
+		read_lock(&hp_sdc.hook_lock);
+	      	if (hp_sdc.timer != NULL)
+			hp_sdc.timer(irq, dev_id, status, 0);
+		read_unlock(&hp_sdc.hook_lock);
+	}
+	else {
+		/* TODO: pass this on to the HIL handler, or do SAK here? */
+		printk(KERN_WARNING PREFIX "HIL NMI\n");
+	}
+#endif
+	return IRQ_HANDLED;
+}
+
+
+/***************** Kernel (tasklet) context functions ****************/
+
+unsigned long hp_sdc_put(void);
+
+static void hp_sdc_tasklet(unsigned long foo) {
+
+	write_lock_irq(&hp_sdc.rtq_lock);
+	if (hp_sdc.rcurr >= 0) {
+		struct timeval tv;
+		do_gettimeofday(&tv);
+		if (tv.tv_sec > hp_sdc.rtv.tv_sec) tv.tv_usec += 1000000;
+		if (tv.tv_usec - hp_sdc.rtv.tv_usec > HP_SDC_MAX_REG_DELAY) {
+			hp_sdc_transaction *curr;
+			uint8_t tmp;
+
+			curr = hp_sdc.tq[hp_sdc.rcurr];
+			/* If this turns out to be a normal failure mode
+			 * we'll need to figure out a way to communicate
+			 * it back to the application. and be less verbose.
+			 */
+			printk(KERN_WARNING PREFIX "read timeout (%ius)!\n",
+			       tv.tv_usec - hp_sdc.rtv.tv_usec);
+			curr->idx += hp_sdc.rqty;
+			hp_sdc.rqty = 0;
+			tmp = curr->seq[curr->actidx];
+			curr->seq[curr->actidx] |= HP_SDC_ACT_DEAD;
+			if(tmp & HP_SDC_ACT_SEMAPHORE) {
+				if (curr->act.semaphore) 
+					up(curr->act.semaphore);
+			}
+			if(tmp & HP_SDC_ACT_CALLBACK) {
+				/* Note this means that irqhooks may be called
+				 * in tasklet/bh context.
+				 */
+				if (curr->act.irqhook) 
+					curr->act.irqhook(0, 0, 0, 0);
+			}
+			curr->actidx = curr->idx;
+			curr->idx++;
+			hp_sdc.rcurr = -1; 
+		}
+	}
+	write_unlock_irq(&hp_sdc.rtq_lock);
+	hp_sdc_put();
+}
+
+unsigned long hp_sdc_put(void) {
+	hp_sdc_transaction *curr;
+	uint8_t act;
+	int idx, curridx;
+
+	int limit = 0;
+
+	write_lock(&hp_sdc.lock);
+
+	/* If i8042 buffers are full, we cannot do anything that
+	   requires output, so we skip to the administrativa. */
+	if (hp_sdc.ibf) {
+		hp_sdc_status_in8();
+		if (hp_sdc.ibf) goto finish;
+	}
+
+ anew:
+	/* See if we are in the middle of a sequence. */
+	if (hp_sdc.wcurr < 0) hp_sdc.wcurr = 0;
+	read_lock_irq(&hp_sdc.rtq_lock);
+	if (hp_sdc.rcurr == hp_sdc.wcurr) hp_sdc.wcurr++;
+	read_unlock_irq(&hp_sdc.rtq_lock);
+	if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0;
+	curridx = hp_sdc.wcurr;
+
+	if (hp_sdc.tq[curridx] != NULL) goto start;
+
+	while (++curridx != hp_sdc.wcurr) {
+		if (curridx >= HP_SDC_QUEUE_LEN) {
+			curridx = -1; /* Wrap to top */
+			continue;
+		}
+		read_lock_irq(&hp_sdc.rtq_lock);
+		if (hp_sdc.rcurr == curridx) {
+			read_unlock_irq(&hp_sdc.rtq_lock);
+			continue;
+		}
+		read_unlock_irq(&hp_sdc.rtq_lock);
+		if (hp_sdc.tq[curridx] != NULL) break; /* Found one. */
+	}
+	if (curridx == hp_sdc.wcurr) { /* There's nothing queued to do. */
+		curridx = -1;
+	}
+	hp_sdc.wcurr = curridx;
+
+ start:
+
+	/* Check to see if the interrupt mask needs to be set. */
+	if (hp_sdc.set_im) {
+		hp_sdc_status_out8(hp_sdc.im | HP_SDC_CMD_SET_IM);
+		hp_sdc.set_im = 0;
+		goto finish;
+	}
+
+	if (hp_sdc.wcurr == -1) goto done;
+
+	curr = hp_sdc.tq[curridx];
+	idx = curr->actidx;
+
+	if (curr->actidx >= curr->endidx) {
+		hp_sdc.tq[curridx] = NULL;
+		/* Interleave outbound data between the transactions. */
+		hp_sdc.wcurr++;
+		if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0;
+		goto finish;	
+	}
+
+	act = curr->seq[idx];
+	idx++;
+
+	if (curr->idx >= curr->endidx) {
+		if (act & HP_SDC_ACT_DEALLOC) kfree(curr);
+		hp_sdc.tq[curridx] = NULL;
+		/* Interleave outbound data between the transactions. */
+		hp_sdc.wcurr++;
+		if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0;
+		goto finish;	
+	}
+
+	while (act & HP_SDC_ACT_PRECMD) {
+		if (curr->idx != idx) {
+			idx++;
+			act &= ~HP_SDC_ACT_PRECMD;
+			break;
+		}
+		hp_sdc_status_out8(curr->seq[idx]);
+		curr->idx++;
+		/* act finished? */
+		if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_PRECMD)
+		  goto actdone;
+		/* skip quantity field if data-out sequence follows. */
+		if (act & HP_SDC_ACT_DATAOUT) curr->idx++;
+		goto finish;
+	}
+	if (act & HP_SDC_ACT_DATAOUT) {
+		int qty;
+
+		qty = curr->seq[idx];
+		idx++;
+		if (curr->idx - idx < qty) {
+			hp_sdc_data_out8(curr->seq[curr->idx]);
+			curr->idx++;
+			/* act finished? */
+			if ((curr->idx - idx >= qty) && 
+			    ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAOUT))
+				goto actdone;
+			goto finish;
+		}
+		idx += qty;
+		act &= ~HP_SDC_ACT_DATAOUT;
+	}
+	else while (act & HP_SDC_ACT_DATAREG) {
+		int mask;
+		uint8_t w7[4];
+
+		mask = curr->seq[idx];
+		if (idx != curr->idx) {
+			idx++;
+			idx += !!(mask & 1);
+			idx += !!(mask & 2);
+			idx += !!(mask & 4);
+			idx += !!(mask & 8);
+			act &= ~HP_SDC_ACT_DATAREG;
+			break;
+		}
+		
+		w7[0] = (mask & 1) ? curr->seq[++idx] : hp_sdc.r7[0];
+		w7[1] = (mask & 2) ? curr->seq[++idx] : hp_sdc.r7[1];
+		w7[2] = (mask & 4) ? curr->seq[++idx] : hp_sdc.r7[2];
+		w7[3] = (mask & 8) ? curr->seq[++idx] : hp_sdc.r7[3];
+		
+		if (hp_sdc.wi > 0x73 || hp_sdc.wi < 0x70 ||
+		        w7[hp_sdc.wi-0x70] == hp_sdc.r7[hp_sdc.wi-0x70]) {
+			int i = 0;
+
+			/* Need to point the write index register */	
+			while ((i < 4) && w7[i] == hp_sdc.r7[i]) i++;
+			if (i < 4) {
+				hp_sdc_status_out8(HP_SDC_CMD_SET_D0 + i);
+				hp_sdc.wi = 0x70 + i;
+				goto finish;
+			}
+			idx++;
+			if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAREG)
+				goto actdone;
+			curr->idx = idx;
+			act &= ~HP_SDC_ACT_DATAREG;
+			break;
+		}
+
+		hp_sdc_data_out8(w7[hp_sdc.wi - 0x70]);
+		hp_sdc.r7[hp_sdc.wi - 0x70] = w7[hp_sdc.wi - 0x70];
+		hp_sdc.wi++; /* write index register autoincrements */
+		{
+			int i = 0;
+
+			while ((i < 4) && w7[i] == hp_sdc.r7[i]) i++;
+			if (i >= 4) {
+				curr->idx = idx + 1;
+				if ((act & HP_SDC_ACT_DURING) == 
+				    HP_SDC_ACT_DATAREG)
+				        goto actdone;
+			}
+		}
+		goto finish;
+	}
+	/* We don't go any further in the command if there is a pending read,
+	   because we don't want interleaved results. */
+	read_lock_irq(&hp_sdc.rtq_lock);
+	if (hp_sdc.rcurr >= 0) {
+		read_unlock_irq(&hp_sdc.rtq_lock);
+		goto finish;
+	}
+	read_unlock_irq(&hp_sdc.rtq_lock);
+
+
+	if (act & HP_SDC_ACT_POSTCMD) {
+	  	uint8_t postcmd;
+
+		/* curr->idx should == idx at this point. */
+		postcmd = curr->seq[idx];
+		curr->idx++;
+		if (act & HP_SDC_ACT_DATAIN) {
+
+			/* Start a new read */
+	  		hp_sdc.rqty = curr->seq[curr->idx];
+			do_gettimeofday(&hp_sdc.rtv);
+			curr->idx++;
+			/* Still need to lock here in case of spurious irq. */
+			write_lock_irq(&hp_sdc.rtq_lock);
+			hp_sdc.rcurr = curridx; 
+			write_unlock_irq(&hp_sdc.rtq_lock);
+			hp_sdc_status_out8(postcmd);
+			goto finish;
+		}
+		hp_sdc_status_out8(postcmd);
+		goto actdone;
+	}
+
+actdone:
+	if (act & HP_SDC_ACT_SEMAPHORE) {
+		up(curr->act.semaphore);
+	}
+	else if (act & HP_SDC_ACT_CALLBACK) {
+		curr->act.irqhook(0,0,0,0);
+	}
+	if (curr->idx >= curr->endidx) { /* This transaction is over. */
+		if (act & HP_SDC_ACT_DEALLOC) kfree(curr);
+		hp_sdc.tq[curridx] = NULL;
+	}
+	else {
+		curr->actidx = idx + 1;
+		curr->idx = idx + 2;
+	}
+	/* Interleave outbound data between the transactions. */
+	hp_sdc.wcurr++;
+	if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0;
+
+ finish:
+	/* If by some quirk IBF has cleared and our ISR has run to 
+	   see that that has happened, do it all again. */
+	if (!hp_sdc.ibf && limit++ < 20) goto anew;
+
+ done:
+	if (hp_sdc.wcurr >= 0) tasklet_schedule(&hp_sdc.task);
+	write_unlock(&hp_sdc.lock);
+	return 0;
+}
+
+/******* Functions called in either user or kernel context ****/
+int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) {
+	unsigned long flags;
+	int i;
+
+	if (this == NULL) {
+		tasklet_schedule(&hp_sdc.task);
+		return -EINVAL;
+	};
+
+	write_lock_irqsave(&hp_sdc.lock, flags);
+
+	/* Can't have same transaction on queue twice */
+	for (i=0; i < HP_SDC_QUEUE_LEN; i++)
+		if (hp_sdc.tq[i] == this) goto fail;
+
+	this->actidx = 0;
+	this->idx = 1;
+
+	/* Search for empty slot */
+	for (i=0; i < HP_SDC_QUEUE_LEN; i++) {
+		if (hp_sdc.tq[i] == NULL) {
+			hp_sdc.tq[i] = this;
+			write_unlock_irqrestore(&hp_sdc.lock, flags);
+			tasklet_schedule(&hp_sdc.task);
+			return 0;
+		}
+	}
+	write_unlock_irqrestore(&hp_sdc.lock, flags);
+	printk(KERN_WARNING PREFIX "No free slot to add transaction.\n");
+	return -EBUSY;
+
+ fail:
+	write_unlock_irqrestore(&hp_sdc.lock,flags);
+	printk(KERN_WARNING PREFIX "Transaction add failed: transaction already queued?\n");
+	return -EINVAL;
+}
+
+int hp_sdc_dequeue_transaction(hp_sdc_transaction *this) {
+	unsigned long flags;
+	int i;
+
+	write_lock_irqsave(&hp_sdc.lock, flags);
+
+	/* TODO: don't remove it if it's not done. */
+
+	for (i=0; i < HP_SDC_QUEUE_LEN; i++)
+		if (hp_sdc.tq[i] == this) hp_sdc.tq[i] = NULL;
+
+	write_unlock_irqrestore(&hp_sdc.lock, flags);
+	return 0;
+}
+
+
+
+/********************** User context functions **************************/
+int hp_sdc_request_timer_irq(hp_sdc_irqhook *callback) {
+
+	if (callback == NULL || hp_sdc.dev == NULL) {
+		return -EINVAL;
+	}
+	write_lock_irq(&hp_sdc.hook_lock);
+	if (hp_sdc.timer != NULL) {
+		write_unlock_irq(&hp_sdc.hook_lock);
+		return -EBUSY;
+	}
+
+	hp_sdc.timer = callback;
+	/* Enable interrupts from the timers */
+	hp_sdc.im &= ~HP_SDC_IM_FH;
+        hp_sdc.im &= ~HP_SDC_IM_PT;
+	hp_sdc.im &= ~HP_SDC_IM_TIMERS;
+	hp_sdc.set_im = 1;
+	write_unlock_irq(&hp_sdc.hook_lock);
+
+	tasklet_schedule(&hp_sdc.task);
+
+	return 0;
+}
+
+int hp_sdc_request_hil_irq(hp_sdc_irqhook *callback) {
+
+	if (callback == NULL || hp_sdc.dev == NULL) {
+		return -EINVAL;
+	}
+	write_lock_irq(&hp_sdc.hook_lock);
+	if (hp_sdc.hil != NULL) {
+		write_unlock_irq(&hp_sdc.hook_lock);
+		return -EBUSY;
+	}
+
+	hp_sdc.hil = callback;
+	hp_sdc.im &= ~(HP_SDC_IM_HIL | HP_SDC_IM_RESET);
+	hp_sdc.set_im = 1;
+	write_unlock_irq(&hp_sdc.hook_lock);
+
+	tasklet_schedule(&hp_sdc.task);
+
+	return 0;
+}
+
+int hp_sdc_request_cooked_irq(hp_sdc_irqhook *callback) {
+
+	if (callback == NULL || hp_sdc.dev == NULL) {
+		return -EINVAL;
+	}
+	write_lock_irq(&hp_sdc.hook_lock);
+	if (hp_sdc.cooked != NULL) {
+		write_unlock_irq(&hp_sdc.hook_lock);
+		return -EBUSY;
+	}
+
+	/* Enable interrupts from the HIL MLC */
+	hp_sdc.cooked = callback;
+	hp_sdc.im &= ~(HP_SDC_IM_HIL | HP_SDC_IM_RESET);
+	hp_sdc.set_im = 1;
+	write_unlock_irq(&hp_sdc.hook_lock);
+
+	tasklet_schedule(&hp_sdc.task);
+
+	return 0;
+}
+
+int hp_sdc_release_timer_irq(hp_sdc_irqhook *callback) {
+
+
+	write_lock_irq(&hp_sdc.hook_lock);
+	if ((callback != hp_sdc.timer) ||
+	    (hp_sdc.timer == NULL)) {
+		write_unlock_irq(&hp_sdc.hook_lock);
+		return -EINVAL;
+	}
+
+	/* Disable interrupts from the timers */
+	hp_sdc.timer = NULL;
+	hp_sdc.im |= HP_SDC_IM_TIMERS;
+	hp_sdc.im |= HP_SDC_IM_FH;
+	hp_sdc.im |= HP_SDC_IM_PT;
+	hp_sdc.set_im = 1;
+	write_unlock_irq(&hp_sdc.hook_lock);
+	tasklet_schedule(&hp_sdc.task);
+
+	return 0;
+}
+
+int hp_sdc_release_hil_irq(hp_sdc_irqhook *callback) {
+
+	write_lock_irq(&hp_sdc.hook_lock);
+	if ((callback != hp_sdc.hil) ||
+	    (hp_sdc.hil == NULL)) {
+		write_unlock_irq(&hp_sdc.hook_lock);
+		return -EINVAL;
+	}
+
+	hp_sdc.hil = NULL;
+	/* Disable interrupts from HIL only if there is no cooked driver. */
+	if(hp_sdc.cooked == NULL) {
+		hp_sdc.im |= (HP_SDC_IM_HIL | HP_SDC_IM_RESET);
+		hp_sdc.set_im = 1;
+	}
+	write_unlock_irq(&hp_sdc.hook_lock);
+	tasklet_schedule(&hp_sdc.task);
+
+	return 0;
+}
+
+int hp_sdc_release_cooked_irq(hp_sdc_irqhook *callback) {
+
+	write_lock_irq(&hp_sdc.hook_lock);
+	if ((callback != hp_sdc.cooked) ||
+	    (hp_sdc.cooked == NULL)) {
+		write_unlock_irq(&hp_sdc.hook_lock);
+		return -EINVAL;
+	}
+
+	hp_sdc.cooked = NULL;
+	/* Disable interrupts from HIL only if there is no raw HIL driver. */
+	if(hp_sdc.hil == NULL) {
+		hp_sdc.im |= (HP_SDC_IM_HIL | HP_SDC_IM_RESET);
+		hp_sdc.set_im = 1;
+	}
+	write_unlock_irq(&hp_sdc.hook_lock);
+	tasklet_schedule(&hp_sdc.task);
+
+	return 0;
+}
+
+/************************* Keepalive timer task *********************/
+
+void hp_sdc_kicker (unsigned long data) {
+	tasklet_schedule(&hp_sdc.task);
+	/* Re-insert the periodic task. */
+	mod_timer(&hp_sdc.kicker, jiffies + HZ);
+}
+
+/************************** Module Initialization ***************************/
+
+#if defined(__hppa__)
+
+static struct parisc_device_id hp_sdc_tbl[] = {
+	{
+		.hw_type =	HPHW_FIO, 
+		.hversion_rev =	HVERSION_REV_ANY_ID,
+		.hversion =	HVERSION_ANY_ID,
+		.sversion =	0x73, 
+	 },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(parisc, hp_sdc_tbl);
+
+static int __init hp_sdc_init_hppa(struct parisc_device *d);
+
+static struct parisc_driver hp_sdc_driver = {
+	.name =		"HP SDC",
+	.id_table =	hp_sdc_tbl,
+	.probe =	hp_sdc_init_hppa,
+};
+
+#endif /* __hppa__ */
+
+static int __init hp_sdc_init(void)
+{
+	int i;
+	char *errstr;
+	hp_sdc_transaction t_sync;
+	uint8_t ts_sync[6];
+	struct semaphore s_sync;
+
+  	rwlock_init(&hp_sdc.lock);
+  	rwlock_init(&hp_sdc.ibf_lock);
+  	rwlock_init(&hp_sdc.rtq_lock);
+  	rwlock_init(&hp_sdc.hook_lock);
+
+	hp_sdc.timer		= NULL;
+	hp_sdc.hil		= NULL;
+	hp_sdc.pup		= NULL;
+	hp_sdc.cooked		= NULL;
+	hp_sdc.im		= HP_SDC_IM_MASK;  /* Mask maskable irqs */
+	hp_sdc.set_im		= 1;
+	hp_sdc.wi		= 0xff;
+	hp_sdc.r7[0]		= 0xff;
+	hp_sdc.r7[1]		= 0xff;
+	hp_sdc.r7[2]		= 0xff;
+	hp_sdc.r7[3]		= 0xff;
+	hp_sdc.ibf		= 1;
+
+	for (i = 0; i < HP_SDC_QUEUE_LEN; i++) hp_sdc.tq[i] = NULL;
+	hp_sdc.wcurr		= -1;
+        hp_sdc.rcurr		= -1;
+	hp_sdc.rqty		= 0;
+
+	hp_sdc.dev_err = -ENODEV;
+
+	errstr = "IO not found for";
+	if (!hp_sdc.base_io) goto err0;
+
+	errstr = "IRQ not found for";
+	if (!hp_sdc.irq) goto err0;
+
+	hp_sdc.dev_err = -EBUSY;
+
+#if defined(__hppa__)
+	errstr = "IO not available for";
+        if (request_region(hp_sdc.data_io, 2, hp_sdc_driver.name)) goto err0;
+#endif	
+
+	errstr = "IRQ not available for";
+        if(request_irq(hp_sdc.irq, &hp_sdc_isr, 0, "HP SDC",
+		       (void *) hp_sdc.base_io)) goto err1;
+
+	errstr = "NMI not available for";
+	if (request_irq(hp_sdc.nmi, &hp_sdc_nmisr, 0, "HP SDC NMI", 
+			(void *) hp_sdc.base_io)) goto err2;
+
+	printk(KERN_INFO PREFIX "HP SDC at 0x%p, IRQ %d (NMI IRQ %d)\n", 
+	       (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi);
+
+	hp_sdc_status_in8();
+	hp_sdc_data_in8();
+
+	tasklet_init(&hp_sdc.task, hp_sdc_tasklet, 0);
+
+	/* Sync the output buffer registers, thus scheduling hp_sdc_tasklet. */
+	t_sync.actidx	= 0;
+	t_sync.idx	= 1;
+	t_sync.endidx	= 6;
+	t_sync.seq	= ts_sync;
+	ts_sync[0]	= HP_SDC_ACT_DATAREG | HP_SDC_ACT_SEMAPHORE;
+	ts_sync[1]	= 0x0f;
+	ts_sync[2] = ts_sync[3]	= ts_sync[4] = ts_sync[5] = 0;
+	t_sync.act.semaphore = &s_sync;
+	init_MUTEX_LOCKED(&s_sync);
+	hp_sdc_enqueue_transaction(&t_sync);
+	down(&s_sync); /* Wait for t_sync to complete */
+
+	/* Create the keepalive task */
+	init_timer(&hp_sdc.kicker);
+	hp_sdc.kicker.expires = jiffies + HZ;
+	hp_sdc.kicker.function = &hp_sdc_kicker;
+	add_timer(&hp_sdc.kicker);
+
+	hp_sdc.dev_err = 0;
+	return 0;
+ err2:
+	free_irq(hp_sdc.irq, NULL);
+ err1:
+	release_region(hp_sdc.data_io, 2);
+ err0:
+	printk(KERN_WARNING PREFIX ": %s SDC IO=0x%p IRQ=0x%x NMI=0x%x\n", 
+		errstr, (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi);
+	hp_sdc.dev = NULL;
+	return hp_sdc.dev_err;
+}
+
+#if defined(__hppa__)
+
+static int __init hp_sdc_init_hppa(struct parisc_device *d)
+{
+	if (!d) return 1;
+	if (hp_sdc.dev != NULL) return 1;	/* We only expect one SDC */
+
+	hp_sdc.dev		= d;
+	hp_sdc.irq		= d->irq;
+	hp_sdc.nmi		= d->aux_irq;
+	hp_sdc.base_io		= d->hpa;
+	hp_sdc.data_io		= d->hpa + 0x800;
+	hp_sdc.status_io	= d->hpa + 0x801;
+
+	return hp_sdc_init();
+}
+
+#endif /* __hppa__ */
+
+#if !defined(__mc68000__) /* Link error on m68k! */
+static void __exit hp_sdc_exit(void)
+#else
+static void hp_sdc_exit(void)
+#endif
+{
+	write_lock_irq(&hp_sdc.lock);
+
+	/* Turn off all maskable "sub-function" irq's. */
+	hp_sdc_spin_ibf();
+	sdc_writeb(HP_SDC_CMD_SET_IM | HP_SDC_IM_MASK, hp_sdc.status_io);
+
+	/* Wait until we know this has been processed by the i8042 */
+	hp_sdc_spin_ibf();
+
+	free_irq(hp_sdc.nmi, NULL);
+	free_irq(hp_sdc.irq, NULL);
+	write_unlock_irq(&hp_sdc.lock);
+
+	del_timer(&hp_sdc.kicker);
+
+	tasklet_kill(&hp_sdc.task);
+
+/*        release_region(hp_sdc.data_io, 2); */
+
+#if defined(__hppa__)
+	if (unregister_parisc_driver(&hp_sdc_driver)) 
+		printk(KERN_WARNING PREFIX "Error unregistering HP SDC");
+#endif
+}
+
+static int __init hp_sdc_register(void)
+{
+	hp_sdc_transaction tq_init;
+	uint8_t tq_init_seq[5];
+	struct semaphore tq_init_sem;
+#if defined(__mc68000__)
+	mm_segment_t fs;
+	unsigned char i;
+#endif
+	
+	hp_sdc.dev = NULL;
+	hp_sdc.dev_err = 0;
+#if defined(__hppa__)
+	if (register_parisc_driver(&hp_sdc_driver)) {
+		printk(KERN_WARNING PREFIX "Error registering SDC with system bus tree.\n");
+		return -ENODEV;
+	}
+#elif defined(__mc68000__)
+	if (!MACH_IS_HP300)
+	    return -ENODEV;
+
+	hp_sdc.irq	 = 1;
+	hp_sdc.nmi	 = 7;
+	hp_sdc.base_io	 = (unsigned long) 0xf0428000;
+	hp_sdc.data_io	 = (unsigned long) hp_sdc.base_io + 1;
+	hp_sdc.status_io = (unsigned long) hp_sdc.base_io + 3;
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	if (!get_user(i, (unsigned char *)hp_sdc.data_io))
+		hp_sdc.dev = (void *)1;
+	set_fs(fs);
+	hp_sdc.dev_err   = hp_sdc_init();
+#endif
+	if (hp_sdc.dev == NULL) {
+		printk(KERN_WARNING PREFIX "No SDC found.\n");
+		return hp_sdc.dev_err;
+	}
+
+	init_MUTEX_LOCKED(&tq_init_sem);
+
+	tq_init.actidx		= 0;
+	tq_init.idx		= 1;
+	tq_init.endidx		= 5;
+	tq_init.seq		= tq_init_seq;
+	tq_init.act.semaphore	= &tq_init_sem;
+
+	tq_init_seq[0] = 
+	  HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE;
+	tq_init_seq[1] = HP_SDC_CMD_READ_KCC;
+	tq_init_seq[2] = 1;
+	tq_init_seq[3] = 0;
+	tq_init_seq[4] = 0;
+
+	hp_sdc_enqueue_transaction(&tq_init);
+
+	down(&tq_init_sem);
+	up(&tq_init_sem);
+
+	if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) {
+		printk(KERN_WARNING PREFIX "Error reading config byte.\n");
+		hp_sdc_exit();
+		return -ENODEV;
+	}
+	hp_sdc.r11 = tq_init_seq[4];
+	if (hp_sdc.r11 & HP_SDC_CFG_NEW) {
+		char *str;
+		printk(KERN_INFO PREFIX "New style SDC\n");
+		tq_init_seq[1] = HP_SDC_CMD_READ_XTD;
+		tq_init.actidx		= 0;
+		tq_init.idx		= 1;
+		down(&tq_init_sem);
+		hp_sdc_enqueue_transaction(&tq_init);		
+		down(&tq_init_sem);
+		up(&tq_init_sem);
+		if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) {
+			printk(KERN_WARNING PREFIX "Error reading extended config byte.\n");
+			return -ENODEV;
+		}
+		hp_sdc.r7e = tq_init_seq[4];
+		HP_SDC_XTD_REV_STRINGS(hp_sdc.r7e & HP_SDC_XTD_REV, str)
+		printk(KERN_INFO PREFIX "Revision: %s\n", str);
+		if (hp_sdc.r7e & HP_SDC_XTD_BEEPER) {
+			printk(KERN_INFO PREFIX "TI SN76494 beeper present\n");
+		}
+		if (hp_sdc.r7e & HP_SDC_XTD_BBRTC) {
+			printk(KERN_INFO PREFIX "OKI MSM-58321 BBRTC present\n");
+		}
+		printk(KERN_INFO PREFIX "Spunking the self test register to force PUP "
+		       "on next firmware reset.\n");
+		tq_init_seq[0] = HP_SDC_ACT_PRECMD | 
+			HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE;
+		tq_init_seq[1] = HP_SDC_CMD_SET_STR;
+		tq_init_seq[2] = 1;
+		tq_init_seq[3] = 0;
+		tq_init.actidx		= 0;
+		tq_init.idx		= 1;
+		tq_init.endidx		= 4;
+		down(&tq_init_sem);
+		hp_sdc_enqueue_transaction(&tq_init);		
+		down(&tq_init_sem);
+		up(&tq_init_sem);
+	}
+	else {
+		printk(KERN_INFO PREFIX "Old style SDC (1820-%s).\n", 
+		       (hp_sdc.r11 & HP_SDC_CFG_REV) ? "3300" : "2564/3087");
+	}
+
+        return 0;
+}
+
+module_init(hp_sdc_register);
+module_exit(hp_sdc_exit);
+
+/* Timing notes:  These measurements taken on my 64MHz 7100-LC (715/64) 
+ *                                              cycles cycles-adj    time
+ * between two consecutive mfctl(16)'s:              4        n/a    63ns
+ * hp_sdc_spin_ibf when idle:                      119        115   1.7us
+ * gsc_writeb status register:                      83         79   1.2us
+ * IBF to clear after sending SET_IM:             6204       6006    93us
+ * IBF to clear after sending LOAD_RT:            4467       4352    68us  
+ * IBF to clear after sending two LOAD_RTs:      18974      18859   295us
+ * READ_T1, read status/data, IRQ, call handler: 35564        n/a   556us
+ * cmd to ~IBF READ_T1 2nd time right after:   5158403        n/a    81ms
+ * between IRQ received and ~IBF for above:    2578877        n/a    40ms
+ *
+ * Performance stats after a run of this module configuring HIL and
+ * receiving a few mouse events:
+ *
+ * status in8  282508 cycles 7128 calls
+ * status out8   8404 cycles  341 calls
+ * data out8     1734 cycles   78 calls
+ * isr         174324 cycles  617 calls (includes take)
+ * take          1241 cycles    2 calls
+ * put        1411504 cycles 6937 calls
+ * task       1655209 cycles 6937 calls (includes put)
+ *
+ */
diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c
new file mode 100644
index 0000000..e3c44ff
--- /dev/null
+++ b/drivers/input/serio/hp_sdc_mlc.c
@@ -0,0 +1,358 @@
+/*
+ * Access to HP-HIL MLC through HP System Device Controller.
+ *
+ * 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
+ * System Device Controller Microprocessor Firmware Theory of Operation
+ *      for Part Number 1820-4784 Revision B.  Dwg No. A-1820-4784-2
+ *
+ */
+
+#include <linux/hil_mlc.h>
+#include <linux/hp_sdc.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#define PREFIX "HP SDC MLC: "
+
+static hil_mlc hp_sdc_mlc;
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION("Glue for onboard HIL MLC in HP-PARISC machines");
+MODULE_LICENSE("Dual BSD/GPL");
+
+struct hp_sdc_mlc_priv_s {
+	int emtestmode;
+	hp_sdc_transaction trans;
+	u8 tseq[16];
+	int got5x;
+} hp_sdc_mlc_priv;
+
+/************************* Interrupt context ******************************/
+static void hp_sdc_mlc_isr (int irq, void *dev_id, 
+			    uint8_t status, uint8_t data) {
+  	int idx;
+	hil_mlc *mlc = &hp_sdc_mlc;
+
+	write_lock(&(mlc->lock));
+	if (mlc->icount < 0) {
+		printk(KERN_WARNING PREFIX "HIL Overflow!\n");
+		up(&mlc->isem);
+		goto out;
+	}
+	idx = 15 - mlc->icount;
+	if ((status & HP_SDC_STATUS_IRQMASK) == HP_SDC_STATUS_HILDATA) {
+		mlc->ipacket[idx] |= data | HIL_ERR_INT;
+		mlc->icount--;
+		if (hp_sdc_mlc_priv.got5x) goto check;
+		if (!idx) goto check;
+		if ((mlc->ipacket[idx-1] & HIL_PKT_ADDR_MASK) !=
+		    (mlc->ipacket[idx] & HIL_PKT_ADDR_MASK)) {
+			mlc->ipacket[idx] &= ~HIL_PKT_ADDR_MASK;
+			mlc->ipacket[idx] |= (mlc->ipacket[idx-1] 
+						    & HIL_PKT_ADDR_MASK);
+		}
+		goto check;
+	}
+	/* We know status is 5X */
+	if (data & HP_SDC_HIL_ISERR) goto err;
+	mlc->ipacket[idx] = 
+		(data & HP_SDC_HIL_R1MASK) << HIL_PKT_ADDR_SHIFT;
+	hp_sdc_mlc_priv.got5x = 1;
+	goto out;
+
+ check:
+	hp_sdc_mlc_priv.got5x = 0;
+	if (mlc->imatch == 0) goto done;
+	if ((mlc->imatch == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) 
+	    && (mlc->ipacket[idx] == (mlc->imatch | idx))) goto done;
+	if (mlc->ipacket[idx] == mlc->imatch) goto done;
+	goto out;
+
+ err:				
+	printk(KERN_DEBUG PREFIX "err code %x\n", data);
+	switch (data) {
+	case HP_SDC_HIL_RC_DONE:
+		printk(KERN_WARNING PREFIX "Bastard SDC reconfigured loop!\n");
+		break;
+	case HP_SDC_HIL_ERR:
+		mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_PERR | 
+		  HIL_ERR_FERR | HIL_ERR_FOF;
+		break;
+	case HP_SDC_HIL_TO:
+		mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_LERR;
+		break;
+	case HP_SDC_HIL_RC:
+		printk(KERN_WARNING PREFIX "Bastard SDC decided to reconfigure loop!\n");
+		break;
+	default:
+		printk(KERN_WARNING PREFIX "Unkown HIL Error status (%x)!\n", data);
+		break;
+	}
+	/* No more data will be coming due to an error. */
+ done:
+	tasklet_schedule(mlc->tasklet);
+	up(&(mlc->isem));
+ out:
+	write_unlock(&(mlc->lock));
+}
+
+
+/******************** Tasklet or userspace context functions ****************/
+
+static int hp_sdc_mlc_in (hil_mlc *mlc, suseconds_t timeout) {
+	unsigned long flags;
+	struct hp_sdc_mlc_priv_s *priv;
+	int rc = 2;
+
+	priv = mlc->priv;
+
+	write_lock_irqsave(&(mlc->lock), flags);
+
+	/* Try to down the semaphore */
+	if (down_trylock(&(mlc->isem))) {
+		struct timeval tv;
+		if (priv->emtestmode) {
+			mlc->ipacket[0] = 
+				HIL_ERR_INT | (mlc->opacket & 
+					       (HIL_PKT_CMD | 
+						HIL_PKT_ADDR_MASK | 
+						HIL_PKT_DATA_MASK));
+			mlc->icount = 14;
+			/* printk(KERN_DEBUG PREFIX ">[%x]\n", mlc->ipacket[0]); */
+			goto wasup;
+		}
+		do_gettimeofday(&tv);
+		tv.tv_usec += 1000000 * (tv.tv_sec - mlc->instart.tv_sec);
+		if (tv.tv_usec - mlc->instart.tv_usec > mlc->intimeout) {
+		  /*		  printk("!%i %i", 
+				  tv.tv_usec - mlc->instart.tv_usec, 
+				  mlc->intimeout);
+		  */
+			rc = 1;
+			up(&(mlc->isem));
+		}
+		goto done;
+	}
+ wasup:
+	up(&(mlc->isem));
+	rc = 0;
+	goto done;
+ done:
+	write_unlock_irqrestore(&(mlc->lock), flags);
+	return rc;
+}
+
+static int hp_sdc_mlc_cts (hil_mlc *mlc) {
+	struct hp_sdc_mlc_priv_s *priv;
+	unsigned long flags;
+
+	priv = mlc->priv;	
+
+	write_lock_irqsave(&(mlc->lock), flags);
+
+	/* Try to down the semaphores -- they should be up. */
+	if (down_trylock(&(mlc->isem))) {
+		BUG();
+		goto busy;
+	}
+	if (down_trylock(&(mlc->osem))) {
+	 	BUG();
+		up(&(mlc->isem));
+		goto busy;
+	}
+	up(&(mlc->isem));
+	up(&(mlc->osem));
+
+	if (down_trylock(&(mlc->csem))) {
+		if (priv->trans.act.semaphore != &(mlc->csem)) goto poll;
+		goto busy;
+	}
+	if (!(priv->tseq[4] & HP_SDC_USE_LOOP)) goto done;
+
+ poll:
+	priv->trans.act.semaphore = &(mlc->csem);
+	priv->trans.actidx = 0;
+	priv->trans.idx = 1;
+	priv->trans.endidx = 5;
+	priv->tseq[0] = 
+		HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE;
+	priv->tseq[1] = HP_SDC_CMD_READ_USE;
+	priv->tseq[2] = 1;
+	priv->tseq[3] = 0;
+	priv->tseq[4] = 0;
+	hp_sdc_enqueue_transaction(&(priv->trans));
+ busy:
+	write_unlock_irqrestore(&(mlc->lock), flags);
+	return 1;
+ done:
+	priv->trans.act.semaphore = &(mlc->osem);
+	up(&(mlc->csem));
+	write_unlock_irqrestore(&(mlc->lock), flags);
+	return 0;
+}
+
+static void hp_sdc_mlc_out (hil_mlc *mlc) {
+	struct hp_sdc_mlc_priv_s *priv;
+	unsigned long flags;
+
+	priv = mlc->priv;
+
+	write_lock_irqsave(&(mlc->lock), flags);
+	
+	/* Try to down the semaphore -- it should be up. */
+	if (down_trylock(&(mlc->osem))) {
+	 	BUG();
+		goto done;
+	}
+
+	if (mlc->opacket & HIL_DO_ALTER_CTRL) goto do_control;
+
+ do_data:
+	if (priv->emtestmode) {
+		up(&(mlc->osem));
+		goto done;
+	}
+	/* Shouldn't be sending commands when loop may be busy */
+	if (down_trylock(&(mlc->csem))) {
+	 	BUG();
+		goto done;
+	}
+	up(&(mlc->csem));
+
+	priv->trans.actidx = 0;
+	priv->trans.idx = 1;
+	priv->trans.act.semaphore = &(mlc->osem);
+	priv->trans.endidx = 6;
+	priv->tseq[0] = 
+		HP_SDC_ACT_DATAREG | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_SEMAPHORE;
+	priv->tseq[1] = 0x7;
+	priv->tseq[2] = 
+		(mlc->opacket & 
+		 (HIL_PKT_ADDR_MASK | HIL_PKT_CMD))
+		   >> HIL_PKT_ADDR_SHIFT;
+	priv->tseq[3] = 
+		(mlc->opacket & HIL_PKT_DATA_MASK) 
+		  >> HIL_PKT_DATA_SHIFT;
+	priv->tseq[4] = 0;  /* No timeout */
+	if (priv->tseq[3] == HIL_CMD_DHR) priv->tseq[4] = 1;
+	priv->tseq[5] = HP_SDC_CMD_DO_HIL;
+	goto enqueue;
+
+ do_control:
+	priv->emtestmode = mlc->opacket & HIL_CTRL_TEST;
+	if ((mlc->opacket & (HIL_CTRL_APE | HIL_CTRL_IPF)) == HIL_CTRL_APE) {
+		BUG(); /* we cannot emulate this, it should not be used. */
+	}
+	if ((mlc->opacket & HIL_CTRL_ONLY) == HIL_CTRL_ONLY) goto control_only;
+	if (mlc->opacket & HIL_CTRL_APE) { 
+		BUG(); /* Should not send command/data after engaging APE */
+		goto done;
+	}
+	/* Disengaging APE this way would not be valid either since 
+	 * the loop must be allowed to idle.
+	 *
+	 * So, it works out that we really never actually send control 
+	 * and data when using SDC, we just send the data. 
+	 */
+	goto do_data;
+
+ control_only:
+	priv->trans.actidx = 0;
+	priv->trans.idx = 1;
+	priv->trans.act.semaphore = &(mlc->osem);
+	priv->trans.endidx = 4;
+	priv->tseq[0] = 
+	  HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE;
+	priv->tseq[1] = HP_SDC_CMD_SET_LPC;
+	priv->tseq[2] = 1;
+	//	priv->tseq[3] = (mlc->ddc + 1) | HP_SDC_LPS_ACSUCC;
+	priv->tseq[3] = 0;
+	if (mlc->opacket & HIL_CTRL_APE) {
+		priv->tseq[3] |= HP_SDC_LPC_APE_IPF;
+		down_trylock(&(mlc->csem));
+	} 
+ enqueue:
+	hp_sdc_enqueue_transaction(&(priv->trans));
+ done:
+	write_unlock_irqrestore(&(mlc->lock), flags);
+}
+
+static int __init hp_sdc_mlc_init(void)
+{
+	hil_mlc *mlc = &hp_sdc_mlc;
+
+	printk(KERN_INFO PREFIX "Registering the System Domain Controller's HIL MLC.\n");
+
+	hp_sdc_mlc_priv.emtestmode = 0;
+	hp_sdc_mlc_priv.trans.seq = hp_sdc_mlc_priv.tseq;
+	hp_sdc_mlc_priv.trans.act.semaphore = &(mlc->osem);
+	hp_sdc_mlc_priv.got5x = 0;
+
+	mlc->cts		= &hp_sdc_mlc_cts;
+	mlc->in			= &hp_sdc_mlc_in;
+	mlc->out		= &hp_sdc_mlc_out;
+
+	if (hil_mlc_register(mlc)) {
+		printk(KERN_WARNING PREFIX "Failed to register MLC structure with hil_mlc\n");
+		goto err0;
+	}
+	mlc->priv		= &hp_sdc_mlc_priv;
+
+	if (hp_sdc_request_hil_irq(&hp_sdc_mlc_isr)) {
+		printk(KERN_WARNING PREFIX "Request for raw HIL ISR hook denied\n");
+		goto err1;
+	}
+	return 0;
+ err1:
+	if (hil_mlc_unregister(mlc)) {
+		printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
+			"This is bad.  Could cause an oops.\n");
+	}
+ err0:
+	return -EBUSY;
+}
+
+static void __exit hp_sdc_mlc_exit(void)
+{
+	hil_mlc *mlc = &hp_sdc_mlc;
+	if (hp_sdc_release_hil_irq(&hp_sdc_mlc_isr)) {
+		printk(KERN_ERR PREFIX "Failed to release the raw HIL ISR hook.\n"
+			"This is bad.  Could cause an oops.\n");
+	}
+	if (hil_mlc_unregister(mlc)) {
+		printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
+			"This is bad.  Could cause an oops.\n");
+	}
+}
+
+module_init(hp_sdc_mlc_init);
+module_exit(hp_sdc_mlc_exit);
diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h
new file mode 100644
index 0000000..c9e633d
--- /dev/null
+++ b/drivers/input/serio/i8042-io.h
@@ -0,0 +1,93 @@
+#ifndef _I8042_IO_H
+#define _I8042_IO_H
+
+/*
+ * 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.
+ */
+
+/*
+ * Names.
+ */
+
+#define I8042_KBD_PHYS_DESC "isa0060/serio0"
+#define I8042_AUX_PHYS_DESC "isa0060/serio1"
+#define I8042_MUX_PHYS_DESC "isa0060/serio%d"
+
+/*
+ * IRQs.
+ */
+
+#ifdef __alpha__
+# define I8042_KBD_IRQ	1
+# define I8042_AUX_IRQ	(RTC_PORT(0) == 0x170 ? 9 : 12)	/* Jensen is special */
+#elif defined(__arm__)
+/* defined in include/asm-arm/arch-xxx/irqs.h */
+#include <asm/irq.h>
+#elif defined(CONFIG_SUPERH64)
+#include <asm/irq.h>
+#else
+# define I8042_KBD_IRQ	1
+# define I8042_AUX_IRQ	12
+#endif
+
+
+/*
+ * Register numbers.
+ */
+
+#define I8042_COMMAND_REG	0x64
+#define I8042_STATUS_REG	0x64
+#define I8042_DATA_REG		0x60
+
+static inline int i8042_read_data(void)
+{
+	return inb(I8042_DATA_REG);
+}
+
+static inline int i8042_read_status(void)
+{
+	return inb(I8042_STATUS_REG);
+}
+
+static inline void i8042_write_data(int val)
+{
+	outb(val, I8042_DATA_REG);
+}
+
+static inline void i8042_write_command(int val)
+{
+	outb(val, I8042_COMMAND_REG);
+}
+
+static inline int i8042_platform_init(void)
+{
+/*
+ * On some platforms touching the i8042 data register region can do really
+ * bad things. Because of this the region is always reserved on such boxes.
+ */
+#if !defined(__sh__) && !defined(__alpha__) && !defined(__mips__) && !defined(CONFIG_PPC64)
+	if (!request_region(I8042_DATA_REG, 16, "i8042"))
+		return -1;
+#endif
+
+        i8042_reset = 1;
+
+#if defined(CONFIG_PPC64)
+	if (check_legacy_ioport(I8042_DATA_REG))
+		return -1;
+	if (!request_region(I8042_DATA_REG, 16, "i8042"))
+		return -1;
+#endif
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+#if !defined(__sh__) && !defined(__alpha__) && !defined(CONFIG_PPC64)
+	release_region(I8042_DATA_REG, 16);
+#endif
+}
+
+#endif /* _I8042_IO_H */
diff --git a/drivers/input/serio/i8042-ip22io.h b/drivers/input/serio/i8042-ip22io.h
new file mode 100644
index 0000000..863b9c9
--- /dev/null
+++ b/drivers/input/serio/i8042-ip22io.h
@@ -0,0 +1,76 @@
+#ifndef _I8042_IP22_H
+#define _I8042_IP22_H
+
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/ip22.h>
+
+/*
+ * 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.
+ */
+
+/*
+ * Names.
+ */
+
+#define I8042_KBD_PHYS_DESC "hpc3ps2/serio0"
+#define I8042_AUX_PHYS_DESC "hpc3ps2/serio1"
+#define I8042_MUX_PHYS_DESC "hpc3ps2/serio%d"
+
+/*
+ * IRQs.
+ */
+
+#define I8042_KBD_IRQ SGI_KEYBD_IRQ
+#define I8042_AUX_IRQ SGI_KEYBD_IRQ
+
+/*
+ * Register numbers.
+ */
+
+#define I8042_COMMAND_REG	((unsigned long)&sgioc->kbdmouse.command)
+#define I8042_STATUS_REG	((unsigned long)&sgioc->kbdmouse.command)
+#define I8042_DATA_REG		((unsigned long)&sgioc->kbdmouse.data)
+
+static inline int i8042_read_data(void)
+{
+	return sgioc->kbdmouse.data;
+}
+
+static inline int i8042_read_status(void)
+{
+	return sgioc->kbdmouse.command;
+}
+
+static inline void i8042_write_data(int val)
+{
+	sgioc->kbdmouse.data = val;
+}
+
+static inline void i8042_write_command(int val)
+{
+	sgioc->kbdmouse.command = val;
+}
+
+static inline int i8042_platform_init(void)
+{
+#if 0
+	/* XXX sgi_kh is a virtual address */
+	if (!request_mem_region(sgi_kh, sizeof(struct hpc_keyb), "i8042"))
+		return 1;
+#endif
+
+	i8042_reset = 1;
+
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+#if 0
+	release_mem_region(JAZZ_KEYBOARD_ADDRESS, sizeof(struct hpc_keyb));
+#endif
+}
+
+#endif /* _I8042_IP22_H */
diff --git a/drivers/input/serio/i8042-jazzio.h b/drivers/input/serio/i8042-jazzio.h
new file mode 100644
index 0000000..5c20ab1
--- /dev/null
+++ b/drivers/input/serio/i8042-jazzio.h
@@ -0,0 +1,69 @@
+#ifndef _I8042_JAZZ_H
+#define _I8042_JAZZ_H
+
+#include <asm/jazz.h>
+
+/*
+ * 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.
+ */
+
+/*
+ * Names.
+ */
+
+#define I8042_KBD_PHYS_DESC "R4030/serio0"
+#define I8042_AUX_PHYS_DESC "R4030/serio1"
+#define I8042_MUX_PHYS_DESC "R4030/serio%d"
+
+/*
+ * IRQs.
+ */
+
+#define I8042_KBD_IRQ JAZZ_KEYBOARD_IRQ
+#define I8042_AUX_IRQ JAZZ_MOUSE_IRQ
+
+#define I8042_COMMAND_REG	((unsigned long)&jazz_kh->command)
+#define I8042_STATUS_REG	((unsigned long)&jazz_kh->command)
+#define I8042_DATA_REG		((unsigned long)&jazz_kh->data)
+
+static inline int i8042_read_data(void)
+{
+	return jazz_kh->data;
+}
+
+static inline int i8042_read_status(void)
+{
+	return jazz_kh->command;
+}
+
+static inline void i8042_write_data(int val)
+{
+	jazz_kh->data = val;
+}
+
+static inline void i8042_write_command(int val)
+{
+	jazz_kh->command = val;
+}
+
+static inline int i8042_platform_init(void)
+{
+#if 0
+	/* XXX JAZZ_KEYBOARD_ADDRESS is a virtual address */
+	if (!request_mem_region(JAZZ_KEYBOARD_ADDRESS, 2, "i8042"))
+		return 1;
+#endif
+
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+#if 0
+	release_mem_region(JAZZ_KEYBOARD_ADDRESS, 2);
+#endif
+}
+
+#endif /* _I8042_JAZZ_H */
diff --git a/drivers/input/serio/i8042-ppcio.h b/drivers/input/serio/i8042-ppcio.h
new file mode 100644
index 0000000..2906e1b
--- /dev/null
+++ b/drivers/input/serio/i8042-ppcio.h
@@ -0,0 +1,136 @@
+#ifndef _I8042_PPCIO_H
+#define _I8042_PPCIO_H
+
+/*
+ * 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.
+ */
+
+#if defined(CONFIG_WALNUT)
+
+#define I8042_KBD_IRQ 25
+#define I8042_AUX_IRQ 26
+
+#define I8042_KBD_PHYS_DESC "walnutps2/serio0"
+#define I8042_AUX_PHYS_DESC "walnutps2/serio1"
+#define I8042_MUX_PHYS_DESC "walnutps2/serio%d"
+
+extern void *kb_cs;
+extern void *kb_data;
+
+#define I8042_COMMAND_REG (*(int *)kb_cs)
+#define I8042_DATA_REG (*(int *)kb_data)
+
+static inline int i8042_read_data(void)
+{
+	return readb(kb_data);
+}
+
+static inline int i8042_read_status(void)
+{
+	return readb(kb_cs);
+}
+
+static inline void i8042_write_data(int val)
+{
+	writeb(val, kb_data);
+}
+
+static inline void i8042_write_command(int val)
+{
+	writeb(val, kb_cs);
+}
+
+static inline int i8042_platform_init(void)
+{
+	i8042_reset = 1;
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+}
+
+#elif defined(CONFIG_SPRUCE)
+
+#define I8042_KBD_IRQ 22
+#define I8042_AUX_IRQ 21
+
+#define I8042_KBD_PHYS_DESC "spruceps2/serio0"
+#define I8042_AUX_PHYS_DESC "spruceps2/serio1"
+#define I8042_MUX_PHYS_DESC "spruceps2/serio%d"
+
+#define I8042_COMMAND_REG 0xff810000
+#define I8042_DATA_REG 0xff810001
+
+static inline int i8042_read_data(void)
+{
+	unsigned long kbd_data;
+
+	__raw_writel(0x00000088, 0xff500008);
+	eieio();
+
+	__raw_writel(0x03000000, 0xff50000c);
+	eieio();
+
+	asm volatile("lis     7,0xff88        \n\
+		      lswi    6,7,0x8         \n\
+		      mr      %0,6"
+	              : "=r" (kbd_data) :: "6", "7");
+
+	__raw_writel(0x00000000, 0xff50000c);
+	eieio();
+
+	return (unsigned char)(kbd_data >> 24);
+}
+
+static inline int i8042_read_status(void)
+{
+	unsigned long kbd_status;
+
+	__raw_writel(0x00000088, 0xff500008);
+	eieio();
+
+	__raw_writel(0x03000000, 0xff50000c);
+	eieio();
+
+	asm volatile("lis     7,0xff88        \n\
+		      ori     7,7,0x8         \n\
+		      lswi    6,7,0x8         \n\
+		      mr      %0,6"
+		      : "=r" (kbd_status) :: "6", "7");
+
+	__raw_writel(0x00000000, 0xff50000c);
+	eieio();
+
+	return (unsigned char)(kbd_status >> 24);
+}
+
+static inline void i8042_write_data(int val)
+{
+	*((unsigned char *)0xff810000) = (char)val;
+}
+
+static inline void i8042_write_command(int val)
+{
+	*((unsigned char *)0xff810001) = (char)val;
+}
+
+static inline int i8042_platform_init(void)
+{
+	i8042_reset = 1;
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+}
+
+#else
+
+#include "i8042-io.h"
+
+#endif
+
+#endif /* _I8042_PPCIO_H */
diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h
new file mode 100644
index 0000000..da2a198
--- /dev/null
+++ b/drivers/input/serio/i8042-sparcio.h
@@ -0,0 +1,116 @@
+#ifndef _I8042_SPARCIO_H
+#define _I8042_SPARCIO_H
+
+#include <linux/config.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_PCI
+#include <asm/oplib.h>
+#include <asm/ebus.h>
+#endif
+
+static int i8042_kbd_irq = -1;
+static int i8042_aux_irq = -1;
+#define I8042_KBD_IRQ i8042_kbd_irq
+#define I8042_AUX_IRQ i8042_aux_irq
+
+#define I8042_KBD_PHYS_DESC "sparcps2/serio0"
+#define I8042_AUX_PHYS_DESC "sparcps2/serio1"
+#define I8042_MUX_PHYS_DESC "sparcps2/serio%d"
+
+static void __iomem *kbd_iobase;
+
+#define I8042_COMMAND_REG	(kbd_iobase + 0x64UL)
+#define I8042_DATA_REG		(kbd_iobase + 0x60UL)
+
+static inline int i8042_read_data(void)
+{
+	return readb(kbd_iobase + 0x60UL);
+}
+
+static inline int i8042_read_status(void)
+{
+	return readb(kbd_iobase + 0x64UL);
+}
+
+static inline void i8042_write_data(int val)
+{
+	writeb(val, kbd_iobase + 0x60UL);
+}
+
+static inline void i8042_write_command(int val)
+{
+	writeb(val, kbd_iobase + 0x64UL);
+}
+
+#define OBP_PS2KBD_NAME1	"kb_ps2"
+#define OBP_PS2KBD_NAME2	"keyboard"
+#define OBP_PS2MS_NAME1		"kdmouse"
+#define OBP_PS2MS_NAME2		"mouse"
+
+static int i8042_platform_init(void)
+{
+#ifndef CONFIG_PCI
+	return -1;
+#else
+	char prop[128];
+	int len;
+
+	len = prom_getproperty(prom_root_node, "name", prop, sizeof(prop));
+	if (len < 0) {
+		printk("i8042: Cannot get name property of root OBP node.\n");
+		return -1;
+	}
+	if (strncmp(prop, "SUNW,JavaStation-1", len) == 0) {
+		/* Hardcoded values for MrCoffee.  */
+		i8042_kbd_irq = i8042_aux_irq = 13 | 0x20;
+		kbd_iobase = ioremap(0x71300060, 8);
+		if (!kbd_iobase)
+			return -1;
+	} else {
+		struct linux_ebus *ebus;
+		struct linux_ebus_device *edev;
+		struct linux_ebus_child *child;
+
+		for_each_ebus(ebus) {
+			for_each_ebusdev(edev, ebus) {
+				if (!strcmp(edev->prom_name, "8042"))
+					goto edev_found;
+			}
+		}
+		return -1;
+
+	edev_found:
+		for_each_edevchild(edev, child) {
+			if (!strcmp(child->prom_name, OBP_PS2KBD_NAME1) ||
+			    !strcmp(child->prom_name, OBP_PS2KBD_NAME2)) {
+				i8042_kbd_irq = child->irqs[0];
+				kbd_iobase =
+					ioremap(child->resource[0].start, 8);
+			}
+			if (!strcmp(child->prom_name, OBP_PS2MS_NAME1) ||
+			    !strcmp(child->prom_name, OBP_PS2MS_NAME2))
+				i8042_aux_irq = child->irqs[0];
+		}
+		if (i8042_kbd_irq == -1 ||
+		    i8042_aux_irq == -1) {
+			printk("i8042: Error, 8042 device lacks both kbd and "
+			       "mouse nodes.\n");
+			return -1;
+		}
+	}
+
+	i8042_reset = 1;
+
+	return 0;
+#endif /* CONFIG_PCI */
+}
+
+static inline void i8042_platform_exit(void)
+{
+#ifdef CONFIG_PCI
+	iounmap(kbd_iobase);
+#endif
+}
+
+#endif /* _I8042_SPARCIO_H */
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
new file mode 100644
index 0000000..f648678
--- /dev/null
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -0,0 +1,333 @@
+#ifndef _I8042_X86IA64IO_H
+#define _I8042_X86IA64IO_H
+
+/*
+ * 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.
+ */
+
+/*
+ * Names.
+ */
+
+#define I8042_KBD_PHYS_DESC "isa0060/serio0"
+#define I8042_AUX_PHYS_DESC "isa0060/serio1"
+#define I8042_MUX_PHYS_DESC "isa0060/serio%d"
+
+/*
+ * IRQs.
+ */
+
+#if defined(__ia64__)
+# define I8042_MAP_IRQ(x)	isa_irq_to_vector((x))
+#else
+# define I8042_MAP_IRQ(x)	(x)
+#endif
+
+#define I8042_KBD_IRQ	i8042_kbd_irq
+#define I8042_AUX_IRQ	i8042_aux_irq
+
+static int i8042_kbd_irq;
+static int i8042_aux_irq;
+
+/*
+ * Register numbers.
+ */
+
+#define I8042_COMMAND_REG	i8042_command_reg
+#define I8042_STATUS_REG	i8042_command_reg
+#define I8042_DATA_REG		i8042_data_reg
+
+static int i8042_command_reg = 0x64;
+static int i8042_data_reg = 0x60;
+
+
+static inline int i8042_read_data(void)
+{
+	return inb(I8042_DATA_REG);
+}
+
+static inline int i8042_read_status(void)
+{
+	return inb(I8042_STATUS_REG);
+}
+
+static inline void i8042_write_data(int val)
+{
+	outb(val, I8042_DATA_REG);
+}
+
+static inline void i8042_write_command(int val)
+{
+	outb(val, I8042_COMMAND_REG);
+}
+
+#if defined(__i386__)
+
+#include <linux/dmi.h>
+
+static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
+	{
+		.ident = "Compaq Proliant 8500",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
+			DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "8500"),
+		},
+	},
+	{
+		.ident = "Compaq Proliant DL760",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
+			DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "DL760"),
+		},
+	},
+	{ }
+};
+
+/*
+ * Some Fujitsu notebooks are ahving trouble with touhcpads if
+ * active multiplexing mode is activated. Luckily they don't have
+ * external PS/2 ports so we can safely disable it.
+ */
+static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
+	{
+		.ident = "Fujitsu Lifebook P7010/P7010D",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "P7010"),
+		},
+	},
+	{
+		.ident = "Fujitsu Lifebook P5020D",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"),
+		},
+	},
+	{
+		.ident = "Fujitsu Lifebook S2000",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"),
+		},
+	},
+	{
+		.ident = "Fujitsu T70H",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"),
+		},
+	},
+	{ }
+};
+
+
+
+#endif
+
+
+#ifdef CONFIG_PNP
+#include <linux/pnp.h>
+
+static int i8042_pnp_kbd_registered;
+static int i8042_pnp_aux_registered;
+
+static int i8042_pnp_command_reg;
+static int i8042_pnp_data_reg;
+static int i8042_pnp_kbd_irq;
+static int i8042_pnp_aux_irq;
+
+static char i8042_pnp_kbd_name[32];
+static char i8042_pnp_aux_name[32];
+
+static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id *did)
+{
+	if (pnp_port_valid(dev, 0) && pnp_port_len(dev, 0) == 1)
+		i8042_pnp_data_reg = pnp_port_start(dev,0);
+
+	if (pnp_port_valid(dev, 1) && pnp_port_len(dev, 1) == 1)
+		i8042_pnp_command_reg = pnp_port_start(dev, 1);
+
+	if (pnp_irq_valid(dev,0))
+		i8042_pnp_kbd_irq = pnp_irq(dev, 0);
+
+	strncpy(i8042_pnp_kbd_name, did->id, sizeof(i8042_pnp_kbd_name));
+	if (strlen(pnp_dev_name(dev))) {
+		strncat(i8042_pnp_kbd_name, ":", sizeof(i8042_pnp_kbd_name));
+		strncat(i8042_pnp_kbd_name, pnp_dev_name(dev), sizeof(i8042_pnp_kbd_name));
+	}
+
+	return 0;
+}
+
+static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id *did)
+{
+	if (pnp_port_valid(dev, 0) && pnp_port_len(dev, 0) == 1)
+		i8042_pnp_data_reg = pnp_port_start(dev,0);
+
+	if (pnp_port_valid(dev, 1) && pnp_port_len(dev, 1) == 1)
+		i8042_pnp_command_reg = pnp_port_start(dev, 1);
+
+	if (pnp_irq_valid(dev, 0))
+		i8042_pnp_aux_irq = pnp_irq(dev, 0);
+
+	strncpy(i8042_pnp_aux_name, did->id, sizeof(i8042_pnp_aux_name));
+	if (strlen(pnp_dev_name(dev))) {
+		strncat(i8042_pnp_aux_name, ":", sizeof(i8042_pnp_aux_name));
+		strncat(i8042_pnp_aux_name, pnp_dev_name(dev), sizeof(i8042_pnp_aux_name));
+	}
+
+	return 0;
+}
+
+static struct pnp_device_id pnp_kbd_devids[] = {
+	{ .id = "PNP0303", .driver_data = 0 },
+	{ .id = "PNP030b", .driver_data = 0 },
+	{ .id = "", },
+};
+
+static struct pnp_driver i8042_pnp_kbd_driver = {
+	.name           = "i8042 kbd",
+	.id_table       = pnp_kbd_devids,
+	.probe          = i8042_pnp_kbd_probe,
+};
+
+static struct pnp_device_id pnp_aux_devids[] = {
+	{ .id = "PNP0f03", .driver_data = 0 },
+	{ .id = "PNP0f0b", .driver_data = 0 },
+	{ .id = "PNP0f0e", .driver_data = 0 },
+	{ .id = "PNP0f12", .driver_data = 0 },
+	{ .id = "PNP0f13", .driver_data = 0 },
+	{ .id = "PNP0f19", .driver_data = 0 },
+	{ .id = "PNP0f1c", .driver_data = 0 },
+	{ .id = "SYN0801", .driver_data = 0 },
+	{ .id = "", },
+};
+
+static struct pnp_driver i8042_pnp_aux_driver = {
+	.name           = "i8042 aux",
+	.id_table       = pnp_aux_devids,
+	.probe          = i8042_pnp_aux_probe,
+};
+
+static void i8042_pnp_exit(void)
+{
+	if (i8042_pnp_kbd_registered)
+		pnp_unregister_driver(&i8042_pnp_kbd_driver);
+
+	if (i8042_pnp_aux_registered)
+		pnp_unregister_driver(&i8042_pnp_aux_driver);
+}
+
+static int i8042_pnp_init(void)
+{
+	int result_kbd, result_aux;
+
+	if (i8042_nopnp) {
+		printk("i8042: PNP detection disabled\n");
+		return 0;
+	}
+
+	if ((result_kbd = pnp_register_driver(&i8042_pnp_kbd_driver)) >= 0)
+		i8042_pnp_kbd_registered = 1;
+	if ((result_aux = pnp_register_driver(&i8042_pnp_aux_driver)) >= 0)
+		i8042_pnp_aux_registered = 1;
+
+	if (result_kbd <= 0 && result_aux <= 0) {
+		i8042_pnp_exit();
+#if defined(__ia64__)
+		return -ENODEV;
+#else
+		printk(KERN_WARNING "PNP: No PS/2 controller found. Probing ports directly.\n");
+		return 0;
+#endif
+	}
+
+	if (((i8042_pnp_data_reg & ~0xf) == (i8042_data_reg & ~0xf) &&
+	      i8042_pnp_data_reg != i8042_data_reg) || !i8042_pnp_data_reg) {
+		printk(KERN_WARNING "PNP: PS/2 controller has invalid data port %#x; using default %#x\n",
+			i8042_pnp_data_reg, i8042_data_reg);
+		i8042_pnp_data_reg = i8042_data_reg;
+	}
+
+	if (((i8042_pnp_command_reg & ~0xf) == (i8042_command_reg & ~0xf) &&
+	      i8042_pnp_command_reg != i8042_command_reg) || !i8042_pnp_command_reg) {
+		printk(KERN_WARNING "PNP: PS/2 controller has invalid command port %#x; using default %#x\n",
+			i8042_pnp_command_reg, i8042_command_reg);
+		i8042_pnp_command_reg = i8042_command_reg;
+	}
+
+	if (!i8042_pnp_kbd_irq) {
+		printk(KERN_WARNING "PNP: PS/2 controller doesn't have KBD irq; using default %#x\n", i8042_kbd_irq);
+		i8042_pnp_kbd_irq = i8042_kbd_irq;
+	}
+
+	if (result_aux > 0 && !i8042_pnp_aux_irq) {
+		printk(KERN_WARNING "PNP: PS/2 controller doesn't have AUX irq; using default %#x\n", i8042_aux_irq);
+		i8042_pnp_aux_irq = i8042_aux_irq;
+	}
+
+#if defined(__ia64__)
+	if (result_aux <= 0)
+		i8042_noaux = 1;
+#endif
+
+	i8042_data_reg = i8042_pnp_data_reg;
+	i8042_command_reg = i8042_pnp_command_reg;
+	i8042_kbd_irq = i8042_pnp_kbd_irq;
+	i8042_aux_irq = i8042_pnp_aux_irq;
+
+	printk(KERN_INFO "PNP: PS/2 Controller [%s%s%s] at %#x,%#x irq %d%s%d\n",
+		i8042_pnp_kbd_name, (result_kbd > 0 && result_aux > 0) ? "," : "", i8042_pnp_aux_name,
+		i8042_data_reg, i8042_command_reg, i8042_kbd_irq,
+		(result_aux > 0) ? "," : "", i8042_aux_irq);
+
+	return 0;
+}
+
+#endif
+
+static inline int i8042_platform_init(void)
+{
+/*
+ * On ix86 platforms touching the i8042 data register region can do really
+ * bad things. Because of this the region is always reserved on ix86 boxes.
+ *
+ *	if (!request_region(I8042_DATA_REG, 16, "i8042"))
+ *		return -1;
+ */
+
+	i8042_kbd_irq = I8042_MAP_IRQ(1);
+	i8042_aux_irq = I8042_MAP_IRQ(12);
+
+#ifdef CONFIG_PNP
+	if (i8042_pnp_init())
+		return -1;
+#endif
+
+#if defined(__ia64__)
+        i8042_reset = 1;
+#endif
+
+#if defined(__i386__)
+	if (dmi_check_system(i8042_dmi_noloop_table))
+		i8042_noloop = 1;
+
+	if (dmi_check_system(i8042_dmi_nomux_table))
+		i8042_nomux = 1;
+#endif
+
+	return 0;
+}
+
+static inline void i8042_platform_exit(void)
+{
+#ifdef CONFIG_PNP
+	i8042_pnp_exit();
+#endif
+}
+
+#endif /* _I8042_X86IA64IO_H */
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
new file mode 100644
index 0000000..8e63e46
--- /dev/null
+++ b/drivers/input/serio/i8042.c
@@ -0,0 +1,1116 @@
+/*
+ *  i8042 keyboard and mouse controller driver for Linux
+ *
+ *  Copyright (c) 1999-2004 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/err.h>
+#include <linux/rcupdate.h>
+
+#include <asm/io.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver");
+MODULE_LICENSE("GPL");
+
+static unsigned int i8042_noaux;
+module_param_named(noaux, i8042_noaux, bool, 0);
+MODULE_PARM_DESC(noaux, "Do not probe or use AUX (mouse) port.");
+
+static unsigned int i8042_nomux;
+module_param_named(nomux, i8042_nomux, bool, 0);
+MODULE_PARM_DESC(nomux, "Do not check whether an active multiplexing conrtoller is present.");
+
+static unsigned int i8042_unlock;
+module_param_named(unlock, i8042_unlock, bool, 0);
+MODULE_PARM_DESC(unlock, "Ignore keyboard lock.");
+
+static unsigned int i8042_reset;
+module_param_named(reset, i8042_reset, bool, 0);
+MODULE_PARM_DESC(reset, "Reset controller during init and cleanup.");
+
+static unsigned int i8042_direct;
+module_param_named(direct, i8042_direct, bool, 0);
+MODULE_PARM_DESC(direct, "Put keyboard port into non-translated mode.");
+
+static unsigned int i8042_dumbkbd;
+module_param_named(dumbkbd, i8042_dumbkbd, bool, 0);
+MODULE_PARM_DESC(dumbkbd, "Pretend that controller can only read data from keyboard");
+
+static unsigned int i8042_noloop;
+module_param_named(noloop, i8042_noloop, bool, 0);
+MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port");
+
+static unsigned int i8042_blink_frequency = 500;
+module_param_named(panicblink, i8042_blink_frequency, uint, 0600);
+MODULE_PARM_DESC(panicblink, "Frequency with which keyboard LEDs should blink when kernel panics");
+
+#ifdef CONFIG_PNP
+static int i8042_nopnp;
+module_param_named(nopnp, i8042_nopnp, bool, 0);
+MODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings");
+#endif
+
+#define DEBUG
+#ifdef DEBUG
+static int i8042_debug;
+module_param_named(debug, i8042_debug, bool, 0600);
+MODULE_PARM_DESC(debug, "Turn i8042 debugging mode on and off");
+#endif
+
+__obsolete_setup("i8042_noaux");
+__obsolete_setup("i8042_nomux");
+__obsolete_setup("i8042_unlock");
+__obsolete_setup("i8042_reset");
+__obsolete_setup("i8042_direct");
+__obsolete_setup("i8042_dumbkbd");
+
+#include "i8042.h"
+
+static DEFINE_SPINLOCK(i8042_lock);
+
+struct i8042_port {
+	struct serio *serio;
+	int irq;
+	unsigned char disable;
+	unsigned char irqen;
+	unsigned char exists;
+	signed char mux;
+	char name[8];
+};
+
+#define I8042_KBD_PORT_NO	0
+#define I8042_AUX_PORT_NO	1
+#define I8042_MUX_PORT_NO	2
+#define I8042_NUM_PORTS		(I8042_NUM_MUX_PORTS + 2)
+static struct i8042_port i8042_ports[I8042_NUM_PORTS] = {
+	{
+		.disable	= I8042_CTR_KBDDIS,
+		.irqen 		= I8042_CTR_KBDINT,
+		.mux		= -1,
+		.name		= "KBD",
+	},
+	{
+		.disable	= I8042_CTR_AUXDIS,
+		.irqen		= I8042_CTR_AUXINT,
+		.mux		= -1,
+		.name		= "AUX",
+	}
+};
+
+static unsigned char i8042_initial_ctr;
+static unsigned char i8042_ctr;
+static unsigned char i8042_mux_open;
+static unsigned char i8042_mux_present;
+static struct timer_list i8042_timer;
+static struct platform_device *i8042_platform_device;
+
+
+/*
+ * Shared IRQ's require a device pointer, but this driver doesn't support
+ * multiple devices
+ */
+#define i8042_request_irq_cookie (&i8042_timer)
+
+static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+/*
+ * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
+ * be ready for reading values from it / writing values to it.
+ * Called always with i8042_lock held.
+ */
+
+static int i8042_wait_read(void)
+{
+	int i = 0;
+	while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) {
+		udelay(50);
+		i++;
+	}
+	return -(i == I8042_CTL_TIMEOUT);
+}
+
+static int i8042_wait_write(void)
+{
+	int i = 0;
+	while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) {
+		udelay(50);
+		i++;
+	}
+	return -(i == I8042_CTL_TIMEOUT);
+}
+
+/*
+ * i8042_flush() flushes all data that may be in the keyboard and mouse buffers
+ * of the i8042 down the toilet.
+ */
+
+static int i8042_flush(void)
+{
+	unsigned long flags;
+	unsigned char data, str;
+	int i = 0;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+
+	while (((str = i8042_read_status()) & I8042_STR_OBF) && (i < I8042_BUFFER_SIZE)) {
+		udelay(50);
+		data = i8042_read_data();
+		i++;
+		dbg("%02x <- i8042 (flush, %s)", data,
+			str & I8042_STR_AUXDATA ? "aux" : "kbd");
+	}
+
+	spin_unlock_irqrestore(&i8042_lock, flags);
+
+	return i;
+}
+
+/*
+ * i8042_command() executes a command on the i8042. It also sends the input
+ * parameter(s) of the commands to it, and receives the output value(s). The
+ * parameters are to be stored in the param array, and the output is placed
+ * into the same array. The number of the parameters and output values is
+ * encoded in bits 8-11 of the command number.
+ */
+
+static int i8042_command(unsigned char *param, int command)
+{
+	unsigned long flags;
+	int retval = 0, i = 0;
+
+	if (i8042_noloop && command == I8042_CMD_AUX_LOOP)
+		return -1;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+
+	retval = i8042_wait_write();
+	if (!retval) {
+		dbg("%02x -> i8042 (command)", command & 0xff);
+		i8042_write_command(command & 0xff);
+	}
+
+	if (!retval)
+		for (i = 0; i < ((command >> 12) & 0xf); i++) {
+			if ((retval = i8042_wait_write())) break;
+			dbg("%02x -> i8042 (parameter)", param[i]);
+			i8042_write_data(param[i]);
+		}
+
+	if (!retval)
+		for (i = 0; i < ((command >> 8) & 0xf); i++) {
+			if ((retval = i8042_wait_read())) break;
+			if (i8042_read_status() & I8042_STR_AUXDATA)
+				param[i] = ~i8042_read_data();
+			else
+				param[i] = i8042_read_data();
+			dbg("%02x <- i8042 (return)", param[i]);
+		}
+
+	spin_unlock_irqrestore(&i8042_lock, flags);
+
+	if (retval)
+		dbg("     -- i8042 (timeout)");
+
+	return retval;
+}
+
+/*
+ * i8042_kbd_write() sends a byte out through the keyboard interface.
+ */
+
+static int i8042_kbd_write(struct serio *port, unsigned char c)
+{
+	unsigned long flags;
+	int retval = 0;
+
+	spin_lock_irqsave(&i8042_lock, flags);
+
+	if(!(retval = i8042_wait_write())) {
+		dbg("%02x -> i8042 (kbd-data)", c);
+		i8042_write_data(c);
+	}
+
+	spin_unlock_irqrestore(&i8042_lock, flags);
+
+	return retval;
+}
+
+/*
+ * i8042_aux_write() sends a byte out through the aux interface.
+ */
+
+static int i8042_aux_write(struct serio *serio, unsigned char c)
+{
+	struct i8042_port *port = serio->port_data;
+	int retval;
+
+/*
+ * Send the byte out.
+ */
+
+	if (port->mux == -1)
+		retval = i8042_command(&c, I8042_CMD_AUX_SEND);
+	else
+		retval = i8042_command(&c, I8042_CMD_MUX_SEND + port->mux);
+
+/*
+ * Make sure the interrupt happens and the character is received even
+ * in the case the IRQ isn't wired, so that we can receive further
+ * characters later.
+ */
+
+	i8042_interrupt(0, NULL, NULL);
+	return retval;
+}
+
+/*
+ * i8042_activate_port() enables port on a chip.
+ */
+
+static int i8042_activate_port(struct i8042_port *port)
+{
+	if (!port->serio)
+		return -1;
+
+	i8042_flush();
+
+	/*
+	 * Enable port again here because it is disabled if we are
+	 * resuming (normally it is enabled already).
+	 */
+	i8042_ctr &= ~port->disable;
+
+	i8042_ctr |= port->irqen;
+
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+		i8042_ctr &= ~port->irqen;
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/*
+ * i8042_open() is called when a port is open by the higher layer.
+ * It allocates the interrupt and calls i8042_enable_port.
+ */
+
+static int i8042_open(struct serio *serio)
+{
+	struct i8042_port *port = serio->port_data;
+
+	if (port->mux != -1)
+		if (i8042_mux_open++)
+			return 0;
+
+	if (request_irq(port->irq, i8042_interrupt,
+			SA_SHIRQ, "i8042", i8042_request_irq_cookie)) {
+		printk(KERN_ERR "i8042.c: Can't get irq %d for %s, unregistering the port.\n", port->irq, port->name);
+		goto irq_fail;
+	}
+
+	if (i8042_activate_port(port)) {
+		printk(KERN_ERR "i8042.c: Can't activate %s, unregistering the port\n", port->name);
+		goto activate_fail;
+	}
+
+	i8042_interrupt(0, NULL, NULL);
+
+	return 0;
+
+activate_fail:
+	free_irq(port->irq, i8042_request_irq_cookie);
+
+irq_fail:
+	serio_unregister_port_delayed(serio);
+
+	return -1;
+}
+
+/*
+ * i8042_close() frees the interrupt, so that it can possibly be used
+ * by another driver. We never know - if the user doesn't have a mouse,
+ * the BIOS could have used the AUX interrupt for PCI.
+ */
+
+static void i8042_close(struct serio *serio)
+{
+	struct i8042_port *port = serio->port_data;
+
+	if (port->mux != -1)
+		if (--i8042_mux_open)
+			return;
+
+	i8042_ctr &= ~port->irqen;
+
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+		printk(KERN_WARNING "i8042.c: Can't write CTR while closing %s.\n", port->name);
+/*
+ * We still want to continue and free IRQ so if more data keeps coming in
+ * kernel will just ignore the irq.
+ */
+	}
+
+	free_irq(port->irq, i8042_request_irq_cookie);
+
+	i8042_flush();
+}
+
+/*
+ * i8042_start() is called by serio core when port is about to finish
+ * registering. It will mark port as existing so i8042_interrupt can
+ * start sending data through it.
+ */
+static int i8042_start(struct serio *serio)
+{
+	struct i8042_port *port = serio->port_data;
+
+	port->exists = 1;
+	mb();
+	return 0;
+}
+
+/*
+ * i8042_stop() marks serio port as non-existing so i8042_interrupt
+ * will not try to send data to the port that is about to go away.
+ * The function is called by serio core as part of unregister procedure.
+ */
+static void i8042_stop(struct serio *serio)
+{
+	struct i8042_port *port = serio->port_data;
+
+	port->exists = 0;
+	synchronize_kernel();
+	port->serio = NULL;
+}
+
+/*
+ * i8042_interrupt() is the most important function in this driver -
+ * it handles the interrupts from the i8042, and sends incoming bytes
+ * to the upper layers.
+ */
+
+static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct i8042_port *port;
+	unsigned long flags;
+	unsigned char str, data;
+	unsigned int dfl;
+	unsigned int port_no;
+	int ret;
+
+	mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
+
+	spin_lock_irqsave(&i8042_lock, flags);
+	str = i8042_read_status();
+	if (unlikely(~str & I8042_STR_OBF)) {
+		spin_unlock_irqrestore(&i8042_lock, flags);
+		if (irq) dbg("Interrupt %d, without any data", irq);
+		ret = 0;
+		goto out;
+	}
+	data = i8042_read_data();
+	spin_unlock_irqrestore(&i8042_lock, flags);
+
+	if (i8042_mux_present && (str & I8042_STR_AUXDATA)) {
+		static unsigned long last_transmit;
+		static unsigned char last_str;
+
+		dfl = 0;
+		if (str & I8042_STR_MUXERR) {
+			dbg("MUX error, status is %02x, data is %02x", str, data);
+			switch (data) {
+				default:
+/*
+ * When MUXERR condition is signalled the data register can only contain
+ * 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately
+ * it is not always the case. Some KBC just get confused which port the
+ * data came from and signal error leaving the data intact. They _do not_
+ * revert to legacy mode (actually I've never seen KBC reverting to legacy
+ * mode yet, when we see one we'll add proper handling).
+ * Anyway, we will assume that the data came from the same serio last byte
+ * was transmitted (if transmission happened not too long ago).
+ */
+					if (time_before(jiffies, last_transmit + HZ/10)) {
+						str = last_str;
+						break;
+					}
+					/* fall through - report timeout */
+				case 0xfd:
+				case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break;
+				case 0xff: dfl = SERIO_PARITY;  data = 0xfe; break;
+			}
+		}
+
+		port_no = I8042_MUX_PORT_NO + ((str >> 6) & 3);
+		last_str = str;
+		last_transmit = jiffies;
+	} else {
+
+		dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
+		      ((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
+
+		port_no = (str & I8042_STR_AUXDATA) ?
+				I8042_AUX_PORT_NO : I8042_KBD_PORT_NO;
+	}
+
+	port = &i8042_ports[port_no];
+
+	dbg("%02x <- i8042 (interrupt, %s, %d%s%s)",
+	    data, port->name, irq,
+	    dfl & SERIO_PARITY ? ", bad parity" : "",
+	    dfl & SERIO_TIMEOUT ? ", timeout" : "");
+
+	if (likely(port->exists))
+		serio_interrupt(port->serio, data, dfl, regs);
+
+	ret = 1;
+out:
+	return IRQ_RETVAL(ret);
+}
+
+/*
+ * i8042_set_mux_mode checks whether the controller has an active
+ * multiplexor and puts the chip into Multiplexed (1) or Legacy (0) mode.
+ */
+
+static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version)
+{
+
+	unsigned char param;
+/*
+ * Get rid of bytes in the queue.
+ */
+
+	i8042_flush();
+
+/*
+ * Internal loopback test - send three bytes, they should come back from the
+ * mouse interface, the last should be version. Note that we negate mouseport
+ * command responses for the i8042_check_aux() routine.
+ */
+
+	param = 0xf0;
+	if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != 0x0f)
+		return -1;
+	param = mode ? 0x56 : 0xf6;
+	if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != (mode ? 0xa9 : 0x09))
+		return -1;
+	param = mode ? 0xa4 : 0xa5;
+	if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param == (mode ? 0x5b : 0x5a))
+		return -1;
+
+	if (mux_version)
+		*mux_version = ~param;
+
+	return 0;
+}
+
+
+/*
+ * i8042_enable_mux_ports enables 4 individual AUX ports after
+ * the controller has been switched into Multiplexed mode
+ */
+
+static int i8042_enable_mux_ports(void)
+{
+	unsigned char param;
+	int i;
+/*
+ * Disable all muxed ports by disabling AUX.
+ */
+
+	i8042_ctr |= I8042_CTR_AUXDIS;
+	i8042_ctr &= ~I8042_CTR_AUXINT;
+
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+		printk(KERN_ERR "i8042.c: Failed to disable AUX port, can't use MUX.\n");
+		return -1;
+	}
+
+/*
+ * Enable all muxed ports.
+ */
+
+	for (i = 0; i < 4; i++) {
+		i8042_command(&param, I8042_CMD_MUX_PFX + i);
+		i8042_command(&param, I8042_CMD_AUX_ENABLE);
+	}
+
+	return 0;
+}
+
+
+/*
+ * i8042_check_mux() checks whether the controller supports the PS/2 Active
+ * Multiplexing specification by Synaptics, Phoenix, Insyde and
+ * LCS/Telegraphics.
+ */
+
+static int __init i8042_check_mux(void)
+{
+	unsigned char mux_version;
+
+	if (i8042_set_mux_mode(1, &mux_version))
+		return -1;
+
+	/* Workaround for interference with USB Legacy emulation */
+	/* that causes a v10.12 MUX to be found. */
+	if (mux_version == 0xAC)
+		return -1;
+
+	printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n",
+		(mux_version >> 4) & 0xf, mux_version & 0xf);
+
+	if (i8042_enable_mux_ports())
+		return -1;
+
+	i8042_mux_present = 1;
+	return 0;
+}
+
+
+/*
+ * i8042_check_aux() applies as much paranoia as it can at detecting
+ * the presence of an AUX interface.
+ */
+
+static int __init i8042_check_aux(void)
+{
+	unsigned char param;
+	static int i8042_check_aux_cookie;
+
+/*
+ * Check if AUX irq is available. If it isn't, then there is no point
+ * in trying to detect AUX presence.
+ */
+
+	if (request_irq(i8042_ports[I8042_AUX_PORT_NO].irq, i8042_interrupt,
+			SA_SHIRQ, "i8042", &i8042_check_aux_cookie))
+                return -1;
+	free_irq(i8042_ports[I8042_AUX_PORT_NO].irq, &i8042_check_aux_cookie);
+
+/*
+ * Get rid of bytes in the queue.
+ */
+
+	i8042_flush();
+
+/*
+ * Internal loopback test - filters out AT-type i8042's. Unfortunately
+ * SiS screwed up and their 5597 doesn't support the LOOP command even
+ * though it has an AUX port.
+ */
+
+	param = 0x5a;
+	if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != 0xa5) {
+
+/*
+ * External connection test - filters out AT-soldered PS/2 i8042's
+ * 0x00 - no error, 0x01-0x03 - clock/data stuck, 0xff - general error
+ * 0xfa - no error on some notebooks which ignore the spec
+ * Because it's common for chipsets to return error on perfectly functioning
+ * AUX ports, we test for this only when the LOOP command failed.
+ */
+
+		if (i8042_command(&param, I8042_CMD_AUX_TEST)
+		    	|| (param && param != 0xfa && param != 0xff))
+				return -1;
+	}
+
+/*
+ * Bit assignment test - filters out PS/2 i8042's in AT mode
+ */
+
+	if (i8042_command(&param, I8042_CMD_AUX_DISABLE))
+		return -1;
+	if (i8042_command(&param, I8042_CMD_CTL_RCTR) || (~param & I8042_CTR_AUXDIS)) {
+		printk(KERN_WARNING "Failed to disable AUX port, but continuing anyway... Is this a SiS?\n");
+		printk(KERN_WARNING "If AUX port is really absent please use the 'i8042.noaux' option.\n");
+	}
+
+	if (i8042_command(&param, I8042_CMD_AUX_ENABLE))
+		return -1;
+	if (i8042_command(&param, I8042_CMD_CTL_RCTR) || (param & I8042_CTR_AUXDIS))
+		return -1;
+
+/*
+ * Disable the interface.
+ */
+
+	i8042_ctr |= I8042_CTR_AUXDIS;
+	i8042_ctr &= ~I8042_CTR_AUXINT;
+
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+		return -1;
+
+	return 0;
+}
+
+
+/*
+ * i8042_port_register() marks the device as existing,
+ * registers it, and reports to the user.
+ */
+
+static int __init i8042_port_register(struct i8042_port *port)
+{
+	i8042_ctr &= ~port->disable;
+
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+		printk(KERN_WARNING "i8042.c: Can't write CTR while registering.\n");
+		kfree(port->serio);
+		port->serio = NULL;
+		i8042_ctr |= port->disable;
+		return -1;
+	}
+
+	printk(KERN_INFO "serio: i8042 %s port at %#lx,%#lx irq %d\n",
+	       port->name,
+	       (unsigned long) I8042_DATA_REG,
+	       (unsigned long) I8042_COMMAND_REG,
+	       port->irq);
+
+	serio_register_port(port->serio);
+
+	return 0;
+}
+
+
+static void i8042_timer_func(unsigned long data)
+{
+	i8042_interrupt(0, NULL, NULL);
+}
+
+
+/*
+ * i8042_controller init initializes the i8042 controller, and,
+ * most importantly, sets it into non-xlated mode if that's
+ * desired.
+ */
+
+static int i8042_controller_init(void)
+{
+	unsigned long flags;
+
+/*
+ * Test the i8042. We need to know if it thinks it's working correctly
+ * before doing anything else.
+ */
+
+	if (i8042_flush() == I8042_BUFFER_SIZE) {
+		printk(KERN_ERR "i8042.c: No controller found.\n");
+		return -1;
+	}
+
+	if (i8042_reset) {
+
+		unsigned char param;
+
+		if (i8042_command(&param, I8042_CMD_CTL_TEST)) {
+			printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n");
+			return -1;
+		}
+
+		if (param != I8042_RET_CTL_TEST) {
+			printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n",
+				 param, I8042_RET_CTL_TEST);
+			return -1;
+		}
+	}
+
+/*
+ * Save the CTR for restoral on unload / reboot.
+ */
+
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) {
+		printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n");
+		return -1;
+	}
+
+	i8042_initial_ctr = i8042_ctr;
+
+/*
+ * Disable the keyboard interface and interrupt.
+ */
+
+	i8042_ctr |= I8042_CTR_KBDDIS;
+	i8042_ctr &= ~I8042_CTR_KBDINT;
+
+/*
+ * Handle keylock.
+ */
+
+	spin_lock_irqsave(&i8042_lock, flags);
+	if (~i8042_read_status() & I8042_STR_KEYLOCK) {
+		if (i8042_unlock)
+			i8042_ctr |= I8042_CTR_IGNKEYLOCK;
+		 else
+			printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n");
+	}
+	spin_unlock_irqrestore(&i8042_lock, flags);
+
+/*
+ * If the chip is configured into nontranslated mode by the BIOS, don't
+ * bother enabling translating and be happy.
+ */
+
+	if (~i8042_ctr & I8042_CTR_XLATE)
+		i8042_direct = 1;
+
+/*
+ * Set nontranslated mode for the kbd interface if requested by an option.
+ * After this the kbd interface becomes a simple serial in/out, like the aux
+ * interface is. We don't do this by default, since it can confuse notebook
+ * BIOSes.
+ */
+
+	if (i8042_direct)
+		i8042_ctr &= ~I8042_CTR_XLATE;
+
+/*
+ * Write CTR back.
+ */
+
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+		printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Reset the controller.
+ */
+static void i8042_controller_reset(void)
+{
+	unsigned char param;
+
+/*
+ * Reset the controller if requested.
+ */
+
+	if (i8042_reset)
+		if (i8042_command(&param, I8042_CMD_CTL_TEST))
+			printk(KERN_ERR "i8042.c: i8042 controller reset timeout.\n");
+
+/*
+ * Disable MUX mode if present.
+ */
+
+	if (i8042_mux_present)
+		i8042_set_mux_mode(0, NULL);
+
+/*
+ * Restore the original control register setting.
+ */
+
+	i8042_ctr = i8042_initial_ctr;
+
+	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+		printk(KERN_WARNING "i8042.c: Can't restore CTR.\n");
+}
+
+
+/*
+ * Here we try to reset everything back to a state in which the BIOS will be
+ * able to talk to the hardware when rebooting.
+ */
+
+static void i8042_controller_cleanup(void)
+{
+	int i;
+
+	i8042_flush();
+
+/*
+ * Reset anything that is connected to the ports.
+ */
+
+	for (i = 0; i < I8042_NUM_PORTS; i++)
+		if (i8042_ports[i].exists)
+			serio_cleanup(i8042_ports[i].serio);
+
+	i8042_controller_reset();
+}
+
+
+/*
+ * i8042_panic_blink() will flash the keyboard LEDs and is called when
+ * kernel panics. Flashing LEDs is useful for users running X who may
+ * not see the console and will help distingushing panics from "real"
+ * lockups.
+ *
+ * Note that DELAY has a limit of 10ms so we will not get stuck here
+ * waiting for KBC to free up even if KBD interrupt is off
+ */
+
+#define DELAY do { mdelay(1); if (++delay > 10) return delay; } while(0)
+
+static long i8042_panic_blink(long count)
+{
+	long delay = 0;
+	static long last_blink;
+	static char led;
+
+	/*
+	 * We expect frequency to be about 1/2s. KDB uses about 1s.
+	 * Make sure they are different.
+	 */
+	if (!i8042_blink_frequency)
+		return 0;
+	if (count - last_blink < i8042_blink_frequency)
+		return 0;
+
+	led ^= 0x01 | 0x04;
+	while (i8042_read_status() & I8042_STR_IBF)
+		DELAY;
+	i8042_write_data(0xed); /* set leds */
+	DELAY;
+	while (i8042_read_status() & I8042_STR_IBF)
+		DELAY;
+	DELAY;
+	i8042_write_data(led);
+	DELAY;
+	last_blink = count;
+	return delay;
+}
+
+#undef DELAY
+
+/*
+ * Here we try to restore the original BIOS settings
+ */
+
+static int i8042_suspend(struct device *dev, pm_message_t state, u32 level)
+{
+	if (level == SUSPEND_DISABLE) {
+		del_timer_sync(&i8042_timer);
+		i8042_controller_reset();
+	}
+
+	return 0;
+}
+
+
+/*
+ * Here we try to reset everything back to a state in which suspended
+ */
+
+static int i8042_resume(struct device *dev, u32 level)
+{
+	int i;
+
+	if (level != RESUME_ENABLE)
+		return 0;
+
+	if (i8042_controller_init()) {
+		printk(KERN_ERR "i8042: resume failed\n");
+		return -1;
+	}
+
+	if (i8042_mux_present)
+		if (i8042_set_mux_mode(1, NULL) || i8042_enable_mux_ports())
+			printk(KERN_WARNING "i8042: failed to resume active multiplexor, mouse won't work.\n");
+
+/*
+ * Activate all ports.
+ */
+
+	for (i = 0; i < I8042_NUM_PORTS; i++)
+		i8042_activate_port(&i8042_ports[i]);
+
+/*
+ * Restart timer (for polling "stuck" data)
+ */
+	mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
+
+	panic_blink = i8042_panic_blink;
+
+	return 0;
+
+}
+
+/*
+ * We need to reset the 8042 back to original mode on system shutdown,
+ * because otherwise BIOSes will be confused.
+ */
+
+static void i8042_shutdown(struct device *dev)
+{
+	i8042_controller_cleanup();
+}
+
+static struct device_driver i8042_driver = {
+	.name		= "i8042",
+	.bus		= &platform_bus_type,
+	.suspend	= i8042_suspend,
+	.resume		= i8042_resume,
+	.shutdown	= i8042_shutdown,
+};
+
+static void __init i8042_create_kbd_port(void)
+{
+	struct serio *serio;
+	struct i8042_port *port = &i8042_ports[I8042_KBD_PORT_NO];
+
+	serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (serio) {
+		memset(serio, 0, sizeof(struct serio));
+		serio->id.type		= i8042_direct ? SERIO_8042 : SERIO_8042_XL;
+		serio->write		= i8042_dumbkbd ? NULL : i8042_kbd_write;
+		serio->open		= i8042_open;
+		serio->close		= i8042_close;
+		serio->start		= i8042_start;
+		serio->stop		= i8042_stop;
+		serio->port_data	= port;
+		serio->dev.parent	= &i8042_platform_device->dev;
+		strlcpy(serio->name, "i8042 Kbd Port", sizeof(serio->name));
+		strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys));
+
+		port->serio = serio;
+		i8042_port_register(port);
+	}
+}
+
+static void __init i8042_create_aux_port(void)
+{
+	struct serio *serio;
+	struct i8042_port *port = &i8042_ports[I8042_AUX_PORT_NO];
+
+	serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (serio) {
+		memset(serio, 0, sizeof(struct serio));
+		serio->id.type		= SERIO_8042;
+		serio->write		= i8042_aux_write;
+		serio->open		= i8042_open;
+		serio->close		= i8042_close;
+		serio->start		= i8042_start;
+		serio->stop		= i8042_stop;
+		serio->port_data	= port;
+		serio->dev.parent	= &i8042_platform_device->dev;
+		strlcpy(serio->name, "i8042 Aux Port", sizeof(serio->name));
+		strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys));
+
+		port->serio = serio;
+		i8042_port_register(port);
+	}
+}
+
+static void __init i8042_create_mux_port(int index)
+{
+	struct serio *serio;
+	struct i8042_port *port = &i8042_ports[I8042_MUX_PORT_NO + index];
+
+	serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (serio) {
+		memset(serio, 0, sizeof(struct serio));
+		serio->id.type		= SERIO_8042;
+		serio->write		= i8042_aux_write;
+		serio->open		= i8042_open;
+		serio->close		= i8042_close;
+		serio->start		= i8042_start;
+		serio->stop		= i8042_stop;
+		serio->port_data	= port;
+		serio->dev.parent	= &i8042_platform_device->dev;
+		snprintf(serio->name, sizeof(serio->name), "i8042 Aux-%d Port", index);
+		snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, index + 1);
+
+		*port = i8042_ports[I8042_AUX_PORT_NO];
+		port->exists = 0;
+		snprintf(port->name, sizeof(port->name), "AUX%d", index);
+		port->mux = index;
+		port->serio = serio;
+		i8042_port_register(port);
+	}
+}
+
+static int __init i8042_init(void)
+{
+	int i;
+	int err;
+
+	dbg_init();
+
+	init_timer(&i8042_timer);
+	i8042_timer.function = i8042_timer_func;
+
+	if (i8042_platform_init())
+		return -EBUSY;
+
+	i8042_ports[I8042_AUX_PORT_NO].irq = I8042_AUX_IRQ;
+	i8042_ports[I8042_KBD_PORT_NO].irq = I8042_KBD_IRQ;
+
+	if (i8042_controller_init()) {
+		i8042_platform_exit();
+		return -ENODEV;
+	}
+
+	err = driver_register(&i8042_driver);
+	if (err) {
+		i8042_platform_exit();
+		return err;
+	}
+
+	i8042_platform_device = platform_device_register_simple("i8042", -1, NULL, 0);
+	if (IS_ERR(i8042_platform_device)) {
+		driver_unregister(&i8042_driver);
+		i8042_platform_exit();
+		return PTR_ERR(i8042_platform_device);
+	}
+
+	if (!i8042_noaux && !i8042_check_aux()) {
+		if (!i8042_nomux && !i8042_check_mux())
+			for (i = 0; i < I8042_NUM_MUX_PORTS; i++)
+				i8042_create_mux_port(i);
+		else
+			i8042_create_aux_port();
+	}
+
+	i8042_create_kbd_port();
+
+	mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
+
+	return 0;
+}
+
+static void __exit i8042_exit(void)
+{
+	int i;
+
+	i8042_controller_cleanup();
+
+	for (i = 0; i < I8042_NUM_PORTS; i++)
+		if (i8042_ports[i].exists)
+			serio_unregister_port(i8042_ports[i].serio);
+
+	del_timer_sync(&i8042_timer);
+
+	platform_device_unregister(i8042_platform_device);
+	driver_unregister(&i8042_driver);
+
+	i8042_platform_exit();
+
+	panic_blink = NULL;
+}
+
+module_init(i8042_init);
+module_exit(i8042_exit);
diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h
new file mode 100644
index 0000000..1383503
--- /dev/null
+++ b/drivers/input/serio/i8042.h
@@ -0,0 +1,133 @@
+#ifndef _I8042_H
+#define _I8042_H
+
+#include <linux/config.h>
+
+/*
+ *  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.
+ */
+
+/*
+ * Arch-dependent inline functions and defines.
+ */
+
+#if defined(CONFIG_MACH_JAZZ)
+#include "i8042-jazzio.h"
+#elif defined(CONFIG_SGI_IP22)
+#include "i8042-ip22io.h"
+#elif defined(CONFIG_PPC)
+#include "i8042-ppcio.h"
+#elif defined(CONFIG_SPARC32) || defined(CONFIG_SPARC64)
+#include "i8042-sparcio.h"
+#elif defined(CONFIG_X86) || defined(CONFIG_IA64)
+#include "i8042-x86ia64io.h"
+#else
+#include "i8042-io.h"
+#endif
+
+/*
+ * This is in 50us units, the time we wait for the i8042 to react. This
+ * has to be long enough for the i8042 itself to timeout on sending a byte
+ * to a non-existent mouse.
+ */
+
+#define I8042_CTL_TIMEOUT	10000
+
+/*
+ * When the device isn't opened and it's interrupts aren't used, we poll it at
+ * regular intervals to see if any characters arrived. If yes, we can start
+ * probing for any mouse / keyboard connected. This is the period of the
+ * polling.
+ */
+
+#define I8042_POLL_PERIOD	HZ/20
+
+/*
+ * Status register bits.
+ */
+
+#define I8042_STR_PARITY	0x80
+#define I8042_STR_TIMEOUT	0x40
+#define I8042_STR_AUXDATA	0x20
+#define I8042_STR_KEYLOCK	0x10
+#define I8042_STR_CMDDAT	0x08
+#define I8042_STR_MUXERR	0x04
+#define I8042_STR_IBF		0x02
+#define	I8042_STR_OBF		0x01
+
+/*
+ * Control register bits.
+ */
+
+#define I8042_CTR_KBDINT	0x01
+#define I8042_CTR_AUXINT	0x02
+#define I8042_CTR_IGNKEYLOCK	0x08
+#define I8042_CTR_KBDDIS	0x10
+#define I8042_CTR_AUXDIS	0x20
+#define I8042_CTR_XLATE		0x40
+
+/*
+ * Commands.
+ */
+
+#define I8042_CMD_CTL_RCTR	0x0120
+#define I8042_CMD_CTL_WCTR	0x1060
+#define I8042_CMD_CTL_TEST	0x01aa
+
+#define I8042_CMD_KBD_DISABLE	0x00ad
+#define I8042_CMD_KBD_ENABLE	0x00ae
+#define I8042_CMD_KBD_TEST	0x01ab
+#define I8042_CMD_KBD_LOOP	0x11d2
+
+#define I8042_CMD_AUX_DISABLE	0x00a7
+#define I8042_CMD_AUX_ENABLE	0x00a8
+#define I8042_CMD_AUX_TEST	0x01a9
+#define I8042_CMD_AUX_SEND	0x10d4
+#define I8042_CMD_AUX_LOOP	0x11d3
+
+#define I8042_CMD_MUX_PFX	0x0090
+#define I8042_CMD_MUX_SEND	0x1090
+
+/*
+ * Return codes.
+ */
+
+#define I8042_RET_CTL_TEST	0x55
+
+/*
+ * Expected maximum internal i8042 buffer size. This is used for flushing
+ * the i8042 buffers.
+ */
+
+#define I8042_BUFFER_SIZE	16
+
+/*
+ * Number of AUX ports on controllers supporting active multiplexing
+ * specification
+ */
+
+#define I8042_NUM_MUX_PORTS	4
+
+/*
+ * Debug.
+ */
+
+#ifdef DEBUG
+static unsigned long i8042_start_time;
+#define dbg_init() do { i8042_start_time = jiffies; } while (0)
+#define dbg(format, arg...) 							\
+	do { 									\
+		if (i8042_debug)						\
+			printk(KERN_DEBUG __FILE__ ": " format " [%d]\n" ,	\
+	 			## arg, (int) (jiffies - i8042_start_time));	\
+	} while (0)
+#else
+#define dbg_init() do { } while (0)
+#define dbg(format, arg...) do {} while (0)
+#endif
+
+#endif /* _I8042_H */
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
new file mode 100644
index 0000000..c978657
--- /dev/null
+++ b/drivers/input/serio/libps2.c
@@ -0,0 +1,305 @@
+/*
+ * PS/2 driver library
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ * Copyright (c) 2004 Dmitry Torokhov
+ */
+
+/*
+ * 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/libps2.h>
+
+#define DRIVER_DESC	"PS/2 driver library"
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION("PS/2 driver library");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(ps2_init);
+EXPORT_SYMBOL(ps2_sendbyte);
+EXPORT_SYMBOL(ps2_command);
+EXPORT_SYMBOL(ps2_schedule_command);
+EXPORT_SYMBOL(ps2_handle_ack);
+EXPORT_SYMBOL(ps2_handle_response);
+EXPORT_SYMBOL(ps2_cmd_aborted);
+
+/* Work structure to schedule execution of a command */
+struct ps2work {
+	struct work_struct work;
+	struct ps2dev *ps2dev;
+	int command;
+	unsigned char param[0];
+};
+
+
+/*
+ * ps2_sendbyte() sends a byte to the mouse, and waits for acknowledge.
+ * It doesn't handle retransmission, though it could - because when there would
+ * be need for retransmissions, the mouse has to be replaced anyway.
+ *
+ * ps2_sendbyte() can only be called from a process context
+ */
+
+int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout)
+{
+	serio_pause_rx(ps2dev->serio);
+	ps2dev->nak = 1;
+	ps2dev->flags |= PS2_FLAG_ACK;
+	serio_continue_rx(ps2dev->serio);
+
+	if (serio_write(ps2dev->serio, byte) == 0)
+		wait_event_timeout(ps2dev->wait,
+				   !(ps2dev->flags & PS2_FLAG_ACK),
+				   msecs_to_jiffies(timeout));
+
+	serio_pause_rx(ps2dev->serio);
+	ps2dev->flags &= ~PS2_FLAG_ACK;
+	serio_continue_rx(ps2dev->serio);
+
+	return -ps2dev->nak;
+}
+
+/*
+ * ps2_command() sends a command and its parameters to the mouse,
+ * then waits for the response and puts it in the param array.
+ *
+ * ps2_command() can only be called from a process context
+ */
+
+int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
+{
+	int timeout;
+	int send = (command >> 12) & 0xf;
+	int receive = (command >> 8) & 0xf;
+	int rc = -1;
+	int i;
+
+	down(&ps2dev->cmd_sem);
+
+	serio_pause_rx(ps2dev->serio);
+	ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0;
+	ps2dev->cmdcnt = receive;
+	if (receive && param)
+		for (i = 0; i < receive; i++)
+			ps2dev->cmdbuf[(receive - 1) - i] = param[i];
+	serio_continue_rx(ps2dev->serio);
+
+	/*
+	 * Some devices (Synaptics) peform the reset before
+	 * ACKing the reset command, and so it can take a long
+	 * time before the ACK arrrives.
+	 */
+	if (command & 0xff)
+		if (ps2_sendbyte(ps2dev, command & 0xff,
+			command == PS2_CMD_RESET_BAT ? 1000 : 200))
+			goto out;
+
+	for (i = 0; i < send; i++)
+		if (ps2_sendbyte(ps2dev, param[i], 200))
+			goto out;
+
+	/*
+	 * The reset command takes a long time to execute.
+	 */
+	timeout = msecs_to_jiffies(command == PS2_CMD_RESET_BAT ? 4000 : 500);
+
+	timeout = wait_event_timeout(ps2dev->wait,
+				     !(ps2dev->flags & PS2_FLAG_CMD1), timeout);
+
+	if (ps2dev->cmdcnt && timeout > 0) {
+
+		if (command == PS2_CMD_RESET_BAT && timeout > msecs_to_jiffies(100)) {
+			/*
+			 * Device has sent the first response byte
+			 * after a reset command, reset is thus done,
+			 * shorten the timeout. The next byte will come
+			 * soon (keyboard) or not at all (mouse).
+			 */
+			timeout = msecs_to_jiffies(100);
+		}
+
+		if (command == PS2_CMD_GETID &&
+		    ps2dev->cmdbuf[receive - 1] != 0xab && /* Regular keyboards */
+		    ps2dev->cmdbuf[receive - 1] != 0xac && /* NCD Sun keyboard */
+		    ps2dev->cmdbuf[receive - 1] != 0x2b && /* Trust keyboard, translated */
+		    ps2dev->cmdbuf[receive - 1] != 0x5d && /* Trust keyboard */
+		    ps2dev->cmdbuf[receive - 1] != 0x60 && /* NMB SGI keyboard, translated */
+		    ps2dev->cmdbuf[receive - 1] != 0x47) { /* NMB SGI keyboard */
+			/*
+			 * Device behind the port is not a keyboard
+			 * so we don't need to wait for the 2nd byte
+			 * of ID response.
+			 */
+			serio_pause_rx(ps2dev->serio);
+			ps2dev->flags = ps2dev->cmdcnt = 0;
+			serio_continue_rx(ps2dev->serio);
+		}
+
+		wait_event_timeout(ps2dev->wait,
+				   !(ps2dev->flags & PS2_FLAG_CMD), timeout);
+	}
+
+	if (param)
+		for (i = 0; i < receive; i++)
+			param[i] = ps2dev->cmdbuf[(receive - 1) - i];
+
+	if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1))
+		goto out;
+
+	rc = 0;
+
+out:
+	serio_pause_rx(ps2dev->serio);
+	ps2dev->flags = 0;
+	serio_continue_rx(ps2dev->serio);
+
+	up(&ps2dev->cmd_sem);
+	return rc;
+}
+
+/*
+ * ps2_execute_scheduled_command() sends a command, previously scheduled by
+ * ps2_schedule_command(), to a PS/2 device (keyboard, mouse, etc.)
+ */
+
+static void ps2_execute_scheduled_command(void *data)
+{
+	struct ps2work *ps2work = data;
+
+	ps2_command(ps2work->ps2dev, ps2work->param, ps2work->command);
+	kfree(ps2work);
+}
+
+/*
+ * ps2_schedule_command() allows to schedule delayed execution of a PS/2
+ * command and can be used to issue a command from an interrupt or softirq
+ * context.
+ */
+
+int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int command)
+{
+	struct ps2work *ps2work;
+	int send = (command >> 12) & 0xf;
+	int receive = (command >> 8) & 0xf;
+
+	if (!(ps2work = kmalloc(sizeof(struct ps2work) + max(send, receive), GFP_ATOMIC)))
+		return -1;
+
+	memset(ps2work, 0, sizeof(struct ps2work));
+	ps2work->ps2dev = ps2dev;
+	ps2work->command = command;
+	memcpy(ps2work->param, param, send);
+	INIT_WORK(&ps2work->work, ps2_execute_scheduled_command, ps2work);
+
+	if (!schedule_work(&ps2work->work)) {
+		kfree(ps2work);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * ps2_init() initializes ps2dev structure
+ */
+
+void ps2_init(struct ps2dev *ps2dev, struct serio *serio)
+{
+	init_MUTEX(&ps2dev->cmd_sem);
+	init_waitqueue_head(&ps2dev->wait);
+	ps2dev->serio = serio;
+}
+
+/*
+ * ps2_handle_ack() is supposed to be used in interrupt handler
+ * to properly process ACK/NAK of a command from a PS/2 device.
+ */
+
+int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
+{
+	switch (data) {
+		case PS2_RET_ACK:
+			ps2dev->nak = 0;
+			break;
+
+		case PS2_RET_NAK:
+			ps2dev->nak = 1;
+			break;
+
+		/*
+		 * Workaround for mice which don't ACK the Get ID command.
+		 * These are valid mouse IDs that we recognize.
+		 */
+		case 0x00:
+		case 0x03:
+		case 0x04:
+			if (ps2dev->flags & PS2_FLAG_WAITID) {
+				ps2dev->nak = 0;
+				break;
+			}
+			/* Fall through */
+		default:
+			return 0;
+	}
+
+
+	if (!ps2dev->nak && ps2dev->cmdcnt)
+		ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1;
+
+	ps2dev->flags &= ~PS2_FLAG_ACK;
+	wake_up(&ps2dev->wait);
+
+	if (data != PS2_RET_ACK)
+		ps2_handle_response(ps2dev, data);
+
+	return 1;
+}
+
+/*
+ * ps2_handle_response() is supposed to be used in interrupt handler
+ * to properly store device's response to a command and notify process
+ * waiting for completion of the command.
+ */
+
+int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data)
+{
+	if (ps2dev->cmdcnt)
+		ps2dev->cmdbuf[--ps2dev->cmdcnt] = data;
+
+	if (ps2dev->flags & PS2_FLAG_CMD1) {
+		ps2dev->flags &= ~PS2_FLAG_CMD1;
+		if (ps2dev->cmdcnt)
+			wake_up(&ps2dev->wait);
+	}
+
+	if (!ps2dev->cmdcnt) {
+		ps2dev->flags &= ~PS2_FLAG_CMD;
+		wake_up(&ps2dev->wait);
+	}
+
+	return 1;
+}
+
+void ps2_cmd_aborted(struct ps2dev *ps2dev)
+{
+	if (ps2dev->flags & PS2_FLAG_ACK)
+		ps2dev->nak = 1;
+
+	if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD))
+		wake_up(&ps2dev->wait);
+
+	ps2dev->flags = 0;
+}
+
diff --git a/drivers/input/serio/maceps2.c b/drivers/input/serio/maceps2.c
new file mode 100644
index 0000000..9880fc1
--- /dev/null
+++ b/drivers/input/serio/maceps2.c
@@ -0,0 +1,176 @@
+/*
+ * SGI O2 MACE PS2 controller driver for linux
+ *
+ * Copyright (C) 2002 Vivien Chappelier
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/ip32/mace.h>
+#include <asm/ip32/ip32_ints.h>
+
+MODULE_AUTHOR("Vivien Chappelier <vivien.chappelier@linux-mips.org");
+MODULE_DESCRIPTION("SGI O2 MACE PS2 controller driver");
+MODULE_LICENSE("GPL");
+
+#define MACE_PS2_TIMEOUT 10000 /* in 50us unit */
+
+#define PS2_STATUS_CLOCK_SIGNAL  BIT(0) /* external clock signal */
+#define PS2_STATUS_CLOCK_INHIBIT BIT(1) /* clken output signal */
+#define PS2_STATUS_TX_INPROGRESS BIT(2) /* transmission in progress */
+#define PS2_STATUS_TX_EMPTY      BIT(3) /* empty transmit buffer */
+#define PS2_STATUS_RX_FULL       BIT(4) /* full receive buffer */
+#define PS2_STATUS_RX_INPROGRESS BIT(5) /* reception in progress */
+#define PS2_STATUS_ERROR_PARITY  BIT(6) /* parity error */
+#define PS2_STATUS_ERROR_FRAMING BIT(7) /* framing error */
+
+#define PS2_CONTROL_TX_CLOCK_DISABLE BIT(0) /* inhibit clock signal after TX */
+#define PS2_CONTROL_TX_ENABLE        BIT(1) /* transmit enable */
+#define PS2_CONTROL_TX_INT_ENABLE    BIT(2) /* enable transmit interrupt */
+#define PS2_CONTROL_RX_INT_ENABLE    BIT(3) /* enable receive interrupt */
+#define PS2_CONTROL_RX_CLOCK_ENABLE  BIT(4) /* pause reception if set to 0 */
+#define PS2_CONTROL_RESET            BIT(5) /* reset */
+
+struct maceps2_data {
+	struct mace_ps2port *port;
+	int irq;
+};
+
+static struct maceps2_data port_data[2];
+static struct serio *maceps2_port[2];
+static struct platform_device *maceps2_device;
+
+static int maceps2_write(struct serio *dev, unsigned char val)
+{
+	struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port;
+	unsigned int timeout = MACE_PS2_TIMEOUT;
+
+	do {
+		if (port->status & PS2_STATUS_TX_EMPTY) {
+			port->tx = val;
+			return 0;
+		}
+		udelay(50);
+	} while (timeout--);
+
+	return -1;
+}
+
+static irqreturn_t maceps2_interrupt(int irq, void *dev_id,
+				     struct pt_regs *regs)
+{
+	struct serio *dev = dev_id;
+	struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port;
+	unsigned long byte;
+
+	if (port->status & PS2_STATUS_RX_FULL) {
+		byte = port->rx;
+		serio_interrupt(dev, byte & 0xff, 0, regs);
+        }
+
+	return IRQ_HANDLED;
+}
+
+static int maceps2_open(struct serio *dev)
+{
+	struct maceps2_data *data = (struct maceps2_data *)dev->port_data;
+
+	if (request_irq(data->irq, maceps2_interrupt, 0, "PS2 port", dev)) {
+		printk(KERN_ERR "Could not allocate PS/2 IRQ\n");
+		return -EBUSY;
+	}
+
+	/* Reset port */
+	data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET;
+	udelay(100);
+
+        /* Enable interrupts */
+	data->port->control = PS2_CONTROL_RX_CLOCK_ENABLE |
+			      PS2_CONTROL_TX_ENABLE |
+			      PS2_CONTROL_RX_INT_ENABLE;
+
+	return 0;
+}
+
+static void maceps2_close(struct serio *dev)
+{
+	struct maceps2_data *data = (struct maceps2_data *)dev->port_data;
+
+	data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET;
+	udelay(100);
+	free_irq(data->irq, dev);
+}
+
+
+static struct serio * __init maceps2_allocate_port(int idx)
+{
+	struct serio *serio;
+
+	serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (serio) {
+		memset(serio, 0, sizeof(struct serio));
+		serio->id.type		= SERIO_8042;
+		serio->write		= maceps2_write;
+		serio->open		= maceps2_open;
+		serio->close		= maceps2_close;
+		snprintf(serio->name, sizeof(serio->name), "MACE PS/2 port%d", idx);
+		snprintf(serio->phys, sizeof(serio->phys), "mace/serio%d", idx);
+		serio->port_data	= &port_data[idx];
+		serio->dev.parent	= &maceps2_device->dev;
+	}
+
+	return serio;
+}
+
+
+static int __init maceps2_init(void)
+{
+	maceps2_device = platform_device_register_simple("maceps2", -1, NULL, 0);
+	if (IS_ERR(maceps2_device))
+		return PTR_ERR(maceps2_device);
+
+	port_data[0].port = &mace->perif.ps2.keyb;
+	port_data[0].irq  = MACEISA_KEYB_IRQ;
+	port_data[1].port = &mace->perif.ps2.mouse;
+	port_data[1].irq  = MACEISA_MOUSE_IRQ;
+
+	maceps2_port[0] = maceps2_allocate_port(0);
+	maceps2_port[1] = maceps2_allocate_port(1);
+	if (!maceps2_port[0] || !maceps2_port[1]) {
+		kfree(maceps2_port[0]);
+		kfree(maceps2_port[1]);
+		platform_device_unregister(maceps2_device);
+		return -ENOMEM;
+	}
+
+	serio_register_port(maceps2_port[0]);
+	serio_register_port(maceps2_port[1]);
+
+	return 0;
+}
+
+static void __exit maceps2_exit(void)
+{
+	serio_unregister_port(maceps2_port[0]);
+	serio_unregister_port(maceps2_port[1]);
+	platform_device_unregister(maceps2_device);
+}
+
+module_init(maceps2_init);
+module_exit(maceps2_exit);
diff --git a/drivers/input/serio/parkbd.c b/drivers/input/serio/parkbd.c
new file mode 100644
index 0000000..1d15c28
--- /dev/null
+++ b/drivers/input/serio/parkbd.c
@@ -0,0 +1,218 @@
+/*
+ *  Parallel port to Keyboard port adapter driver for Linux
+ *
+ *  Copyright (c) 1999-2004 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.
+ */
+
+/*
+ * To connect an AT or XT keyboard to the parallel port, a fairly simple adapter
+ * can be made:
+ * 
+ *  Parallel port            Keyboard port
+ *
+ *     +5V --------------------- +5V (4)
+ *  
+ *                 ______
+ *     +5V -------|______|--.
+ *                          |
+ *     ACK (10) ------------|
+ *                          |--- KBD CLOCK (5)
+ *     STROBE (1) ---|<|----'
+ *     
+ *                 ______
+ *     +5V -------|______|--.
+ *                          |
+ *     BUSY (11) -----------|
+ *                          |--- KBD DATA (1)
+ *     AUTOFD (14) --|<|----'
+ *
+ *     GND (18-25) ------------- GND (3)
+ *     
+ * The diodes can be fairly any type, and the resistors should be somewhere
+ * around 5 kOhm, but the adapter will likely work without the resistors,
+ * too.
+ *
+ * The +5V source can be taken either from USB, from mouse or keyboard ports,
+ * or from a joystick port. Unfortunately, the parallel port of a PC doesn't
+ * have a +5V pin, and feeding the keyboard from signal pins is out of question
+ * with 300 mA power reqirement of a typical AT keyboard.
+ */
+
+#include <linux/module.h>
+#include <linux/parport.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Parallel port to Keyboard port adapter driver");
+MODULE_LICENSE("GPL");
+
+static unsigned int parkbd_pp_no;
+module_param_named(port, parkbd_pp_no, int, 0);
+MODULE_PARM_DESC(port, "Parallel port the adapter is connected to (default is 0)");
+
+static unsigned int parkbd_mode = SERIO_8042;
+module_param_named(mode, parkbd_mode, uint, 0);
+MODULE_PARM_DESC(mode, "Mode of operation: XT = 0/AT = 1 (default)");
+
+#define PARKBD_CLOCK	0x01	/* Strobe & Ack */
+#define PARKBD_DATA	0x02	/* AutoFd & Busy */
+
+static int parkbd_buffer;
+static int parkbd_counter;
+static unsigned long parkbd_last;
+static int parkbd_writing;
+static unsigned long parkbd_start;
+
+static struct pardevice *parkbd_dev;
+static struct serio *parkbd_port;
+
+static int parkbd_readlines(void)
+{
+	return (parport_read_status(parkbd_dev->port) >> 6) ^ 2;
+}
+
+static void parkbd_writelines(int data)
+{
+	parport_write_control(parkbd_dev->port, (~data & 3) | 0x10);
+}
+
+static int parkbd_write(struct serio *port, unsigned char c)
+{
+	unsigned char p;
+
+	if (!parkbd_mode) return -1;
+
+        p = c ^ (c >> 4);
+	p = p ^ (p >> 2);
+	p = p ^ (p >> 1);
+
+	parkbd_counter = 0;
+	parkbd_writing = 1;
+	parkbd_buffer = c | (((int) (~p & 1)) << 8) | 0x600;
+
+	parkbd_writelines(2);
+
+	return 0;
+}
+
+static void parkbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+
+	if (parkbd_writing) {
+
+		if (parkbd_counter && ((parkbd_counter == 11) || time_after(jiffies, parkbd_last + HZ/100))) {
+			parkbd_counter = 0;
+			parkbd_buffer = 0;
+			parkbd_writing = 0;
+			parkbd_writelines(3);
+			return;
+		}
+
+		parkbd_writelines(((parkbd_buffer >> parkbd_counter++) & 1) | 2);
+
+		if (parkbd_counter == 11) {
+			parkbd_counter = 0;
+			parkbd_buffer = 0;
+			parkbd_writing = 0;
+			parkbd_writelines(3);
+		}
+
+	} else {
+
+		if ((parkbd_counter == parkbd_mode + 10) || time_after(jiffies, parkbd_last + HZ/100)) {
+			parkbd_counter = 0;
+			parkbd_buffer = 0;
+		}
+
+		parkbd_buffer |= (parkbd_readlines() >> 1) << parkbd_counter++;
+
+		if (parkbd_counter == parkbd_mode + 10)
+			serio_interrupt(parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0, regs);
+	}
+
+	parkbd_last = jiffies;
+}
+
+static int parkbd_getport(void)
+{
+	struct parport *pp;
+
+	pp = parport_find_number(parkbd_pp_no);
+
+	if (pp == NULL) {
+		printk(KERN_ERR "parkbd: no such parport\n");
+		return -ENODEV;
+	}
+
+	parkbd_dev = parport_register_device(pp, "parkbd", NULL, NULL, parkbd_interrupt, PARPORT_DEV_EXCL, NULL);
+	parport_put_port(pp);
+
+	if (!parkbd_dev)
+		return -ENODEV;
+
+	if (parport_claim(parkbd_dev)) {
+		parport_unregister_device(parkbd_dev);
+		return -EBUSY;
+	}
+
+	parkbd_start = jiffies;
+
+	return 0;
+}
+
+static struct serio * __init parkbd_allocate_serio(void)
+{
+	struct serio *serio;
+
+	serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (serio) {
+		memset(serio, 0, sizeof(struct serio));
+		serio->id.type = parkbd_mode;
+		serio->write = parkbd_write,
+		strlcpy(serio->name, "PARKBD AT/XT keyboard adapter", sizeof(serio->name));
+		snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", parkbd_dev->port->name);
+	}
+
+	return serio;
+}
+
+static int __init parkbd_init(void)
+{
+	int err;
+
+	err = parkbd_getport();
+	if (err)
+		return err;
+
+	parkbd_port = parkbd_allocate_serio();
+	if (!parkbd_port) {
+		parport_release(parkbd_dev);
+		return -ENOMEM;
+	}
+
+	parkbd_writelines(3);
+
+	serio_register_port(parkbd_port);
+
+	printk(KERN_INFO "serio: PARKBD %s adapter on %s\n",
+                        parkbd_mode ? "AT" : "XT", parkbd_dev->port->name);
+
+	return 0;
+}
+
+static void __exit parkbd_exit(void)
+{
+	parport_release(parkbd_dev);
+	serio_unregister_port(parkbd_port);
+	parport_unregister_device(parkbd_dev);
+}
+
+module_init(parkbd_init);
+module_exit(parkbd_exit);
diff --git a/drivers/input/serio/pcips2.c b/drivers/input/serio/pcips2.c
new file mode 100644
index 0000000..1e139c5
--- /dev/null
+++ b/drivers/input/serio/pcips2.c
@@ -0,0 +1,234 @@
+/*
+ * linux/drivers/input/serio/pcips2.c
+ *
+ *  Copyright (C) 2003 Russell King, All Rights Reserved.
+ *
+ * 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.
+ *
+ *  I'm not sure if this is a generic PS/2 PCI interface or specific to
+ *  the Mobility Electronics docking station.
+ */
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/input.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#define PS2_CTRL		(0)
+#define PS2_STATUS		(1)
+#define PS2_DATA		(2)
+
+#define PS2_CTRL_CLK		(1<<0)
+#define PS2_CTRL_DAT		(1<<1)
+#define PS2_CTRL_TXIRQ		(1<<2)
+#define PS2_CTRL_ENABLE		(1<<3)
+#define PS2_CTRL_RXIRQ		(1<<4)
+
+#define PS2_STAT_CLK		(1<<0)
+#define PS2_STAT_DAT		(1<<1)
+#define PS2_STAT_PARITY		(1<<2)
+#define PS2_STAT_RXFULL		(1<<5)
+#define PS2_STAT_TXBUSY		(1<<6)
+#define PS2_STAT_TXEMPTY	(1<<7)
+
+struct pcips2_data {
+	struct serio	*io;
+	unsigned int	base;
+	struct pci_dev	*dev;
+};
+
+static int pcips2_write(struct serio *io, unsigned char val)
+{
+	struct pcips2_data *ps2if = io->port_data;
+	unsigned int stat;
+
+	do {
+		stat = inb(ps2if->base + PS2_STATUS);
+		cpu_relax();
+	} while (!(stat & PS2_STAT_TXEMPTY));
+
+	outb(val, ps2if->base + PS2_DATA);
+
+	return 0;
+}
+
+static irqreturn_t pcips2_interrupt(int irq, void *devid, struct pt_regs *regs)
+{
+	struct pcips2_data *ps2if = devid;
+	unsigned char status, scancode;
+	int handled = 0;
+
+	do {
+		unsigned int flag;
+
+		status = inb(ps2if->base + PS2_STATUS);
+		if (!(status & PS2_STAT_RXFULL))
+			break;
+		handled = 1;
+		scancode = inb(ps2if->base + PS2_DATA);
+		if (status == 0xff && scancode == 0xff)
+			break;
+
+		flag = (status & PS2_STAT_PARITY) ? 0 : SERIO_PARITY;
+
+		if (hweight8(scancode) & 1)
+			flag ^= SERIO_PARITY;
+
+		serio_interrupt(ps2if->io, scancode, flag, regs);
+	} while (1);
+	return IRQ_RETVAL(handled);
+}
+
+static void pcips2_flush_input(struct pcips2_data *ps2if)
+{
+	unsigned char status, scancode;
+
+	do {
+		status = inb(ps2if->base + PS2_STATUS);
+		if (!(status & PS2_STAT_RXFULL))
+			break;
+		scancode = inb(ps2if->base + PS2_DATA);
+		if (status == 0xff && scancode == 0xff)
+			break;
+	} while (1);
+}
+
+static int pcips2_open(struct serio *io)
+{
+	struct pcips2_data *ps2if = io->port_data;
+	int ret, val = 0;
+
+	outb(PS2_CTRL_ENABLE, ps2if->base);
+	pcips2_flush_input(ps2if);
+
+	ret = request_irq(ps2if->dev->irq, pcips2_interrupt, SA_SHIRQ,
+			  "pcips2", ps2if);
+	if (ret == 0)
+		val = PS2_CTRL_ENABLE | PS2_CTRL_RXIRQ;
+
+	outb(val, ps2if->base);
+
+	return ret;
+}
+
+static void pcips2_close(struct serio *io)
+{
+	struct pcips2_data *ps2if = io->port_data;
+
+	outb(0, ps2if->base);
+
+	free_irq(ps2if->dev->irq, ps2if);
+}
+
+static int __devinit pcips2_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct pcips2_data *ps2if;
+	struct serio *serio;
+	int ret;
+
+	ret = pci_enable_device(dev);
+	if (ret)
+		goto out;
+
+	ret = pci_request_regions(dev, "pcips2");
+	if (ret)
+		goto disable;
+
+	ps2if = kmalloc(sizeof(struct pcips2_data), GFP_KERNEL);
+	serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!ps2if || !serio) {
+		ret = -ENOMEM;
+		goto release;
+	}
+
+	memset(ps2if, 0, sizeof(struct pcips2_data));
+	memset(serio, 0, sizeof(struct serio));
+
+	serio->id.type		= SERIO_8042;
+	serio->write		= pcips2_write;
+	serio->open		= pcips2_open;
+	serio->close		= pcips2_close;
+	strlcpy(serio->name, pci_name(dev), sizeof(serio->name));
+	strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys));
+	serio->port_data	= ps2if;
+	serio->dev.parent	= &dev->dev;
+	ps2if->io		= serio;
+	ps2if->dev		= dev;
+	ps2if->base		= pci_resource_start(dev, 0);
+
+	pci_set_drvdata(dev, ps2if);
+
+	serio_register_port(ps2if->io);
+	return 0;
+
+ release:
+	kfree(ps2if);
+	kfree(serio);
+	pci_release_regions(dev);
+ disable:
+	pci_disable_device(dev);
+ out:
+	return ret;
+}
+
+static void __devexit pcips2_remove(struct pci_dev *dev)
+{
+	struct pcips2_data *ps2if = pci_get_drvdata(dev);
+
+	serio_unregister_port(ps2if->io);
+	pci_set_drvdata(dev, NULL);
+	kfree(ps2if);
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+}
+
+static struct pci_device_id pcips2_ids[] = {
+	{
+		.vendor		= 0x14f2,	/* MOBILITY */
+		.device		= 0x0123,	/* Keyboard */
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.class		= PCI_CLASS_INPUT_KEYBOARD << 8,
+		.class_mask	= 0xffff00,
+	},
+	{
+		.vendor		= 0x14f2,	/* MOBILITY */
+		.device		= 0x0124,	/* Mouse */
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.class		= PCI_CLASS_INPUT_MOUSE << 8,
+		.class_mask	= 0xffff00,
+	},
+	{ 0, }
+};
+
+static struct pci_driver pcips2_driver = {
+	.name			= "pcips2",
+	.id_table		= pcips2_ids,
+	.probe			= pcips2_probe,
+	.remove			= __devexit_p(pcips2_remove),
+};
+
+static int __init pcips2_init(void)
+{
+	return pci_register_driver(&pcips2_driver);
+}
+
+static void __exit pcips2_exit(void)
+{
+	pci_unregister_driver(&pcips2_driver);
+}
+
+module_init(pcips2_init);
+module_exit(pcips2_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver");
+MODULE_DEVICE_TABLE(pci, pcips2_ids);
diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c
new file mode 100644
index 0000000..46093c5
--- /dev/null
+++ b/drivers/input/serio/q40kbd.c
@@ -0,0 +1,163 @@
+/*
+ * $Id: q40kbd.c,v 1.12 2002/02/02 22:26:44 vojtech Exp $
+ *
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ *  Based on the work of:
+ *	Richard Zidlicky <Richard.Zidlicky@stud.informatik.uni-erlangen.de>
+ */
+
+/*
+ * Q40 PS/2 keyboard controller 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/serio.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/q40_master.h>
+#include <asm/irq.h>
+#include <asm/q40ints.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver");
+MODULE_LICENSE("GPL");
+
+DEFINE_SPINLOCK(q40kbd_lock);
+static struct serio *q40kbd_port;
+static struct platform_device *q40kbd_device;
+
+static irqreturn_t q40kbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&q40kbd_lock, flags);
+
+	if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))
+		serio_interrupt(q40kbd_port, master_inb(KEYCODE_REG), 0, regs);
+
+	master_outb(-1, KEYBOARD_UNLOCK_REG);
+
+	spin_unlock_irqrestore(&q40kbd_lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * q40kbd_flush() flushes all data that may be in the keyboard buffers
+ */
+
+static void q40kbd_flush(void)
+{
+ 	int maxread = 100;
+	unsigned long flags;
+
+	spin_lock_irqsave(&q40kbd_lock, flags);
+
+ 	while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)))
+ 		master_inb(KEYCODE_REG);
+
+	spin_unlock_irqrestore(&q40kbd_lock, flags);
+}
+
+/*
+ * q40kbd_open() is called when a port is open by the higher layer.
+ * It allocates the interrupt and enables in in the chip.
+ */
+
+static int q40kbd_open(struct serio *port)
+{
+	q40kbd_flush();
+
+	if (request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, "q40kbd", NULL)) {
+		printk(KERN_ERR "q40kbd.c: Can't get irq %d.\n", Q40_IRQ_KEYBOARD);
+		return -1;
+	}
+
+ 	/* off we go */
+ 	master_outb(-1, KEYBOARD_UNLOCK_REG);
+ 	master_outb(1, KEY_IRQ_ENABLE_REG);
+
+ 	return 0;
+}
+
+static void q40kbd_close(struct serio *port)
+{
+	master_outb(0, KEY_IRQ_ENABLE_REG);
+	master_outb(-1, KEYBOARD_UNLOCK_REG);
+	free_irq(Q40_IRQ_KEYBOARD, NULL);
+
+	q40kbd_flush();
+}
+
+static struct serio * __init q40kbd_allocate_port(void)
+{
+	struct serio *serio;
+
+	serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (serio) {
+		memset(serio, 0, sizeof(struct serio));
+		serio->id.type		= SERIO_8042;
+		serio->open		= q40kbd_open;
+		serio->close		= q40kbd_close;
+		serio->dev.parent	= &q40kbd_device->dev;
+		strlcpy(serio->name, "Q40 Kbd Port", sizeof(serio->name));
+		strlcpy(serio->phys, "Q40", sizeof(serio->phys));
+	}
+
+	return serio;
+}
+
+static int __init q40kbd_init(void)
+{
+	if (!MACH_IS_Q40)
+		return -EIO;
+
+	q40kbd_device = platform_device_register_simple("q40kbd", -1, NULL, 0);
+	if (IS_ERR(q40kbd_device))
+		return PTR_ERR(q40kbd_device);
+
+	if (!(q40kbd_port = q40kbd_allocate_port())) {
+		platform_device_unregister(q40kbd_device);
+		return -ENOMEM;
+	}
+
+	serio_register_port(q40kbd_port);
+	printk(KERN_INFO "serio: Q40 kbd registered\n");
+
+	return 0;
+}
+
+static void __exit q40kbd_exit(void)
+{
+	serio_unregister_port(q40kbd_port);
+	platform_device_unregister(q40kbd_device);
+}
+
+module_init(q40kbd_init);
+module_exit(q40kbd_exit);
diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c
new file mode 100644
index 0000000..106f5ee
--- /dev/null
+++ b/drivers/input/serio/rpckbd.c
@@ -0,0 +1,156 @@
+/*
+ * $Id: rpckbd.c,v 1.7 2001/09/25 10:12:07 vojtech Exp $
+ *
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *  Copyright (c) 2002 Russell King
+ */
+
+/*
+ * Acorn RiscPC PS/2 keyboard controller 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
+ *
+ * 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/interrupt.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/err.h>
+
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/hardware/iomd.h>
+#include <asm/system.h>
+
+MODULE_AUTHOR("Vojtech Pavlik, Russell King");
+MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver");
+MODULE_LICENSE("GPL");
+
+static int rpckbd_write(struct serio *port, unsigned char val)
+{
+	while (!(iomd_readb(IOMD_KCTRL) & (1 << 7)))
+		cpu_relax();
+
+	iomd_writeb(val, IOMD_KARTTX);
+
+	return 0;
+}
+
+static irqreturn_t rpckbd_rx(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct serio *port = dev_id;
+	unsigned int byte;
+	int handled = IRQ_NONE;
+
+	while (iomd_readb(IOMD_KCTRL) & (1 << 5)) {
+		byte = iomd_readb(IOMD_KARTRX);
+
+		serio_interrupt(port, byte, 0, regs);
+		handled = IRQ_HANDLED;
+	}
+	return handled;
+}
+
+static irqreturn_t rpckbd_tx(int irq, void *dev_id, struct pt_regs *regs)
+{
+	return IRQ_HANDLED;
+}
+
+static int rpckbd_open(struct serio *port)
+{
+	/* Reset the keyboard state machine. */
+	iomd_writeb(0, IOMD_KCTRL);
+	iomd_writeb(8, IOMD_KCTRL);
+	iomd_readb(IOMD_KARTRX);
+
+	if (request_irq(IRQ_KEYBOARDRX, rpckbd_rx, 0, "rpckbd", port) != 0) {
+		printk(KERN_ERR "rpckbd.c: Could not allocate keyboard receive IRQ\n");
+		return -EBUSY;
+	}
+
+	if (request_irq(IRQ_KEYBOARDTX, rpckbd_tx, 0, "rpckbd", port) != 0) {
+		printk(KERN_ERR "rpckbd.c: Could not allocate keyboard transmit IRQ\n");
+		free_irq(IRQ_KEYBOARDRX, NULL);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static void rpckbd_close(struct serio *port)
+{
+	free_irq(IRQ_KEYBOARDRX, port);
+	free_irq(IRQ_KEYBOARDTX, port);
+}
+
+/*
+ * Allocate and initialize serio structure for subsequent registration
+ * with serio core.
+ */
+static int __devinit rpckbd_probe(struct device *dev)
+{
+	struct serio *serio;
+
+	serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!serio)
+		return -ENOMEM;
+
+	memset(serio, 0, sizeof(struct serio));
+	serio->id.type		= SERIO_8042;
+	serio->write		= rpckbd_write;
+	serio->open		= rpckbd_open;
+	serio->close		= rpckbd_close;
+	serio->dev.parent	= dev;
+	strlcpy(serio->name, "RiscPC PS/2 kbd port", sizeof(serio->name));
+	strlcpy(serio->phys, "rpckbd/serio0", sizeof(serio->phys));
+
+	dev_set_drvdata(dev, serio);
+	serio_register_port(serio);
+	return 0;
+}
+
+static int __devexit rpckbd_remove(struct device *dev)
+{
+	struct serio *serio = dev_get_drvdata(dev);
+	serio_unregister_port(serio);
+	return 0;
+}
+
+static struct device_driver rpckbd_driver = {
+	.name		= "kart",
+	.bus		= &platform_bus_type,
+	.probe		= rpckbd_probe,
+	.remove		= __devexit_p(rpckbd_remove),
+};
+
+static int __init rpckbd_init(void)
+{
+	return driver_register(&rpckbd_driver);
+}
+
+static void __exit rpckbd_exit(void)
+{
+	driver_unregister(&rpckbd_driver);
+}
+
+module_init(rpckbd_init);
+module_exit(rpckbd_exit);
diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c
new file mode 100644
index 0000000..3f0df33
--- /dev/null
+++ b/drivers/input/serio/sa1111ps2.c
@@ -0,0 +1,359 @@
+/*
+ *  linux/drivers/input/serio/sa1111ps2.c
+ *
+ *  Copyright (C) 2002 Russell King
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include <asm/hardware/sa1111.h>
+
+struct ps2if {
+	struct serio		*io;
+	struct sa1111_dev	*dev;
+	void __iomem		*base;
+	unsigned int		open;
+	spinlock_t		lock;
+	unsigned int		head;
+	unsigned int		tail;
+	unsigned char		buf[4];
+};
+
+/*
+ * Read all bytes waiting in the PS2 port.  There should be
+ * at the most one, but we loop for safety.  If there was a
+ * framing error, we have to manually clear the status.
+ */
+static irqreturn_t ps2_rxint(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct ps2if *ps2if = dev_id;
+	unsigned int scancode, flag, status;
+
+	status = sa1111_readl(ps2if->base + SA1111_PS2STAT);
+	while (status & PS2STAT_RXF) {
+		if (status & PS2STAT_STP)
+			sa1111_writel(PS2STAT_STP, ps2if->base + SA1111_PS2STAT);
+
+		flag = (status & PS2STAT_STP ? SERIO_FRAME : 0) |
+		       (status & PS2STAT_RXP ? 0 : SERIO_PARITY);
+
+		scancode = sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff;
+
+		if (hweight8(scancode) & 1)
+			flag ^= SERIO_PARITY;
+
+		serio_interrupt(ps2if->io, scancode, flag, regs);
+
+		status = sa1111_readl(ps2if->base + SA1111_PS2STAT);
+        }
+
+        return IRQ_HANDLED;
+}
+
+/*
+ * Completion of ps2 write
+ */
+static irqreturn_t ps2_txint(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct ps2if *ps2if = dev_id;
+	unsigned int status;
+
+	spin_lock(&ps2if->lock);
+	status = sa1111_readl(ps2if->base + SA1111_PS2STAT);
+	if (ps2if->head == ps2if->tail) {
+		disable_irq(irq);
+		/* done */
+	} else if (status & PS2STAT_TXE) {
+		sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + SA1111_PS2DATA);
+		ps2if->tail = (ps2if->tail + 1) & (sizeof(ps2if->buf) - 1);
+	}
+	spin_unlock(&ps2if->lock);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Write a byte to the PS2 port.  We have to wait for the
+ * port to indicate that the transmitter is empty.
+ */
+static int ps2_write(struct serio *io, unsigned char val)
+{
+	struct ps2if *ps2if = io->port_data;
+	unsigned long flags;
+	unsigned int head;
+
+	spin_lock_irqsave(&ps2if->lock, flags);
+
+	/*
+	 * If the TX register is empty, we can go straight out.
+	 */
+	if (sa1111_readl(ps2if->base + SA1111_PS2STAT) & PS2STAT_TXE) {
+		sa1111_writel(val, ps2if->base + SA1111_PS2DATA);
+	} else {
+		if (ps2if->head == ps2if->tail)
+			enable_irq(ps2if->dev->irq[1]);
+		head = (ps2if->head + 1) & (sizeof(ps2if->buf) - 1);
+		if (head != ps2if->tail) {
+			ps2if->buf[ps2if->head] = val;
+			ps2if->head = head;
+		}
+	}
+
+	spin_unlock_irqrestore(&ps2if->lock, flags);
+	return 0;
+}
+
+static int ps2_open(struct serio *io)
+{
+	struct ps2if *ps2if = io->port_data;
+	int ret;
+
+	sa1111_enable_device(ps2if->dev);
+
+	ret = request_irq(ps2if->dev->irq[0], ps2_rxint, 0,
+			  SA1111_DRIVER_NAME(ps2if->dev), ps2if);
+	if (ret) {
+		printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n",
+			ps2if->dev->irq[0], ret);
+		return ret;
+	}
+
+	ret = request_irq(ps2if->dev->irq[1], ps2_txint, 0,
+			  SA1111_DRIVER_NAME(ps2if->dev), ps2if);
+	if (ret) {
+		printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n",
+			ps2if->dev->irq[1], ret);
+		free_irq(ps2if->dev->irq[0], ps2if);
+		return ret;
+	}
+
+	ps2if->open = 1;
+
+	enable_irq_wake(ps2if->dev->irq[0]);
+
+	sa1111_writel(PS2CR_ENA, ps2if->base + SA1111_PS2CR);
+	return 0;
+}
+
+static void ps2_close(struct serio *io)
+{
+	struct ps2if *ps2if = io->port_data;
+
+	sa1111_writel(0, ps2if->base + SA1111_PS2CR);
+
+	disable_irq_wake(ps2if->dev->irq[0]);
+
+	ps2if->open = 0;
+
+	free_irq(ps2if->dev->irq[1], ps2if);
+	free_irq(ps2if->dev->irq[0], ps2if);
+
+	sa1111_disable_device(ps2if->dev);
+}
+
+/*
+ * Clear the input buffer.
+ */
+static void __init ps2_clear_input(struct ps2if *ps2if)
+{
+	int maxread = 100;
+
+	while (maxread--) {
+		if ((sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff) == 0xff)
+			break;
+	}
+}
+
+static inline unsigned int
+ps2_test_one(struct ps2if *ps2if, unsigned int mask)
+{
+	unsigned int val;
+
+	sa1111_writel(PS2CR_ENA | mask, ps2if->base + SA1111_PS2CR);
+
+	udelay(2);
+
+	val = sa1111_readl(ps2if->base + SA1111_PS2STAT);
+	return val & (PS2STAT_KBC | PS2STAT_KBD);
+}
+
+/*
+ * Test the keyboard interface.  We basically check to make sure that
+ * we can drive each line to the keyboard independently of each other.
+ */
+static int __init ps2_test(struct ps2if *ps2if)
+{
+	unsigned int stat;
+	int ret = 0;
+
+	stat = ps2_test_one(ps2if, PS2CR_FKC);
+	if (stat != PS2STAT_KBD) {
+		printk("PS/2 interface test failed[1]: %02x\n", stat);
+		ret = -ENODEV;
+	}
+
+	stat = ps2_test_one(ps2if, 0);
+	if (stat != (PS2STAT_KBC | PS2STAT_KBD)) {
+		printk("PS/2 interface test failed[2]: %02x\n", stat);
+		ret = -ENODEV;
+	}
+
+	stat = ps2_test_one(ps2if, PS2CR_FKD);
+	if (stat != PS2STAT_KBC) {
+		printk("PS/2 interface test failed[3]: %02x\n", stat);
+		ret = -ENODEV;
+	}
+
+	sa1111_writel(0, ps2if->base + SA1111_PS2CR);
+
+	return ret;
+}
+
+/*
+ * Add one device to this driver.
+ */
+static int ps2_probe(struct sa1111_dev *dev)
+{
+	struct ps2if *ps2if;
+	struct serio *serio;
+	int ret;
+
+	ps2if = kmalloc(sizeof(struct ps2if), GFP_KERNEL);
+	serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (!ps2if || !serio) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	memset(ps2if, 0, sizeof(struct ps2if));
+	memset(serio, 0, sizeof(struct serio));
+
+	serio->id.type		= SERIO_8042;
+	serio->write		= ps2_write;
+	serio->open		= ps2_open;
+	serio->close		= ps2_close;
+	strlcpy(serio->name, dev->dev.bus_id, sizeof(serio->name));
+	strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys));
+	serio->port_data	= ps2if;
+	serio->dev.parent	= &dev->dev;
+	ps2if->io		= serio;
+	ps2if->dev		= dev;
+	sa1111_set_drvdata(dev, ps2if);
+
+	spin_lock_init(&ps2if->lock);
+
+	/*
+	 * Request the physical region for this PS2 port.
+	 */
+	if (!request_mem_region(dev->res.start,
+				dev->res.end - dev->res.start + 1,
+				SA1111_DRIVER_NAME(dev))) {
+		ret = -EBUSY;
+		goto free;
+	}
+
+	/*
+	 * Our parent device has already mapped the region.
+	 */
+	ps2if->base = dev->mapbase;
+
+	sa1111_enable_device(ps2if->dev);
+
+	/* Incoming clock is 8MHz */
+	sa1111_writel(0, ps2if->base + SA1111_PS2CLKDIV);
+	sa1111_writel(127, ps2if->base + SA1111_PS2PRECNT);
+
+	/*
+	 * Flush any pending input.
+	 */
+	ps2_clear_input(ps2if);
+
+	/*
+	 * Test the keyboard interface.
+	 */
+	ret = ps2_test(ps2if);
+	if (ret)
+		goto out;
+
+	/*
+	 * Flush any pending input.
+	 */
+	ps2_clear_input(ps2if);
+
+	sa1111_disable_device(ps2if->dev);
+	serio_register_port(ps2if->io);
+	return 0;
+
+ out:
+	sa1111_disable_device(ps2if->dev);
+	release_mem_region(dev->res.start,
+			   dev->res.end - dev->res.start + 1);
+ free:
+	sa1111_set_drvdata(dev, NULL);
+	kfree(ps2if);
+	kfree(serio);
+	return ret;
+}
+
+/*
+ * Remove one device from this driver.
+ */
+static int ps2_remove(struct sa1111_dev *dev)
+{
+	struct ps2if *ps2if = sa1111_get_drvdata(dev);
+
+	serio_unregister_port(ps2if->io);
+	release_mem_region(dev->res.start,
+			   dev->res.end - dev->res.start + 1);
+	sa1111_set_drvdata(dev, NULL);
+
+	kfree(ps2if);
+
+	return 0;
+}
+
+/*
+ * Our device driver structure
+ */
+static struct sa1111_driver ps2_driver = {
+	.drv = {
+		.name	= "sa1111-ps2",
+	},
+	.devid		= SA1111_DEVID_PS2,
+	.probe		= ps2_probe,
+	.remove		= ps2_remove,
+};
+
+static int __init ps2_init(void)
+{
+	return sa1111_driver_register(&ps2_driver);
+}
+
+static void __exit ps2_exit(void)
+{
+	sa1111_driver_unregister(&ps2_driver);
+}
+
+module_init(ps2_init);
+module_exit(ps2_exit);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("SA1111 PS2 controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
new file mode 100644
index 0000000..3313e2d
--- /dev/null
+++ b/drivers/input/serio/serio.c
@@ -0,0 +1,859 @@
+/*
+ *  The Serio abstraction module
+ *
+ *  Copyright (c) 1999-2004 Vojtech Pavlik
+ *  Copyright (c) 2004 Dmitry Torokhov
+ *  Copyright (c) 2003 Daniele Bellucci
+ */
+
+/*
+ * 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/stddef.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Serio abstraction core");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(serio_interrupt);
+EXPORT_SYMBOL(__serio_register_port);
+EXPORT_SYMBOL(serio_unregister_port);
+EXPORT_SYMBOL(__serio_unregister_port_delayed);
+EXPORT_SYMBOL(__serio_register_driver);
+EXPORT_SYMBOL(serio_unregister_driver);
+EXPORT_SYMBOL(serio_open);
+EXPORT_SYMBOL(serio_close);
+EXPORT_SYMBOL(serio_rescan);
+EXPORT_SYMBOL(serio_reconnect);
+
+/*
+ * serio_sem protects entire serio subsystem and is taken every time
+ * serio port or driver registrered or unregistered.
+ */
+static DECLARE_MUTEX(serio_sem);
+
+static LIST_HEAD(serio_list);
+
+static struct bus_type serio_bus = {
+	.name =	"serio",
+};
+
+static void serio_add_port(struct serio *serio);
+static void serio_destroy_port(struct serio *serio);
+static void serio_reconnect_port(struct serio *serio);
+static void serio_disconnect_port(struct serio *serio);
+
+static int serio_match_port(const struct serio_device_id *ids, struct serio *serio)
+{
+	while (ids->type || ids->proto) {
+		if ((ids->type == SERIO_ANY || ids->type == serio->id.type) &&
+		    (ids->proto == SERIO_ANY || ids->proto == serio->id.proto) &&
+		    (ids->extra == SERIO_ANY || ids->extra == serio->id.extra) &&
+		    (ids->id == SERIO_ANY || ids->id == serio->id.id))
+			return 1;
+		ids++;
+	}
+	return 0;
+}
+
+/*
+ * Basic serio -> driver core mappings
+ */
+
+static void serio_bind_driver(struct serio *serio, struct serio_driver *drv)
+{
+	down_write(&serio_bus.subsys.rwsem);
+
+	if (serio_match_port(drv->id_table, serio)) {
+		serio->dev.driver = &drv->driver;
+		if (drv->connect(serio, drv)) {
+			serio->dev.driver = NULL;
+			goto out;
+		}
+		device_bind_driver(&serio->dev);
+	}
+out:
+	up_write(&serio_bus.subsys.rwsem);
+}
+
+static void serio_release_driver(struct serio *serio)
+{
+	down_write(&serio_bus.subsys.rwsem);
+	device_release_driver(&serio->dev);
+	up_write(&serio_bus.subsys.rwsem);
+}
+
+static void serio_find_driver(struct serio *serio)
+{
+	down_write(&serio_bus.subsys.rwsem);
+	device_attach(&serio->dev);
+	up_write(&serio_bus.subsys.rwsem);
+}
+
+
+/*
+ * Serio event processing.
+ */
+
+enum serio_event_type {
+	SERIO_RESCAN,
+	SERIO_RECONNECT,
+	SERIO_REGISTER_PORT,
+	SERIO_UNREGISTER_PORT,
+	SERIO_REGISTER_DRIVER,
+};
+
+struct serio_event {
+	enum serio_event_type type;
+	void *object;
+	struct module *owner;
+	struct list_head node;
+};
+
+static DEFINE_SPINLOCK(serio_event_lock);	/* protects serio_event_list */
+static LIST_HEAD(serio_event_list);
+static DECLARE_WAIT_QUEUE_HEAD(serio_wait);
+static DECLARE_COMPLETION(serio_exited);
+static int serio_pid;
+
+static void serio_queue_event(void *object, struct module *owner,
+			      enum serio_event_type event_type)
+{
+	unsigned long flags;
+	struct serio_event *event;
+
+	spin_lock_irqsave(&serio_event_lock, flags);
+
+	/*
+ 	 * Scan event list for the other events for the same serio port,
+	 * starting with the most recent one. If event is the same we
+	 * do not need add new one. If event is of different type we
+	 * need to add this event and should not look further because
+	 * we need to preseve sequence of distinct events.
+ 	 */
+	list_for_each_entry_reverse(event, &serio_event_list, node) {
+		if (event->object == object) {
+			if (event->type == event_type)
+				goto out;
+			break;
+		}
+	}
+
+	if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) {
+		if (!try_module_get(owner)) {
+			printk(KERN_WARNING "serio: Can't get module reference, dropping event %d\n", event_type);
+			goto out;
+		}
+
+		event->type = event_type;
+		event->object = object;
+		event->owner = owner;
+
+		list_add_tail(&event->node, &serio_event_list);
+		wake_up(&serio_wait);
+	} else {
+		printk(KERN_ERR "serio: Not enough memory to queue event %d\n", event_type);
+	}
+out:
+	spin_unlock_irqrestore(&serio_event_lock, flags);
+}
+
+static void serio_free_event(struct serio_event *event)
+{
+	module_put(event->owner);
+	kfree(event);
+}
+
+static void serio_remove_duplicate_events(struct serio_event *event)
+{
+	struct list_head *node, *next;
+	struct serio_event *e;
+	unsigned long flags;
+
+	spin_lock_irqsave(&serio_event_lock, flags);
+
+	list_for_each_safe(node, next, &serio_event_list) {
+		e = list_entry(node, struct serio_event, node);
+		if (event->object == e->object) {
+			/*
+			 * If this event is of different type we should not
+			 * look further - we only suppress duplicate events
+			 * that were sent back-to-back.
+			 */
+			if (event->type != e->type)
+				break;
+
+			list_del_init(node);
+			serio_free_event(e);
+		}
+	}
+
+	spin_unlock_irqrestore(&serio_event_lock, flags);
+}
+
+
+static struct serio_event *serio_get_event(void)
+{
+	struct serio_event *event;
+	struct list_head *node;
+	unsigned long flags;
+
+	spin_lock_irqsave(&serio_event_lock, flags);
+
+	if (list_empty(&serio_event_list)) {
+		spin_unlock_irqrestore(&serio_event_lock, flags);
+		return NULL;
+	}
+
+	node = serio_event_list.next;
+	event = list_entry(node, struct serio_event, node);
+	list_del_init(node);
+
+	spin_unlock_irqrestore(&serio_event_lock, flags);
+
+	return event;
+}
+
+static void serio_handle_events(void)
+{
+	struct serio_event *event;
+	struct serio_driver *serio_drv;
+
+	down(&serio_sem);
+
+	while ((event = serio_get_event())) {
+
+		switch (event->type) {
+			case SERIO_REGISTER_PORT:
+				serio_add_port(event->object);
+				break;
+
+			case SERIO_UNREGISTER_PORT:
+				serio_disconnect_port(event->object);
+				serio_destroy_port(event->object);
+				break;
+
+			case SERIO_RECONNECT:
+				serio_reconnect_port(event->object);
+				break;
+
+			case SERIO_RESCAN:
+				serio_disconnect_port(event->object);
+				serio_find_driver(event->object);
+				break;
+
+			case SERIO_REGISTER_DRIVER:
+				serio_drv = event->object;
+				driver_register(&serio_drv->driver);
+				break;
+
+			default:
+				break;
+		}
+
+		serio_remove_duplicate_events(event);
+		serio_free_event(event);
+	}
+
+	up(&serio_sem);
+}
+
+/*
+ * Remove all events that have been submitted for a given serio port.
+ */
+static void serio_remove_pending_events(struct serio *serio)
+{
+	struct list_head *node, *next;
+	struct serio_event *event;
+	unsigned long flags;
+
+	spin_lock_irqsave(&serio_event_lock, flags);
+
+	list_for_each_safe(node, next, &serio_event_list) {
+		event = list_entry(node, struct serio_event, node);
+		if (event->object == serio) {
+			list_del_init(node);
+			serio_free_event(event);
+		}
+	}
+
+	spin_unlock_irqrestore(&serio_event_lock, flags);
+}
+
+/*
+ * Destroy child serio port (if any) that has not been fully registered yet.
+ *
+ * Note that we rely on the fact that port can have only one child and therefore
+ * only one child registration request can be pending. Additionally, children
+ * are registered by driver's connect() handler so there can't be a grandchild
+ * pending registration together with a child.
+ */
+static struct serio *serio_get_pending_child(struct serio *parent)
+{
+	struct serio_event *event;
+	struct serio *serio, *child = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&serio_event_lock, flags);
+
+	list_for_each_entry(event, &serio_event_list, node) {
+		if (event->type == SERIO_REGISTER_PORT) {
+			serio = event->object;
+			if (serio->parent == parent) {
+				child = serio;
+				break;
+			}
+		}
+	}
+
+	spin_unlock_irqrestore(&serio_event_lock, flags);
+	return child;
+}
+
+static int serio_thread(void *nothing)
+{
+	lock_kernel();
+	daemonize("kseriod");
+	allow_signal(SIGTERM);
+
+	do {
+		serio_handle_events();
+		wait_event_interruptible(serio_wait, !list_empty(&serio_event_list));
+		try_to_freeze(PF_FREEZE);
+	} while (!signal_pending(current));
+
+	printk(KERN_DEBUG "serio: kseriod exiting\n");
+
+	unlock_kernel();
+	complete_and_exit(&serio_exited, 0);
+}
+
+
+/*
+ * Serio port operations
+ */
+
+static ssize_t serio_show_description(struct device *dev, char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+	return sprintf(buf, "%s\n", serio->name);
+}
+
+static ssize_t serio_show_id_type(struct device *dev, char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+	return sprintf(buf, "%02x\n", serio->id.type);
+}
+
+static ssize_t serio_show_id_proto(struct device *dev, char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+	return sprintf(buf, "%02x\n", serio->id.proto);
+}
+
+static ssize_t serio_show_id_id(struct device *dev, char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+	return sprintf(buf, "%02x\n", serio->id.id);
+}
+
+static ssize_t serio_show_id_extra(struct device *dev, char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+	return sprintf(buf, "%02x\n", serio->id.extra);
+}
+
+static ssize_t serio_rebind_driver(struct device *dev, const char *buf, size_t count)
+{
+	struct serio *serio = to_serio_port(dev);
+	struct device_driver *drv;
+	int retval;
+
+	retval = down_interruptible(&serio_sem);
+	if (retval)
+		return retval;
+
+	retval = count;
+	if (!strncmp(buf, "none", count)) {
+		serio_disconnect_port(serio);
+	} else if (!strncmp(buf, "reconnect", count)) {
+		serio_reconnect_port(serio);
+	} else if (!strncmp(buf, "rescan", count)) {
+		serio_disconnect_port(serio);
+		serio_find_driver(serio);
+	} else if ((drv = driver_find(buf, &serio_bus)) != NULL) {
+		serio_disconnect_port(serio);
+		serio_bind_driver(serio, to_serio_driver(drv));
+		put_driver(drv);
+	} else {
+		retval = -EINVAL;
+	}
+
+	up(&serio_sem);
+
+	return retval;
+}
+
+static ssize_t serio_show_bind_mode(struct device *dev, char *buf)
+{
+	struct serio *serio = to_serio_port(dev);
+	return sprintf(buf, "%s\n", serio->manual_bind ? "manual" : "auto");
+}
+
+static ssize_t serio_set_bind_mode(struct device *dev, const char *buf, size_t count)
+{
+	struct serio *serio = to_serio_port(dev);
+	int retval;
+
+	retval = count;
+	if (!strncmp(buf, "manual", count)) {
+		serio->manual_bind = 1;
+	} else if (!strncmp(buf, "auto", count)) {
+		serio->manual_bind = 0;
+	} else {
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+
+static struct device_attribute serio_device_attrs[] = {
+	__ATTR(description, S_IRUGO, serio_show_description, NULL),
+	__ATTR(id_type, S_IRUGO, serio_show_id_type, NULL),
+	__ATTR(id_proto, S_IRUGO, serio_show_id_proto, NULL),
+	__ATTR(id_id, S_IRUGO, serio_show_id_id, NULL),
+	__ATTR(id_extra, S_IRUGO, serio_show_id_extra, NULL),
+	__ATTR(drvctl, S_IWUSR, NULL, serio_rebind_driver),
+	__ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode),
+	__ATTR_NULL
+};
+
+
+static void serio_release_port(struct device *dev)
+{
+	struct serio *serio = to_serio_port(dev);
+
+	kfree(serio);
+	module_put(THIS_MODULE);
+}
+
+/*
+ * Prepare serio port for registration.
+ */
+static void serio_init_port(struct serio *serio)
+{
+	static atomic_t serio_no = ATOMIC_INIT(0);
+
+	__module_get(THIS_MODULE);
+
+	spin_lock_init(&serio->lock);
+	init_MUTEX(&serio->drv_sem);
+	device_initialize(&serio->dev);
+	snprintf(serio->dev.bus_id, sizeof(serio->dev.bus_id),
+		 "serio%ld", (long)atomic_inc_return(&serio_no) - 1);
+	serio->dev.bus = &serio_bus;
+	serio->dev.release = serio_release_port;
+	if (serio->parent)
+		serio->dev.parent = &serio->parent->dev;
+}
+
+/*
+ * Complete serio port registration.
+ * Driver core will attempt to find appropriate driver for the port.
+ */
+static void serio_add_port(struct serio *serio)
+{
+	if (serio->parent) {
+		serio_pause_rx(serio->parent);
+		serio->parent->child = serio;
+		serio_continue_rx(serio->parent);
+	}
+
+	list_add_tail(&serio->node, &serio_list);
+	if (serio->start)
+		serio->start(serio);
+	device_add(&serio->dev);
+	serio->registered = 1;
+}
+
+/*
+ * serio_destroy_port() completes deregistration process and removes
+ * port from the system
+ */
+static void serio_destroy_port(struct serio *serio)
+{
+	struct serio *child;
+
+	child = serio_get_pending_child(serio);
+	if (child) {
+		serio_remove_pending_events(child);
+		put_device(&child->dev);
+	}
+
+	if (serio->stop)
+		serio->stop(serio);
+
+	if (serio->parent) {
+		serio_pause_rx(serio->parent);
+		serio->parent->child = NULL;
+		serio_continue_rx(serio->parent);
+		serio->parent = NULL;
+	}
+
+	if (serio->registered) {
+		device_del(&serio->dev);
+		list_del_init(&serio->node);
+		serio->registered = 0;
+	}
+
+	serio_remove_pending_events(serio);
+	put_device(&serio->dev);
+}
+
+/*
+ * Reconnect serio port and all its children (re-initialize attached devices)
+ */
+static void serio_reconnect_port(struct serio *serio)
+{
+	do {
+		if (!serio->drv || !serio->drv->reconnect || serio->drv->reconnect(serio)) {
+			serio_disconnect_port(serio);
+			serio_find_driver(serio);
+			/* Ok, old children are now gone, we are done */
+			break;
+		}
+		serio = serio->child;
+	} while (serio);
+}
+
+/*
+ * serio_disconnect_port() unbinds a port from its driver. As a side effect
+ * all child ports are unbound and destroyed.
+ */
+static void serio_disconnect_port(struct serio *serio)
+{
+	struct serio *s, *parent;
+
+	if (serio->child) {
+		/*
+		 * Children ports should be disconnected and destroyed
+		 * first, staring with the leaf one, since we don't want
+		 * to do recursion
+		 */
+		for (s = serio; s->child; s = s->child)
+			/* empty */;
+
+		do {
+			parent = s->parent;
+
+			serio_release_driver(s);
+			serio_destroy_port(s);
+		} while ((s = parent) != serio);
+	}
+
+	/*
+	 * Ok, no children left, now disconnect this port
+	 */
+	serio_release_driver(serio);
+}
+
+void serio_rescan(struct serio *serio)
+{
+	serio_queue_event(serio, NULL, SERIO_RESCAN);
+}
+
+void serio_reconnect(struct serio *serio)
+{
+	serio_queue_event(serio, NULL, SERIO_RECONNECT);
+}
+
+/*
+ * Submits register request to kseriod for subsequent execution.
+ * Note that port registration is always asynchronous.
+ */
+void __serio_register_port(struct serio *serio, struct module *owner)
+{
+	serio_init_port(serio);
+	serio_queue_event(serio, owner, SERIO_REGISTER_PORT);
+}
+
+/*
+ * Synchronously unregisters serio port.
+ */
+void serio_unregister_port(struct serio *serio)
+{
+	down(&serio_sem);
+	serio_disconnect_port(serio);
+	serio_destroy_port(serio);
+	up(&serio_sem);
+}
+
+/*
+ * Submits register request to kseriod for subsequent execution.
+ * Can be used when it is not obvious whether the serio_sem is
+ * taken or not and when delayed execution is feasible.
+ */
+void __serio_unregister_port_delayed(struct serio *serio, struct module *owner)
+{
+	serio_queue_event(serio, owner, SERIO_UNREGISTER_PORT);
+}
+
+
+/*
+ * Serio driver operations
+ */
+
+static ssize_t serio_driver_show_description(struct device_driver *drv, char *buf)
+{
+	struct serio_driver *driver = to_serio_driver(drv);
+	return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)");
+}
+
+static ssize_t serio_driver_show_bind_mode(struct device_driver *drv, char *buf)
+{
+	struct serio_driver *serio_drv = to_serio_driver(drv);
+	return sprintf(buf, "%s\n", serio_drv->manual_bind ? "manual" : "auto");
+}
+
+static ssize_t serio_driver_set_bind_mode(struct device_driver *drv, const char *buf, size_t count)
+{
+	struct serio_driver *serio_drv = to_serio_driver(drv);
+	int retval;
+
+	retval = count;
+	if (!strncmp(buf, "manual", count)) {
+		serio_drv->manual_bind = 1;
+	} else if (!strncmp(buf, "auto", count)) {
+		serio_drv->manual_bind = 0;
+	} else {
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+
+
+static struct driver_attribute serio_driver_attrs[] = {
+	__ATTR(description, S_IRUGO, serio_driver_show_description, NULL),
+	__ATTR(bind_mode, S_IWUSR | S_IRUGO,
+		serio_driver_show_bind_mode, serio_driver_set_bind_mode),
+	__ATTR_NULL
+};
+
+static int serio_driver_probe(struct device *dev)
+{
+	struct serio *serio = to_serio_port(dev);
+	struct serio_driver *drv = to_serio_driver(dev->driver);
+
+	return drv->connect(serio, drv);
+}
+
+static int serio_driver_remove(struct device *dev)
+{
+	struct serio *serio = to_serio_port(dev);
+	struct serio_driver *drv = to_serio_driver(dev->driver);
+
+	drv->disconnect(serio);
+	return 0;
+}
+
+void __serio_register_driver(struct serio_driver *drv, struct module *owner)
+{
+	drv->driver.bus = &serio_bus;
+	drv->driver.probe = serio_driver_probe;
+	drv->driver.remove = serio_driver_remove;
+
+	serio_queue_event(drv, owner, SERIO_REGISTER_DRIVER);
+}
+
+void serio_unregister_driver(struct serio_driver *drv)
+{
+	struct serio *serio;
+
+	down(&serio_sem);
+	drv->manual_bind = 1;	/* so serio_find_driver ignores it */
+
+start_over:
+	list_for_each_entry(serio, &serio_list, node) {
+		if (serio->drv == drv) {
+			serio_disconnect_port(serio);
+			serio_find_driver(serio);
+			/* we could've deleted some ports, restart */
+			goto start_over;
+		}
+	}
+
+	driver_unregister(&drv->driver);
+	up(&serio_sem);
+}
+
+static void serio_set_drv(struct serio *serio, struct serio_driver *drv)
+{
+	down(&serio->drv_sem);
+	serio_pause_rx(serio);
+	serio->drv = drv;
+	serio_continue_rx(serio);
+	up(&serio->drv_sem);
+}
+
+static int serio_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct serio *serio = to_serio_port(dev);
+	struct serio_driver *serio_drv = to_serio_driver(drv);
+
+	if (serio->manual_bind || serio_drv->manual_bind)
+		return 0;
+
+	return serio_match_port(serio_drv->id_table, serio);
+}
+
+#ifdef CONFIG_HOTPLUG
+
+#define PUT_ENVP(fmt, val) 						\
+do {									\
+	envp[i++] = buffer;						\
+	length += snprintf(buffer, buffer_size - length, fmt, val);	\
+	if (buffer_size - length <= 0 || i >= num_envp)			\
+		return -ENOMEM;						\
+	length++;							\
+	buffer += length;						\
+} while (0)
+static int serio_hotplug(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
+{
+	struct serio *serio;
+	int i = 0;
+	int length = 0;
+
+	if (!dev)
+		return -ENODEV;
+
+	serio = to_serio_port(dev);
+
+	PUT_ENVP("SERIO_TYPE=%02x", serio->id.type);
+	PUT_ENVP("SERIO_PROTO=%02x", serio->id.proto);
+	PUT_ENVP("SERIO_ID=%02x", serio->id.id);
+	PUT_ENVP("SERIO_EXTRA=%02x", serio->id.extra);
+
+	envp[i] = NULL;
+
+	return 0;
+}
+#undef PUT_ENVP
+
+#else
+
+static int serio_hotplug(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
+{
+	return -ENODEV;
+}
+
+#endif /* CONFIG_HOTPLUG */
+
+static int serio_resume(struct device *dev)
+{
+	struct serio *serio = to_serio_port(dev);
+
+	if (!serio->drv || !serio->drv->reconnect || serio->drv->reconnect(serio)) {
+		serio_disconnect_port(serio);
+		/*
+		 * Driver re-probing can take a while, so better let kseriod
+		 * deal with it.
+		 */
+		serio_rescan(serio);
+	}
+
+	return 0;
+}
+
+/* called from serio_driver->connect/disconnect methods under serio_sem */
+int serio_open(struct serio *serio, struct serio_driver *drv)
+{
+	serio_set_drv(serio, drv);
+
+	if (serio->open && serio->open(serio)) {
+		serio_set_drv(serio, NULL);
+		return -1;
+	}
+	return 0;
+}
+
+/* called from serio_driver->connect/disconnect methods under serio_sem */
+void serio_close(struct serio *serio)
+{
+	if (serio->close)
+		serio->close(serio);
+
+	serio_set_drv(serio, NULL);
+}
+
+irqreturn_t serio_interrupt(struct serio *serio,
+		unsigned char data, unsigned int dfl, struct pt_regs *regs)
+{
+	unsigned long flags;
+	irqreturn_t ret = IRQ_NONE;
+
+	spin_lock_irqsave(&serio->lock, flags);
+
+        if (likely(serio->drv)) {
+                ret = serio->drv->interrupt(serio, data, dfl, regs);
+	} else if (!dfl && serio->registered) {
+		serio_rescan(serio);
+		ret = IRQ_HANDLED;
+	}
+
+	spin_unlock_irqrestore(&serio->lock, flags);
+
+	return ret;
+}
+
+static int __init serio_init(void)
+{
+	if (!(serio_pid = kernel_thread(serio_thread, NULL, CLONE_KERNEL))) {
+		printk(KERN_ERR "serio: Failed to start kseriod\n");
+		return -1;
+	}
+
+	serio_bus.dev_attrs = serio_device_attrs;
+	serio_bus.drv_attrs = serio_driver_attrs;
+	serio_bus.match = serio_bus_match;
+	serio_bus.hotplug = serio_hotplug;
+	serio_bus.resume = serio_resume;
+	bus_register(&serio_bus);
+
+	return 0;
+}
+
+static void __exit serio_exit(void)
+{
+	bus_unregister(&serio_bus);
+	kill_proc(serio_pid, SIGTERM, 1);
+	wait_for_completion(&serio_exited);
+}
+
+module_init(serio_init);
+module_exit(serio_exit);
diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c
new file mode 100644
index 0000000..d914e7e
--- /dev/null
+++ b/drivers/input/serio/serio_raw.c
@@ -0,0 +1,403 @@
+/*
+ * Raw serio device providing access to a raw byte stream from underlying
+ * serio port. Closely emulates behavior of pre-2.6 /dev/psaux device
+ *
+ * Copyright (c) 2004 Dmitry Torokhov
+ *
+ * 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/slab.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/major.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/wait.h>
+
+#define DRIVER_DESC	"Raw serio driver"
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define SERIO_RAW_QUEUE_LEN	64
+struct serio_raw {
+	unsigned char queue[SERIO_RAW_QUEUE_LEN];
+	unsigned int tail, head;
+
+	char name[16];
+	unsigned int refcnt;
+	struct serio *serio;
+	struct miscdevice dev;
+	wait_queue_head_t wait;
+	struct list_head list;
+	struct list_head node;
+};
+
+struct serio_raw_list {
+	struct fasync_struct *fasync;
+	struct serio_raw *serio_raw;
+	struct list_head node;
+};
+
+static DECLARE_MUTEX(serio_raw_sem);
+static LIST_HEAD(serio_raw_list);
+static unsigned int serio_raw_no;
+
+/*********************************************************************
+ *             Interface with userspace (file operations)            *
+ *********************************************************************/
+
+static int serio_raw_fasync(int fd, struct file *file, int on)
+{
+	struct serio_raw_list *list = file->private_data;
+	int retval;
+
+	retval = fasync_helper(fd, file, on, &list->fasync);
+	return retval < 0 ? retval : 0;
+}
+
+static struct serio_raw *serio_raw_locate(int minor)
+{
+	struct serio_raw *serio_raw;
+
+	list_for_each_entry(serio_raw, &serio_raw_list, node) {
+		if (serio_raw->dev.minor == minor)
+			return serio_raw;
+	}
+
+	return NULL;
+}
+
+static int serio_raw_open(struct inode *inode, struct file *file)
+{
+	struct serio_raw *serio_raw;
+	struct serio_raw_list *list;
+	int retval = 0;
+
+	retval = down_interruptible(&serio_raw_sem);
+	if (retval)
+		return retval;
+
+	if (!(serio_raw = serio_raw_locate(iminor(inode)))) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	if (!serio_raw->serio) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	if (!(list = kmalloc(sizeof(struct serio_raw_list), GFP_KERNEL))) {
+		retval = -ENOMEM;
+		goto out;
+	}
+
+	memset(list, 0, sizeof(struct serio_raw_list));
+	list->serio_raw = serio_raw;
+	file->private_data = list;
+
+	serio_raw->refcnt++;
+	list_add_tail(&list->node, &serio_raw->list);
+
+out:
+	up(&serio_raw_sem);
+	return retval;
+}
+
+static int serio_raw_cleanup(struct serio_raw *serio_raw)
+{
+	if (--serio_raw->refcnt == 0) {
+		misc_deregister(&serio_raw->dev);
+		list_del_init(&serio_raw->node);
+		kfree(serio_raw);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+static int serio_raw_release(struct inode *inode, struct file *file)
+{
+	struct serio_raw_list *list = file->private_data;
+	struct serio_raw *serio_raw = list->serio_raw;
+
+	down(&serio_raw_sem);
+
+	serio_raw_fasync(-1, file, 0);
+	serio_raw_cleanup(serio_raw);
+
+	up(&serio_raw_sem);
+	return 0;
+}
+
+static int serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c)
+{
+	unsigned long flags;
+	int empty;
+
+	spin_lock_irqsave(&serio_raw->serio->lock, flags);
+
+	empty = serio_raw->head == serio_raw->tail;
+	if (!empty) {
+		*c = serio_raw->queue[serio_raw->tail];
+		serio_raw->tail = (serio_raw->tail + 1) % SERIO_RAW_QUEUE_LEN;
+	}
+
+	spin_unlock_irqrestore(&serio_raw->serio->lock, flags);
+
+	return !empty;
+}
+
+static ssize_t serio_raw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+{
+	struct serio_raw_list *list = file->private_data;
+	struct serio_raw *serio_raw = list->serio_raw;
+	char c;
+	ssize_t retval = 0;
+
+	if (!serio_raw->serio)
+		return -ENODEV;
+
+	if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK))
+		return -EAGAIN;
+
+	retval = wait_event_interruptible(list->serio_raw->wait,
+					  serio_raw->head != serio_raw->tail || !serio_raw->serio);
+	if (retval)
+		return retval;
+
+	if (!serio_raw->serio)
+		return -ENODEV;
+
+	while (retval < count && serio_raw_fetch_byte(serio_raw, &c)) {
+		if (put_user(c, buffer++))
+			return -EFAULT;
+		retval++;
+	}
+
+	return retval;
+}
+
+static ssize_t serio_raw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
+{
+	struct serio_raw_list *list = file->private_data;
+	ssize_t written = 0;
+	int retval;
+	unsigned char c;
+
+	retval = down_interruptible(&serio_raw_sem);
+	if (retval)
+		return retval;
+
+	if (!list->serio_raw->serio) {
+		retval = -ENODEV;
+		goto out;
+	}
+
+	if (count > 32)
+		count = 32;
+
+	while (count--) {
+		if (get_user(c, buffer++)) {
+			retval = -EFAULT;
+			goto out;
+		}
+		if (serio_write(list->serio_raw->serio, c)) {
+			retval = -EIO;
+			goto out;
+		}
+		written++;
+	};
+
+out:
+	up(&serio_raw_sem);
+	return written;
+}
+
+static unsigned int serio_raw_poll(struct file *file, poll_table *wait)
+{
+	struct serio_raw_list *list = file->private_data;
+
+	poll_wait(file, &list->serio_raw->wait, wait);
+
+	if (list->serio_raw->head != list->serio_raw->tail)
+		return POLLIN | POLLRDNORM;
+
+	return 0;
+}
+
+static struct file_operations serio_raw_fops = {
+	.owner =	THIS_MODULE,
+	.open =		serio_raw_open,
+	.release =	serio_raw_release,
+	.read =		serio_raw_read,
+	.write =	serio_raw_write,
+	.poll =		serio_raw_poll,
+	.fasync =	serio_raw_fasync,
+};
+
+
+/*********************************************************************
+ *                   Interface with serio port   	             *
+ *********************************************************************/
+
+static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data,
+					unsigned int dfl, struct pt_regs *regs)
+{
+	struct serio_raw *serio_raw = serio_get_drvdata(serio);
+	struct serio_raw_list *list;
+	unsigned int head = serio_raw->head;
+
+	/* we are holding serio->lock here so we are prootected */
+	serio_raw->queue[head] = data;
+	head = (head + 1) % SERIO_RAW_QUEUE_LEN;
+	if (likely(head != serio_raw->tail)) {
+		serio_raw->head = head;
+		list_for_each_entry(list, &serio_raw->list, node)
+			kill_fasync(&list->fasync, SIGIO, POLL_IN);
+		wake_up_interruptible(&serio_raw->wait);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct serio_raw *serio_raw;
+	int err;
+
+	if (!(serio_raw = kmalloc(sizeof(struct serio_raw), GFP_KERNEL))) {
+		printk(KERN_ERR "serio_raw.c: can't allocate memory for a device\n");
+		return -ENOMEM;
+	}
+
+	down(&serio_raw_sem);
+
+	memset(serio_raw, 0, sizeof(struct serio_raw));
+	snprintf(serio_raw->name, sizeof(serio_raw->name), "serio_raw%d", serio_raw_no++);
+	serio_raw->refcnt = 1;
+	serio_raw->serio = serio;
+	INIT_LIST_HEAD(&serio_raw->list);
+	init_waitqueue_head(&serio_raw->wait);
+
+	serio_set_drvdata(serio, serio_raw);
+
+	err = serio_open(serio, drv);
+	if (err)
+		goto out_free;
+
+	list_add_tail(&serio_raw->node, &serio_raw_list);
+
+	serio_raw->dev.minor = PSMOUSE_MINOR;
+	serio_raw->dev.name = serio_raw->name;
+	serio_raw->dev.fops = &serio_raw_fops;
+
+	err = misc_register(&serio_raw->dev);
+	if (err) {
+		serio_raw->dev.minor = MISC_DYNAMIC_MINOR;
+		err = misc_register(&serio_raw->dev);
+	}
+
+	if (err) {
+		printk(KERN_INFO "serio_raw: failed to register raw access device for %s\n",
+			serio->phys);
+		goto out_close;
+	}
+
+	printk(KERN_INFO "serio_raw: raw access enabled on %s (%s, minor %d)\n",
+		serio->phys, serio_raw->name, serio_raw->dev.minor);
+	goto out;
+
+out_close:
+	serio_close(serio);
+	list_del_init(&serio_raw->node);
+out_free:
+	serio_set_drvdata(serio, NULL);
+	kfree(serio_raw);
+out:
+	up(&serio_raw_sem);
+	return err;
+}
+
+static int serio_raw_reconnect(struct serio *serio)
+{
+	struct serio_raw *serio_raw = serio_get_drvdata(serio);
+	struct serio_driver *drv = serio->drv;
+
+	if (!drv || !serio_raw) {
+		printk(KERN_DEBUG "serio_raw: reconnect request, but serio is disconnected, ignoring...\n");
+		return -1;
+	}
+
+	/*
+	 * Nothing needs to be done here, we just need this method to
+	 * keep the same device.
+	 */
+	return 0;
+}
+
+static void serio_raw_disconnect(struct serio *serio)
+{
+	struct serio_raw *serio_raw;
+
+	down(&serio_raw_sem);
+
+	serio_raw = serio_get_drvdata(serio);
+
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+
+	serio_raw->serio = NULL;
+	if (!serio_raw_cleanup(serio_raw))
+		wake_up_interruptible(&serio_raw->wait);
+
+	up(&serio_raw_sem);
+}
+
+static struct serio_device_id serio_raw_serio_ids[] = {
+	{
+		.type	= SERIO_8042,
+		.proto	= SERIO_ANY,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, serio_raw_serio_ids);
+
+static struct serio_driver serio_raw_drv = {
+	.driver		= {
+		.name	= "serio_raw",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= serio_raw_serio_ids,
+	.interrupt	= serio_raw_interrupt,
+	.connect	= serio_raw_connect,
+	.reconnect	= serio_raw_reconnect,
+	.disconnect	= serio_raw_disconnect,
+	.manual_bind	= 1,
+};
+
+static int __init serio_raw_init(void)
+{
+	serio_register_driver(&serio_raw_drv);
+	return 0;
+}
+
+static void __exit serio_raw_exit(void)
+{
+	serio_unregister_driver(&serio_raw_drv);
+}
+
+module_init(serio_raw_init);
+module_exit(serio_raw_exit);
diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c
new file mode 100644
index 0000000..22f7368
--- /dev/null
+++ b/drivers/input/serio/serport.c
@@ -0,0 +1,226 @@
+/*
+ * Input device TTY line discipline
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ *
+ * This is a module that converts a tty line into a much simpler
+ * 'serial io port' abstraction that the input device drivers use.
+ */
+
+/*
+ * 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 <asm/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/tty.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Input device TTY line discipline");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_LDISC(N_MOUSE);
+
+#define SERPORT_BUSY	1
+
+struct serport {
+	struct tty_struct *tty;
+	wait_queue_head_t wait;
+	struct serio *serio;
+	unsigned long flags;
+};
+
+/*
+ * Callback functions from the serio code.
+ */
+
+static int serport_serio_write(struct serio *serio, unsigned char data)
+{
+	struct serport *serport = serio->port_data;
+	return -(serport->tty->driver->write(serport->tty, &data, 1) != 1);
+}
+
+static void serport_serio_close(struct serio *serio)
+{
+	struct serport *serport = serio->port_data;
+
+	serport->serio->id.type = 0;
+	wake_up_interruptible(&serport->wait);
+}
+
+/*
+ * serport_ldisc_open() is the routine that is called upon setting our line
+ * discipline on a tty. It prepares the serio struct.
+ */
+
+static int serport_ldisc_open(struct tty_struct *tty)
+{
+	struct serport *serport;
+	struct serio *serio;
+	char name[64];
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	serport = kmalloc(sizeof(struct serport), GFP_KERNEL);
+	serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (unlikely(!serport || !serio)) {
+		kfree(serport);
+		kfree(serio);
+		return -ENOMEM;
+	}
+
+	memset(serport, 0, sizeof(struct serport));
+	serport->serio = serio;
+	set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+	serport->tty = tty;
+	tty->disc_data = serport;
+
+	memset(serio, 0, sizeof(struct serio));
+	strlcpy(serio->name, "Serial port", sizeof(serio->name));
+	snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty, name));
+	serio->id.type = SERIO_RS232;
+	serio->write = serport_serio_write;
+	serio->close = serport_serio_close;
+	serio->port_data = serport;
+
+	init_waitqueue_head(&serport->wait);
+
+	return 0;
+}
+
+/*
+ * serport_ldisc_close() is the opposite of serport_ldisc_open()
+ */
+
+static void serport_ldisc_close(struct tty_struct *tty)
+{
+	struct serport *serport = (struct serport*) tty->disc_data;
+	kfree(serport);
+}
+
+/*
+ * serport_ldisc_receive() is called by the low level tty driver when characters
+ * are ready for us. We forward the characters, one by one to the 'interrupt'
+ * routine.
+ *
+ * FIXME: We should get pt_regs from the tty layer and forward them to
+ *	  serio_interrupt here.
+ */
+
+static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
+{
+	struct serport *serport = (struct serport*) tty->disc_data;
+	int i;
+	for (i = 0; i < count; i++)
+		serio_interrupt(serport->serio, cp[i], 0, NULL);
+}
+
+/*
+ * serport_ldisc_room() reports how much room we do have for receiving data.
+ * Although we in fact have infinite room, we need to specify some value
+ * here, and 256 seems to be reasonable.
+ */
+
+static int serport_ldisc_room(struct tty_struct *tty)
+{
+	return 256;
+}
+
+/*
+ * serport_ldisc_read() just waits indefinitely if everything goes well.
+ * However, when the serio driver closes the serio port, it finishes,
+ * returning 0 characters.
+ */
+
+static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, unsigned char __user * buf, size_t nr)
+{
+	struct serport *serport = (struct serport*) tty->disc_data;
+	char name[64];
+
+	if (test_and_set_bit(SERPORT_BUSY, &serport->flags))
+		return -EBUSY;
+
+	serio_register_port(serport->serio);
+	printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty, name));
+	wait_event_interruptible(serport->wait, !serport->serio->id.type);
+	serio_unregister_port(serport->serio);
+
+	clear_bit(SERPORT_BUSY, &serport->flags);
+
+	return 0;
+}
+
+/*
+ * serport_ldisc_ioctl() allows to set the port protocol, and device ID
+ */
+
+static int serport_ldisc_ioctl(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg)
+{
+	struct serport *serport = (struct serport*) tty->disc_data;
+	struct serio *serio = serport->serio;
+	unsigned long type;
+
+	if (cmd == SPIOCSTYPE) {
+		if (get_user(type, (unsigned long __user *) arg))
+			return -EFAULT;
+
+		serio->id.proto	= type & 0x000000ff;
+		serio->id.id	= (type & 0x0000ff00) >> 8;
+		serio->id.extra	= (type & 0x00ff0000) >> 16;
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static void serport_ldisc_write_wakeup(struct tty_struct * tty)
+{
+	struct serport *sp = (struct serport *) tty->disc_data;
+
+	serio_drv_write_wakeup(sp->serio);
+}
+
+/*
+ * The line discipline structure.
+ */
+
+static struct tty_ldisc serport_ldisc = {
+	.owner =	THIS_MODULE,
+	.name =		"input",
+	.open =		serport_ldisc_open,
+	.close =	serport_ldisc_close,
+	.read =		serport_ldisc_read,
+	.ioctl =	serport_ldisc_ioctl,
+	.receive_buf =	serport_ldisc_receive,
+	.receive_room =	serport_ldisc_room,
+	.write_wakeup =	serport_ldisc_write_wakeup
+};
+
+/*
+ * The functions for insering/removing us as a module.
+ */
+
+static int __init serport_init(void)
+{
+	int retval;
+	retval = tty_register_ldisc(N_MOUSE, &serport_ldisc);
+	if (retval)
+		printk(KERN_ERR "serport.c: Error registering line discipline.\n");
+
+	return  retval;
+}
+
+static void __exit serport_exit(void)
+{
+	tty_register_ldisc(N_MOUSE, NULL);
+}
+
+module_init(serport_init);
+module_exit(serport_exit);
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
new file mode 100644
index 0000000..7e99127
--- /dev/null
+++ b/drivers/input/touchscreen/Kconfig
@@ -0,0 +1,98 @@
+#
+# Mouse driver configuration
+#
+menuconfig INPUT_TOUCHSCREEN
+	bool "Touchscreens"
+	help
+	  Say Y here, and a list of supported touchscreens will be displayed.
+	  This option doesn't affect the kernel.
+
+	  If unsure, say Y.
+
+if INPUT_TOUCHSCREEN
+
+config TOUCHSCREEN_BITSY
+	tristate "Compaq iPAQ H3600 (Bitsy) touchscreen"
+	depends on SA1100_BITSY
+	select SERIO
+	help
+	  Say Y here if you have the h3600 (Bitsy) touchscreen.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called h3600_ts_input.
+
+config TOUCHSCREEN_CORGI
+	tristate "Corgi touchscreen (for Sharp SL-C7xx)"
+	depends on PXA_SHARPSL
+	default y	
+	help
+	  Say Y here to enable the driver for the touchscreen on the 
+	  Sharp SL-C7xx series of PDAs.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ads7846_ts.
+
+config TOUCHSCREEN_GUNZE
+	tristate "Gunze AHL-51S touchscreen"
+	select SERIO
+	help
+	  Say Y here if you have the Gunze AHL-51 touchscreen connected to
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gunze.
+
+config TOUCHSCREEN_ELO
+	tristate "Elo serial touchscreens"
+	select SERIO
+	help
+	  Say Y here if you have an Elo serial touchscreen connected to
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called gunze.
+
+config TOUCHSCREEN_MTOUCH
+	tristate "MicroTouch serial touchscreens"
+	select SERIO
+	help
+	  Say Y here if you have a MicroTouch (3M) serial touchscreen connected to
+	  your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mtouch.
+
+config TOUCHSCREEN_MK712
+	tristate "ICS MicroClock MK712 touchscreen"
+	help
+	  Say Y here if you have the ICS MicroClock MK712 touchscreen
+	  controller chip in your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mk712.
+
+config TOUCHSCREEN_HP600
+	tristate "HP Jornada 680/690 touchscreen"
+	depends on SH_HP600 && SH_ADC
+	help
+	  Say Y here if you have a HP Jornada 680 or 690 and want to
+          support the built-in touchscreen.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called hp680_ts_input.
+
+endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
new file mode 100644
index 0000000..6842869
--- /dev/null
+++ b/drivers/input/touchscreen/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the mouse drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_TOUCHSCREEN_BITSY)	+= h3600_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_CORGI)	+= corgi_ts.o
+obj-$(CONFIG_TOUCHSCREEN_GUNZE)	+= gunze.o
+obj-$(CONFIG_TOUCHSCREEN_ELO)	+= elo.o
+obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
+obj-$(CONFIG_TOUCHSCREEN_MK712)	+= mk712.o
+obj-$(CONFIG_TOUCHSCREEN_HP600)	+= hp680_ts_input.o
diff --git a/drivers/input/touchscreen/corgi_ts.c b/drivers/input/touchscreen/corgi_ts.c
new file mode 100644
index 0000000..3f8b61c
--- /dev/null
+++ b/drivers/input/touchscreen/corgi_ts.c
@@ -0,0 +1,380 @@
+/*
+ *  Touchscreen driver for Sharp Corgi models (SL-C7xx)
+ *
+ *  Copyright (c) 2004-2005 Richard Purdie
+ *
+ *  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>
+
+
+#define PWR_MODE_ACTIVE		0
+#define PWR_MODE_SUSPEND	1
+
+#define X_AXIS_MAX		3830
+#define X_AXIS_MIN		150
+#define Y_AXIS_MAX		3830
+#define Y_AXIS_MIN		190
+#define PRESSURE_MIN		0
+#define PRESSURE_MAX		15000
+
+struct ts_event {
+	short pressure;
+	short x;
+	short y;
+};
+
+struct corgi_ts {
+	char phys[32];
+	struct input_dev input;
+	struct timer_list timer;
+	struct ts_event tc;
+	int pendown;
+	int power_mode;
+};
+
+#define STATUS_HSYNC		(GPLR(CORGI_GPIO_HSYNC) & GPIO_bit(CORGI_GPIO_HSYNC))
+
+#define SyncHS()	while((STATUS_HSYNC) == 0); while((STATUS_HSYNC) != 0);
+#define CCNT(a)		asm volatile ("mrc p14, 0, %0, C1, C0, 0" : "=r"(a))
+#define CCNT_ON()	{int pmnc = 1; asm volatile ("mcr p14, 0, %0, C0, C0, 0" : : "r"(pmnc));}
+#define CCNT_OFF()	{int pmnc = 0; asm volatile ("mcr p14, 0, %0, C0, C0, 0" : : "r"(pmnc));}
+
+#define WAIT_HS_400_VGA		7013U	// 17.615us
+#define WAIT_HS_400_QVGA	16622U	// 41.750us
+
+
+/* ADS7846 Touch Screen Controller bit definitions */
+#define ADSCTRL_PD0		(1u << 0)	/* PD0 */
+#define ADSCTRL_PD1		(1u << 1)	/* PD1 */
+#define ADSCTRL_DFR		(1u << 2)	/* SER/DFR */
+#define ADSCTRL_MOD		(1u << 3)	/* Mode */
+#define ADSCTRL_ADR_SH	4	/* Address setting */
+#define ADSCTRL_STS		(1u << 7)	/* Start Bit */
+
+/* External Functions */
+extern int w100fb_get_xres(void);
+extern int w100fb_get_blanking(void);
+extern int w100fb_get_fastsysclk(void);
+extern unsigned int get_clk_frequency_khz(int info);
+
+static unsigned long calc_waittime(void)
+{
+	int w100fb_xres = w100fb_get_xres();
+	unsigned int waittime = 0;
+
+	if (w100fb_xres == 480 || w100fb_xres == 640) {
+		waittime = WAIT_HS_400_VGA * get_clk_frequency_khz(0) / 398131U;
+
+		if (w100fb_get_fastsysclk() == 100)
+			waittime = waittime * 75 / 100;
+
+		if (w100fb_xres == 640)
+			waittime *= 3;
+
+		return waittime;
+	}
+
+	return WAIT_HS_400_QVGA * get_clk_frequency_khz(0) / 398131U;
+}
+
+static int sync_receive_data_send_cmd(int doRecive, int doSend, unsigned int address, unsigned long wait_time)
+{
+	int pos = 0;
+	unsigned long timer1 = 0, timer2;
+	int dosleep;
+
+	dosleep = !w100fb_get_blanking();
+
+	if (dosleep && doSend) {
+		CCNT_ON();
+		/* polling HSync */
+		SyncHS();
+		/* get CCNT */
+		CCNT(timer1);
+	}
+
+	if (doRecive)
+		pos = corgi_ssp_ads7846_get();
+
+	if (doSend) {
+		int cmd = ADSCTRL_PD0 | ADSCTRL_PD1 | (address << ADSCTRL_ADR_SH) | ADSCTRL_STS;
+		/* dummy command */
+		corgi_ssp_ads7846_put(cmd);
+		corgi_ssp_ads7846_get();
+
+		if (dosleep) {
+			/* Wait after HSync */
+			CCNT(timer2);
+			if (timer2-timer1 > wait_time) {
+				/* timeout */
+				SyncHS();
+				/* get OSCR */
+				CCNT(timer1);
+				/* Wait after HSync */
+				CCNT(timer2);
+			}
+			while (timer2 - timer1 < wait_time)
+				CCNT(timer2);
+		}
+		corgi_ssp_ads7846_put(cmd);
+		if (dosleep)
+			CCNT_OFF();
+	}
+	return pos;
+}
+
+static int read_xydata(struct corgi_ts *corgi_ts)
+{
+	unsigned int x, y, z1, z2;
+	unsigned long flags, wait_time;
+
+	/* critical section */
+	local_irq_save(flags);
+	corgi_ssp_ads7846_lock();
+	wait_time=calc_waittime();
+
+	/* Y-axis */
+	sync_receive_data_send_cmd(0, 1, 1u, wait_time);
+
+	/* Y-axis */
+	sync_receive_data_send_cmd(1, 1, 1u, wait_time);
+
+	/* X-axis */
+	y = sync_receive_data_send_cmd(1, 1, 5u, wait_time);
+
+	/* Z1 */
+	x = sync_receive_data_send_cmd(1, 1, 3u, wait_time);
+
+	/* Z2 */
+	z1 = sync_receive_data_send_cmd(1, 1, 4u, wait_time);
+	z2 = sync_receive_data_send_cmd(1, 0, 4u, wait_time);
+
+	/* Power-Down Enable */
+	corgi_ssp_ads7846_put((1u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
+	corgi_ssp_ads7846_get();
+
+	corgi_ssp_ads7846_unlock();
+	local_irq_restore(flags);
+
+	if (x== 0 || y == 0 || z1 == 0 || (x * (z2 - z1) / z1) >= 15000) {
+		corgi_ts->tc.pressure = 0;
+		return 0;
+	}
+
+	corgi_ts->tc.x = x;
+	corgi_ts->tc.y = y;
+	corgi_ts->tc.pressure = (x * (z2 - z1)) / z1;
+	return 1;
+}
+
+static void new_data(struct corgi_ts *corgi_ts, struct pt_regs *regs)
+{
+	if (corgi_ts->power_mode != PWR_MODE_ACTIVE)
+		return;
+
+	if (!corgi_ts->tc.pressure && corgi_ts->pendown == 0)
+		return;
+
+	if (regs)
+		input_regs(&corgi_ts->input, regs);
+
+	input_report_abs(&corgi_ts->input, ABS_X, corgi_ts->tc.x);
+	input_report_abs(&corgi_ts->input, ABS_Y, corgi_ts->tc.y);
+	input_report_abs(&corgi_ts->input, ABS_PRESSURE, corgi_ts->tc.pressure);
+	input_report_key(&corgi_ts->input, BTN_TOUCH, (corgi_ts->pendown != 0));
+	input_sync(&corgi_ts->input);
+}
+
+static void ts_interrupt_main(struct corgi_ts *corgi_ts, int isTimer, struct pt_regs *regs)
+{
+	if ((GPLR(CORGI_GPIO_TP_INT) & GPIO_bit(CORGI_GPIO_TP_INT)) == 0) {
+		/* Disable Interrupt */
+		set_irq_type(CORGI_IRQ_GPIO_TP_INT, IRQT_NOEDGE);
+		if (read_xydata(corgi_ts)) {
+			corgi_ts->pendown = 1;
+			new_data(corgi_ts, regs);
+		}
+		mod_timer(&corgi_ts->timer, jiffies + HZ / 100);
+	} else {
+		if (corgi_ts->pendown == 1 || corgi_ts->pendown == 2) {
+			mod_timer(&corgi_ts->timer, jiffies + HZ / 100);
+			corgi_ts->pendown++;
+			return;
+		}
+
+		if (corgi_ts->pendown) {
+			corgi_ts->tc.pressure = 0;
+			new_data(corgi_ts, regs);
+		}
+
+		/* Enable Falling Edge */
+		set_irq_type(CORGI_IRQ_GPIO_TP_INT, IRQT_FALLING);
+		corgi_ts->pendown = 0;
+	}
+}
+
+static void corgi_ts_timer(unsigned long data)
+{
+	struct corgi_ts *corgits_data = (struct corgi_ts *) data;
+	ts_interrupt_main(corgits_data, 1, NULL);
+}
+
+static irqreturn_t ts_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct corgi_ts *corgits_data = dev_id;
+	ts_interrupt_main(corgits_data, 0, regs);
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM
+static int corgits_suspend(struct device *dev, uint32_t state, uint32_t level)
+{
+	if (level == SUSPEND_POWER_DOWN) {
+		struct corgi_ts *corgi_ts = dev_get_drvdata(dev);
+
+		if (corgi_ts->pendown) {
+			del_timer_sync(&corgi_ts->timer);
+			corgi_ts->tc.pressure = 0;
+			new_data(corgi_ts, NULL);
+			corgi_ts->pendown = 0;
+		}
+		corgi_ts->power_mode = PWR_MODE_SUSPEND;
+
+		corgi_ssp_ads7846_putget((1u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
+	}
+	return 0;
+}
+
+static int corgits_resume(struct device *dev, uint32_t level)
+{
+	if (level == RESUME_POWER_ON) {
+		struct corgi_ts *corgi_ts = dev_get_drvdata(dev);
+
+		corgi_ssp_ads7846_putget((4u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
+		/* Enable Falling Edge */
+		set_irq_type(CORGI_IRQ_GPIO_TP_INT, IRQT_FALLING);
+		corgi_ts->power_mode = PWR_MODE_ACTIVE;
+	}
+	return 0;
+}
+#else
+#define corgits_suspend		NULL
+#define corgits_resume		NULL
+#endif
+
+static int __init corgits_probe(struct device *dev)
+{
+	struct corgi_ts *corgi_ts;
+
+	if (!(corgi_ts = kmalloc(sizeof(struct corgi_ts), GFP_KERNEL)))
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, corgi_ts);
+
+	memset(corgi_ts, 0, sizeof(struct corgi_ts));
+
+	init_input_dev(&corgi_ts->input);
+	corgi_ts->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+	corgi_ts->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+	input_set_abs_params(&corgi_ts->input, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0);
+	input_set_abs_params(&corgi_ts->input, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0);
+	input_set_abs_params(&corgi_ts->input, ABS_PRESSURE, PRESSURE_MIN, PRESSURE_MAX, 0, 0);
+
+	strcpy(corgi_ts->phys, "corgits/input0");
+
+	corgi_ts->input.private = corgi_ts;
+	corgi_ts->input.name = "Corgi Touchscreen";
+	corgi_ts->input.dev = dev;
+	corgi_ts->input.phys = corgi_ts->phys;
+	corgi_ts->input.id.bustype = BUS_HOST;
+	corgi_ts->input.id.vendor = 0x0001;
+	corgi_ts->input.id.product = 0x0002;
+	corgi_ts->input.id.version = 0x0100;
+
+	pxa_gpio_mode(CORGI_GPIO_TP_INT | GPIO_IN);
+	pxa_gpio_mode(CORGI_GPIO_HSYNC | GPIO_IN);
+
+	/* Initiaize ADS7846 Difference Reference mode */
+	corgi_ssp_ads7846_putget((1u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
+	mdelay(5);
+	corgi_ssp_ads7846_putget((3u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
+	mdelay(5);
+	corgi_ssp_ads7846_putget((4u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
+	mdelay(5);
+	corgi_ssp_ads7846_putget((5u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
+	mdelay(5);
+
+	init_timer(&corgi_ts->timer);
+	corgi_ts->timer.data = (unsigned long) corgi_ts;
+	corgi_ts->timer.function = corgi_ts_timer;
+
+	input_register_device(&corgi_ts->input);
+	corgi_ts->power_mode = PWR_MODE_ACTIVE;
+
+	if (request_irq(CORGI_IRQ_GPIO_TP_INT, ts_interrupt, SA_INTERRUPT, "ts", corgi_ts)) {
+		input_unregister_device(&corgi_ts->input);
+		kfree(corgi_ts);
+		return -EBUSY;
+	}
+
+	/* Enable Falling Edge */
+	set_irq_type(CORGI_IRQ_GPIO_TP_INT, IRQT_FALLING);
+
+	printk(KERN_INFO "input: Corgi Touchscreen Registered\n");
+
+	return 0;
+}
+
+static int corgits_remove(struct device *dev)
+{
+	struct corgi_ts *corgi_ts = dev_get_drvdata(dev);
+
+	free_irq(CORGI_IRQ_GPIO_TP_INT, NULL);
+	del_timer_sync(&corgi_ts->timer);
+	input_unregister_device(&corgi_ts->input);
+	kfree(corgi_ts);
+	return 0;
+}
+
+static struct device_driver corgits_driver = {
+	.name		= "corgi-ts",
+	.bus		= &platform_bus_type,
+	.probe		= corgits_probe,
+	.remove		= corgits_remove,
+	.suspend	= corgits_suspend,
+	.resume		= corgits_resume,
+};
+
+static int __devinit corgits_init(void)
+{
+	return driver_register(&corgits_driver);
+}
+
+static void __exit corgits_exit(void)
+{
+	driver_unregister(&corgits_driver);
+}
+
+module_init(corgits_init);
+module_exit(corgits_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
+MODULE_DESCRIPTION("Corgi TouchScreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c
new file mode 100644
index 0000000..546ce59
--- /dev/null
+++ b/drivers/input/touchscreen/elo.c
@@ -0,0 +1,315 @@
+/*
+ * Elo serial touchscreen driver
+ *
+ * Copyright (c) 2004 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 serial Elo touchscreens using either the Elo standard
+ * 'E271-2210' 10-byte protocol, Elo legacy 'E281A-4002' 6-byte protocol, Elo
+ * legacy 'E271-140' 4-byte protocol and Elo legacy 'E261-280' 3-byte protocol.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC	"Elo serial touchscreen driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define	ELO_MAX_LENGTH	10
+
+static char *elo_name = "Elo Serial TouchScreen";
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct elo {
+	struct input_dev dev;
+	struct serio *serio;
+	int id;
+	int idx;
+	unsigned char csum;
+	unsigned char data[ELO_MAX_LENGTH];
+	char phys[32];
+};
+
+static void elo_process_data_10(struct elo* elo, unsigned char data, struct pt_regs *regs)
+{
+	struct input_dev *dev = &elo->dev;
+
+	elo->csum += elo->data[elo->idx] = data;
+
+	switch (elo->idx++) {
+
+		case 0:
+			if (data != 'U') {
+				elo->idx = 0;
+				elo->csum = 0;
+			}
+			break;
+
+		case 1:
+			if (data != 'T') {
+				elo->idx = 0;
+				elo->csum = 0;
+			}
+			break;
+
+		case 9:
+			if (elo->csum) {
+				input_regs(dev, regs);
+				input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]);
+				input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]);
+				input_report_abs(dev, ABS_PRESSURE, (elo->data[8] << 8) | elo->data[7]);
+				input_report_key(dev, BTN_TOUCH, elo->data[2] & 3);
+				input_sync(dev);
+			}
+			elo->idx = 0;
+			elo->csum = 0;
+			break;
+	}
+}
+
+static void elo_process_data_6(struct elo* elo, unsigned char data, struct pt_regs *regs)
+{
+	struct input_dev *dev = &elo->dev;
+
+	elo->data[elo->idx] = data;
+
+	switch (elo->idx++) {
+
+		case 0: if ((data & 0xc0) != 0xc0) elo->idx = 0; break;
+		case 1: if ((data & 0xc0) != 0x80) elo->idx = 0; break;
+		case 2: if ((data & 0xc0) != 0x40) elo->idx = 0; break;
+
+		case 3:
+			if (data & 0xc0) {
+				elo->idx = 0;
+				break;
+			}
+
+			input_regs(dev, regs);
+			input_report_abs(dev, ABS_X, ((elo->data[0] & 0x3f) << 6) | (elo->data[1] & 0x3f));
+			input_report_abs(dev, ABS_Y, ((elo->data[2] & 0x3f) << 6) | (elo->data[3] & 0x3f));
+
+			if (elo->id == 2) {
+				input_report_key(dev, BTN_TOUCH, 1);
+				input_sync(dev);
+				elo->idx = 0;
+			}
+
+			break;
+
+		case 4:
+			if (data) {
+				input_sync(dev);
+				elo->idx = 0;
+			}
+			break;
+
+		case 5:
+			if ((data & 0xf0) == 0) {
+				input_report_abs(dev, ABS_PRESSURE, elo->data[5]);
+				input_report_key(dev, BTN_TOUCH, elo->data[5]);
+			}
+			input_sync(dev);
+			elo->idx = 0;
+			break;
+	}
+}
+
+static void elo_process_data_3(struct elo* elo, unsigned char data, struct pt_regs *regs)
+{
+	struct input_dev *dev = &elo->dev;
+
+	elo->data[elo->idx] = data;
+
+	switch (elo->idx++) {
+
+		case 0:
+			if ((data & 0x7f) != 0x01)
+				elo->idx = 0;
+			break;
+		case 2:
+			input_regs(dev, regs);
+			input_report_key(dev, BTN_TOUCH, !(elo->data[1] & 0x80));
+			input_report_abs(dev, ABS_X, elo->data[1]);
+			input_report_abs(dev, ABS_Y, elo->data[2]);
+			input_sync(dev);
+			elo->idx = 0;
+			break;
+	}
+}
+
+static irqreturn_t elo_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+	struct elo* elo = serio_get_drvdata(serio);
+
+	switch(elo->id) {
+		case 0:
+			elo_process_data_10(elo, data, regs);
+			break;
+
+		case 1:
+		case 2:
+			elo_process_data_6(elo, data, regs);
+			break;
+
+		case 3:
+			elo_process_data_3(elo, data, regs);
+			break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * elo_disconnect() is the opposite of elo_connect()
+ */
+
+static void elo_disconnect(struct serio *serio)
+{
+	struct elo* elo = serio_get_drvdata(serio);
+
+	input_unregister_device(&elo->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(elo);
+}
+
+/*
+ * elo_connect() is the routine that is called when someone adds a
+ * new serio device that supports Gunze protocol and registers it as
+ * an input device.
+ */
+
+static int elo_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct elo *elo;
+	int err;
+
+	if (!(elo = kmalloc(sizeof(struct elo), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(elo, 0, sizeof(struct elo));
+
+	init_input_dev(&elo->dev);
+	elo->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+	elo->dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+
+	elo->id = serio->id.id;
+
+	switch (elo->id) {
+
+		case 0: /* 10-byte protocol */
+			input_set_abs_params(&elo->dev, ABS_X, 96, 4000, 0, 0);
+			input_set_abs_params(&elo->dev, ABS_Y, 96, 4000, 0, 0);
+			input_set_abs_params(&elo->dev, ABS_PRESSURE, 0, 255, 0, 0);
+			break;
+		
+		case 1: /* 6-byte protocol */
+			input_set_abs_params(&elo->dev, ABS_PRESSURE, 0, 15, 0, 0);
+
+		case 2: /* 4-byte protocol */
+			input_set_abs_params(&elo->dev, ABS_X, 96, 4000, 0, 0);
+			input_set_abs_params(&elo->dev, ABS_Y, 96, 4000, 0, 0);
+			break;
+
+		case 3: /* 3-byte protocol */
+			input_set_abs_params(&elo->dev, ABS_X, 0, 255, 0, 0);
+			input_set_abs_params(&elo->dev, ABS_Y, 0, 255, 0, 0);
+			break;
+	}
+
+	elo->serio = serio;
+
+	sprintf(elo->phys, "%s/input0", serio->phys);
+
+	elo->dev.private = elo;
+	elo->dev.name = elo_name;
+	elo->dev.phys = elo->phys;
+	elo->dev.id.bustype = BUS_RS232;
+	elo->dev.id.vendor = SERIO_ELO;
+	elo->dev.id.product = elo->id;
+	elo->dev.id.version = 0x0100;
+
+	serio_set_drvdata(serio, elo);
+
+	err = serio_open(serio, drv);
+	if (err) {
+		serio_set_drvdata(serio, NULL);
+		kfree(elo);
+		return err;
+	}
+
+	input_register_device(&elo->dev);
+
+	printk(KERN_INFO "input: %s on %s\n", elo_name, serio->phys);
+
+	return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id elo_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_ELO,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, elo_serio_ids);
+
+static struct serio_driver elo_drv = {
+	.driver		= {
+		.name	= "elo",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= elo_serio_ids,
+	.interrupt	= elo_interrupt,
+	.connect	= elo_connect,
+	.disconnect	= elo_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init elo_init(void)
+{
+	serio_register_driver(&elo_drv);
+	return 0;
+}
+
+static void __exit elo_exit(void)
+{
+	serio_unregister_driver(&elo_drv);
+}
+
+module_init(elo_init);
+module_exit(elo_exit);
diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c
new file mode 100644
index 0000000..c9d0a15
--- /dev/null
+++ b/drivers/input/touchscreen/gunze.c
@@ -0,0 +1,205 @@
+/*
+ * $Id: gunze.c,v 1.12 2001/09/25 10:12:07 vojtech Exp $
+ *
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ */
+
+/*
+ * Gunze AHL-51S touchscreen 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC	"Gunze AHL-51S touchscreen driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define	GUNZE_MAX_LENGTH	10
+
+static char *gunze_name = "Gunze AHL-51S TouchScreen";
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct gunze {
+	struct input_dev dev;
+	struct serio *serio;
+	int idx;
+	unsigned char data[GUNZE_MAX_LENGTH];
+	char phys[32];
+};
+
+static void gunze_process_packet(struct gunze* gunze, struct pt_regs *regs)
+{
+	struct input_dev *dev = &gunze->dev;
+
+	if (gunze->idx != GUNZE_MAX_LENGTH || gunze->data[5] != ',' ||
+		(gunze->data[0] != 'T' && gunze->data[0] != 'R')) {
+		gunze->data[10] = 0;
+		printk(KERN_WARNING "gunze.c: bad packet: >%s<\n", gunze->data);
+		return;
+	}
+
+	input_regs(dev, regs);
+	input_report_abs(dev, ABS_X, simple_strtoul(gunze->data + 1, NULL, 10));
+	input_report_abs(dev, ABS_Y, 1024 - simple_strtoul(gunze->data + 6, NULL, 10));
+	input_report_key(dev, BTN_TOUCH, gunze->data[0] == 'T');
+	input_sync(dev);
+}
+
+static irqreturn_t gunze_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+	struct gunze* gunze = serio_get_drvdata(serio);
+
+	if (data == '\r') {
+		gunze_process_packet(gunze, regs);
+		gunze->idx = 0;
+	} else {
+		if (gunze->idx < GUNZE_MAX_LENGTH)
+			gunze->data[gunze->idx++] = data;
+	}
+	return IRQ_HANDLED;
+}
+
+/*
+ * gunze_disconnect() is the opposite of gunze_connect()
+ */
+
+static void gunze_disconnect(struct serio *serio)
+{
+	struct gunze* gunze = serio_get_drvdata(serio);
+
+	input_unregister_device(&gunze->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(gunze);
+}
+
+/*
+ * gunze_connect() is the routine that is called when someone adds a
+ * new serio device that supports Gunze protocol and registers it as
+ * an input device.
+ */
+
+static int gunze_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct gunze *gunze;
+	int err;
+
+	if (!(gunze = kmalloc(sizeof(struct gunze), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(gunze, 0, sizeof(struct gunze));
+
+	init_input_dev(&gunze->dev);
+	gunze->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+	gunze->dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+	input_set_abs_params(&gunze->dev, ABS_X, 24, 1000, 0, 0);
+	input_set_abs_params(&gunze->dev, ABS_Y, 24, 1000, 0, 0);
+
+	gunze->serio = serio;
+
+	sprintf(gunze->phys, "%s/input0", serio->phys);
+
+	gunze->dev.private = gunze;
+	gunze->dev.name = gunze_name;
+	gunze->dev.phys = gunze->phys;
+	gunze->dev.id.bustype = BUS_RS232;
+	gunze->dev.id.vendor = SERIO_GUNZE;
+	gunze->dev.id.product = 0x0051;
+	gunze->dev.id.version = 0x0100;
+
+	serio_set_drvdata(serio, gunze);
+
+	err = serio_open(serio, drv);
+	if (err) {
+		serio_set_drvdata(serio, NULL);
+		kfree(gunze);
+		return err;
+	}
+
+	input_register_device(&gunze->dev);
+
+	printk(KERN_INFO "input: %s on %s\n", gunze_name, serio->phys);
+
+	return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id gunze_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_GUNZE,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, gunze_serio_ids);
+
+static struct serio_driver gunze_drv = {
+	.driver		= {
+		.name	= "gunze",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= gunze_serio_ids,
+	.interrupt	= gunze_interrupt,
+	.connect	= gunze_connect,
+	.disconnect	= gunze_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init gunze_init(void)
+{
+	serio_register_driver(&gunze_drv);
+	return 0;
+}
+
+static void __exit gunze_exit(void)
+{
+	serio_unregister_driver(&gunze_drv);
+}
+
+module_init(gunze_init);
+module_exit(gunze_exit);
diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c
new file mode 100644
index 0000000..acb9137
--- /dev/null
+++ b/drivers/input/touchscreen/h3600_ts_input.c
@@ -0,0 +1,528 @@
+/*
+ * $Id: h3600_ts_input.c,v 1.4 2002/01/23 06:39:37 jsimmons Exp $
+ *
+ *  Copyright (c) 2001 "Crazy" James Simmons jsimmons@transvirtual.com
+ *
+ *  Sponsored by Transvirtual Technology.
+ *
+ *  Derived from the code in h3600_ts.[ch] by Charles Flynn
+ */
+
+/*
+ * Driver for the h3600 Touch Screen and other Atmel controlled devices.
+ */
+
+/*
+ * 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 by
+ * e-mail - mail your message to <jsimmons@transvirtual.com>.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+
+/* SA1100 serial defines */
+#include <asm/arch/hardware.h>
+#include <asm/arch/irqs.h>
+
+#define DRIVER_DESC	"H3600 touchscreen driver"
+
+MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+/* The start and end of frame characters SOF and EOF */
+#define CHAR_SOF                0x02
+#define CHAR_EOF                0x03
+#define FRAME_OVERHEAD          3       /* CHAR_SOF,CHAR_EOF,LENGTH = 3 */
+
+/*
+        Atmel events and response IDs contained in frame.
+        Programmer has no control over these numbers.
+        TODO there are holes - specifically  1,7,0x0a
+*/
+#define VERSION_ID              0       /* Get Version (request/respose) */
+#define KEYBD_ID                2       /* Keyboard (event) */
+#define TOUCHS_ID               3       /* Touch Screen (event)*/
+#define EEPROM_READ_ID          4       /* (request/response) */
+#define EEPROM_WRITE_ID         5       /* (request/response) */
+#define THERMAL_ID              6       /* (request/response) */
+#define NOTIFY_LED_ID           8       /* (request/response) */
+#define BATTERY_ID              9       /* (request/response) */
+#define SPI_READ_ID             0x0b    /* ( request/response) */
+#define SPI_WRITE_ID            0x0c    /* ( request/response) */
+#define FLITE_ID                0x0d    /* backlight ( request/response) */
+#define STX_ID                  0xa1    /* extension pack status (req/resp) */
+
+#define MAX_ID                  14
+
+#define H3600_MAX_LENGTH 16
+#define H3600_KEY 0xf
+
+#define H3600_SCANCODE_RECORD	1	 /* 1 -> record button */
+#define H3600_SCANCODE_CALENDAR 2	 /* 2 -> calendar */
+#define H3600_SCANCODE_CONTACTS 3	 /* 3 -> contact */
+#define H3600_SCANCODE_Q	4	 /* 4 -> Q button */
+#define	H3600_SCANCODE_START	5	 /* 5 -> start menu */
+#define	H3600_SCANCODE_UP	6	 /* 6 -> up */
+#define H3600_SCANCODE_RIGHT	7 	 /* 7 -> right */
+#define H3600_SCANCODE_LEFT 	8	 /* 8 -> left */
+#define H3600_SCANCODE_DOWN 	9	 /* 9 -> down */
+
+static char *h3600_name = "H3600 TouchScreen";
+
+/*
+ * Per-touchscreen data.
+ */
+struct h3600_dev {
+	struct input_dev dev;
+	struct pm_dev *pm_dev;
+	struct serio *serio;
+	struct pm_dev *pm_dev;
+	unsigned char event;	/* event ID from packet */
+	unsigned char chksum;
+	unsigned char len;
+	unsigned char idx;
+	unsigned char buf[H3600_MAX_LENGTH];
+	char phys[32];
+};
+
+static irqreturn_t action_button_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+        int down = (GPLR & GPIO_BITSY_ACTION_BUTTON) ? 0 : 1;
+	struct input_dev *dev = (struct input_dev *) dev_id;
+
+	input_regs(dev, regs);
+	input_report_key(dev, KEY_ENTER, down);
+	input_sync(dev);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t npower_button_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+        int down = (GPLR & GPIO_BITSY_NPOWER_BUTTON) ? 0 : 1;
+	struct input_dev *dev = (struct input_dev *) dev_id;
+
+	/*
+	 * This interrupt is only called when we release the key. So we have
+	 * to fake a key press.
+	 */
+	input_regs(dev, regs);
+	input_report_key(dev, KEY_SUSPEND, 1);
+	input_report_key(dev, KEY_SUSPEND, down);
+	input_sync(dev);
+
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM
+
+static int flite_brightness = 25;
+
+enum flite_pwr {
+        FLITE_PWR_OFF = 0,
+        FLITE_PWR_ON = 1
+};
+
+/*
+ * h3600_flite_power: enables or disables power to frontlight, using last bright */
+unsigned int h3600_flite_power(struct input_dev *dev, enum flite_pwr pwr)
+{
+	unsigned char brightness = (pwr == FLITE_PWR_OFF) ? 0 : flite_brightness;
+	struct h3600_dev *ts = dev->private;
+
+	/* Must be in this order */
+       	ts->serio->write(ts->serio, 1);
+	ts->serio->write(ts->serio, pwr);
+      	ts->serio->write(ts->serio, brightness);
+	return 0;
+}
+
+static int suspended = 0;
+static int h3600ts_pm_callback(struct pm_dev *pm_dev, pm_request_t req,
+				void *data)
+{
+	struct input_dev *dev = (struct input_dev *) data;
+
+        switch (req) {
+        case PM_SUSPEND: /* enter D1-D3 */
+                suspended = 1;
+                h3600_flite_power(dev, FLITE_PWR_OFF);
+                break;
+        case PM_BLANK:
+                if (!suspended)
+                        h3600_flite_power(dev, FLITE_PWR_OFF);
+                break;
+        case PM_RESUME:  /* enter D0 */
+                /* same as unblank */
+        case PM_UNBLANK:
+                if (suspended) {
+                        //initSerial();
+                        suspended = 0;
+                }
+                h3600_flite_power(dev, FLITE_PWR_ON);
+                break;
+        }
+        return 0;
+}
+#endif
+
+/*
+ * This function translates the native event packets to linux input event
+ * packets. Some packets coming from serial are not touchscreen related. In
+ * this case we send them off to be processed elsewhere.
+ */
+static void h3600ts_process_packet(struct h3600_dev *ts, struct pt_regs *regs)
+{
+        struct input_dev *dev = &ts->dev;
+	static int touched = 0;
+	int key, down = 0;
+
+	input_regs(dev, regs);
+
+        switch (ts->event) {
+                /*
+                   Buttons - returned as a single byte
+                        7 6 5 4 3 2 1 0
+                        S x x x N N N N
+
+                   S       switch state ( 0=pressed 1=released)
+                   x       Unused.
+                   NNNN    switch number 0-15
+
+                Note: This is true for non interrupt generated key events.
+                */
+                case KEYBD_ID:
+			down = (ts->buf[0] & 0x80) ? 0 : 1;
+
+			switch (ts->buf[0] & 0x7f) {
+				case H3600_SCANCODE_RECORD:
+					key = KEY_RECORD;
+					break;
+				case H3600_SCANCODE_CALENDAR:
+					key = KEY_PROG1;
+                                        break;
+				case H3600_SCANCODE_CONTACTS:
+					key = KEY_PROG2;
+                                        break;
+				case H3600_SCANCODE_Q:
+					key = KEY_Q;
+                                        break;
+				case H3600_SCANCODE_START:
+					key = KEY_PROG3;
+                                        break;
+				case H3600_SCANCODE_UP:
+					key = KEY_UP;
+                                        break;
+				case H3600_SCANCODE_RIGHT:
+					key = KEY_RIGHT;
+                                        break;
+				case H3600_SCANCODE_LEFT:
+					key = KEY_LEFT;
+                                        break;
+				case H3600_SCANCODE_DOWN:
+					key = KEY_DOWN;
+                                        break;
+				default:
+					key = 0;
+			}
+                        if (key)
+                        	input_report_key(dev, key, down);
+                        break;
+                /*
+                 * Native touchscreen event data is formatted as shown below:-
+                 *
+                 *      +-------+-------+-------+-------+
+                 *      | Xmsb  | Xlsb  | Ymsb  | Ylsb  |
+                 *      +-------+-------+-------+-------+
+                 *       byte 0    1       2       3
+                 */
+                case TOUCHS_ID:
+			if (!touched) {
+				input_report_key(dev, BTN_TOUCH, 1);
+				touched = 1;
+			}
+
+			if (ts->len) {
+				unsigned short x, y;
+
+				x = ts->buf[0]; x <<= 8; x += ts->buf[1];
+                                y = ts->buf[2]; y <<= 8; y += ts->buf[3];
+
+                       		input_report_abs(dev, ABS_X, x);
+                       		input_report_abs(dev, ABS_Y, y);
+			} else {
+		               	input_report_key(dev, BTN_TOUCH, 0);
+				touched = 0;
+			}
+                        break;
+		default:
+			/* Send a non input event elsewhere */
+			break;
+        }
+
+	input_sync(dev);
+}
+
+/*
+ * h3600ts_event() handles events from the input module.
+ */
+static int h3600ts_event(struct input_dev *dev, unsigned int type,
+		 	 unsigned int code, int value)
+{
+	struct h3600_dev *ts = dev->private;
+
+	switch (type) {
+		case EV_LED: {
+		//	ts->serio->write(ts->serio, SOME_CMD);
+			return 0;
+		}
+	}
+	return -1;
+}
+
+/*
+        Frame format
+  byte    1       2               3              len + 4
+        +-------+---------------+---------------+--=------------+
+        |SOF    |id     |len    | len bytes     | Chksum        |
+        +-------+---------------+---------------+--=------------+
+  bit   0     7  8    11 12   15 16
+
+        +-------+---------------+-------+
+        |SOF    |id     |0      |Chksum | - Note Chksum does not include SOF
+        +-------+---------------+-------+
+  bit   0     7  8    11 12   15 16
+
+*/
+
+static int state;
+
+/* decode States  */
+#define STATE_SOF       0       /* start of FRAME */
+#define STATE_ID        1       /* state where we decode the ID & len */
+#define STATE_DATA      2       /* state where we decode data */
+#define STATE_EOF       3       /* state where we decode checksum or EOF */
+
+static irqreturn_t h3600ts_interrupt(struct serio *serio, unsigned char data,
+                                     unsigned int flags, struct pt_regs *regs)
+{
+        struct h3600_dev *ts = serio_get_drvdata(serio);
+
+	/*
+         * We have a new frame coming in.
+         */
+	switch (state) {
+		case STATE_SOF:
+        		if (data == CHAR_SOF)
+                		state = STATE_ID;
+			break;
+        	case STATE_ID:
+			ts->event = (data & 0xf0) >> 4;
+			ts->len = (data & 0xf);
+			ts->idx = 0;
+			if (ts->event >= MAX_ID) {
+				state = STATE_SOF;
+                        	break;
+			}
+			ts->chksum = data;
+                	state = (ts->len > 0) ? STATE_DATA : STATE_EOF;
+			break;
+		case STATE_DATA:
+			ts->chksum += data;
+			ts->buf[ts->idx]= data;
+			if(++ts->idx == ts->len)
+                        	state = STATE_EOF;
+			break;
+		case STATE_EOF:
+                	state = STATE_SOF;
+                	if (data == CHAR_EOF || data == ts->chksum)
+				h3600ts_process_packet(ts, regs);
+                	break;
+        	default:
+                	printk("Error3\n");
+                	break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * h3600ts_connect() is the routine that is called when someone adds a
+ * new serio device that supports H3600 protocol and registers it as
+ * an input device.
+ */
+static int h3600ts_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct h3600_dev *ts;
+	int err;
+
+	if (!(ts = kmalloc(sizeof(struct h3600_dev), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(ts, 0, sizeof(struct h3600_dev));
+
+	init_input_dev(&ts->dev);
+
+	/* Device specific stuff */
+        set_GPIO_IRQ_edge(GPIO_BITSY_ACTION_BUTTON, GPIO_BOTH_EDGES);
+        set_GPIO_IRQ_edge(GPIO_BITSY_NPOWER_BUTTON, GPIO_RISING_EDGE);
+
+        if (request_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, action_button_handler,
+			SA_SHIRQ | SA_INTERRUPT | SA_SAMPLE_RANDOM,
+			"h3600_action", &ts->dev)) {
+		printk(KERN_ERR "h3600ts.c: Could not allocate Action Button IRQ!\n");
+		kfree(ts);
+		return -EBUSY;
+	}
+
+        if (request_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, npower_button_handler,
+			SA_SHIRQ | SA_INTERRUPT | SA_SAMPLE_RANDOM,
+			"h3600_suspend", &ts->dev)) {
+		free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, &ts->dev);
+		printk(KERN_ERR "h3600ts.c: Could not allocate Power Button IRQ!\n");
+		kfree(ts);
+		return -EBUSY;
+	}
+
+	/* Now we have things going we setup our input device */
+	ts->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_LED) | BIT(EV_PWR);
+	ts->dev.ledbit[0] = BIT(LED_SLEEP);
+	input_set_abs_params(&ts->dev, ABS_X, 60, 985, 0, 0);
+	input_set_abs_params(&ts->dev, ABS_Y, 35, 1024, 0, 0);
+
+	set_bit(KEY_RECORD, ts->dev.keybit);
+	set_bit(KEY_Q, ts->dev.keybit);
+	set_bit(KEY_PROG1, ts->dev.keybit);
+	set_bit(KEY_PROG2, ts->dev.keybit);
+	set_bit(KEY_PROG3, ts->dev.keybit);
+	set_bit(KEY_UP, ts->dev.keybit);
+	set_bit(KEY_RIGHT, ts->dev.keybit);
+	set_bit(KEY_LEFT, ts->dev.keybit);
+	set_bit(KEY_DOWN, ts->dev.keybit);
+	set_bit(KEY_ENTER, ts->dev.keybit);
+	ts->dev.keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH);
+	ts->dev.keybit[LONG(KEY_SUSPEND)] |= BIT(KEY_SUSPEND);
+
+	ts->serio = serio;
+
+	sprintf(ts->phys, "%s/input0", serio->phys);
+
+       	ts->dev.event = h3600ts_event;
+	ts->dev.private = ts;
+	ts->dev.name = h3600_name;
+	ts->dev.phys = ts->phys;
+	ts->dev.id.bustype = BUS_RS232;
+	ts->dev.id.vendor = SERIO_H3600;
+	ts->dev.id.product = 0x0666;  /* FIXME !!! We can ask the hardware */
+	ts->dev.id.version = 0x0100;
+
+	serio_set_drvdata(serio, ts);
+
+	err = serio_open(serio, drv);
+	if (err) {
+        	free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts);
+        	free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts);
+		serio_set_drvdata(serio, NULL);
+		kfree(ts);
+		return err;
+	}
+
+	//h3600_flite_control(1, 25);     /* default brightness */
+#ifdef CONFIG_PM
+	ts->pm_dev = pm_register(PM_ILLUMINATION_DEV, PM_SYS_LIGHT,
+				h3600ts_pm_callback);
+	printk("registered pm callback\n");
+#endif
+	input_register_device(&ts->dev);
+
+	printk(KERN_INFO "input: %s on %s\n", h3600_name, serio->phys);
+
+	return 0;
+}
+
+/*
+ * h3600ts_disconnect() is the opposite of h3600ts_connect()
+ */
+
+static void h3600ts_disconnect(struct serio *serio)
+{
+	struct h3600_dev *ts = serio_get_drvdata(serio);
+
+	free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, &ts->dev);
+	free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, &ts->dev);
+	input_unregister_device(&ts->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(ts);
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id h3600ts_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_H3600,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, h3600ts_serio_ids);
+
+static struct serio_driver h3600ts_drv = {
+	.driver		= {
+		.name	= "h3600ts",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= h3600ts_serio_ids,
+	.interrupt	= h3600ts_interrupt,
+	.connect	= h3600ts_connect,
+	.disconnect	= h3600ts_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init h3600ts_init(void)
+{
+	serio_register_driver(&h3600ts_drv);
+	return 0;
+}
+
+static void __exit h3600ts_exit(void)
+{
+	serio_unregister_driver(&h3600ts_drv);
+}
+
+module_init(h3600ts_init);
+module_exit(h3600ts_exit);
diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c
new file mode 100644
index 0000000..7e14044
--- /dev/null
+++ b/drivers/input/touchscreen/hp680_ts_input.c
@@ -0,0 +1,135 @@
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/adc.h>
+#include <asm/hp6xx/hp6xx.h>
+
+#define MODNAME "hp680_ts_input"
+
+#define HP680_TS_ABS_X_MIN	40
+#define HP680_TS_ABS_X_MAX	950
+#define HP680_TS_ABS_Y_MIN	80
+#define HP680_TS_ABS_Y_MAX	910
+
+#define	SCPCR	0xa4000116
+#define	PHDR	0xa400012e
+#define SCPDR	0xa4000136
+
+static void do_softint(void *data);
+
+static struct input_dev hp680_ts_dev;
+static DECLARE_WORK(work, do_softint, 0);
+static char *hp680_ts_name = "HP Jornada touchscreen";
+static char *hp680_ts_phys = "input0";
+
+static void do_softint(void *data)
+{
+	int absx = 0, absy = 0;
+	u8 scpdr;
+	int touched = 0;
+
+	if (ctrl_inb(PHDR) & PHDR_TS_PEN_DOWN) {
+		scpdr = ctrl_inb(SCPDR);
+		scpdr |= SCPDR_TS_SCAN_ENABLE;
+		scpdr &= ~SCPDR_TS_SCAN_Y;
+		ctrl_outb(scpdr, SCPDR);
+		udelay(30);
+
+		absy = adc_single(ADC_CHANNEL_TS_Y);
+
+		scpdr = ctrl_inb(SCPDR);
+		scpdr |= SCPDR_TS_SCAN_Y;
+		scpdr &= ~SCPDR_TS_SCAN_X;
+		ctrl_outb(scpdr, SCPDR);
+		udelay(30);
+
+		absx = adc_single(ADC_CHANNEL_TS_X);
+
+		scpdr = ctrl_inb(SCPDR);
+		scpdr |= SCPDR_TS_SCAN_X;
+		scpdr &= ~SCPDR_TS_SCAN_ENABLE;
+		ctrl_outb(scpdr, SCPDR);
+		udelay(100);
+		touched = ctrl_inb(PHDR) & PHDR_TS_PEN_DOWN;
+	}
+
+	if (touched) {
+		input_report_key(&hp680_ts_dev, BTN_TOUCH, 1);
+		input_report_abs(&hp680_ts_dev, ABS_X, absx);
+		input_report_abs(&hp680_ts_dev, ABS_Y, absy);
+	} else {
+		input_report_key(&hp680_ts_dev, BTN_TOUCH, 0);
+	}
+
+	input_sync(&hp680_ts_dev);
+	enable_irq(HP680_TS_IRQ);
+}
+
+static irqreturn_t hp680_ts_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+	disable_irq_nosync(irq);
+	schedule_delayed_work(&work, HZ / 20);
+
+	return IRQ_HANDLED;
+}
+
+static int __init hp680_ts_init(void)
+{
+	u8 scpdr;
+	u16 scpcr;
+
+	scpdr = ctrl_inb(SCPDR);
+	scpdr |= SCPDR_TS_SCAN_X | SCPDR_TS_SCAN_Y;
+	scpdr &= ~SCPDR_TS_SCAN_ENABLE;
+	ctrl_outb(scpdr, SCPDR);
+
+	scpcr = ctrl_inw(SCPCR);
+	scpcr &= ~SCPCR_TS_MASK;
+	scpcr |= SCPCR_TS_ENABLE;
+	ctrl_outw(scpcr, SCPCR);
+
+	memset(&hp680_ts_dev, 0, sizeof(hp680_ts_dev));
+	init_input_dev(&hp680_ts_dev);
+
+	hp680_ts_dev.evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
+	hp680_ts_dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+	hp680_ts_dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+
+	hp680_ts_dev.absmin[ABS_X] = HP680_TS_ABS_X_MIN;
+	hp680_ts_dev.absmin[ABS_Y] = HP680_TS_ABS_Y_MIN;
+	hp680_ts_dev.absmax[ABS_X] = HP680_TS_ABS_X_MAX;
+	hp680_ts_dev.absmax[ABS_Y] = HP680_TS_ABS_Y_MAX;
+
+	hp680_ts_dev.name = hp680_ts_name;
+	hp680_ts_dev.phys = hp680_ts_phys;
+	input_register_device(&hp680_ts_dev);
+
+	if (request_irq
+	    (HP680_TS_IRQ, hp680_ts_interrupt, SA_INTERRUPT, MODNAME, 0) < 0) {
+		printk(KERN_ERR "hp680_touchscreen.c : Can't allocate irq %d\n",
+		       HP680_TS_IRQ);
+		input_unregister_device(&hp680_ts_dev);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static void __exit hp680_ts_exit(void)
+{
+	free_irq(HP680_TS_IRQ, 0);
+	cancel_delayed_work(&work);
+	flush_scheduled_work();
+	input_unregister_device(&hp680_ts_dev);
+}
+
+module_init(hp680_ts_init);
+module_exit(hp680_ts_exit);
+
+MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua");
+MODULE_DESCRIPTION("HP Jornada 680 touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/mk712.c b/drivers/input/touchscreen/mk712.c
new file mode 100644
index 0000000..2d14a57
--- /dev/null
+++ b/drivers/input/touchscreen/mk712.c
@@ -0,0 +1,222 @@
+/*
+ * ICS MK712 touchscreen controller driver
+ *
+ * Copyright (c) 1999-2002 Transmeta Corporation
+ * Copyright (c) 2005 Rick Koch <n1gp@hotmail.com>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
+ */
+
+/*
+ * 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 supports the ICS MicroClock MK712 TouchScreen controller,
+ * found in Gateway AOL Connected Touchpad computers.
+ *
+ * Documentation for ICS MK712 can be found at:
+ * 	http://www.icst.com/pdf/mk712.pdf
+ */
+
+/*
+ * 1999-12-18: original version, Daniel Quinlan
+ * 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll
+ *             to use queue_empty, Nathan Laredo
+ * 1999-12-20: improved random point rejection, Nathan Laredo
+ * 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed
+ *             queue code, added module options, other fixes, Daniel Quinlan
+ * 2002-03-15: Clean up for kernel merge <alan@redhat.com>
+ *             Fixed multi open race, fixed memory checks, fixed resource
+ *             allocation, fixed close/powerdown bug, switched to new init
+ * 2005-01-18: Ported to 2.6 from 2.4.28, Rick Koch
+ * 2005-02-05: Rewritten for the input layer, Vojtech Pavlik
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Daniel Quinlan <quinlan@pathname.com>, Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("ICS MicroClock MK712 TouchScreen driver");
+MODULE_LICENSE("GPL");
+
+static unsigned int mk712_io = 0x260;	/* Also 0x200, 0x208, 0x300 */
+module_param_named(io, mk712_io, uint, 0);
+MODULE_PARM_DESC(io, "I/O base address of MK712 touchscreen controller");
+
+static unsigned int mk712_irq = 10;	/* Also 12, 14, 15 */
+module_param_named(irq, mk712_irq, uint, 0);
+MODULE_PARM_DESC(irq, "IRQ of MK712 touchscreen controller");
+
+/* eight 8-bit registers */
+#define MK712_STATUS		0
+#define MK712_X			2
+#define MK712_Y			4
+#define MK712_CONTROL		6
+#define MK712_RATE		7
+
+/* status */
+#define	MK712_STATUS_TOUCH			0x10
+#define	MK712_CONVERSION_COMPLETE		0x80
+
+/* control */
+#define MK712_ENABLE_INT			0x01
+#define MK712_INT_ON_CONVERSION_COMPLETE	0x02
+#define MK712_INT_ON_CHANGE_IN_TOUCH_STATUS	0x04
+#define MK712_ENABLE_PERIODIC_CONVERSIONS	0x10
+#define MK712_READ_ONE_POINT			0x20
+#define MK712_POWERUP				0x40
+
+static int mk712_used = 0;
+static struct input_dev mk712_dev;
+static DEFINE_SPINLOCK(mk712_lock);
+
+static irqreturn_t mk712_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned char status;
+	static int debounce = 1;
+	static unsigned short last_x;
+	static unsigned short last_y;
+
+	spin_lock(&mk712_lock);
+	input_regs(&mk712_dev, regs);
+
+	status = inb(mk712_io + MK712_STATUS);
+
+	if (~status & MK712_CONVERSION_COMPLETE) {
+		debounce = 1;
+		goto end;
+	}
+
+	if (~status & MK712_STATUS_TOUCH)
+	{
+		debounce = 1;
+		input_report_key(&mk712_dev, BTN_TOUCH, 0);
+		goto end;
+	}
+
+	if (debounce)
+	{
+		debounce = 0;
+		goto end;
+	}
+
+	input_report_key(&mk712_dev, BTN_TOUCH, 1);
+	input_report_abs(&mk712_dev, ABS_X, last_x);
+	input_report_abs(&mk712_dev, ABS_Y, last_y);
+
+end:
+
+	last_x = inw(mk712_io + MK712_X) & 0x0fff;
+	last_y = inw(mk712_io + MK712_Y) & 0x0fff;
+	input_sync(&mk712_dev);
+	spin_unlock(&mk712_lock);
+	return IRQ_HANDLED;
+}
+
+static int mk712_open(struct input_dev *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mk712_lock, flags);
+
+	if (!mk712_used++) {
+
+		outb(0, mk712_io + MK712_CONTROL); /* Reset */
+
+		outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE |
+			MK712_INT_ON_CHANGE_IN_TOUCH_STATUS |
+			MK712_ENABLE_PERIODIC_CONVERSIONS |
+			MK712_POWERUP, mk712_io + MK712_CONTROL);
+
+		outb(10, mk712_io + MK712_RATE); /* 187 points per second */
+	}
+
+	spin_unlock_irqrestore(&mk712_lock, flags);
+
+	return 0;
+}
+
+static void mk712_close(struct input_dev *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mk712_lock, flags);
+
+	if (!--mk712_used)
+		outb(0, mk712_io + MK712_CONTROL);
+
+	spin_unlock_irqrestore(&mk712_lock, flags);
+}
+
+static struct input_dev mk712_dev = {
+	.evbit   = { BIT(EV_KEY) | BIT(EV_ABS) },
+	.keybit  = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
+	.absbit  = { BIT(ABS_X) | BIT(ABS_Y) },
+	.open    = mk712_open,
+	.close   = mk712_close,
+	.name    = "ICS MicroClock MK712 TouchScreen",
+	.phys    = "isa0260/input0",
+	.absmin  = { [ABS_X] = 0, [ABS_Y] = 0 },
+	.absmax  = { [ABS_X] = 0xfff, [ABS_Y] = 0xfff },
+	.absfuzz = { [ABS_X] = 88, [ABS_Y] = 88 },
+	.id      = {
+		.bustype = BUS_ISA,
+		.vendor  = 0x0005,
+		.product = 0x0001,
+		.version = 0x0100,
+	},
+};
+
+int __init mk712_init(void)
+{
+
+	if(!request_region(mk712_io, 8, "mk712"))
+	{
+		printk(KERN_WARNING "mk712: unable to get IO region\n");
+		return -ENODEV;
+	}
+
+	outb(0, mk712_io + MK712_CONTROL);
+
+	if ((inw(mk712_io + MK712_X) & 0xf000) ||	/* Sanity check */
+	    (inw(mk712_io + MK712_Y) & 0xf000) ||
+	    (inw(mk712_io + MK712_STATUS) & 0xf333)) {
+		printk(KERN_WARNING "mk712: device not present\n");
+		release_region(mk712_io, 8);
+		return -ENODEV;
+	}
+
+	if(request_irq(mk712_irq, mk712_interrupt, 0, "mk712", &mk712_dev))
+	{
+		printk(KERN_WARNING "mk712: unable to get IRQ\n");
+		release_region(mk712_io, 8);
+		return -EBUSY;
+	}
+
+	input_register_device(&mk712_dev);
+
+	printk(KERN_INFO "input: ICS MicroClock MK712 TouchScreen at %#x irq %d\n", mk712_io, mk712_irq);
+
+	return 0;
+}
+
+static void __exit mk712_exit(void)
+{
+	input_unregister_device(&mk712_dev);
+	free_irq(mk712_irq, &mk712_dev);
+	release_region(mk712_io, 8);
+}
+
+module_init(mk712_init);
+module_exit(mk712_exit);
diff --git a/drivers/input/touchscreen/mtouch.c b/drivers/input/touchscreen/mtouch.c
new file mode 100644
index 0000000..aa8ee78
--- /dev/null
+++ b/drivers/input/touchscreen/mtouch.c
@@ -0,0 +1,219 @@
+/*
+ * MicroTouch (3M) serial touchscreen driver
+ *
+ * Copyright (c) 2004 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.
+ */
+
+/*
+ * 2005/02/19 Dan Streetman <ddstreet@ieee.org>
+ *   Copied elo.c and edited for MicroTouch protocol
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC	"MicroTouch serial touchscreen driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define MTOUCH_FORMAT_TABLET_STATUS_BIT 0x80
+#define MTOUCH_FORMAT_TABLET_TOUCH_BIT 0x40
+#define MTOUCH_FORMAT_TABLET_LENGTH 5
+#define MTOUCH_RESPONSE_BEGIN_BYTE 0x01
+#define MTOUCH_RESPONSE_END_BYTE 0x0d
+
+/* todo: check specs for max length of all responses */
+#define MTOUCH_MAX_LENGTH 16
+
+#define MTOUCH_MIN_XC 0
+#define MTOUCH_MAX_XC 0x3fff
+#define MTOUCH_MIN_YC 0
+#define MTOUCH_MAX_YC 0x3fff
+
+#define MTOUCH_GET_XC(data) (((data[2])<<7) | data[1])
+#define MTOUCH_GET_YC(data) (((data[4])<<7) | data[3])
+#define MTOUCH_GET_TOUCHED(data) (MTOUCH_FORMAT_TABLET_TOUCH_BIT & data[0])
+
+static char *mtouch_name = "MicroTouch Serial TouchScreen";
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct mtouch {
+	struct input_dev dev;
+	struct serio *serio;
+	int idx;
+	unsigned char data[MTOUCH_MAX_LENGTH];
+	char phys[32];
+};
+
+static void mtouch_process_format_tablet(struct mtouch *mtouch, struct pt_regs *regs)
+{
+	struct input_dev *dev = &mtouch->dev;
+
+	if (MTOUCH_FORMAT_TABLET_LENGTH == ++mtouch->idx) {
+		input_regs(dev, regs);
+		input_report_abs(dev, ABS_X, MTOUCH_GET_XC(mtouch->data));
+		input_report_abs(dev, ABS_Y, MTOUCH_MAX_YC - MTOUCH_GET_YC(mtouch->data));
+		input_report_key(dev, BTN_TOUCH, MTOUCH_GET_TOUCHED(mtouch->data));
+		input_sync(dev);
+
+		mtouch->idx = 0;
+	}
+}
+
+static void mtouch_process_response(struct mtouch *mtouch, struct pt_regs *regs)
+{
+	if (MTOUCH_RESPONSE_END_BYTE == mtouch->data[mtouch->idx++]) {
+		/* FIXME - process response */
+		mtouch->idx = 0;
+	} else if (MTOUCH_MAX_LENGTH == mtouch->idx) {
+		printk(KERN_ERR "mtouch.c: too many response bytes\n");
+		mtouch->idx = 0;
+	}
+}
+
+static irqreturn_t mtouch_interrupt(struct serio *serio,
+		unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+	struct mtouch* mtouch = serio_get_drvdata(serio);
+
+	mtouch->data[mtouch->idx] = data;
+
+	if (MTOUCH_FORMAT_TABLET_STATUS_BIT & mtouch->data[0])
+		mtouch_process_format_tablet(mtouch, regs);
+	else if (MTOUCH_RESPONSE_BEGIN_BYTE == mtouch->data[0])
+		mtouch_process_response(mtouch, regs);
+	else
+		printk(KERN_DEBUG "mtouch.c: unknown/unsynchronized data from device, byte %x\n",mtouch->data[0]);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * mtouch_disconnect() is the opposite of mtouch_connect()
+ */
+
+static void mtouch_disconnect(struct serio *serio)
+{
+	struct mtouch* mtouch = serio_get_drvdata(serio);
+
+	input_unregister_device(&mtouch->dev);
+	serio_close(serio);
+	serio_set_drvdata(serio, NULL);
+	kfree(mtouch);
+}
+
+/*
+ * mtouch_connect() is the routine that is called when someone adds a
+ * new serio device that supports MicroTouch (Format Tablet) protocol and registers it as
+ * an input device.
+ */
+
+static int mtouch_connect(struct serio *serio, struct serio_driver *drv)
+{
+	struct mtouch *mtouch;
+	int err;
+
+	if (!(mtouch = kmalloc(sizeof(*mtouch), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(mtouch, 0, sizeof(*mtouch));
+
+	init_input_dev(&mtouch->dev);
+	mtouch->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+	mtouch->dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+
+	input_set_abs_params(&mtouch->dev, ABS_X, MTOUCH_MIN_XC, MTOUCH_MAX_XC, 0, 0);
+	input_set_abs_params(&mtouch->dev, ABS_Y, MTOUCH_MIN_YC, MTOUCH_MAX_YC, 0, 0);
+
+	mtouch->serio = serio;
+
+	sprintf(mtouch->phys, "%s/input0", serio->phys);
+
+	mtouch->dev.private = mtouch;
+	mtouch->dev.name = mtouch_name;
+	mtouch->dev.phys = mtouch->phys;
+	mtouch->dev.id.bustype = BUS_RS232;
+	mtouch->dev.id.vendor = SERIO_MICROTOUCH;
+	mtouch->dev.id.product = 0;
+	mtouch->dev.id.version = 0x0100;
+
+	serio_set_drvdata(serio, mtouch);
+
+	err = serio_open(serio, drv);
+	if (err) {
+		serio_set_drvdata(serio, NULL);
+		kfree(mtouch);
+		return err;
+	}
+
+	input_register_device(&mtouch->dev);
+
+	printk(KERN_INFO "input: %s on %s\n", mtouch->dev.name, serio->phys);
+
+	return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id mtouch_serio_ids[] = {
+	{
+		.type	= SERIO_RS232,
+		.proto	= SERIO_MICROTOUCH,
+		.id	= SERIO_ANY,
+		.extra	= SERIO_ANY,
+	},
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, mtouch_serio_ids);
+
+static struct serio_driver mtouch_drv = {
+	.driver		= {
+		.name	= "mtouch",
+	},
+	.description	= DRIVER_DESC,
+	.id_table	= mtouch_serio_ids,
+	.interrupt	= mtouch_interrupt,
+	.connect	= mtouch_connect,
+	.disconnect	= mtouch_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init mtouch_init(void)
+{
+	serio_register_driver(&mtouch_drv);
+	return 0;
+}
+
+static void __exit mtouch_exit(void)
+{
+	serio_unregister_driver(&mtouch_drv);
+}
+
+module_init(mtouch_init);
+module_exit(mtouch_exit);
diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c
new file mode 100644
index 0000000..d0afba8
--- /dev/null
+++ b/drivers/input/tsdev.c
@@ -0,0 +1,492 @@
+/*
+ * $Id: tsdev.c,v 1.15 2002/04/10 16:50:19 jsimmons Exp $
+ *
+ *  Copyright (c) 2001 "Crazy" james Simmons
+ *
+ *  Compaq touchscreen protocol driver. The protocol emulated by this driver
+ *  is obsolete; for new programs use the tslib library which can read directly
+ *  from evdev and perform dejittering, variance filtering and calibration -
+ *  all in user space, not at kernel level. The meaning of this driver is
+ *  to allow usage of newer input drivers with old applications that use the
+ *  old /dev/h3600_ts and /dev/h3600_tsraw devices.
+ *
+ *  09-Apr-2004: Andrew Zabolotny <zap@homelink.ru>
+ *      Fixed to actually work, not just output random numbers.
+ *      Added support for both h3600_ts and h3600_tsraw protocol
+ *      emulation.
+ */
+
+/*
+ * 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 <jsimmons@infradead.org>.
+ */
+
+#define TSDEV_MINOR_BASE 	128
+#define TSDEV_MINORS		32
+/* First 16 devices are h3600_ts compatible; second 16 are h3600_tsraw */
+#define TSDEV_MINOR_MASK	15
+#define TSDEV_BUFFER_SIZE	64
+
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/major.h>
+#include <linux/config.h>
+#include <linux/smp_lock.h>
+#include <linux/random.h>
+#include <linux/time.h>
+#include <linux/device.h>
+#include <linux/devfs_fs_kernel.h>
+
+#ifndef CONFIG_INPUT_TSDEV_SCREEN_X
+#define CONFIG_INPUT_TSDEV_SCREEN_X	240
+#endif
+#ifndef CONFIG_INPUT_TSDEV_SCREEN_Y
+#define CONFIG_INPUT_TSDEV_SCREEN_Y	320
+#endif
+
+/* This driver emulates both protocols of the old h3600_ts and h3600_tsraw
+ * devices. The first one must output X/Y data in 'cooked' format, e.g.
+ * filtered, dejittered and calibrated. Second device just outputs raw
+ * data received from the hardware.
+ *
+ * This driver doesn't support filtering and dejittering; it supports only
+ * calibration. Filtering and dejittering must be done in the low-level
+ * driver, if needed, because it may gain additional benefits from knowing
+ * the low-level details, the nature of noise and so on.
+ *
+ * The driver precomputes a calibration matrix given the initial xres and
+ * yres values (quite innacurate for most touchscreens) that will result
+ * in a more or less expected range of output values. The driver supports
+ * the TS_SET_CAL ioctl, which will replace the calibration matrix with a
+ * new one, supposedly generated from the values taken from the raw device.
+ */
+
+MODULE_AUTHOR("James Simmons <jsimmons@transvirtual.com>");
+MODULE_DESCRIPTION("Input driver to touchscreen converter");
+MODULE_LICENSE("GPL");
+
+static int xres = CONFIG_INPUT_TSDEV_SCREEN_X;
+module_param(xres, uint, 0);
+MODULE_PARM_DESC(xres, "Horizontal screen resolution (can be negative for X-mirror)");
+
+static int yres = CONFIG_INPUT_TSDEV_SCREEN_Y;
+module_param(yres, uint, 0);
+MODULE_PARM_DESC(yres, "Vertical screen resolution (can be negative for Y-mirror)");
+
+/* From Compaq's Touch Screen Specification version 0.2 (draft) */
+struct ts_event {
+	short pressure;
+	short x;
+	short y;
+	short millisecs;
+};
+
+struct ts_calibration {
+	int xscale;
+	int xtrans;
+	int yscale;
+	int ytrans;
+	int xyswap;
+};
+
+struct tsdev {
+	int exist;
+	int open;
+	int minor;
+	char name[8];
+	wait_queue_head_t wait;
+	struct list_head list;
+	struct input_handle handle;
+	int x, y, pressure;
+	struct ts_calibration cal;
+};
+
+struct tsdev_list {
+	struct fasync_struct *fasync;
+	struct list_head node;
+	struct tsdev *tsdev;
+	int head, tail;
+	struct ts_event event[TSDEV_BUFFER_SIZE];
+	int raw;
+};
+
+/* The following ioctl codes are defined ONLY for backward compatibility.
+ * Don't use tsdev for new developement; use the tslib library instead.
+ * Touchscreen calibration is a fully userspace task.
+ */
+/* Use 'f' as magic number */
+#define IOC_H3600_TS_MAGIC  'f'
+#define TS_GET_CAL	_IOR(IOC_H3600_TS_MAGIC, 10, struct ts_calibration)
+#define TS_SET_CAL	_IOW(IOC_H3600_TS_MAGIC, 11, struct ts_calibration)
+
+static struct input_handler tsdev_handler;
+
+static struct tsdev *tsdev_table[TSDEV_MINORS/2];
+
+static int tsdev_fasync(int fd, struct file *file, int on)
+{
+	struct tsdev_list *list = file->private_data;
+	int retval;
+
+	retval = fasync_helper(fd, file, on, &list->fasync);
+	return retval < 0 ? retval : 0;
+}
+
+static int tsdev_open(struct inode *inode, struct file *file)
+{
+	int i = iminor(inode) - TSDEV_MINOR_BASE;
+	struct tsdev_list *list;
+
+	if (i >= TSDEV_MINORS || !tsdev_table[i & TSDEV_MINOR_MASK])
+		return -ENODEV;
+
+	if (!(list = kmalloc(sizeof(struct tsdev_list), GFP_KERNEL)))
+		return -ENOMEM;
+	memset(list, 0, sizeof(struct tsdev_list));
+
+	list->raw = (i >= TSDEV_MINORS/2) ? 1 : 0;
+
+	i &= TSDEV_MINOR_MASK;
+	list->tsdev = tsdev_table[i];
+	list_add_tail(&list->node, &tsdev_table[i]->list);
+	file->private_data = list;
+
+	if (!list->tsdev->open++)
+		if (list->tsdev->exist)
+			input_open_device(&list->tsdev->handle);
+	return 0;
+}
+
+static void tsdev_free(struct tsdev *tsdev)
+{
+	tsdev_table[tsdev->minor] = NULL;
+	kfree(tsdev);
+}
+
+static int tsdev_release(struct inode *inode, struct file *file)
+{
+	struct tsdev_list *list = file->private_data;
+
+	tsdev_fasync(-1, file, 0);
+	list_del(&list->node);
+
+	if (!--list->tsdev->open) {
+		if (list->tsdev->exist)
+			input_close_device(&list->tsdev->handle);
+		else
+			tsdev_free(list->tsdev);
+	}
+	kfree(list);
+	return 0;
+}
+
+static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count,
+			  loff_t * ppos)
+{
+	struct tsdev_list *list = file->private_data;
+	int retval = 0;
+
+	if (list->head == list->tail && list->tsdev->exist && (file->f_flags & O_NONBLOCK))
+		return -EAGAIN;
+
+	retval = wait_event_interruptible(list->tsdev->wait,
+			list->head != list->tail || !list->tsdev->exist);
+
+	if (retval)
+		return retval;
+
+	if (!list->tsdev->exist)
+		return -ENODEV;
+
+	while (list->head != list->tail &&
+	       retval + sizeof (struct ts_event) <= count) {
+		if (copy_to_user (buffer + retval, list->event + list->tail,
+				  sizeof (struct ts_event)))
+			return -EFAULT;
+		list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1);
+		retval += sizeof (struct ts_event);
+	}
+
+	return retval;
+}
+
+/* No kernel lock - fine */
+static unsigned int tsdev_poll(struct file *file, poll_table * wait)
+{
+	struct tsdev_list *list = file->private_data;
+	poll_wait(file, &list->tsdev->wait, wait);
+	return ((list->head == list->tail) ? 0 : (POLLIN | POLLRDNORM)) |
+		(list->tsdev->exist ? 0 : (POLLHUP | POLLERR));
+}
+
+static int tsdev_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	struct tsdev_list *list = file->private_data;
+	struct tsdev *tsdev = list->tsdev;
+	int retval = 0;
+
+	switch (cmd) {
+	case TS_GET_CAL:
+		if (copy_to_user ((void __user *)arg, &tsdev->cal,
+				  sizeof (struct ts_calibration)))
+			retval = -EFAULT;
+		break;
+	case TS_SET_CAL:
+		if (copy_from_user (&tsdev->cal, (void __user *)arg,
+				    sizeof (struct ts_calibration)))
+			retval = -EFAULT;
+		break;
+	default:
+		retval = -EINVAL;
+		break;
+	}
+
+	return retval;
+}
+
+static struct file_operations tsdev_fops = {
+	.owner =	THIS_MODULE,
+	.open =		tsdev_open,
+	.release =	tsdev_release,
+	.read =		tsdev_read,
+	.poll =		tsdev_poll,
+	.fasync =	tsdev_fasync,
+	.ioctl =	tsdev_ioctl,
+};
+
+static void tsdev_event(struct input_handle *handle, unsigned int type,
+			unsigned int code, int value)
+{
+	struct tsdev *tsdev = handle->private;
+	struct tsdev_list *list;
+	struct timeval time;
+
+	switch (type) {
+	case EV_ABS:
+		switch (code) {
+		case ABS_X:
+			tsdev->x = value;
+			break;
+		case ABS_Y:
+			tsdev->y = value;
+			break;
+		case ABS_PRESSURE:
+			if (value > handle->dev->absmax[ABS_PRESSURE])
+				value = handle->dev->absmax[ABS_PRESSURE];
+			value -= handle->dev->absmin[ABS_PRESSURE];
+			if (value < 0)
+				value = 0;
+			tsdev->pressure = value;
+			break;
+		}
+		break;
+
+	case EV_REL:
+		switch (code) {
+		case REL_X:
+			tsdev->x += value;
+			if (tsdev->x < 0)
+				tsdev->x = 0;
+			else if (tsdev->x > xres)
+				tsdev->x = xres;
+			break;
+		case REL_Y:
+			tsdev->y += value;
+			if (tsdev->y < 0)
+				tsdev->y = 0;
+			else if (tsdev->y > yres)
+				tsdev->y = yres;
+			break;
+		}
+		break;
+
+	case EV_KEY:
+		if (code == BTN_TOUCH || code == BTN_MOUSE) {
+			switch (value) {
+			case 0:
+				tsdev->pressure = 0;
+				break;
+			case 1:
+				if (!tsdev->pressure)
+					tsdev->pressure = 1;
+				break;
+			}
+		}
+		break;
+	}
+
+	if (type != EV_SYN || code != SYN_REPORT)
+		return;
+
+	list_for_each_entry(list, &tsdev->list, node) {
+		int x, y, tmp;
+
+		do_gettimeofday(&time);
+		list->event[list->head].millisecs = time.tv_usec / 100;
+		list->event[list->head].pressure = tsdev->pressure;
+
+		x = tsdev->x;
+		y = tsdev->y;
+
+		/* Calibration */
+		if (!list->raw) {
+			x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans;
+			y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans;
+			if (tsdev->cal.xyswap) {
+				tmp = x; x = y; y = tmp;
+			}
+		}
+
+		list->event[list->head].x = x;
+		list->event[list->head].y = y;
+		list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1);
+		kill_fasync(&list->fasync, SIGIO, POLL_IN);
+	}
+	wake_up_interruptible(&tsdev->wait);
+}
+
+static struct input_handle *tsdev_connect(struct input_handler *handler,
+					  struct input_dev *dev,
+					  struct input_device_id *id)
+{
+	struct tsdev *tsdev;
+	int minor, delta;
+
+	for (minor = 0; minor < TSDEV_MINORS/2 && tsdev_table[minor];
+	     minor++);
+	if (minor >= TSDEV_MINORS/2) {
+		printk(KERN_ERR
+		       "tsdev: You have way too many touchscreens\n");
+		return NULL;
+	}
+
+	if (!(tsdev = kmalloc(sizeof(struct tsdev), GFP_KERNEL)))
+		return NULL;
+	memset(tsdev, 0, sizeof(struct tsdev));
+
+	INIT_LIST_HEAD(&tsdev->list);
+	init_waitqueue_head(&tsdev->wait);
+
+	sprintf(tsdev->name, "ts%d", minor);
+
+	tsdev->exist = 1;
+	tsdev->minor = minor;
+	tsdev->handle.dev = dev;
+	tsdev->handle.name = tsdev->name;
+	tsdev->handle.handler = handler;
+	tsdev->handle.private = tsdev;
+
+	/* Precompute the rough calibration matrix */
+	delta = dev->absmax [ABS_X] - dev->absmin [ABS_X] + 1;
+	if (delta == 0)
+		delta = 1;
+	tsdev->cal.xscale = (xres << 8) / delta;
+	tsdev->cal.xtrans = - ((dev->absmin [ABS_X] * tsdev->cal.xscale) >> 8);
+
+	delta = dev->absmax [ABS_Y] - dev->absmin [ABS_Y] + 1;
+	if (delta == 0)
+		delta = 1;
+	tsdev->cal.yscale = (yres << 8) / delta;
+	tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8);
+
+	tsdev_table[minor] = tsdev;
+
+	devfs_mk_cdev(MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor),
+			S_IFCHR|S_IRUGO|S_IWUSR, "input/ts%d", minor);
+	devfs_mk_cdev(MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor + TSDEV_MINORS/2),
+			S_IFCHR|S_IRUGO|S_IWUSR, "input/tsraw%d", minor);
+	class_simple_device_add(input_class,
+				MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor),
+				dev->dev, "ts%d", minor);
+
+	return &tsdev->handle;
+}
+
+static void tsdev_disconnect(struct input_handle *handle)
+{
+	struct tsdev *tsdev = handle->private;
+	struct tsdev_list *list;
+
+	class_simple_device_remove(MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + tsdev->minor));
+	devfs_remove("input/ts%d", tsdev->minor);
+	devfs_remove("input/tsraw%d", tsdev->minor);
+	tsdev->exist = 0;
+
+	if (tsdev->open) {
+		input_close_device(handle);
+		wake_up_interruptible(&tsdev->wait);
+		list_for_each_entry(list, &tsdev->list, node)
+			kill_fasync(&list->fasync, SIGIO, POLL_HUP);
+	} else
+		tsdev_free(tsdev);
+}
+
+static struct input_device_id tsdev_ids[] = {
+	{
+	      .flags	= INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
+	      .evbit	= { BIT(EV_KEY) | BIT(EV_REL) },
+	      .keybit	= { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) },
+	      .relbit	= { BIT(REL_X) | BIT(REL_Y) },
+	 },/* A mouse like device, at least one button, two relative axes */
+
+	{
+	      .flags	= INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
+	      .evbit	= { BIT(EV_KEY) | BIT(EV_ABS) },
+	      .keybit	= { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
+	      .absbit	= { BIT(ABS_X) | BIT(ABS_Y) },
+	 },/* A tablet like device, at least touch detection, two absolute axes */
+
+	{
+	      .flags	= INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
+	      .evbit	= { BIT(EV_ABS) },
+	      .absbit	= { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) },
+	 },/* A tablet like device with several gradations of pressure */
+
+	{},/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(input, tsdev_ids);
+
+static struct input_handler tsdev_handler = {
+	.event =	tsdev_event,
+	.connect =	tsdev_connect,
+	.disconnect =	tsdev_disconnect,
+	.fops =		&tsdev_fops,
+	.minor =	TSDEV_MINOR_BASE,
+	.name =		"tsdev",
+	.id_table =	tsdev_ids,
+};
+
+static int __init tsdev_init(void)
+{
+	input_register_handler(&tsdev_handler);
+	printk(KERN_INFO "ts: Compaq touchscreen protocol output\n");
+	return 0;
+}
+
+static void __exit tsdev_exit(void)
+{
+	input_unregister_handler(&tsdev_handler);
+}
+
+module_init(tsdev_init);
+module_exit(tsdev_exit);