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/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);