summaryrefslogtreecommitdiffstats
path: root/drivers/input/joystick
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 01:02:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 01:02:30 +0000
commit76cb841cb886eef6b3bee341a2266c76578724ad (patch)
treef5892e5ba6cc11949952a6ce4ecbe6d516d6ce58 /drivers/input/joystick
parentInitial commit. (diff)
downloadlinux-76cb841cb886eef6b3bee341a2266c76578724ad.tar.xz
linux-76cb841cb886eef6b3bee341a2266c76578724ad.zip
Adding upstream version 4.19.249.upstream/4.19.249
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/input/joystick')
-rw-r--r--drivers/input/joystick/Kconfig364
-rw-r--r--drivers/input/joystick/Makefile38
-rw-r--r--drivers/input/joystick/a3d.c411
-rw-r--r--drivers/input/joystick/adi.c563
-rw-r--r--drivers/input/joystick/amijoy.c172
-rw-r--r--drivers/input/joystick/analog.c798
-rw-r--r--drivers/input/joystick/as5011.c372
-rw-r--r--drivers/input/joystick/cobra.c259
-rw-r--r--drivers/input/joystick/db9.c723
-rw-r--r--drivers/input/joystick/gamecon.c1065
-rw-r--r--drivers/input/joystick/gf2k.c371
-rw-r--r--drivers/input/joystick/grip.c422
-rw-r--r--drivers/input/joystick/grip_mp.c689
-rw-r--r--drivers/input/joystick/guillemot.c279
-rw-r--r--drivers/input/joystick/iforce/Kconfig32
-rw-r--r--drivers/input/joystick/iforce/Makefile11
-rw-r--r--drivers/input/joystick/iforce/iforce-ff.c539
-rw-r--r--drivers/input/joystick/iforce/iforce-main.c477
-rw-r--r--drivers/input/joystick/iforce/iforce-packets.c301
-rw-r--r--drivers/input/joystick/iforce/iforce-serio.c185
-rw-r--r--drivers/input/joystick/iforce/iforce-usb.c236
-rw-r--r--drivers/input/joystick/iforce/iforce.h167
-rw-r--r--drivers/input/joystick/interact.c309
-rw-r--r--drivers/input/joystick/joydump.c157
-rw-r--r--drivers/input/joystick/magellan.c220
-rw-r--r--drivers/input/joystick/maplecontrol.c192
-rw-r--r--drivers/input/joystick/psxpad-spi.c401
-rw-r--r--drivers/input/joystick/pxrc.c281
-rw-r--r--drivers/input/joystick/sidewinder.c818
-rw-r--r--drivers/input/joystick/spaceball.c305
-rw-r--r--drivers/input/joystick/spaceorb.c235
-rw-r--r--drivers/input/joystick/stinger.c206
-rw-r--r--drivers/input/joystick/tmdc.c434
-rw-r--r--drivers/input/joystick/turbografx.c324
-rw-r--r--drivers/input/joystick/twidjoy.c259
-rw-r--r--drivers/input/joystick/walkera0701.c314
-rw-r--r--drivers/input/joystick/warrior.c215
-rw-r--r--drivers/input/joystick/xpad.c1989
-rw-r--r--drivers/input/joystick/zhenhua.c218
39 files changed, 15351 insertions, 0 deletions
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
new file mode 100644
index 000000000..d8f9c6e1f
--- /dev/null
+++ b/drivers/input/joystick/Kconfig
@@ -0,0 +1,364 @@
+#
+# Joystick driver configuration
+#
+menuconfig INPUT_JOYSTICK
+ bool "Joysticks/Gamepads"
+ 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/joydev/joystick.rst> 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/joydev/joystick.rst> which
+ contains more information.
+
+ To compile this driver as a module, choose M here: the
+ module will be called analog.
+
+config JOYSTICK_A3D
+ tristate "Assassin 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_ZHENHUA
+ tristate "5-byte Zhenhua RC transmitter"
+ select SERIO
+ select BITREVERSE
+ help
+ Say Y here if you have a Zhen Hua PPM-4CH transmitter which is
+ supplied with a ready to fly micro electric indoor helicopters
+ such as EasyCopter, Lama, MiniCopter, DragonFly or Jabo and want
+ to use it via serial cable as a joystick.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zhenhua.
+
+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/devices/joystick-parport.rst>.
+
+ 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
+ select INPUT_FF_MEMLESS
+ ---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/devices/joystick-parport.rst>.
+
+ 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/devices/joystick-parport.rst>.
+
+ 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_AS5011
+ tristate "Austria Microsystem AS5011 joystick"
+ depends on I2C
+ help
+ Say Y here if you have an AS5011 digital joystick connected to your
+ system.
+
+ To compile this driver as a module, choose M here: the
+ module will be called as5011.
+
+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.
+
+config JOYSTICK_XPAD
+ tristate "X-Box gamepad support"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use the X-Box pad with your computer.
+ Make sure to say Y to "Joystick support" (CONFIG_INPUT_JOYDEV)
+ and/or "Event interface support" (CONFIG_INPUT_EVDEV) as well.
+
+ For information about how to connect the X-Box pad to USB, see
+ <file:Documentation/input/devices/xpad.rst>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called xpad.
+
+config JOYSTICK_XPAD_FF
+ bool "X-Box gamepad rumble support"
+ depends on JOYSTICK_XPAD && INPUT
+ select INPUT_FF_MEMLESS
+ ---help---
+ Say Y here if you want to take advantage of xbox 360 rumble features.
+
+config JOYSTICK_XPAD_LEDS
+ bool "LED Support for Xbox360 controller 'BigX' LED"
+ depends on JOYSTICK_XPAD && (LEDS_CLASS=y || LEDS_CLASS=JOYSTICK_XPAD)
+ ---help---
+ This option enables support for the LED which surrounds the Big X on
+ XBox 360 controller.
+
+config JOYSTICK_WALKERA0701
+ tristate "Walkera WK-0701 RC transmitter"
+ depends on HIGH_RES_TIMERS && PARPORT
+ help
+ Say Y or M here if you have a Walkera WK-0701 transmitter which is
+ supplied with a ready to fly Walkera helicopters such as HM36,
+ HM37, HM60 and want to use it via parport as a joystick. More
+ information is available: <file:Documentation/input/devices/walkera0701.rst>
+
+ To compile this driver as a module, choose M here: the
+ module will be called walkera0701.
+
+config JOYSTICK_MAPLE
+ tristate "Dreamcast control pad"
+ depends on MAPLE
+ help
+ Say Y here if you have a SEGA Dreamcast and want to use your
+ controller as a joystick.
+
+ Most Dreamcast users will say Y.
+
+ To compile this as a module choose M here: the module will be called
+ maplecontrol.
+
+config JOYSTICK_PSXPAD_SPI
+ tristate "PlayStation 1/2 joypads via SPI interface"
+ depends on SPI
+ select INPUT_POLLDEV
+ help
+ Say Y here if you wish to connect PlayStation 1/2 joypads
+ via SPI interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called psxpad-spi.
+
+config JOYSTICK_PSXPAD_SPI_FF
+ bool "PlayStation 1/2 joypads force feedback (rumble) support"
+ depends on JOYSTICK_PSXPAD_SPI
+ select INPUT_FF_MEMLESS
+ help
+ Say Y here if you want to take advantage of PlayStation 1/2
+ joypads rumble features.
+
+ To drive rumble motor a dedicated power supply is required.
+
+config JOYSTICK_PXRC
+ tristate "PhoenixRC Flight Controller Adapter"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use the PhoenixRC Flight Controller Adapter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pxrc.
+
+endif
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
new file mode 100644
index 000000000..dd0492ebb
--- /dev/null
+++ b/drivers/input/joystick/Makefile
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# 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_AS5011) += as5011.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_IFORCE) += iforce/
+obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o
+obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o
+obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o
+obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o
+obj-$(CONFIG_JOYSTICK_PSXPAD_SPI) += psxpad-spi.o
+obj-$(CONFIG_JOYSTICK_PXRC) += pxrc.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_XPAD) += xpad.o
+obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o
+obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o
+
diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c
new file mode 100644
index 000000000..98307039a
--- /dev/null
+++ b/drivers/input/joystick/a3d.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright (c) 1998-2001 Vojtech Pavlik
+ */
+
+/*
+ * FP-Gaming Assassin 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/jiffies.h>
+
+#define DRIVER_DESC "FP-Gaming Assassin 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 = input_get_drvdata(dev);
+
+ 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 = input_get_drvdata(dev);
+
+ 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 input_dev *input_dev;
+ struct gameport *adc;
+ unsigned char data[A3D_MAX_LENGTH];
+ int i;
+ int err;
+
+ a3d = kzalloc(sizeof(struct a3d), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!a3d || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ a3d->dev = input_dev;
+ 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);
+
+ snprintf(a3d->phys, sizeof(a3d->phys), "%s/input0", gameport->phys);
+
+ input_dev->name = a3d_names[a3d->mode];
+ input_dev->phys = a3d->phys;
+ input_dev->id.bustype = BUS_GAMEPORT;
+ input_dev->id.vendor = GAMEPORT_ID_VENDOR_MADCATZ;
+ input_dev->id.product = a3d->mode;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &gameport->dev;
+ input_dev->open = a3d_open;
+ input_dev->close = a3d_close;
+
+ input_set_drvdata(input_dev, a3d);
+
+ if (a3d->mode == A3D_MODE_PXL) {
+
+ int axes[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER };
+
+ a3d->length = 33;
+
+ input_dev->evbit[0] |= BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY) |
+ BIT_MASK(EV_REL);
+ input_dev->relbit[0] |= BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+ input_dev->absbit[0] |= BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
+ BIT_MASK(ABS_THROTTLE) | BIT_MASK(ABS_RUDDER) |
+ BIT_MASK(ABS_HAT0X) | BIT_MASK(ABS_HAT0Y) |
+ BIT_MASK(ABS_HAT1X) | BIT_MASK(ABS_HAT1Y);
+ input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_RIGHT) |
+ BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) |
+ BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
+ input_dev->keybit[BIT_WORD(BTN_JOYSTICK)] |=
+ BIT_MASK(BTN_TRIGGER) | BIT_MASK(BTN_THUMB) |
+ BIT_MASK(BTN_TOP) | BIT_MASK(BTN_PINKIE);
+
+ a3d_read(a3d, data);
+
+ for (i = 0; i < 4; i++) {
+ if (i < 2)
+ input_set_abs_params(input_dev, axes[i],
+ 48, input_abs_get_val(input_dev, axes[i]) * 2 - 48, 0, 8);
+ else
+ input_set_abs_params(input_dev, axes[i], 2, 253, 0, 0);
+ input_set_abs_params(input_dev, ABS_HAT0X + i, -1, 1, 0, 0);
+ }
+
+ } else {
+ a3d->length = 29;
+
+ input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ input_dev->relbit[0] |= BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+ input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_RIGHT) |
+ BIT_MASK(BTN_LEFT) | BIT_MASK(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);
+ }
+ }
+
+ err = input_register_device(a3d->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: if (a3d->adc)
+ gameport_unregister_port(a3d->adc);
+ fail2: gameport_close(gameport);
+ fail1: gameport_set_drvdata(gameport, NULL);
+ input_free_device(input_dev);
+ 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);
+ gameport_close(gameport);
+ gameport_set_drvdata(gameport, NULL);
+ kfree(a3d);
+}
+
+static struct gameport_driver a3d_drv = {
+ .driver = {
+ .name = "adc",
+ .owner = THIS_MODULE,
+ },
+ .description = DRIVER_DESC,
+ .connect = a3d_connect,
+ .disconnect = a3d_disconnect,
+};
+
+module_gameport_driver(a3d_drv);
diff --git a/drivers/input/joystick/adi.c b/drivers/input/joystick/adi.c
new file mode 100644
index 000000000..f466c0d34
--- /dev/null
+++ b/drivers/input/joystick/adi.c
@@ -0,0 +1,563 @@
+/*
+ * 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
+ */
+
+#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/jiffies.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 64
+#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 = input_get_drvdata(dev);
+
+ 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 = input_get_drvdata(dev);
+
+ 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)
+{
+ static const 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 int adi_init_input(struct adi *adi, struct adi_port *port, int half)
+{
+ struct input_dev *input_dev;
+ char buf[ADI_MAX_NAME_LENGTH];
+ int i, t;
+
+ adi->dev = input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ 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 [%s]", buf, adi->cname);
+ snprintf(adi->phys, ADI_MAX_PHYS_LENGTH, "%s/input%d", port->gameport->phys, half);
+
+ adi->abs = adi_abs[t];
+ adi->key = adi_key[t];
+
+ input_dev->name = adi->name;
+ input_dev->phys = adi->phys;
+ input_dev->id.bustype = BUS_GAMEPORT;
+ input_dev->id.vendor = GAMEPORT_ID_VENDOR_LOGITECH;
+ input_dev->id.product = adi->id;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &port->gameport->dev;
+
+ input_set_drvdata(input_dev, port);
+
+ input_dev->open = adi_open;
+ input_dev->close = adi_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ for (i = 0; i < adi->axes10 + adi->axes8 + (adi->hats + (adi->pad != -1)) * 2; i++)
+ set_bit(adi->abs[i], input_dev->absbit);
+
+ for (i = 0; i < adi->buttons; i++)
+ set_bit(adi->key[i], input_dev->keybit);
+
+ return 0;
+}
+
+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 = input_abs_get_val(adi->dev, 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;
+
+ port = kzalloc(sizeof(struct adi_port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->gameport = gameport;
+
+ gameport_set_drvdata(gameport, port);
+
+ err = gameport_open(gameport, drv, GAMEPORT_MODE_RAW);
+ if (err)
+ goto fail1;
+
+ 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);
+
+ if (!port->adi[i].length)
+ continue;
+
+ err = adi_init_input(port->adi + i, port, i);
+ if (err)
+ goto fail2;
+ }
+
+ if (!port->adi[0].length && !port->adi[1].length) {
+ err = -ENODEV;
+ goto fail2;
+ }
+
+ 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);
+ err = input_register_device(port->adi[i].dev);
+ if (err)
+ goto fail3;
+ }
+
+ return 0;
+
+ fail3: while (--i >= 0) {
+ if (port->adi[i].length > 0) {
+ input_unregister_device(port->adi[i].dev);
+ port->adi[i].dev = NULL;
+ }
+ }
+ fail2: for (i = 0; i < 2; i++)
+ input_free_device(port->adi[i].dev);
+ gameport_close(gameport);
+ fail1: gameport_set_drvdata(gameport, NULL);
+ kfree(port);
+ return err;
+}
+
+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);
+}
+
+static struct gameport_driver adi_drv = {
+ .driver = {
+ .name = "adi",
+ },
+ .description = DRIVER_DESC,
+ .connect = adi_connect,
+ .disconnect = adi_disconnect,
+};
+
+module_gameport_driver(adi_drv);
diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c
new file mode 100644
index 000000000..2b82a838c
--- /dev/null
+++ b/drivers/input/joystick/amijoy.c
@@ -0,0 +1,172 @@
+/*
+ * 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
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.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)");
+
+static int amijoy_used;
+static DEFINE_MUTEX(amijoy_mutex);
+static struct input_dev *amijoy_dev[2];
+static char *amijoy_phys[2] = { "amijoy/input0", "amijoy/input1" };
+
+static irqreturn_t amijoy_interrupt(int irq, void *dummy)
+{
+ int i, data = 0, button = 0;
+
+ for (i = 0; i < 2; i++)
+ if (amijoy[i]) {
+
+ switch (i) {
+ case 0: data = ~amiga_custom.joy0dat; button = (~ciaa.pra >> 6) & 1; break;
+ case 1: data = ~amiga_custom.joy1dat; button = (~ciaa.pra >> 7) & 1; break;
+ }
+
+ 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 err;
+
+ err = mutex_lock_interruptible(&amijoy_mutex);
+ if (err)
+ return err;
+
+ if (!amijoy_used && request_irq(IRQ_AMIGA_VERTB, amijoy_interrupt, 0, "amijoy", amijoy_interrupt)) {
+ printk(KERN_ERR "amijoy.c: Can't allocate irq %d\n", IRQ_AMIGA_VERTB);
+ err = -EBUSY;
+ goto out;
+ }
+
+ amijoy_used++;
+out:
+ mutex_unlock(&amijoy_mutex);
+ return err;
+}
+
+static void amijoy_close(struct input_dev *dev)
+{
+ mutex_lock(&amijoy_mutex);
+ if (!--amijoy_used)
+ free_irq(IRQ_AMIGA_VERTB, amijoy_interrupt);
+ mutex_unlock(&amijoy_mutex);
+}
+
+static int __init amijoy_init(void)
+{
+ int i, j;
+ int err;
+
+ if (!MACH_IS_AMIGA)
+ return -ENODEV;
+
+ for (i = 0; i < 2; i++) {
+ if (!amijoy[i])
+ continue;
+
+ amijoy_dev[i] = input_allocate_device();
+ if (!amijoy_dev[i]) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ if (!request_mem_region(CUSTOM_PHYSADDR + 10 + i * 2, 2, "amijoy [Denise]")) {
+ input_free_device(amijoy_dev[i]);
+ err = -EBUSY;
+ goto fail;
+ }
+
+ amijoy_dev[i]->name = "Amiga joystick";
+ 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]->open = amijoy_open;
+ amijoy_dev[i]->close = amijoy_close;
+
+ amijoy_dev[i]->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ amijoy_dev[i]->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y);
+ amijoy_dev[i]->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+ for (j = 0; j < 2; j++) {
+ input_set_abs_params(amijoy_dev[i], ABS_X + j,
+ -1, 1, 0, 0);
+ }
+
+ err = input_register_device(amijoy_dev[i]);
+ if (err) {
+ input_free_device(amijoy_dev[i]);
+ goto fail;
+ }
+ }
+ return 0;
+
+ fail: while (--i >= 0)
+ if (amijoy[i]) {
+ input_unregister_device(amijoy_dev[i]);
+ release_mem_region(CUSTOM_PHYSADDR + 10 + i * 2, 2);
+ }
+ return err;
+}
+
+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 000000000..2b445c8d3
--- /dev/null
+++ b/drivers/input/joystick/analog.c
@@ -0,0 +1,798 @@
+/*
+ * 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
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/gameport.h>
+#include <linux/jiffies.h>
+#include <linux/timex.h>
+#include <linux/timekeeping.h>
+
+#define DRIVER_DESC "Analog joystick and gamepad driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static bool use_ktime = true;
+module_param(use_ktime, bool, 0400);
+MODULE_PARM_DESC(use_ktime, "Use ktime for measuring I/O speed");
+
+/*
+ * Option parsing.
+ */
+
+#define ANALOG_PORTS 16
+
+static char *js[ANALOG_PORTS];
+static unsigned 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");
+
+/*
+ * 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__
+
+#include <linux/i8253.h>
+
+#define GET_TIME(x) do { if (boot_cpu_has(X86_FEATURE_TSC)) x = (unsigned int)rdtsc(); else x = get_time_pit(); } while (0)
+#define DELTA(x,y) (boot_cpu_has(X86_FEATURE_TSC) ? ((y) - (x)) : ((x) - (y) + ((x) < (y) ? PIT_TICK_RATE / HZ : 0)))
+#define TIME_NAME (boot_cpu_has(X86_FEATURE_TSC)?"TSC":"PIT")
+static unsigned int get_time_pit(void)
+{
+ unsigned long flags;
+ unsigned int count;
+
+ raw_spin_lock_irqsave(&i8253_lock, flags);
+ outb_p(0x00, 0x43);
+ count = inb_p(0x40);
+ count |= inb_p(0x40) << 8;
+ raw_spin_unlock_irqrestore(&i8253_lock, flags);
+
+ return count;
+}
+#elif defined(__x86_64__)
+#define GET_TIME(x) do { x = (unsigned int)rdtsc(); } while (0)
+#define DELTA(x,y) ((y)-(x))
+#define TIME_NAME "TSC"
+#elif defined(__alpha__) || defined(CONFIG_ARM) || defined(CONFIG_ARM64) || defined(CONFIG_PPC) || defined(CONFIG_RISCV)
+#define GET_TIME(x) do { x = get_cycles(); } while (0)
+#define DELTA(x,y) ((y)-(x))
+#define TIME_NAME "get_cycles"
+#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
+
+static inline u64 get_time(void)
+{
+ if (use_ktime) {
+ return ktime_get_ns();
+ } else {
+ unsigned int x;
+ GET_TIME(x);
+ return x;
+ }
+}
+
+static inline unsigned int delta(u64 x, u64 y)
+{
+ if (use_ktime)
+ return y - x;
+ else
+ return DELTA((unsigned int)x, (unsigned int)y);
+}
+
+/*
+ * 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;
+ u64 time[4], start, loop, now;
+ unsigned int 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);
+ now = get_time();
+ 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;
+ now = get_time();
+ 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 = input_get_drvdata(dev);
+
+ 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 = input_get_drvdata(dev);
+
+ 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;
+ u64 t1, t2, t3;
+ unsigned long flags;
+
+ if (use_ktime) {
+ port->speed = 1000000;
+ } else {
+ local_irq_save(flags);
+ t1 = get_time();
+#ifdef FAKE_TIME
+ analog_faketime += 830;
+#endif
+ mdelay(1);
+ t2 = get_time();
+ t3 = get_time();
+ local_irq_restore(flags);
+
+ port->speed = delta(t1, t2) - delta(t2, t3);
+ }
+
+ tx = ~0;
+
+ for (i = 0; i < 50; i++) {
+ local_irq_save(flags);
+ t1 = get_time();
+ for (t = 0; t < 50; t++) {
+ gameport_read(gameport);
+ t2 = get_time();
+ }
+ t3 = get_time();
+ 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)
+{
+ snprintf(analog->name, sizeof(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)
+ snprintf(analog->name, sizeof(analog->name), "%s %d-hat",
+ analog->name, hweight16(analog->mask & ANALOG_HATS_ALL));
+
+ if (analog->mask & ANALOG_HAT_FCS)
+ strlcat(analog->name, " FCS", sizeof(analog->name));
+ if (analog->mask & ANALOG_ANY_CHF)
+ strlcat(analog->name, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF",
+ sizeof(analog->name));
+
+ strlcat(analog->name, (analog->mask & ANALOG_GAMEPAD) ? " gamepad": " joystick",
+ sizeof(analog->name));
+}
+
+/*
+ * analog_init_device()
+ */
+
+static int analog_init_device(struct analog_port *port, struct analog *analog, int index)
+{
+ struct input_dev *input_dev;
+ int i, j, t, v, w, x, y, z;
+ int error;
+
+ analog_name(analog);
+ snprintf(analog->phys, sizeof(analog->phys),
+ "%s/input%d", port->gameport->phys, index);
+ analog->buttons = (analog->mask & ANALOG_GAMEPAD) ? analog_pad_btn : analog_joy_btn;
+
+ analog->dev = input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ input_dev->name = analog->name;
+ input_dev->phys = analog->phys;
+ input_dev->id.bustype = BUS_GAMEPORT;
+ input_dev->id.vendor = GAMEPORT_ID_VENDOR_ANALOG;
+ input_dev->id.product = analog->mask >> 4;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &port->gameport->dev;
+
+ input_set_drvdata(input_dev, port);
+
+ input_dev->open = analog_open;
+ input_dev->close = analog_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(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);
+
+ 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);
+ }
+
+ input_set_abs_params(input_dev, t, v, (x << 1) - v, port->fuzz, 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++];
+ input_set_abs_params(input_dev, t, -1, 1, 0, 0);
+ }
+
+ for (i = j = 0; i < 4; i++)
+ if (analog->mask & (0x10 << i))
+ set_bit(analog->buttons[j++], input_dev->keybit);
+
+ if (analog->mask & ANALOG_BTNS_CHF)
+ for (i = 0; i < 2; i++)
+ set_bit(analog->buttons[j++], input_dev->keybit);
+
+ if (analog->mask & ANALOG_HBTN_CHF)
+ for (i = 0; i < 4; i++)
+ set_bit(analog->buttons[j++], input_dev->keybit);
+
+ for (i = 0; i < 4; i++)
+ if (analog->mask & (ANALOG_BTN_TL << i))
+ set_bit(analog_pads[i], input_dev->keybit);
+
+ analog_decode(analog, port->axes, port->initial, port->buttons);
+
+ error = input_register_device(analog->dev);
+ if (error) {
+ input_free_device(analog->dev);
+ return error;
+ }
+
+ return 0;
+}
+
+/*
+ * 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 = kzalloc(sizeof(struct analog_port), GFP_KERNEL)))
+ return - ENOMEM;
+
+ err = analog_init_port(gameport, drv, port);
+ if (err)
+ goto fail1;
+
+ err = analog_init_masks(port);
+ if (err)
+ goto fail2;
+
+ gameport_set_poll_handler(gameport, analog_poll);
+ gameport_set_poll_interval(gameport, 10);
+
+ for (i = 0; i < 2; i++)
+ if (port->analog[i].mask) {
+ err = analog_init_device(port, port->analog + i, i);
+ if (err)
+ goto fail3;
+ }
+
+ return 0;
+
+ fail3: while (--i >= 0)
+ if (port->analog[i].mask)
+ input_unregister_device(port->analog[i].dev);
+ fail2: gameport_close(gameport);
+ fail1: gameport_set_drvdata(gameport, NULL);
+ kfree(port);
+ return err;
+}
+
+static void analog_disconnect(struct gameport *gameport)
+{
+ struct analog_port *port = gameport_get_drvdata(gameport);
+ int i;
+
+ 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();
+ return gameport_register_driver(&analog_drv);
+}
+
+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/as5011.c b/drivers/input/joystick/as5011.c
new file mode 100644
index 000000000..f051993c5
--- /dev/null
+++ b/drivers/input/joystick/as5011.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2010, 2011 Fabien Marteau <fabien.marteau@armadeus.com>
+ * Sponsored by ARMadeus Systems
+ *
+ * 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
+ *
+ * Driver for Austria Microsystems joysticks AS5011
+ *
+ * TODO:
+ * - Power on the chip when open() and power down when close()
+ * - Manage power mode
+ */
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/input/as5011.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#define DRIVER_DESC "Driver for Austria Microsystems AS5011 joystick"
+#define MODULE_DEVICE_ALIAS "as5011"
+
+MODULE_AUTHOR("Fabien Marteau <fabien.marteau@armadeus.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/* registers */
+#define AS5011_CTRL1 0x76
+#define AS5011_CTRL2 0x75
+#define AS5011_XP 0x43
+#define AS5011_XN 0x44
+#define AS5011_YP 0x53
+#define AS5011_YN 0x54
+#define AS5011_X_REG 0x41
+#define AS5011_Y_REG 0x42
+#define AS5011_X_RES_INT 0x51
+#define AS5011_Y_RES_INT 0x52
+
+/* CTRL1 bits */
+#define AS5011_CTRL1_LP_PULSED 0x80
+#define AS5011_CTRL1_LP_ACTIVE 0x40
+#define AS5011_CTRL1_LP_CONTINUE 0x20
+#define AS5011_CTRL1_INT_WUP_EN 0x10
+#define AS5011_CTRL1_INT_ACT_EN 0x08
+#define AS5011_CTRL1_EXT_CLK_EN 0x04
+#define AS5011_CTRL1_SOFT_RST 0x02
+#define AS5011_CTRL1_DATA_VALID 0x01
+
+/* CTRL2 bits */
+#define AS5011_CTRL2_EXT_SAMPLE_EN 0x08
+#define AS5011_CTRL2_RC_BIAS_ON 0x04
+#define AS5011_CTRL2_INV_SPINNING 0x02
+
+#define AS5011_MAX_AXIS 80
+#define AS5011_MIN_AXIS (-80)
+#define AS5011_FUZZ 8
+#define AS5011_FLAT 40
+
+struct as5011_device {
+ struct input_dev *input_dev;
+ struct i2c_client *i2c_client;
+ unsigned int button_gpio;
+ unsigned int button_irq;
+ unsigned int axis_irq;
+};
+
+static int as5011_i2c_write(struct i2c_client *client,
+ uint8_t aregaddr,
+ uint8_t avalue)
+{
+ uint8_t data[2] = { aregaddr, avalue };
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = I2C_M_IGNORE_NAK,
+ .len = 2,
+ .buf = (uint8_t *)data
+ };
+ int error;
+
+ error = i2c_transfer(client->adapter, &msg, 1);
+ return error < 0 ? error : 0;
+}
+
+static int as5011_i2c_read(struct i2c_client *client,
+ uint8_t aregaddr, signed char *value)
+{
+ uint8_t data[2] = { aregaddr };
+ struct i2c_msg msg_set[2] = {
+ {
+ .addr = client->addr,
+ .flags = I2C_M_REV_DIR_ADDR,
+ .len = 1,
+ .buf = (uint8_t *)data
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD | I2C_M_NOSTART,
+ .len = 1,
+ .buf = (uint8_t *)data
+ }
+ };
+ int error;
+
+ error = i2c_transfer(client->adapter, msg_set, 2);
+ if (error < 0)
+ return error;
+
+ *value = data[0] & 0x80 ? -1 * (1 + ~data[0]) : data[0];
+ return 0;
+}
+
+static irqreturn_t as5011_button_interrupt(int irq, void *dev_id)
+{
+ struct as5011_device *as5011 = dev_id;
+ int val = gpio_get_value_cansleep(as5011->button_gpio);
+
+ input_report_key(as5011->input_dev, BTN_JOYSTICK, !val);
+ input_sync(as5011->input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t as5011_axis_interrupt(int irq, void *dev_id)
+{
+ struct as5011_device *as5011 = dev_id;
+ int error;
+ signed char x, y;
+
+ error = as5011_i2c_read(as5011->i2c_client, AS5011_X_RES_INT, &x);
+ if (error < 0)
+ goto out;
+
+ error = as5011_i2c_read(as5011->i2c_client, AS5011_Y_RES_INT, &y);
+ if (error < 0)
+ goto out;
+
+ input_report_abs(as5011->input_dev, ABS_X, x);
+ input_report_abs(as5011->input_dev, ABS_Y, y);
+ input_sync(as5011->input_dev);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int as5011_configure_chip(struct as5011_device *as5011,
+ const struct as5011_platform_data *plat_dat)
+{
+ struct i2c_client *client = as5011->i2c_client;
+ int error;
+ signed char value;
+
+ /* chip soft reset */
+ error = as5011_i2c_write(client, AS5011_CTRL1,
+ AS5011_CTRL1_SOFT_RST);
+ if (error < 0) {
+ dev_err(&client->dev, "Soft reset failed\n");
+ return error;
+ }
+
+ mdelay(10);
+
+ error = as5011_i2c_write(client, AS5011_CTRL1,
+ AS5011_CTRL1_LP_PULSED |
+ AS5011_CTRL1_LP_ACTIVE |
+ AS5011_CTRL1_INT_ACT_EN);
+ if (error < 0) {
+ dev_err(&client->dev, "Power config failed\n");
+ return error;
+ }
+
+ error = as5011_i2c_write(client, AS5011_CTRL2,
+ AS5011_CTRL2_INV_SPINNING);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't invert spinning\n");
+ return error;
+ }
+
+ /* write threshold */
+ error = as5011_i2c_write(client, AS5011_XP, plat_dat->xp);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't write threshold\n");
+ return error;
+ }
+
+ error = as5011_i2c_write(client, AS5011_XN, plat_dat->xn);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't write threshold\n");
+ return error;
+ }
+
+ error = as5011_i2c_write(client, AS5011_YP, plat_dat->yp);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't write threshold\n");
+ return error;
+ }
+
+ error = as5011_i2c_write(client, AS5011_YN, plat_dat->yn);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't write threshold\n");
+ return error;
+ }
+
+ /* to free irq gpio in chip */
+ error = as5011_i2c_read(client, AS5011_X_RES_INT, &value);
+ if (error < 0) {
+ dev_err(&client->dev, "Can't read i2c X resolution value\n");
+ return error;
+ }
+
+ return 0;
+}
+
+static int as5011_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct as5011_platform_data *plat_data;
+ struct as5011_device *as5011;
+ struct input_dev *input_dev;
+ int irq;
+ int error;
+
+ plat_data = dev_get_platdata(&client->dev);
+ if (!plat_data)
+ return -EINVAL;
+
+ if (!plat_data->axis_irq) {
+ dev_err(&client->dev, "No axis IRQ?\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_NOSTART |
+ I2C_FUNC_PROTOCOL_MANGLING)) {
+ dev_err(&client->dev,
+ "need i2c bus that supports protocol mangling\n");
+ return -ENODEV;
+ }
+
+ as5011 = kmalloc(sizeof(struct as5011_device), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!as5011 || !input_dev) {
+ dev_err(&client->dev,
+ "Can't allocate memory for device structure\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ as5011->i2c_client = client;
+ as5011->input_dev = input_dev;
+ as5011->button_gpio = plat_data->button_gpio;
+ as5011->axis_irq = plat_data->axis_irq;
+
+ input_dev->name = "Austria Microsystem as5011 joystick";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ input_set_capability(input_dev, EV_KEY, BTN_JOYSTICK);
+
+ input_set_abs_params(input_dev, ABS_X,
+ AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT);
+ input_set_abs_params(as5011->input_dev, ABS_Y,
+ AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT);
+
+ error = gpio_request(as5011->button_gpio, "AS5011 button");
+ if (error < 0) {
+ dev_err(&client->dev, "Failed to request button gpio\n");
+ goto err_free_mem;
+ }
+
+ irq = gpio_to_irq(as5011->button_gpio);
+ if (irq < 0) {
+ dev_err(&client->dev,
+ "Failed to get irq number for button gpio\n");
+ error = irq;
+ goto err_free_button_gpio;
+ }
+
+ as5011->button_irq = irq;
+
+ error = request_threaded_irq(as5011->button_irq,
+ NULL, as5011_button_interrupt,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "as5011_button", as5011);
+ if (error < 0) {
+ dev_err(&client->dev,
+ "Can't allocate button irq %d\n", as5011->button_irq);
+ goto err_free_button_gpio;
+ }
+
+ error = as5011_configure_chip(as5011, plat_data);
+ if (error)
+ goto err_free_button_irq;
+
+ error = request_threaded_irq(as5011->axis_irq, NULL,
+ as5011_axis_interrupt,
+ plat_data->axis_irqflags | IRQF_ONESHOT,
+ "as5011_joystick", as5011);
+ if (error) {
+ dev_err(&client->dev,
+ "Can't allocate axis irq %d\n", plat_data->axis_irq);
+ goto err_free_button_irq;
+ }
+
+ error = input_register_device(as5011->input_dev);
+ if (error) {
+ dev_err(&client->dev, "Failed to register input device\n");
+ goto err_free_axis_irq;
+ }
+
+ i2c_set_clientdata(client, as5011);
+
+ return 0;
+
+err_free_axis_irq:
+ free_irq(as5011->axis_irq, as5011);
+err_free_button_irq:
+ free_irq(as5011->button_irq, as5011);
+err_free_button_gpio:
+ gpio_free(as5011->button_gpio);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(as5011);
+
+ return error;
+}
+
+static int as5011_remove(struct i2c_client *client)
+{
+ struct as5011_device *as5011 = i2c_get_clientdata(client);
+
+ free_irq(as5011->axis_irq, as5011);
+ free_irq(as5011->button_irq, as5011);
+ gpio_free(as5011->button_gpio);
+
+ input_unregister_device(as5011->input_dev);
+ kfree(as5011);
+
+ return 0;
+}
+
+static const struct i2c_device_id as5011_id[] = {
+ { MODULE_DEVICE_ALIAS, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, as5011_id);
+
+static struct i2c_driver as5011_driver = {
+ .driver = {
+ .name = "as5011",
+ },
+ .probe = as5011_probe,
+ .remove = as5011_remove,
+ .id_table = as5011_id,
+};
+
+module_i2c_driver(as5011_driver);
diff --git a/drivers/input/joystick/cobra.c b/drivers/input/joystick/cobra.c
new file mode 100644
index 000000000..14cb956be
--- /dev/null
+++ b/drivers/input/joystick/cobra.c
@@ -0,0 +1,259 @@
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/jiffies.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 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 = input_get_drvdata(dev);
+
+ gameport_start_polling(cobra->gameport);
+ return 0;
+}
+
+static void cobra_close(struct input_dev *dev)
+{
+ struct cobra *cobra = input_get_drvdata(dev);
+
+ gameport_stop_polling(cobra->gameport);
+}
+
+static int cobra_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ struct cobra *cobra;
+ struct input_dev *input_dev;
+ unsigned int data[2];
+ int i, j;
+ int err;
+
+ cobra = kzalloc(sizeof(struct cobra), GFP_KERNEL);
+ if (!cobra)
+ 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)
+ continue;
+
+ cobra->dev[i] = input_dev = input_allocate_device();
+ if (!input_dev) {
+ err = -ENOMEM;
+ goto fail3;
+ }
+
+ snprintf(cobra->phys[i], sizeof(cobra->phys[i]),
+ "%s/input%d", gameport->phys, i);
+
+ input_dev->name = "Creative Labs Blaster GamePad Cobra";
+ input_dev->phys = cobra->phys[i];
+ input_dev->id.bustype = BUS_GAMEPORT;
+ input_dev->id.vendor = GAMEPORT_ID_VENDOR_CREATIVE;
+ input_dev->id.product = 0x0008;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &gameport->dev;
+
+ input_set_drvdata(input_dev, cobra);
+
+ input_dev->open = cobra_open;
+ input_dev->close = cobra_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_set_abs_params(input_dev, ABS_X, -1, 1, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, -1, 1, 0, 0);
+ for (j = 0; cobra_btn[j]; j++)
+ set_bit(cobra_btn[j], input_dev->keybit);
+
+ err = input_register_device(cobra->dev[i]);
+ if (err)
+ goto fail4;
+ }
+
+ return 0;
+
+ fail4: input_free_device(cobra->dev[i]);
+ fail3: while (--i >= 0)
+ if (cobra->dev[i])
+ input_unregister_device(cobra->dev[i]);
+ 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,
+};
+
+module_gameport_driver(cobra_drv);
diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c
new file mode 100644
index 000000000..804b1b80a
--- /dev/null
+++ b/drivers/input/joystick/db9.c
@@ -0,0 +1,723 @@
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/parport.h>
+#include <linux/input.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver");
+MODULE_LICENSE("GPL");
+
+struct db9_config {
+ int args[2];
+ unsigned int nargs;
+};
+
+#define DB9_MAX_PORTS 3
+static struct db9_config db9_cfg[DB9_MAX_PORTS];
+
+module_param_array_named(dev, db9_cfg[0].args, int, &db9_cfg[0].nargs, 0);
+MODULE_PARM_DESC(dev, "Describes first attached device (<parport#>,<type>)");
+module_param_array_named(dev2, db9_cfg[1].args, int, &db9_cfg[1].nargs, 0);
+MODULE_PARM_DESC(dev2, "Describes second attached device (<parport#>,<type>)");
+module_param_array_named(dev3, db9_cfg[2].args, int, &db9_cfg[2].nargs, 0);
+MODULE_PARM_DESC(dev3, "Describes third attached device (<parport#>,<type>)");
+
+#define DB9_ARG_PARPORT 0
+#define DB9_ARG_MODE 1
+
+#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_GENESIS6_DELAY 14
+#define DB9_REFRESH_TIME HZ/100
+
+#define DB9_MAX_DEVICES 2
+
+struct db9_mode_data {
+ const char *name;
+ const short *buttons;
+ int n_buttons;
+ int n_pads;
+ int n_axis;
+ int bidirectional;
+ int reverse;
+};
+
+struct db9 {
+ struct input_dev *dev[DB9_MAX_DEVICES];
+ struct timer_list timer;
+ struct pardevice *pd;
+ int mode;
+ int used;
+ int parportno;
+ struct mutex mutex;
+ char phys[DB9_MAX_DEVICES][32];
+};
+
+static struct db9 *db9_base[3];
+
+static const short db9_multi_btn[] = { BTN_TRIGGER, BTN_THUMB };
+static const short db9_genesis_btn[] = { BTN_START, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_MODE };
+static const short db9_cd32_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START };
+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 struct db9_mode_data db9_modes[] = {
+ { NULL, NULL, 0, 0, 0, 0, 0 },
+ { "Multisystem joystick", db9_multi_btn, 1, 1, 2, 1, 1 },
+ { "Multisystem joystick (2 fire)", db9_multi_btn, 2, 1, 2, 1, 1 },
+ { "Genesis pad", db9_genesis_btn, 4, 1, 2, 1, 1 },
+ { NULL, NULL, 0, 0, 0, 0, 0 },
+ { "Genesis 5 pad", db9_genesis_btn, 6, 1, 2, 1, 1 },
+ { "Genesis 6 pad", db9_genesis_btn, 8, 1, 2, 1, 1 },
+ { "Saturn pad", db9_cd32_btn, 9, 6, 7, 0, 1 },
+ { "Multisystem (0.8.0.2) joystick", db9_multi_btn, 1, 1, 2, 1, 1 },
+ { "Multisystem (0.8.0.2-dual) joystick", db9_multi_btn, 1, 2, 2, 1, 1 },
+ { "Amiga CD-32 pad", db9_cd32_btn, 7, 1, 2, 1, 1 },
+ { "Saturn dpp", db9_cd32_btn, 9, 6, 7, 0, 0 },
+ { "Saturn dpp dual", db9_cd32_btn, 9, 12, 7, 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;
+ }
+ /* else: fall through */
+ 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 *devs[], int n, int max_pads)
+{
+ struct input_dev *dev;
+ int tmp, i, j;
+
+ tmp = (id == 0x41) ? 60 : 10;
+ for (j = 0; j < tmp && n < max_pads; j += 10, n++) {
+ dev = devs[n];
+ switch (data[j]) {
+ case 0x16: /* multi controller (analog 4 axis) */
+ input_report_abs(dev, db9_abs[5], data[j + 6]);
+ /* fall through */
+ case 0x15: /* mission stick (analog 3 axis) */
+ input_report_abs(dev, db9_abs[3], data[j + 4]);
+ input_report_abs(dev, db9_abs[4], data[j + 5]);
+ /* fall through */
+ case 0x13: /* racing controller (analog 1 axis) */
+ input_report_abs(dev, db9_abs[2], data[j + 3]);
+ /* fall through */
+ case 0x34: /* saturn keyboard (udlr ZXC ASD QE Esc) */
+ case 0x02: /* digital pad (digital 2 axis + buttons) */
+ input_report_abs(dev, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
+ input_report_abs(dev, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
+ for (i = 0; i < 9; i++)
+ input_report_key(dev, 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, db9_abs[0], !(data[j + 1] & 128) - !(data[j + 1] & 64));
+ input_report_abs(dev, db9_abs[1], !(data[j + 1] & 32) - !(data[j + 1] & 16));
+ for (i = 0; i < 9; i++)
+ input_report_key(dev, db9_cd32_btn[i], ~data[j + db9_saturn_byte[i]] & db9_saturn_mask[i]);
+ input_report_abs(dev, db9_abs[2], data[j + 3]);
+ input_report_abs(dev, db9_abs[3], data[j + 4]);
+ input_report_abs(dev, db9_abs[4], data[j + 5]);
+ /*
+ input_report_abs(dev, db9_abs[8], (data[j + 6] & 128 ? 0 : 1) - (data[j + 6] & 64 ? 0 : 1));
+ input_report_abs(dev, db9_abs[9], (data[j + 6] & 32 ? 0 : 1) - (data[j + 6] & 16 ? 0 : 1));
+ */
+ input_report_abs(dev, db9_abs[6], data[j + 7]);
+ input_report_abs(dev, db9_abs[7], data[j + 8]);
+ input_report_abs(dev, db9_abs[5], data[j + 9]);
+ break;
+ case 0xd3: /* sankyo ff (analog 1 axis + stop btn) */
+ input_report_key(dev, BTN_A, data[j + 3] & 0x80);
+ input_report_abs(dev, db9_abs[2], data[j + 3] & 0x7f);
+ break;
+ case 0xe3: /* shuttle mouse (analog 2 axis + buttons. signed value) */
+ input_report_key(dev, BTN_START, data[j + 1] & 0x08);
+ input_report_key(dev, BTN_A, data[j + 1] & 0x04);
+ input_report_key(dev, BTN_C, data[j + 1] & 0x02);
+ input_report_key(dev, BTN_B, data[j + 1] & 0x01);
+ input_report_abs(dev, db9_abs[2], data[j + 2] ^ 0x80);
+ input_report_abs(dev, db9_abs[3], (0xff-(data[j + 3] ^ 0x80))+1); /* */
+ break;
+ case 0xff:
+ default: /* no pad */
+ input_report_abs(dev, db9_abs[0], 0);
+ input_report_abs(dev, db9_abs[1], 0);
+ for (i = 0; i < 9; i++)
+ input_report_key(dev, db9_cd32_btn[i], 0);
+ break;
+ }
+ }
+ return n;
+}
+
+static int db9_saturn(int mode, struct parport *port, struct input_dev *devs[])
+{
+ 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_modes[mode].n_pads, 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, devs, tmp, max_pads);
+ }
+ return 0;
+}
+
+static void db9_timer(struct timer_list *t)
+{
+ struct db9 *db9 = from_timer(db9, t, timer);
+ struct parport *port = db9->pd->port;
+ struct input_dev *dev = db9->dev[0];
+ struct input_dev *dev2 = db9->dev[1];
+ int data, i;
+
+ switch (db9->mode) {
+ case DB9_MULTI_0802_2:
+
+ data = parport_read_data(port) >> 3;
+
+ input_report_abs(dev2, ABS_X, (data & DB9_RIGHT ? 0 : 1) - (data & DB9_LEFT ? 0 : 1));
+ input_report_abs(dev2, ABS_Y, (data & DB9_DOWN ? 0 : 1) - (data & DB9_UP ? 0 : 1));
+ input_report_key(dev2, BTN_TRIGGER, ~data & DB9_FIRE1);
+ /* fall through */
+
+ 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, db9->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 = input_get_drvdata(dev);
+ struct parport *port = db9->pd->port;
+ int err;
+
+ err = mutex_lock_interruptible(&db9->mutex);
+ if (err)
+ return err;
+
+ if (!db9->used++) {
+ parport_claim(db9->pd);
+ parport_write_data(port, 0xff);
+ if (db9_modes[db9->mode].reverse) {
+ parport_data_reverse(port);
+ parport_write_control(port, DB9_NORMAL);
+ }
+ mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME);
+ }
+
+ mutex_unlock(&db9->mutex);
+ return 0;
+}
+
+static void db9_close(struct input_dev *dev)
+{
+ struct db9 *db9 = input_get_drvdata(dev);
+ struct parport *port = db9->pd->port;
+
+ mutex_lock(&db9->mutex);
+ if (!--db9->used) {
+ del_timer_sync(&db9->timer);
+ parport_write_control(port, 0x00);
+ parport_data_forward(port);
+ parport_release(db9->pd);
+ }
+ mutex_unlock(&db9->mutex);
+}
+
+static void db9_attach(struct parport *pp)
+{
+ struct db9 *db9;
+ const struct db9_mode_data *db9_mode;
+ struct pardevice *pd;
+ struct input_dev *input_dev;
+ int i, j, port_idx;
+ int mode;
+ struct pardev_cb db9_parport_cb;
+
+ for (port_idx = 0; port_idx < DB9_MAX_PORTS; port_idx++) {
+ if (db9_cfg[port_idx].nargs == 0 ||
+ db9_cfg[port_idx].args[DB9_ARG_PARPORT] < 0)
+ continue;
+
+ if (db9_cfg[port_idx].args[DB9_ARG_PARPORT] == pp->number)
+ break;
+ }
+
+ if (port_idx == DB9_MAX_PORTS) {
+ pr_debug("Not using parport%d.\n", pp->number);
+ return;
+ }
+
+ mode = db9_cfg[port_idx].args[DB9_ARG_MODE];
+
+ if (mode < 1 || mode >= DB9_MAX_PAD || !db9_modes[mode].n_buttons) {
+ printk(KERN_ERR "db9.c: Bad device type %d\n", mode);
+ return;
+ }
+
+ db9_mode = &db9_modes[mode];
+
+ if (db9_mode->bidirectional && !(pp->modes & PARPORT_MODE_TRISTATE)) {
+ printk(KERN_ERR "db9.c: specified parport is not bidirectional\n");
+ return;
+ }
+
+ memset(&db9_parport_cb, 0, sizeof(db9_parport_cb));
+ db9_parport_cb.flags = PARPORT_FLAG_EXCL;
+
+ pd = parport_register_dev_model(pp, "db9", &db9_parport_cb, port_idx);
+ if (!pd) {
+ printk(KERN_ERR "db9.c: parport busy already - lp.o loaded?\n");
+ return;
+ }
+
+ db9 = kzalloc(sizeof(struct db9), GFP_KERNEL);
+ if (!db9)
+ goto err_unreg_pardev;
+
+ mutex_init(&db9->mutex);
+ db9->pd = pd;
+ db9->mode = mode;
+ db9->parportno = pp->number;
+ timer_setup(&db9->timer, db9_timer, 0);
+
+ for (i = 0; i < (min(db9_mode->n_pads, DB9_MAX_DEVICES)); i++) {
+
+ db9->dev[i] = input_dev = input_allocate_device();
+ if (!input_dev) {
+ printk(KERN_ERR "db9.c: Not enough memory for input device\n");
+ goto err_unreg_devs;
+ }
+
+ snprintf(db9->phys[i], sizeof(db9->phys[i]),
+ "%s/input%d", db9->pd->port->name, i);
+
+ input_dev->name = db9_mode->name;
+ input_dev->phys = db9->phys[i];
+ input_dev->id.bustype = BUS_PARPORT;
+ input_dev->id.vendor = 0x0002;
+ input_dev->id.product = mode;
+ input_dev->id.version = 0x0100;
+
+ input_set_drvdata(input_dev, db9);
+
+ input_dev->open = db9_open;
+ input_dev->close = db9_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ for (j = 0; j < db9_mode->n_buttons; j++)
+ set_bit(db9_mode->buttons[j], input_dev->keybit);
+ for (j = 0; j < db9_mode->n_axis; j++) {
+ if (j < 2)
+ input_set_abs_params(input_dev, db9_abs[j], -1, 1, 0, 0);
+ else
+ input_set_abs_params(input_dev, db9_abs[j], 1, 255, 0, 0);
+ }
+
+ if (input_register_device(input_dev))
+ goto err_free_dev;
+ }
+
+ db9_base[port_idx] = db9;
+ return;
+
+ err_free_dev:
+ input_free_device(db9->dev[i]);
+ err_unreg_devs:
+ while (--i >= 0)
+ input_unregister_device(db9->dev[i]);
+ kfree(db9);
+ err_unreg_pardev:
+ parport_unregister_device(pd);
+}
+
+static void db9_detach(struct parport *port)
+{
+ int i;
+ struct db9 *db9;
+
+ for (i = 0; i < DB9_MAX_PORTS; i++) {
+ if (db9_base[i] && db9_base[i]->parportno == port->number)
+ break;
+ }
+
+ if (i == DB9_MAX_PORTS)
+ return;
+
+ db9 = db9_base[i];
+ db9_base[i] = NULL;
+
+ for (i = 0; i < min(db9_modes[db9->mode].n_pads, DB9_MAX_DEVICES); i++)
+ input_unregister_device(db9->dev[i]);
+ parport_unregister_device(db9->pd);
+ kfree(db9);
+}
+
+static struct parport_driver db9_parport_driver = {
+ .name = "db9",
+ .match_port = db9_attach,
+ .detach = db9_detach,
+ .devmodel = true,
+};
+
+static int __init db9_init(void)
+{
+ int i;
+ int have_dev = 0;
+
+ for (i = 0; i < DB9_MAX_PORTS; i++) {
+ if (db9_cfg[i].nargs == 0 || db9_cfg[i].args[DB9_ARG_PARPORT] < 0)
+ continue;
+
+ if (db9_cfg[i].nargs < 2) {
+ printk(KERN_ERR "db9.c: Device type must be specified.\n");
+ return -EINVAL;
+ }
+
+ have_dev = 1;
+ }
+
+ if (!have_dev)
+ return -ENODEV;
+
+ return parport_register_driver(&db9_parport_driver);
+}
+
+static void __exit db9_exit(void)
+{
+ parport_unregister_driver(&db9_parport_driver);
+}
+
+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 000000000..d62e73dd9
--- /dev/null
+++ b/drivers/input/joystick/gamecon.c
@@ -0,0 +1,1065 @@
+/*
+ * 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
+ * Raphael Assenat
+ */
+
+/*
+ * 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
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/parport.h>
+#include <linux/input.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("NES, SNES, N64, MultiSystem, PSX gamepad driver");
+MODULE_LICENSE("GPL");
+
+#define GC_MAX_PORTS 3
+#define GC_MAX_DEVICES 5
+
+struct gc_config {
+ int args[GC_MAX_DEVICES + 1];
+ unsigned int nargs;
+};
+
+static struct gc_config gc_cfg[GC_MAX_PORTS];
+
+module_param_array_named(map, gc_cfg[0].args, int, &gc_cfg[0].nargs, 0);
+MODULE_PARM_DESC(map, "Describes first set of devices (<parport#>,<pad1>,<pad2>,..<pad5>)");
+module_param_array_named(map2, gc_cfg[1].args, int, &gc_cfg[1].nargs, 0);
+MODULE_PARM_DESC(map2, "Describes second set of devices");
+module_param_array_named(map3, gc_cfg[2].args, int, &gc_cfg[2].nargs, 0);
+MODULE_PARM_DESC(map3, "Describes third set of devices");
+
+/* see also gs_psx_delay parameter in PSX support section */
+
+enum gc_type {
+ GC_NONE = 0,
+ GC_SNES,
+ GC_NES,
+ GC_NES4,
+ GC_MULTI,
+ GC_MULTI2,
+ GC_N64,
+ GC_PSX,
+ GC_DDR,
+ GC_SNESMOUSE,
+ GC_MAX
+};
+
+#define GC_REFRESH_TIME HZ/100
+
+struct gc_pad {
+ struct input_dev *dev;
+ enum gc_type type;
+ char phys[32];
+};
+
+struct gc {
+ struct pardevice *pd;
+ struct gc_pad pads[GC_MAX_DEVICES];
+ struct timer_list timer;
+ int pad_count[GC_MAX];
+ int used;
+ int parportno;
+ struct mutex mutex;
+};
+
+struct gc_subdev {
+ unsigned int idx;
+};
+
+static struct gc *gc_base[3];
+
+static const int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 };
+
+static const char *gc_names[] = {
+ NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick",
+ "Multisystem 2-button joystick", "N64 controller", "PSX controller",
+ "PSX DDR controller", "SNES mouse"
+};
+
+/*
+ * N64 support.
+ */
+
+static const unsigned char gc_n64_bytes[] = { 0, 1, 13, 15, 14, 12, 10, 11, 2, 3 };
+static const 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_STOP_LENGTH 5 /* Length of encoded stop bit */
+#define GC_N64_CMD_00 0x11111111UL
+#define GC_N64_CMD_01 0xd1111111UL
+#define GC_N64_CMD_03 0xdd111111UL
+#define GC_N64_CMD_1b 0xdd1dd111UL
+#define GC_N64_CMD_c0 0x111111ddUL
+#define GC_N64_CMD_80 0x1111111dUL
+#define GC_N64_STOP_BIT 0x1d /* Encoded stop bit */
+#define GC_N64_REQUEST_DATA GC_N64_CMD_01 /* the request data command */
+#define GC_N64_DELAY 133 /* delay between transmit request, and response ready (us) */
+#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 */
+
+/*
+ * Used for rumble code.
+ */
+
+/* Send encoded command */
+static void gc_n64_send_command(struct gc *gc, unsigned long cmd,
+ unsigned char target)
+{
+ struct parport *port = gc->pd->port;
+ int i;
+
+ for (i = 0; i < GC_N64_LENGTH; i++) {
+ unsigned char data = (cmd >> i) & 1 ? target : 0;
+ parport_write_data(port, GC_N64_POWER_W | data);
+ udelay(GC_N64_DWS);
+ }
+}
+
+/* Send stop bit */
+static void gc_n64_send_stop_bit(struct gc *gc, unsigned char target)
+{
+ struct parport *port = gc->pd->port;
+ int i;
+
+ for (i = 0; i < GC_N64_STOP_LENGTH; i++) {
+ unsigned char data = (GC_N64_STOP_BIT >> i) & 1 ? target : 0;
+ parport_write_data(port, GC_N64_POWER_W | data);
+ udelay(GC_N64_DWS);
+ }
+}
+
+/*
+ * 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);
+ gc_n64_send_command(gc, GC_N64_REQUEST_DATA, GC_N64_OUT);
+ gc_n64_send_stop_bit(gc, GC_N64_OUT);
+ 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);
+ udelay(2);
+ 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.
+ */
+
+}
+
+static void gc_n64_process_packet(struct gc *gc)
+{
+ unsigned char data[GC_N64_LENGTH];
+ struct input_dev *dev;
+ int i, j, s;
+ signed char x, y;
+
+ gc_n64_read_packet(gc, data);
+
+ for (i = 0; i < GC_MAX_DEVICES; i++) {
+
+ if (gc->pads[i].type != GC_N64)
+ continue;
+
+ dev = gc->pads[i].dev;
+ s = gc_status_bit[i];
+
+ if (s & ~(data[8] | data[9])) {
+
+ x = y = 0;
+
+ for (j = 0; j < 8; j++) {
+ if (data[23 - j] & s)
+ x |= 1 << j;
+ if (data[31 - j] & s)
+ y |= 1 << j;
+ }
+
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, -y);
+
+ input_report_abs(dev, ABS_HAT0X,
+ !(s & data[6]) - !(s & data[7]));
+ input_report_abs(dev, ABS_HAT0Y,
+ !(s & data[4]) - !(s & data[5]));
+
+ for (j = 0; j < 10; j++)
+ input_report_key(dev, gc_n64_btn[j],
+ s & data[gc_n64_bytes[j]]);
+
+ input_sync(dev);
+ }
+ }
+}
+
+static int gc_n64_play_effect(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ int i;
+ unsigned long flags;
+ struct gc *gc = input_get_drvdata(dev);
+ struct gc_subdev *sdev = data;
+ unsigned char target = 1 << sdev->idx; /* select desired pin */
+
+ if (effect->type == FF_RUMBLE) {
+ struct ff_rumble_effect *rumble = &effect->u.rumble;
+ unsigned int cmd =
+ rumble->strong_magnitude || rumble->weak_magnitude ?
+ GC_N64_CMD_01 : GC_N64_CMD_00;
+
+ local_irq_save(flags);
+
+ /* Init Rumble - 0x03, 0x80, 0x01, (34)0x80 */
+ gc_n64_send_command(gc, GC_N64_CMD_03, target);
+ gc_n64_send_command(gc, GC_N64_CMD_80, target);
+ gc_n64_send_command(gc, GC_N64_CMD_01, target);
+ for (i = 0; i < 32; i++)
+ gc_n64_send_command(gc, GC_N64_CMD_80, target);
+ gc_n64_send_stop_bit(gc, target);
+
+ udelay(GC_N64_DELAY);
+
+ /* Now start or stop it - 0x03, 0xc0, 0zx1b, (32)0x01/0x00 */
+ gc_n64_send_command(gc, GC_N64_CMD_03, target);
+ gc_n64_send_command(gc, GC_N64_CMD_c0, target);
+ gc_n64_send_command(gc, GC_N64_CMD_1b, target);
+ for (i = 0; i < 32; i++)
+ gc_n64_send_command(gc, cmd, target);
+ gc_n64_send_stop_bit(gc, target);
+
+ local_irq_restore(flags);
+
+ }
+
+ return 0;
+}
+
+static int gc_n64_init_ff(struct input_dev *dev, int i)
+{
+ struct gc_subdev *sdev;
+ int err;
+
+ sdev = kmalloc(sizeof(*sdev), GFP_KERNEL);
+ if (!sdev)
+ return -ENOMEM;
+
+ sdev->idx = i;
+
+ input_set_capability(dev, EV_FF, FF_RUMBLE);
+
+ err = input_ff_create_memless(dev, sdev, gc_n64_play_effect);
+ if (err) {
+ kfree(sdev);
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * 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_SNESMOUSE_LENGTH 32 /* The SNES mouse uses 32 bits, the first
+ 16 bits are equivalent to a gamepad */
+
+#define GC_NES_POWER 0xfc
+#define GC_NES_CLOCK 0x01
+#define GC_NES_LATCH 0x02
+
+static const unsigned char gc_nes_bytes[] = { 0, 1, 2, 3 };
+static const unsigned char gc_snes_bytes[] = { 8, 0, 2, 3, 9, 1, 10, 11 };
+static const 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);
+ }
+}
+
+static void gc_nes_process_packet(struct gc *gc)
+{
+ unsigned char data[GC_SNESMOUSE_LENGTH];
+ struct gc_pad *pad;
+ struct input_dev *dev;
+ int i, j, s, len;
+ char x_rel, y_rel;
+
+ len = gc->pad_count[GC_SNESMOUSE] ? GC_SNESMOUSE_LENGTH :
+ (gc->pad_count[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH);
+
+ gc_nes_read_packet(gc, len, data);
+
+ for (i = 0; i < GC_MAX_DEVICES; i++) {
+
+ pad = &gc->pads[i];
+ dev = pad->dev;
+ s = gc_status_bit[i];
+
+ switch (pad->type) {
+
+ case GC_NES:
+
+ input_report_abs(dev, ABS_X, !(s & data[6]) - !(s & data[7]));
+ input_report_abs(dev, ABS_Y, !(s & data[4]) - !(s & data[5]));
+
+ for (j = 0; j < 4; j++)
+ input_report_key(dev, gc_snes_btn[j],
+ s & data[gc_nes_bytes[j]]);
+ input_sync(dev);
+ break;
+
+ case GC_SNES:
+
+ input_report_abs(dev, ABS_X, !(s & data[6]) - !(s & data[7]));
+ input_report_abs(dev, ABS_Y, !(s & data[4]) - !(s & data[5]));
+
+ for (j = 0; j < 8; j++)
+ input_report_key(dev, gc_snes_btn[j],
+ s & data[gc_snes_bytes[j]]);
+ input_sync(dev);
+ break;
+
+ case GC_SNESMOUSE:
+ /*
+ * The 4 unused bits from SNES controllers appear
+ * to be ID bits so use them to make sure we are
+ * dealing with a mouse.
+ * gamepad is connected. This is important since
+ * my SNES gamepad sends 1's for bits 16-31, which
+ * cause the mouse pointer to quickly move to the
+ * upper left corner of the screen.
+ */
+ if (!(s & data[12]) && !(s & data[13]) &&
+ !(s & data[14]) && (s & data[15])) {
+ input_report_key(dev, BTN_LEFT, s & data[9]);
+ input_report_key(dev, BTN_RIGHT, s & data[8]);
+
+ x_rel = y_rel = 0;
+ for (j = 0; j < 7; j++) {
+ x_rel <<= 1;
+ if (data[25 + j] & s)
+ x_rel |= 1;
+
+ y_rel <<= 1;
+ if (data[17 + j] & s)
+ y_rel |= 1;
+ }
+
+ if (x_rel) {
+ if (data[24] & s)
+ x_rel = -x_rel;
+ input_report_rel(dev, REL_X, x_rel);
+ }
+
+ if (y_rel) {
+ if (data[16] & s)
+ y_rel = -y_rel;
+ input_report_rel(dev, REL_Y, y_rel);
+ }
+
+ input_sync(dev);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/*
+ * 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;
+ }
+}
+
+static void gc_multi_process_packet(struct gc *gc)
+{
+ unsigned char data[GC_MULTI2_LENGTH];
+ int data_len = gc->pad_count[GC_MULTI2] ? GC_MULTI2_LENGTH : GC_MULTI_LENGTH;
+ struct gc_pad *pad;
+ struct input_dev *dev;
+ int i, s;
+
+ gc_multi_read_packet(gc, data_len, data);
+
+ for (i = 0; i < GC_MAX_DEVICES; i++) {
+ pad = &gc->pads[i];
+ dev = pad->dev;
+ s = gc_status_bit[i];
+
+ switch (pad->type) {
+ case GC_MULTI2:
+ input_report_key(dev, BTN_THUMB, s & data[5]);
+ /* fall through */
+
+ case GC_MULTI:
+ input_report_abs(dev, ABS_X,
+ !(s & data[2]) - !(s & data[3]));
+ input_report_abs(dev, ABS_Y,
+ !(s & data[0]) - !(s & data[1]));
+ input_report_key(dev, BTN_TRIGGER, s & data[4]);
+ input_sync(dev);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/*
+ * PSX support
+ *
+ * See documentation at:
+ * http://www.geocities.co.jp/Playtown/2004/psx/ps_eng.txt
+ * http://www.gamesx.com/controldata/psxcont/psxcont.htm
+ *
+ */
+
+#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)");
+
+static const short gc_psx_abs[] = {
+ ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_HAT0X, ABS_HAT0Y
+};
+static const 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 const 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)
+{
+ struct parport *port = gc->pd->port;
+ int i, j, cmd, read;
+
+ memset(data, 0, GC_MAX_DEVICES);
+
+ for (i = 0; i < GC_PSX_LENGTH; i++, b >>= 1) {
+ cmd = (b & 1) ? GC_PSX_COMMAND : 0;
+ parport_write_data(port, cmd | GC_PSX_POWER);
+ udelay(gc_psx_delay);
+
+ read = parport_read_status(port) ^ 0x80;
+
+ for (j = 0; j < GC_MAX_DEVICES; j++) {
+ struct gc_pad *pad = &gc->pads[j];
+
+ if (pad->type == GC_PSX || pad->type == GC_DDR)
+ data[j] |= (read & gc_status_bit[j]) ? (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[GC_MAX_DEVICES][GC_PSX_BYTES],
+ unsigned char id[GC_MAX_DEVICES])
+{
+ int i, j, max_len = 0;
+ unsigned long flags;
+ unsigned char data2[GC_MAX_DEVICES];
+
+ /* Select pad */
+ parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER);
+ udelay(gc_psx_delay);
+ /* Deselect, begin command */
+ parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_POWER);
+ 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 */
+
+ /* Find the longest pad */
+ for (i = 0; i < GC_MAX_DEVICES; i++) {
+ struct gc_pad *pad = &gc->pads[i];
+
+ if ((pad->type == GC_PSX || pad->type == GC_DDR) &&
+ GC_PSX_LEN(id[i]) > max_len &&
+ GC_PSX_LEN(id[i]) <= GC_PSX_BYTES) {
+ max_len = GC_PSX_LEN(id[i]);
+ }
+ }
+
+ /* Read in all the data */
+ for (i = 0; i < max_len; i++) {
+ gc_psx_command(gc, 0, data2);
+ for (j = 0; j < GC_MAX_DEVICES; 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);
+
+ /* Set id's to the real value */
+ for (i = 0; i < GC_MAX_DEVICES; i++)
+ id[i] = GC_PSX_ID(id[i]);
+}
+
+static void gc_psx_report_one(struct gc_pad *pad, unsigned char psx_type,
+ unsigned char *data)
+{
+ struct input_dev *dev = pad->dev;
+ int i;
+
+ switch (psx_type) {
+
+ case GC_PSX_RUMBLE:
+
+ input_report_key(dev, BTN_THUMBL, ~data[0] & 0x04);
+ input_report_key(dev, BTN_THUMBR, ~data[0] & 0x02);
+ /* fall through */
+
+ case GC_PSX_NEGCON:
+ case GC_PSX_ANALOG:
+
+ if (pad->type == GC_DDR) {
+ for (i = 0; i < 4; i++)
+ input_report_key(dev, gc_psx_ddr_btn[i],
+ ~data[0] & (0x10 << i));
+ } else {
+ for (i = 0; i < 4; i++)
+ input_report_abs(dev, gc_psx_abs[i + 2],
+ data[i + 2]);
+
+ input_report_abs(dev, ABS_X,
+ !!(data[0] & 0x80) * 128 + !(data[0] & 0x20) * 127);
+ input_report_abs(dev, ABS_Y,
+ !!(data[0] & 0x10) * 128 + !(data[0] & 0x40) * 127);
+ }
+
+ for (i = 0; i < 8; i++)
+ input_report_key(dev, gc_psx_btn[i], ~data[1] & (1 << i));
+
+ input_report_key(dev, BTN_START, ~data[0] & 0x08);
+ input_report_key(dev, BTN_SELECT, ~data[0] & 0x01);
+
+ input_sync(dev);
+
+ break;
+
+ case GC_PSX_NORMAL:
+
+ if (pad->type == GC_DDR) {
+ for (i = 0; i < 4; i++)
+ input_report_key(dev, gc_psx_ddr_btn[i],
+ ~data[0] & (0x10 << i));
+ } else {
+ input_report_abs(dev, ABS_X,
+ !!(data[0] & 0x80) * 128 + !(data[0] & 0x20) * 127);
+ input_report_abs(dev, ABS_Y,
+ !!(data[0] & 0x10) * 128 + !(data[0] & 0x40) * 127);
+
+ /*
+ * For some reason if the extra axes are left unset
+ * they drift.
+ * for (i = 0; i < 4; i++)
+ input_report_abs(dev, gc_psx_abs[i + 2], 128);
+ * This needs to be debugged properly,
+ * maybe fuzz processing needs to be done
+ * in input_sync()
+ * --vojtech
+ */
+ }
+
+ for (i = 0; i < 8; i++)
+ input_report_key(dev, gc_psx_btn[i], ~data[1] & (1 << i));
+
+ input_report_key(dev, BTN_START, ~data[0] & 0x08);
+ input_report_key(dev, BTN_SELECT, ~data[0] & 0x01);
+
+ input_sync(dev);
+
+ break;
+
+ default: /* not a pad, ignore */
+ break;
+ }
+}
+
+static void gc_psx_process_packet(struct gc *gc)
+{
+ unsigned char data[GC_MAX_DEVICES][GC_PSX_BYTES];
+ unsigned char id[GC_MAX_DEVICES];
+ struct gc_pad *pad;
+ int i;
+
+ gc_psx_read_packet(gc, data, id);
+
+ for (i = 0; i < GC_MAX_DEVICES; i++) {
+ pad = &gc->pads[i];
+ if (pad->type == GC_PSX || pad->type == GC_DDR)
+ gc_psx_report_one(pad, id[i], data[i]);
+ }
+}
+
+/*
+ * gc_timer() initiates reads of console pads data.
+ */
+
+static void gc_timer(struct timer_list *t)
+{
+ struct gc *gc = from_timer(gc, t, timer);
+
+/*
+ * N64 pads - must be read first, any read confuses them for 200 us
+ */
+
+ if (gc->pad_count[GC_N64])
+ gc_n64_process_packet(gc);
+
+/*
+ * NES and SNES pads or mouse
+ */
+
+ if (gc->pad_count[GC_NES] ||
+ gc->pad_count[GC_SNES] ||
+ gc->pad_count[GC_SNESMOUSE]) {
+ gc_nes_process_packet(gc);
+ }
+
+/*
+ * Multi and Multi2 joysticks
+ */
+
+ if (gc->pad_count[GC_MULTI] || gc->pad_count[GC_MULTI2])
+ gc_multi_process_packet(gc);
+
+/*
+ * PSX controllers
+ */
+
+ if (gc->pad_count[GC_PSX] || gc->pad_count[GC_DDR])
+ gc_psx_process_packet(gc);
+
+ mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME);
+}
+
+static int gc_open(struct input_dev *dev)
+{
+ struct gc *gc = input_get_drvdata(dev);
+ int err;
+
+ err = mutex_lock_interruptible(&gc->mutex);
+ if (err)
+ return err;
+
+ if (!gc->used++) {
+ parport_claim(gc->pd);
+ parport_write_control(gc->pd->port, 0x04);
+ mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME);
+ }
+
+ mutex_unlock(&gc->mutex);
+ return 0;
+}
+
+static void gc_close(struct input_dev *dev)
+{
+ struct gc *gc = input_get_drvdata(dev);
+
+ mutex_lock(&gc->mutex);
+ if (!--gc->used) {
+ del_timer_sync(&gc->timer);
+ parport_write_control(gc->pd->port, 0x00);
+ parport_release(gc->pd);
+ }
+ mutex_unlock(&gc->mutex);
+}
+
+static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
+{
+ struct gc_pad *pad = &gc->pads[idx];
+ struct input_dev *input_dev;
+ int i;
+ int err;
+
+ if (pad_type < 1 || pad_type >= GC_MAX) {
+ pr_err("Pad type %d unknown\n", pad_type);
+ return -EINVAL;
+ }
+
+ pad->dev = input_dev = input_allocate_device();
+ if (!input_dev) {
+ pr_err("Not enough memory for input device\n");
+ return -ENOMEM;
+ }
+
+ pad->type = pad_type;
+
+ snprintf(pad->phys, sizeof(pad->phys),
+ "%s/input%d", gc->pd->port->name, idx);
+
+ input_dev->name = gc_names[pad_type];
+ input_dev->phys = pad->phys;
+ input_dev->id.bustype = BUS_PARPORT;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = pad_type;
+ input_dev->id.version = 0x0100;
+
+ input_set_drvdata(input_dev, gc);
+
+ input_dev->open = gc_open;
+ input_dev->close = gc_close;
+
+ if (pad_type != GC_SNESMOUSE) {
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ for (i = 0; i < 2; i++)
+ input_set_abs_params(input_dev, ABS_X + i, -1, 1, 0, 0);
+ } else
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+
+ gc->pad_count[pad_type]++;
+
+ switch (pad_type) {
+
+ case GC_N64:
+ for (i = 0; i < 10; i++)
+ input_set_capability(input_dev, EV_KEY, gc_n64_btn[i]);
+
+ for (i = 0; i < 2; i++) {
+ input_set_abs_params(input_dev, ABS_X + i, -127, 126, 0, 2);
+ input_set_abs_params(input_dev, ABS_HAT0X + i, -1, 1, 0, 0);
+ }
+
+ err = gc_n64_init_ff(input_dev, idx);
+ if (err) {
+ pr_warn("Failed to initiate rumble for N64 device %d\n",
+ idx);
+ goto err_free_dev;
+ }
+
+ break;
+
+ case GC_SNESMOUSE:
+ input_set_capability(input_dev, EV_KEY, BTN_LEFT);
+ input_set_capability(input_dev, EV_KEY, BTN_RIGHT);
+ input_set_capability(input_dev, EV_REL, REL_X);
+ input_set_capability(input_dev, EV_REL, REL_Y);
+ break;
+
+ case GC_SNES:
+ for (i = 4; i < 8; i++)
+ input_set_capability(input_dev, EV_KEY, gc_snes_btn[i]);
+ /* fall through */
+ case GC_NES:
+ for (i = 0; i < 4; i++)
+ input_set_capability(input_dev, EV_KEY, gc_snes_btn[i]);
+ break;
+
+ case GC_MULTI2:
+ input_set_capability(input_dev, EV_KEY, BTN_THUMB);
+ /* fall through */
+ case GC_MULTI:
+ input_set_capability(input_dev, EV_KEY, BTN_TRIGGER);
+ /* fall through */
+ break;
+
+ case GC_PSX:
+ for (i = 0; i < 6; i++)
+ input_set_abs_params(input_dev,
+ gc_psx_abs[i], 4, 252, 0, 2);
+ for (i = 0; i < 12; i++)
+ input_set_capability(input_dev, EV_KEY, gc_psx_btn[i]);
+ break;
+
+ break;
+
+ case GC_DDR:
+ for (i = 0; i < 4; i++)
+ input_set_capability(input_dev, EV_KEY,
+ gc_psx_ddr_btn[i]);
+ for (i = 0; i < 12; i++)
+ input_set_capability(input_dev, EV_KEY, gc_psx_btn[i]);
+
+ break;
+ }
+
+ err = input_register_device(pad->dev);
+ if (err)
+ goto err_free_dev;
+
+ return 0;
+
+err_free_dev:
+ input_free_device(pad->dev);
+ pad->dev = NULL;
+ return err;
+}
+
+static void gc_attach(struct parport *pp)
+{
+ struct gc *gc;
+ struct pardevice *pd;
+ int i, port_idx;
+ int count = 0;
+ int *pads, n_pads;
+ struct pardev_cb gc_parport_cb;
+
+ for (port_idx = 0; port_idx < GC_MAX_PORTS; port_idx++) {
+ if (gc_cfg[port_idx].nargs == 0 || gc_cfg[port_idx].args[0] < 0)
+ continue;
+
+ if (gc_cfg[port_idx].args[0] == pp->number)
+ break;
+ }
+
+ if (port_idx == GC_MAX_PORTS) {
+ pr_debug("Not using parport%d.\n", pp->number);
+ return;
+ }
+ pads = gc_cfg[port_idx].args + 1;
+ n_pads = gc_cfg[port_idx].nargs - 1;
+
+ memset(&gc_parport_cb, 0, sizeof(gc_parport_cb));
+ gc_parport_cb.flags = PARPORT_FLAG_EXCL;
+
+ pd = parport_register_dev_model(pp, "gamecon", &gc_parport_cb,
+ port_idx);
+ if (!pd) {
+ pr_err("parport busy already - lp.o loaded?\n");
+ return;
+ }
+
+ gc = kzalloc(sizeof(struct gc), GFP_KERNEL);
+ if (!gc) {
+ pr_err("Not enough memory\n");
+ goto err_unreg_pardev;
+ }
+
+ mutex_init(&gc->mutex);
+ gc->pd = pd;
+ gc->parportno = pp->number;
+ timer_setup(&gc->timer, gc_timer, 0);
+
+ for (i = 0; i < n_pads && i < GC_MAX_DEVICES; i++) {
+ if (!pads[i])
+ continue;
+
+ if (gc_setup_pad(gc, i, pads[i]))
+ goto err_unreg_devs;
+
+ count++;
+ }
+
+ if (count == 0) {
+ pr_err("No valid devices specified\n");
+ goto err_free_gc;
+ }
+
+ gc_base[port_idx] = gc;
+ return;
+
+ err_unreg_devs:
+ while (--i >= 0)
+ if (gc->pads[i].dev)
+ input_unregister_device(gc->pads[i].dev);
+ err_free_gc:
+ kfree(gc);
+ err_unreg_pardev:
+ parport_unregister_device(pd);
+}
+
+static void gc_detach(struct parport *port)
+{
+ int i;
+ struct gc *gc;
+
+ for (i = 0; i < GC_MAX_PORTS; i++) {
+ if (gc_base[i] && gc_base[i]->parportno == port->number)
+ break;
+ }
+
+ if (i == GC_MAX_PORTS)
+ return;
+
+ gc = gc_base[i];
+ gc_base[i] = NULL;
+
+ for (i = 0; i < GC_MAX_DEVICES; i++)
+ if (gc->pads[i].dev)
+ input_unregister_device(gc->pads[i].dev);
+ parport_unregister_device(gc->pd);
+ kfree(gc);
+}
+
+static struct parport_driver gc_parport_driver = {
+ .name = "gamecon",
+ .match_port = gc_attach,
+ .detach = gc_detach,
+ .devmodel = true,
+};
+
+static int __init gc_init(void)
+{
+ int i;
+ int have_dev = 0;
+
+ for (i = 0; i < GC_MAX_PORTS; i++) {
+ if (gc_cfg[i].nargs == 0 || gc_cfg[i].args[0] < 0)
+ continue;
+
+ if (gc_cfg[i].nargs < 2) {
+ pr_err("at least one device must be specified\n");
+ return -EINVAL;
+ }
+
+ have_dev = 1;
+ }
+
+ if (!have_dev)
+ return -ENODEV;
+
+ return parport_register_driver(&gc_parport_driver);
+}
+
+static void __exit gc_exit(void)
+{
+ parport_unregister_driver(&gc_parport_driver);
+}
+
+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 000000000..50a60065a
--- /dev/null
+++ b/drivers/input/joystick/gf2k.c
@@ -0,0 +1,371 @@
+/*
+ * 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
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/gameport.h>
+#include <linux/jiffies.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 = input_get_drvdata(dev);
+
+ gameport_start_polling(gf2k->gameport);
+ return 0;
+}
+
+static void gf2k_close(struct input_dev *dev)
+{
+ struct gf2k *gf2k = input_get_drvdata(dev);
+
+ 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;
+ struct input_dev *input_dev;
+ unsigned char data[GF2K_LENGTH];
+ int i, err;
+
+ gf2k = kzalloc(sizeof(struct gf2k), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!gf2k || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ gf2k->gameport = gameport;
+ gf2k->dev = input_dev;
+
+ 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);
+
+ snprintf(gf2k->phys, sizeof(gf2k->phys), "%s/input0", gameport->phys);
+
+ gf2k->length = gf2k_lens[gf2k->id];
+
+ input_dev->name = gf2k_names[gf2k->id];
+ input_dev->phys = gf2k->phys;
+ input_dev->id.bustype = BUS_GAMEPORT;
+ input_dev->id.vendor = GAMEPORT_ID_VENDOR_GENIUS;
+ input_dev->id.product = gf2k->id;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &gameport->dev;
+
+ input_set_drvdata(input_dev, gf2k);
+
+ input_dev->open = gf2k_open;
+ input_dev->close = gf2k_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ for (i = 0; i < gf2k_axes[gf2k->id]; i++)
+ set_bit(gf2k_abs[i], input_dev->absbit);
+
+ for (i = 0; i < gf2k_hats[gf2k->id]; i++)
+ input_set_abs_params(input_dev, ABS_HAT0X + i, -1, 1, 0, 0);
+
+ for (i = 0; i < gf2k_joys[gf2k->id]; i++)
+ set_bit(gf2k_btn_joy[i], input_dev->keybit);
+
+ for (i = 0; i < gf2k_pads[gf2k->id]; i++)
+ set_bit(gf2k_btn_pad[i], input_dev->keybit);
+
+ gf2k_read_packet(gameport, gf2k->length, data);
+ gf2k_read(gf2k, data);
+
+ for (i = 0; i < gf2k_axes[gf2k->id]; i++) {
+ int max = i < 2 ?
+ input_abs_get_val(input_dev, gf2k_abs[i]) * 2 :
+ input_abs_get_val(input_dev, gf2k_abs[0]) +
+ input_abs_get_val(input_dev, gf2k_abs[1]);
+ int flat = i < 2 ? 24 : 0;
+
+ input_set_abs_params(input_dev, gf2k_abs[i],
+ 32, max - 32, 8, flat);
+ }
+
+ err = input_register_device(gf2k->dev);
+ if (err)
+ goto fail2;
+
+ return 0;
+
+ fail2: gameport_close(gameport);
+ fail1: gameport_set_drvdata(gameport, NULL);
+ input_free_device(input_dev);
+ 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,
+};
+
+module_gameport_driver(gf2k_drv);
diff --git a/drivers/input/joystick/grip.c b/drivers/input/joystick/grip.c
new file mode 100644
index 000000000..e10395ba6
--- /dev/null
+++ b/drivers/input/joystick/grip.c
@@ -0,0 +1,422 @@
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/jiffies.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];
+ if (!dev)
+ continue;
+
+ 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 = input_get_drvdata(dev);
+
+ gameport_start_polling(grip->gameport);
+ return 0;
+}
+
+static void grip_close(struct input_dev *dev)
+{
+ struct grip *grip = input_get_drvdata(dev);
+
+ gameport_stop_polling(grip->gameport);
+}
+
+static int grip_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ struct grip *grip;
+ struct input_dev *input_dev;
+ unsigned int data[GRIP_LENGTH_XT];
+ int i, j, t;
+ int err;
+
+ if (!(grip = kzalloc(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])
+ continue;
+
+ grip->dev[i] = input_dev = input_allocate_device();
+ if (!input_dev) {
+ err = -ENOMEM;
+ goto fail3;
+ }
+
+ snprintf(grip->phys[i], sizeof(grip->phys[i]),
+ "%s/input%d", gameport->phys, i);
+
+ input_dev->name = grip_name[grip->mode[i]];
+ input_dev->phys = grip->phys[i];
+ input_dev->id.bustype = BUS_GAMEPORT;
+ input_dev->id.vendor = GAMEPORT_ID_VENDOR_GRAVIS;
+ input_dev->id.product = grip->mode[i];
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &gameport->dev;
+
+ input_set_drvdata(input_dev, grip);
+
+ input_dev->open = grip_open;
+ input_dev->close = grip_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(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(input_dev, t, 14, 52, 1, 2);
+ else if (j < grip_anx[grip->mode[i]])
+ input_set_abs_params(input_dev, t, 3, 57, 1, 0);
+ else
+ input_set_abs_params(input_dev, t, -1, 1, 0, 0);
+ }
+
+ for (j = 0; (t = grip_btn[grip->mode[i]][j]) >= 0; j++)
+ if (t > 0)
+ set_bit(t, input_dev->keybit);
+
+ err = input_register_device(grip->dev[i]);
+ if (err)
+ goto fail4;
+ }
+
+ return 0;
+
+ fail4: input_free_device(grip->dev[i]);
+ fail3: while (--i >= 0)
+ if (grip->dev[i])
+ input_unregister_device(grip->dev[i]);
+ 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->dev[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",
+ .owner = THIS_MODULE,
+ },
+ .description = DRIVER_DESC,
+ .connect = grip_connect,
+ .disconnect = grip_disconnect,
+};
+
+module_gameport_driver(grip_drv);
diff --git a/drivers/input/joystick/grip_mp.c b/drivers/input/joystick/grip_mp.c
new file mode 100644
index 000000000..573191dd7
--- /dev/null
+++ b/drivers/input/joystick/grip_mp.c
@@ -0,0 +1,689 @@
+/*
+ * 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/slab.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/jiffies.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
+
+#define GRIP_MAX_PORTS 4
+/*
+ * Grip multiport state
+ */
+
+struct grip_port {
+ struct input_dev *dev;
+ int mode;
+ int registered;
+
+ /* individual gamepad states */
+ int buttons;
+ int xaxes;
+ int yaxes;
+ int dirty; /* has the state been updated? */
+};
+
+struct grip_mp {
+ struct gameport *gameport;
+ struct grip_port *port[GRIP_MAX_PORTS];
+ int reads;
+ int bads;
+};
+
+/*
+ * 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 const int grip_btn_gp[] = { BTN_TR, BTN_TL, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, -1 };
+static const int grip_btn_c64[] = { BTN_JOYSTICK, -1 };
+
+static const int grip_abs_gp[] = { ABS_X, ABS_Y, -1 };
+static const int grip_abs_c64[] = { ABS_X, ABS_Y, -1 };
+
+static const int *grip_abs[] = { NULL, NULL, grip_abs_gp, grip_abs_c64 };
+static const int *grip_btn[] = { NULL, NULL, grip_btn_gp, grip_btn_c64 };
+
+static const 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 const int axis_map[] = { 5, 9, 1, 5, 6, 10, 2, 6, 4, 8, 0, 4, 5, 9, 1, 5 };
+
+static int 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;
+ int flags, tries = 0, bads = 0;
+
+ for (i = 0; i < ARRAY_SIZE(init_seq); 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)
+{
+ struct grip_port *port;
+ u32 packet;
+ int joytype = 0;
+ int slot;
+
+ /* 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;
+
+ port = grip->port[slot];
+
+ /*
+ * 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 (port->registered) {
+ printk(KERN_INFO "grip_mp: removing %s, slot %d\n",
+ grip_name[port->mode], slot);
+ input_unregister_device(port->dev);
+ port->registered = 0;
+ }
+ dbg("Reset: grip multiport slot %d\n", slot);
+ port->mode = 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 */
+ port->buttons = (~packet) & 0xff;
+ port->yaxes = ((axis_map[dir] >> 2) & 3) - 1;
+ port->xaxes = (axis_map[dir] & 3) - 1;
+ port->dirty = 1;
+
+ if (port->mode == GRIP_MODE_RESET)
+ flags |= IO_SLOT_CHANGE;
+
+ port->mode = GRIP_MODE_GP;
+
+ if (!port->registered) {
+ dbg("New Grip pad in multiport slot %d.\n", slot);
+ if (register_slot(slot, grip)) {
+ port->mode = GRIP_MODE_RESET;
+ port->dirty = 0;
+ }
+ }
+ 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->port[slot]->mode == GRIP_MODE_RESET)
+ invalid = 1;
+ if (grip->port[slot]->mode != 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 grip_port *port = grip->port[slot];
+ int i;
+
+ /* Store button states with linux input driver */
+
+ for (i = 0; i < 8; i++)
+ input_report_key(port->dev, grip_btn_gp[i], (port->buttons >> i) & 1);
+
+ /* Store axis states with linux driver */
+
+ input_report_abs(port->dev, ABS_X, port->xaxes);
+ input_report_abs(port->dev, ABS_Y, port->yaxes);
+
+ /* Tell the receiver of the events to process them */
+
+ input_sync(port->dev);
+
+ port->dirty = 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->port[i]->dirty)
+ report_slot(grip, i);
+}
+
+/*
+ * Called when a joystick device file is opened
+ */
+
+static int grip_open(struct input_dev *dev)
+{
+ struct grip_mp *grip = input_get_drvdata(dev);
+
+ 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 = input_get_drvdata(dev);
+
+ gameport_stop_polling(grip->gameport);
+}
+
+/*
+ * Tell the linux input layer about a newly plugged-in gamepad.
+ */
+
+static int register_slot(int slot, struct grip_mp *grip)
+{
+ struct grip_port *port = grip->port[slot];
+ struct input_dev *input_dev;
+ int j, t;
+ int err;
+
+ port->dev = input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ input_dev->name = grip_name[port->mode];
+ input_dev->id.bustype = BUS_GAMEPORT;
+ input_dev->id.vendor = GAMEPORT_ID_VENDOR_GRAVIS;
+ input_dev->id.product = 0x0100 + port->mode;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &grip->gameport->dev;
+
+ input_set_drvdata(input_dev, grip);
+
+ input_dev->open = grip_open;
+ input_dev->close = grip_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ for (j = 0; (t = grip_abs[port->mode][j]) >= 0; j++)
+ input_set_abs_params(input_dev, t, -1, 1, 0, 0);
+
+ for (j = 0; (t = grip_btn[port->mode][j]) >= 0; j++)
+ if (t > 0)
+ set_bit(t, input_dev->keybit);
+
+ err = input_register_device(port->dev);
+ if (err) {
+ input_free_device(port->dev);
+ return err;
+ }
+
+ port->registered = 1;
+
+ if (port->dirty) /* report initial state, if any */
+ report_slot(grip, slot);
+
+ return 0;
+}
+
+static int grip_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ struct grip_mp *grip;
+ int err;
+
+ if (!(grip = kzalloc(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->port[0]->mode && !grip->port[1]->mode && !grip->port[2]->mode && !grip->port[3]->mode) {
+ /* 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->port[i]->registered)
+ input_unregister_device(grip->port[i]->dev);
+ 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,
+};
+
+module_gameport_driver(grip_drv);
diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c
new file mode 100644
index 000000000..43ff817d8
--- /dev/null
+++ b/drivers/input/joystick/guillemot.c
@@ -0,0 +1,279 @@
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/jiffies.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 = input_get_drvdata(dev);
+
+ 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 = input_get_drvdata(dev);
+
+ 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;
+ struct input_dev *input_dev;
+ u8 data[GUILLEMOT_MAX_LENGTH];
+ int i, t;
+ int err;
+
+ guillemot = kzalloc(sizeof(struct guillemot), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!guillemot || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ guillemot->gameport = gameport;
+ guillemot->dev = input_dev;
+
+ 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);
+
+ snprintf(guillemot->phys, sizeof(guillemot->phys), "%s/input0", gameport->phys);
+ guillemot->type = guillemot_type + i;
+
+ input_dev->name = guillemot_type[i].name;
+ input_dev->phys = guillemot->phys;
+ input_dev->id.bustype = BUS_GAMEPORT;
+ input_dev->id.vendor = GAMEPORT_ID_VENDOR_GUILLEMOT;
+ input_dev->id.product = guillemot_type[i].id;
+ input_dev->id.version = (int)data[14] << 8 | data[15];
+ input_dev->dev.parent = &gameport->dev;
+
+ input_set_drvdata(input_dev, guillemot);
+
+ input_dev->open = guillemot_open;
+ input_dev->close = guillemot_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ for (i = 0; (t = guillemot->type->abs[i]) >= 0; i++)
+ input_set_abs_params(input_dev, t, 0, 255, 0, 0);
+
+ if (guillemot->type->hat) {
+ input_set_abs_params(input_dev, ABS_HAT0X, -1, 1, 0, 0);
+ input_set_abs_params(input_dev, ABS_HAT0Y, -1, 1, 0, 0);
+ }
+
+ for (i = 0; (t = guillemot->type->btn[i]) >= 0; i++)
+ set_bit(t, input_dev->keybit);
+
+ err = input_register_device(guillemot->dev);
+ if (err)
+ goto fail2;
+
+ return 0;
+
+fail2: gameport_close(gameport);
+fail1: gameport_set_drvdata(gameport, NULL);
+ input_free_device(input_dev);
+ 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,
+};
+
+module_gameport_driver(guillemot_drv);
diff --git a/drivers/input/joystick/iforce/Kconfig b/drivers/input/joystick/iforce/Kconfig
new file mode 100644
index 000000000..ab4dbcbcb
--- /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/joydev/joystick.rst>
+ and <file:Documentation/input/ff.rst>.
+
diff --git a/drivers/input/joystick/iforce/Makefile b/drivers/input/joystick/iforce/Makefile
new file mode 100644
index 000000000..bc5bda22f
--- /dev/null
+++ b/drivers/input/joystick/iforce/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the I-Force driver
+#
+# By Johann Deneux <johann.deneux@gmail.com>
+#
+
+obj-$(CONFIG_JOYSTICK_IFORCE) += iforce.o
+
+iforce-y := iforce-ff.o iforce-main.o iforce-packets.o
+iforce-$(CONFIG_JOYSTICK_IFORCE_232) += iforce-serio.o
+iforce-$(CONFIG_JOYSTICK_IFORCE_USB) += iforce-usb.o
diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c
new file mode 100644
index 000000000..3536d5f5a
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-ff.c
@@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.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
+ */
+
+#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) {
+ mutex_lock(&iforce->mem_mutex);
+ if (allocate_resource(&(iforce->device_memory), mod_chunk, 2,
+ iforce->device_memory.start, iforce->device_memory.end, 2L,
+ NULL, NULL)) {
+ mutex_unlock(&iforce->mem_mutex);
+ return -ENOSPC;
+ }
+ mutex_unlock(&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(iforce, "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) {
+ mutex_lock(&iforce->mem_mutex);
+ if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c,
+ iforce->device_memory.start, iforce->device_memory.end, 2L,
+ NULL, NULL)) {
+ mutex_unlock(&iforce->mem_mutex);
+ return -ENOSPC;
+ }
+ mutex_unlock(&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) {
+ mutex_lock(&iforce->mem_mutex);
+ if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e,
+ iforce->device_memory.start, iforce->device_memory.end, 2L,
+ NULL, NULL)) {
+ mutex_unlock(&iforce->mem_mutex);
+ return -ENOSPC;
+ }
+ mutex_unlock(&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) {
+ mutex_lock(&iforce->mem_mutex);
+ if (allocate_resource(&(iforce->device_memory), mod_chunk, 8,
+ iforce->device_memory.start, iforce->device_memory.end, 2L,
+ NULL, NULL)) {
+ mutex_unlock(&iforce->mem_mutex);
+ return -ENOSPC;
+ }
+ mutex_unlock(&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(iforce, "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 *old,
+ struct ff_effect *new)
+{
+ int ret = 0;
+ int i;
+
+ if (new->type != FF_SPRING && new->type != FF_FRICTION) {
+ dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
+ __func__);
+ return 0;
+ }
+
+ 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 *old,
+ struct ff_effect *effect)
+{
+ if (effect->type != FF_CONSTANT) {
+ dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
+ __func__);
+ return 0;
+ }
+
+ 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 *old,
+ struct ff_effect *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 1;
+ 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 1;
+ break;
+
+ default:
+ dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
+ __func__);
+ }
+
+ return 0;
+}
+
+/*
+ * 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 *old,
+ struct ff_effect *new)
+{
+ if (new->type != FF_PERIODIC) {
+ dev_warn(&iforce->dev->dev, "bad effect type in %s\n",
+ __func__);
+ return 0;
+ }
+ 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 ff_effect *old, struct ff_effect *new)
+{
+ 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 1;
+
+ return 0;
+}
+/*
+ * 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, struct ff_effect *old)
+{
+ 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 (!old || need_period_modifier(iforce, old, effect)) {
+ param1_err = make_period_modifier(iforce, mod1_chunk,
+ old != NULL,
+ 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 (!old || need_envelope_modifier(iforce, old, effect)) {
+ param2_err = make_envelope_modifier(iforce, mod2_chunk,
+ old !=NULL,
+ 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 (!old || need_core(old, 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, struct ff_effect *old)
+{
+ 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 (!old || need_magnitude_modifier(iforce, old, effect)) {
+ param1_err = make_magnitude_modifier(iforce, mod1_chunk,
+ old != NULL,
+ effect->u.constant.level);
+ if (param1_err)
+ return param1_err;
+ set_bit(FF_MOD1_IS_USED, core_effect->flags);
+ }
+
+ if (!old || need_envelope_modifier(iforce, old, effect)) {
+ param2_err = make_envelope_modifier(iforce, mod2_chunk,
+ old != NULL,
+ 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 (!old || need_core(old, 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, struct ff_effect *old)
+{
+ 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 (!old || need_condition_modifier(iforce, old, effect)) {
+ param_err = make_condition_modifier(iforce, mod1_chunk,
+ old != NULL,
+ 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,
+ old != NULL,
+ 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 (!old || need_core(old, 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 000000000..58d5cfe46
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-main.c
@@ -0,0 +1,477 @@
+/*
+ * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.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
+ */
+
+#include "iforce.h"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.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, BTN_DEAD, -1 };
+
+static signed short btn_joystick_avb[] =
+{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE,
+ BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_DEAD, -1 };
+
+static signed short btn_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_joystick_rudder[] =
+{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, 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_joystick_avb, abs_avb_pegasus, ff_iforce },
+ { 0x05ef, 0x8884, "AVB Mag Turbo Force", btn_wheel, abs_wheel, ff_iforce },
+ { 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //?
+ { 0x061c, 0xc0a4, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce }, //?
+ { 0x061c, 0xc084, "ACT LABS Force RS", btn_wheel, abs_wheel, ff_iforce },
+ { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback", btn_wheel, abs_wheel, ff_iforce }, //?
+ { 0x06f8, 0x0001, "Guillemot Jet Leader Force Feedback", btn_joystick, abs_joystick_rudder, ff_iforce },
+ { 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel", btn_wheel, abs_wheel, ff_iforce }, //?
+ { 0x06f8, 0xa302, "Guillemot Jet Leader 3D", btn_joystick, abs_joystick, ff_iforce }, //?
+ { 0x06d6, 0x29bc, "Trust Force Feedback Race Master", btn_wheel, abs_wheel, ff_iforce },
+ { 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]", btn_joystick, abs_joystick, ff_iforce }
+};
+
+static int iforce_playback(struct input_dev *dev, int effect_id, int value)
+{
+ struct iforce *iforce = input_get_drvdata(dev);
+ struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id];
+
+ if (value > 0)
+ set_bit(FF_CORE_SHOULD_PLAY, core_effect->flags);
+ else
+ clear_bit(FF_CORE_SHOULD_PLAY, core_effect->flags);
+
+ iforce_control_playback(iforce, effect_id, value);
+ return 0;
+}
+
+static void iforce_set_gain(struct input_dev *dev, u16 gain)
+{
+ struct iforce *iforce = input_get_drvdata(dev);
+ unsigned char data[3];
+
+ data[0] = gain >> 9;
+ iforce_send_packet(iforce, FF_CMD_GAIN, data);
+}
+
+static void iforce_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+ struct iforce *iforce = input_get_drvdata(dev);
+ unsigned char data[3];
+
+ data[0] = 0x03;
+ data[1] = magnitude >> 9;
+ iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
+
+ data[0] = 0x04;
+ data[1] = 0x01;
+ iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
+}
+
+/*
+ * 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 ff_effect *old)
+{
+ struct iforce *iforce = input_get_drvdata(dev);
+ struct iforce_core_effect *core_effect = &iforce->core_effects[effect->id];
+ int ret;
+
+ if (__test_and_set_bit(FF_CORE_IS_USED, core_effect->flags)) {
+ /* Check the effect is not already being updated */
+ if (test_bit(FF_CORE_UPDATE, core_effect->flags))
+ return -EAGAIN;
+ }
+
+/*
+ * Upload the effect
+ */
+ switch (effect->type) {
+
+ case FF_PERIODIC:
+ ret = iforce_upload_periodic(iforce, effect, old);
+ break;
+
+ case FF_CONSTANT:
+ ret = iforce_upload_constant(iforce, effect, old);
+ break;
+
+ case FF_SPRING:
+ case FF_DAMPER:
+ ret = iforce_upload_condition(iforce, effect, old);
+ 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, core_effect->flags);
+ }
+ 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 = input_get_drvdata(dev);
+ struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id];
+ int err = 0;
+
+ if (test_bit(FF_MOD1_IS_USED, core_effect->flags))
+ err = release_resource(&core_effect->mod1_chunk);
+
+ if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags))
+ err = release_resource(&core_effect->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 = input_get_drvdata(dev);
+
+ 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
+ }
+
+ if (test_bit(EV_FF, dev->evbit)) {
+ /* Enable force feedback */
+ iforce_send_packet(iforce, FF_CMD_ENABLE, "\004");
+ }
+
+ return 0;
+}
+
+static void iforce_close(struct input_dev *dev)
+{
+ struct iforce *iforce = input_get_drvdata(dev);
+ int i;
+
+ if (test_bit(EV_FF, dev->evbit)) {
+ /* Check: no effects should be present in memory */
+ for (i = 0; i < dev->ff->max_effects; i++) {
+ if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags)) {
+ dev_warn(&dev->dev,
+ "%s: Device still owns effects\n",
+ __func__);
+ break;
+ }
+ }
+
+ /* Disable force feedback playback */
+ iforce_send_packet(iforce, FF_CMD_ENABLE, "\001");
+ /* Wait for the command to complete */
+ wait_event_interruptible(iforce->wait,
+ !test_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags));
+ }
+
+ switch (iforce->bus) {
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+ case IFORCE_USB:
+ usb_kill_urb(iforce->irq);
+ usb_kill_urb(iforce->out);
+ usb_kill_urb(iforce->ctrl);
+ 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)
+{
+ struct input_dev *input_dev;
+ struct ff_device *ff;
+ unsigned char c[] = "CEOV";
+ int i, error;
+ int ff_effects = 0;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ init_waitqueue_head(&iforce->wait);
+ spin_lock_init(&iforce->xmit_lock);
+ mutex_init(&iforce->mem_mutex);
+ iforce->xmit.buf = iforce->xmit_data;
+ iforce->dev = input_dev;
+
+/*
+ * Input device fields.
+ */
+
+ switch (iforce->bus) {
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+ case IFORCE_USB:
+ input_dev->id.bustype = BUS_USB;
+ input_dev->dev.parent = &iforce->usbdev->dev;
+ break;
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+ case IFORCE_232:
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->dev.parent = &iforce->serio->dev;
+ break;
+#endif
+ }
+
+ input_set_drvdata(input_dev, iforce);
+
+ input_dev->name = "Unknown I-Force device";
+ input_dev->open = iforce_open;
+ input_dev->close = iforce_close;
+
+/*
+ * 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 */
+ dev_err(&input_dev->dev,
+ "Timeout waiting for response from device.\n");
+ error = -ENODEV;
+ goto fail;
+ }
+
+/*
+ * Get device info.
+ */
+
+ if (!iforce_get_id_packet(iforce, "M"))
+ input_dev->id.vendor = (iforce->edata[2] << 8) | iforce->edata[1];
+ else
+ dev_warn(&iforce->dev->dev, "Device does not respond to id packet M\n");
+
+ if (!iforce_get_id_packet(iforce, "P"))
+ input_dev->id.product = (iforce->edata[2] << 8) | iforce->edata[1];
+ else
+ dev_warn(&iforce->dev->dev, "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
+ dev_warn(&iforce->dev->dev, "Device does not respond to id packet B\n");
+
+ if (!iforce_get_id_packet(iforce, "N"))
+ ff_effects = iforce->edata[1];
+ else
+ dev_warn(&iforce->dev->dev, "Device does not respond to id packet N\n");
+
+ /* Check if the device can store more effects than the driver can really handle */
+ if (ff_effects > IFORCE_EFFECTS_MAX) {
+ dev_warn(&iforce->dev->dev, "Limiting number of effects to %d (device reports %d)\n",
+ IFORCE_EFFECTS_MAX, ff_effects);
+ ff_effects = IFORCE_EFFECTS_MAX;
+ }
+
+/*
+ * Display additional info.
+ */
+
+ for (i = 0; c[i]; i++)
+ if (!iforce_get_id_packet(iforce, c + i))
+ iforce_dump_packet(iforce, "info", iforce->ecmd, iforce->edata);
+
+/*
+ * Disable spring, enable force feedback.
+ */
+ iforce_set_autocenter(input_dev, 0);
+
+/*
+ * Find appropriate device entry
+ */
+
+ for (i = 0; iforce_device[i].idvendor; i++)
+ if (iforce_device[i].idvendor == input_dev->id.vendor &&
+ iforce_device[i].idproduct == input_dev->id.product)
+ break;
+
+ iforce->type = iforce_device + i;
+ input_dev->name = iforce->type->name;
+
+/*
+ * Set input device bitfields and ranges.
+ */
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) |
+ BIT_MASK(EV_FF_STATUS);
+
+ for (i = 0; iforce->type->btn[i] >= 0; i++)
+ set_bit(iforce->type->btn[i], input_dev->keybit);
+
+ for (i = 0; iforce->type->abs[i] >= 0; i++) {
+
+ signed short t = iforce->type->abs[i];
+
+ switch (t) {
+
+ case ABS_X:
+ case ABS_Y:
+ case ABS_WHEEL:
+
+ input_set_abs_params(input_dev, t, -1920, 1920, 16, 128);
+ set_bit(t, input_dev->ffbit);
+ break;
+
+ case ABS_THROTTLE:
+ case ABS_GAS:
+ case ABS_BRAKE:
+
+ input_set_abs_params(input_dev, t, 0, 255, 0, 0);
+ break;
+
+ case ABS_RUDDER:
+
+ input_set_abs_params(input_dev, t, -128, 127, 0, 0);
+ break;
+
+ case ABS_HAT0X:
+ case ABS_HAT0Y:
+ case ABS_HAT1X:
+ case ABS_HAT1Y:
+
+ input_set_abs_params(input_dev, t, -1, 1, 0, 0);
+ break;
+ }
+ }
+
+ if (ff_effects) {
+
+ for (i = 0; iforce->type->ff[i] >= 0; i++)
+ set_bit(iforce->type->ff[i], input_dev->ffbit);
+
+ error = input_ff_create(input_dev, ff_effects);
+ if (error)
+ goto fail;
+
+ ff = input_dev->ff;
+ ff->upload = iforce_upload_effect;
+ ff->erase = iforce_erase_effect;
+ ff->set_gain = iforce_set_gain;
+ ff->set_autocenter = iforce_set_autocenter;
+ ff->playback = iforce_playback;
+ }
+/*
+ * Register input device.
+ */
+
+ error = input_register_device(iforce->dev);
+ if (error)
+ goto fail;
+
+ return 0;
+
+ fail: input_free_device(input_dev);
+ return error;
+}
+
+static int __init iforce_init(void)
+{
+ int err = 0;
+
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+ err = usb_register(&iforce_usb_driver);
+ if (err)
+ return err;
+#endif
+#ifdef CONFIG_JOYSTICK_IFORCE_232
+ err = serio_register_driver(&iforce_serio_drv);
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+ if (err)
+ usb_deregister(&iforce_usb_driver);
+#endif
+#endif
+ return err;
+}
+
+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 000000000..c10169f45
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-packets.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.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
+ */
+
+#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(struct iforce *iforce, char *msg, u16 cmd, unsigned char *data)
+{
+ dev_dbg(iforce->dev->dev.parent, "%s %s cmd = %04x, data = %*ph\n",
+ __func__, msg, cmd, LO(cmd), data);
+}
+
+/*
+ * 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) {
+ dev_warn(&iforce->dev->dev,
+ "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];
+
+ 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;
+
+ if (!iforce->dev->ff)
+ return 0;
+
+ for (i = 0; i < iforce->dev->ff->max_effects; ++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;
+ }
+ }
+ dev_warn(&iforce->dev->dev, "unused effect %04x updated !!!\n", addr);
+ return -1;
+}
+
+void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data)
+{
+ struct input_dev *dev = iforce->dev;
+ int i;
+ static int being_used = 0;
+
+ if (being_used)
+ dev_warn(&iforce->dev->dev,
+ "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);
+ }
+#endif
+ wake_up(&iforce->wait);
+
+ if (!iforce->type) {
+ being_used--;
+ return;
+ }
+
+ switch (HI(cmd)) {
+
+ case 0x01: /* joystick position data */
+ case 0x03: /* wheel position data */
+ 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_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)
+{
+ switch (iforce->bus) {
+
+ case IFORCE_USB: {
+#ifdef CONFIG_JOYSTICK_IFORCE_USB
+ int status;
+
+ iforce->cr.bRequest = packet[0];
+ iforce->ctrl->dev = iforce->usbdev;
+
+ status = usb_submit_urb(iforce->ctrl, GFP_KERNEL);
+ if (status) {
+ dev_err(&iforce->intf->dev,
+ "usb_submit_urb failed %d\n", status);
+ return -1;
+ }
+
+ wait_event_interruptible_timeout(iforce->wait,
+ iforce->ctrl->status != -EINPROGRESS, HZ);
+
+ if (iforce->ctrl->status) {
+ dev_dbg(&iforce->intf->dev,
+ "iforce->ctrl->status = %d\n",
+ iforce->ctrl->status);
+ usb_unlink_urb(iforce->ctrl);
+ return -1;
+ }
+#else
+ printk(KERN_DEBUG "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);
+
+ wait_event_interruptible_timeout(iforce->wait,
+ !iforce->expect_packet, HZ);
+
+ if (iforce->expect_packet) {
+ iforce->expect_packet = 0;
+ return -1;
+ }
+#else
+ dev_err(&iforce->dev->dev,
+ "iforce_get_id_packet: iforce->bus = SERIO!\n");
+#endif
+ break;
+
+ default:
+ dev_err(&iforce->dev->dev,
+ "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 000000000..f4ba4a751
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-serio.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2001, 2007 Johann Deneux <johann.deneux@gmail.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
+ */
+
+#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 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);
+ 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;
+
+ iforce = kzalloc(sizeof(struct iforce), GFP_KERNEL);
+ if (!iforce)
+ return -ENOMEM;
+
+ iforce->bus = IFORCE_232;
+ iforce->serio = serio;
+
+ serio_set_drvdata(serio, iforce);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail1;
+
+ err = iforce_init_device(iforce);
+ if (err)
+ goto fail2;
+
+ return 0;
+
+ fail2: serio_close(serio);
+ fail1: serio_set_drvdata(serio, NULL);
+ kfree(iforce);
+ return err;
+}
+
+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 const 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 000000000..c431df740
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce-usb.c
@@ -0,0 +1,236 @@
+ /*
+ * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.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
+ */
+
+#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)) ) {
+ clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+ dev_warn(&iforce->intf->dev, "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 iforce *iforce = urb->context;
+ struct device *dev = &iforce->intf->dev;
+ int status;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dev_dbg(dev, "%s - urb shutting down with status: %d\n",
+ __func__, urb->status);
+ return;
+ default:
+ dev_dbg(dev, "%s - urb has status of: %d\n",
+ __func__, urb->status);
+ goto exit;
+ }
+
+ iforce_process_packet(iforce,
+ (iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1);
+
+exit:
+ status = usb_submit_urb (urb, GFP_ATOMIC);
+ if (status)
+ dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
+ __func__, status);
+}
+
+static void iforce_usb_out(struct urb *urb)
+{
+ struct iforce *iforce = urb->context;
+
+ if (urb->status) {
+ clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+ dev_dbg(&iforce->intf->dev, "urb->status %d, exiting\n",
+ urb->status);
+ return;
+ }
+
+ iforce_usb_xmit(iforce);
+
+ wake_up(&iforce->wait);
+}
+
+static void iforce_usb_ctrl(struct urb *urb)
+{
+ 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;
+ int err = -ENOMEM;
+
+ interface = intf->cur_altsetting;
+
+ if (interface->desc.bNumEndpoints < 2)
+ return -ENODEV;
+
+ epirq = &interface->endpoint[0].desc;
+ if (!usb_endpoint_is_int_in(epirq))
+ return -ENODEV;
+
+ epout = &interface->endpoint[1].desc;
+ if (!usb_endpoint_is_int_out(epout))
+ return -ENODEV;
+
+ if (!(iforce = kzalloc(sizeof(struct iforce) + 32, GFP_KERNEL)))
+ goto fail;
+
+ 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->intf = intf;
+
+ iforce->cr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE;
+ iforce->cr.wIndex = 0;
+ iforce->cr.wLength = cpu_to_le16(16);
+
+ usb_fill_int_urb(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress),
+ iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval);
+
+ usb_fill_int_urb(iforce->out, dev, usb_sndintpipe(dev, epout->bEndpointAddress),
+ iforce + 1, 32, iforce_usb_out, iforce, epout->bInterval);
+
+ usb_fill_control_urb(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0),
+ (void*) &iforce->cr, iforce->edata, 16, iforce_usb_ctrl, iforce);
+
+ err = iforce_init_device(iforce);
+ if (err)
+ goto fail;
+
+ usb_set_intfdata(intf, iforce);
+ return 0;
+
+fail:
+ if (iforce) {
+ usb_free_urb(iforce->irq);
+ usb_free_urb(iforce->out);
+ usb_free_urb(iforce->ctrl);
+ kfree(iforce);
+ }
+
+ return err;
+}
+
+static void iforce_usb_disconnect(struct usb_interface *intf)
+{
+ struct iforce *iforce = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+
+ input_unregister_device(iforce->dev);
+
+ usb_free_urb(iforce->irq);
+ usb_free_urb(iforce->out);
+ usb_free_urb(iforce->ctrl);
+
+ kfree(iforce);
+}
+
+static const 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(0x061c, 0xc084) }, /* ACT LABS Force RS */
+ { USB_DEVICE(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */
+ { USB_DEVICE(0x06f8, 0x0003) }, /* Guillemot Jet Leader Force Feedback */
+ { USB_DEVICE(0x06f8, 0x0004) }, /* Guillemot Force Feedback Racing Wheel */
+ { USB_DEVICE(0x06f8, 0xa302) }, /* Guillemot Jet Leader 3D */
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, iforce_usb_ids);
+
+struct usb_driver iforce_usb_driver = {
+ .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 000000000..0e9d01f8b
--- /dev/null
+++ b/drivers/input/joystick/iforce/iforce.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.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
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/serio.h>
+#include <linux/circ_buf.h>
+#include <linux/mutex.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 IFORCE_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_CNT 6
+
+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[BITS_TO_LONGS(FF_MODCORE_CNT)];
+};
+
+#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 usb_interface *intf;
+ 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];
+ unsigned long xmit_flags[1];
+
+ /* Force Feedback */
+ wait_queue_head_t wait;
+ struct resource device_memory;
+ struct iforce_core_effect core_effects[IFORCE_EFFECTS_MAX];
+ struct mutex 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);
+
+/* iforce-main.c */
+int iforce_init_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);
+int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data);
+void iforce_dump_packet(struct iforce *iforce, 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 *, struct ff_effect *);
+int iforce_upload_constant(struct iforce *, struct ff_effect *, struct ff_effect *);
+int iforce_upload_condition(struct iforce *, struct ff_effect *, struct ff_effect *);
+
+/* 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 000000000..598788b3d
--- /dev/null
+++ b/drivers/input/joystick/interact.c
@@ -0,0 +1,309 @@
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/jiffies.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 = input_get_drvdata(dev);
+
+ 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 = input_get_drvdata(dev);
+
+ 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;
+ struct input_dev *input_dev;
+ __u32 data[3];
+ int i, t;
+ int err;
+
+ interact = kzalloc(sizeof(struct interact), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!interact || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ interact->gameport = gameport;
+ interact->dev = input_dev;
+
+ 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);
+
+ snprintf(interact->phys, sizeof(interact->phys), "%s/input0", gameport->phys);
+
+ interact->type = i;
+ interact->length = interact_type[i].length;
+
+ input_dev->name = interact_type[i].name;
+ input_dev->phys = interact->phys;
+ input_dev->id.bustype = BUS_GAMEPORT;
+ input_dev->id.vendor = GAMEPORT_ID_VENDOR_INTERACT;
+ input_dev->id.product = interact_type[i].id;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &gameport->dev;
+
+ input_set_drvdata(input_dev, interact);
+
+ input_dev->open = interact_open;
+ input_dev->close = interact_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ for (i = 0; (t = interact_type[interact->type].abs[i]) >= 0; i++) {
+ if (i < interact_type[interact->type].b8)
+ input_set_abs_params(input_dev, t, 0, 255, 0, 0);
+ else
+ input_set_abs_params(input_dev, t, -1, 1, 0, 0);
+ }
+
+ for (i = 0; (t = interact_type[interact->type].btn[i]) >= 0; i++)
+ __set_bit(t, input_dev->keybit);
+
+ err = input_register_device(interact->dev);
+ if (err)
+ goto fail2;
+
+ return 0;
+
+fail2: gameport_close(gameport);
+fail1: gameport_set_drvdata(gameport, NULL);
+ input_free_device(input_dev);
+ 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,
+};
+
+module_gameport_driver(interact_drv);
diff --git a/drivers/input/joystick/joydump.c b/drivers/input/joystick/joydump.c
new file mode 100644
index 000000000..344ab44ff
--- /dev/null
+++ b/drivers/input/joystick/joydump.c
@@ -0,0 +1,157 @@
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/gameport.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.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_array(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,
+};
+
+module_gameport_driver(joydump_drv);
diff --git a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c
new file mode 100644
index 000000000..95a34ab34
--- /dev/null
+++ b/drivers/input/joystick/magellan.c
@@ -0,0 +1,220 @@
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.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 };
+
+/*
+ * 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 input_dev *dev = magellan->dev;
+ unsigned char *data = magellan->data;
+ int i, t;
+
+ if (!magellan->idx) return;
+
+ 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 magellan* magellan = serio_get_drvdata(serio);
+
+ if (data == '\r') {
+ magellan_process_packet(magellan);
+ 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);
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_unregister_device(magellan->dev);
+ 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;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+ int i;
+
+ magellan = kzalloc(sizeof(struct magellan), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!magellan || !input_dev)
+ goto fail1;
+
+ magellan->dev = input_dev;
+ snprintf(magellan->phys, sizeof(magellan->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "LogiCad3D Magellan / SpaceMouse";
+ input_dev->phys = magellan->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_MAGELLAN;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ for (i = 0; i < 9; i++)
+ set_bit(magellan_buttons[i], input_dev->keybit);
+
+ for (i = 0; i < 6; i++)
+ input_set_abs_params(input_dev, magellan_axes[i], -360, 360, 0, 0);
+
+ serio_set_drvdata(serio, magellan);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(magellan->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(magellan);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static const 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,
+};
+
+module_serio_driver(magellan_drv);
diff --git a/drivers/input/joystick/maplecontrol.c b/drivers/input/joystick/maplecontrol.c
new file mode 100644
index 000000000..ff54e195d
--- /dev/null
+++ b/drivers/input/joystick/maplecontrol.c
@@ -0,0 +1,192 @@
+/*
+ * SEGA Dreamcast controller driver
+ * Based on drivers/usb/iforce.c
+ *
+ * Copyright Yaegashi Takeshi, 2001
+ * Adrian McMenamin, 2008 - 2009
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/maple.h>
+
+MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
+MODULE_DESCRIPTION("SEGA Dreamcast controller driver");
+MODULE_LICENSE("GPL");
+
+struct dc_pad {
+ struct input_dev *dev;
+ struct maple_device *mdev;
+};
+
+static void dc_pad_callback(struct mapleq *mq)
+{
+ unsigned short buttons;
+ struct maple_device *mapledev = mq->dev;
+ struct dc_pad *pad = maple_get_drvdata(mapledev);
+ struct input_dev *dev = pad->dev;
+ unsigned char *res = mq->recvbuf->buf;
+
+ buttons = ~le16_to_cpup((__le16 *)(res + 8));
+
+ input_report_abs(dev, ABS_HAT0Y,
+ (buttons & 0x0010 ? -1 : 0) + (buttons & 0x0020 ? 1 : 0));
+ input_report_abs(dev, ABS_HAT0X,
+ (buttons & 0x0040 ? -1 : 0) + (buttons & 0x0080 ? 1 : 0));
+ input_report_abs(dev, ABS_HAT1Y,
+ (buttons & 0x1000 ? -1 : 0) + (buttons & 0x2000 ? 1 : 0));
+ input_report_abs(dev, ABS_HAT1X,
+ (buttons & 0x4000 ? -1 : 0) + (buttons & 0x8000 ? 1 : 0));
+
+ input_report_key(dev, BTN_C, buttons & 0x0001);
+ input_report_key(dev, BTN_B, buttons & 0x0002);
+ input_report_key(dev, BTN_A, buttons & 0x0004);
+ input_report_key(dev, BTN_START, buttons & 0x0008);
+ input_report_key(dev, BTN_Z, buttons & 0x0100);
+ input_report_key(dev, BTN_Y, buttons & 0x0200);
+ input_report_key(dev, BTN_X, buttons & 0x0400);
+ input_report_key(dev, BTN_SELECT, buttons & 0x0800);
+
+ input_report_abs(dev, ABS_GAS, res[10]);
+ input_report_abs(dev, ABS_BRAKE, res[11]);
+ input_report_abs(dev, ABS_X, res[12]);
+ input_report_abs(dev, ABS_Y, res[13]);
+ input_report_abs(dev, ABS_RX, res[14]);
+ input_report_abs(dev, ABS_RY, res[15]);
+}
+
+static int dc_pad_open(struct input_dev *dev)
+{
+ struct dc_pad *pad = dev_get_platdata(&dev->dev);
+
+ maple_getcond_callback(pad->mdev, dc_pad_callback, HZ/20,
+ MAPLE_FUNC_CONTROLLER);
+
+ return 0;
+}
+
+static void dc_pad_close(struct input_dev *dev)
+{
+ struct dc_pad *pad = dev_get_platdata(&dev->dev);
+
+ maple_getcond_callback(pad->mdev, dc_pad_callback, 0,
+ MAPLE_FUNC_CONTROLLER);
+}
+
+/* allow the controller to be used */
+static int probe_maple_controller(struct device *dev)
+{
+ static const short btn_bit[32] = {
+ BTN_C, BTN_B, BTN_A, BTN_START, -1, -1, -1, -1,
+ BTN_Z, BTN_Y, BTN_X, BTN_SELECT, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+
+ static const short abs_bit[32] = {
+ -1, -1, -1, -1, ABS_HAT0Y, ABS_HAT0Y, ABS_HAT0X, ABS_HAT0X,
+ -1, -1, -1, -1, ABS_HAT1Y, ABS_HAT1Y, ABS_HAT1X, ABS_HAT1X,
+ ABS_GAS, ABS_BRAKE, ABS_X, ABS_Y, ABS_RX, ABS_RY, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+
+ struct maple_device *mdev = to_maple_dev(dev);
+ struct maple_driver *mdrv = to_maple_driver(dev->driver);
+ int i, error;
+ struct dc_pad *pad;
+ struct input_dev *idev;
+ unsigned long data = be32_to_cpu(mdev->devinfo.function_data[0]);
+
+ pad = kzalloc(sizeof(struct dc_pad), GFP_KERNEL);
+ idev = input_allocate_device();
+ if (!pad || !idev) {
+ error = -ENOMEM;
+ goto fail;
+ }
+
+ pad->dev = idev;
+ pad->mdev = mdev;
+
+ idev->open = dc_pad_open;
+ idev->close = dc_pad_close;
+
+ for (i = 0; i < 32; i++) {
+ if (data & (1 << i)) {
+ if (btn_bit[i] >= 0)
+ __set_bit(btn_bit[i], idev->keybit);
+ else if (abs_bit[i] >= 0)
+ __set_bit(abs_bit[i], idev->absbit);
+ }
+ }
+
+ if (idev->keybit[BIT_WORD(BTN_JOYSTICK)])
+ idev->evbit[0] |= BIT_MASK(EV_KEY);
+
+ if (idev->absbit[0])
+ idev->evbit[0] |= BIT_MASK(EV_ABS);
+
+ for (i = ABS_X; i <= ABS_BRAKE; i++)
+ input_set_abs_params(idev, i, 0, 255, 0, 0);
+
+ for (i = ABS_HAT0X; i <= ABS_HAT3Y; i++)
+ input_set_abs_params(idev, i, 1, -1, 0, 0);
+
+ idev->dev.platform_data = pad;
+ idev->dev.parent = &mdev->dev;
+ idev->name = mdev->product_name;
+ idev->id.bustype = BUS_HOST;
+
+ error = input_register_device(idev);
+ if (error)
+ goto fail;
+
+ mdev->driver = mdrv;
+ maple_set_drvdata(mdev, pad);
+
+ return 0;
+
+fail:
+ input_free_device(idev);
+ kfree(pad);
+ maple_set_drvdata(mdev, NULL);
+ return error;
+}
+
+static int remove_maple_controller(struct device *dev)
+{
+ struct maple_device *mdev = to_maple_dev(dev);
+ struct dc_pad *pad = maple_get_drvdata(mdev);
+
+ mdev->callback = NULL;
+ input_unregister_device(pad->dev);
+ maple_set_drvdata(mdev, NULL);
+ kfree(pad);
+
+ return 0;
+}
+
+static struct maple_driver dc_pad_driver = {
+ .function = MAPLE_FUNC_CONTROLLER,
+ .drv = {
+ .name = "Dreamcast_controller",
+ .probe = probe_maple_controller,
+ .remove = remove_maple_controller,
+ },
+};
+
+static int __init dc_pad_init(void)
+{
+ return maple_driver_register(&dc_pad_driver);
+}
+
+static void __exit dc_pad_exit(void)
+{
+ maple_driver_unregister(&dc_pad_driver);
+}
+
+module_init(dc_pad_init);
+module_exit(dc_pad_exit);
diff --git a/drivers/input/joystick/psxpad-spi.c b/drivers/input/joystick/psxpad-spi.c
new file mode 100644
index 000000000..092096ee0
--- /dev/null
+++ b/drivers/input/joystick/psxpad-spi.c
@@ -0,0 +1,401 @@
+/*
+ * PlayStation 1/2 joypads via SPI interface Driver
+ *
+ * Copyright (C) 2017 Tomohiro Yoshidomi <sylph23k@gmail.com>
+ * Licensed under the GPL-2 or later.
+ *
+ * PlayStation 1/2 joypad's plug (not socket)
+ * 123 456 789
+ * (...|...|...)
+ *
+ * 1: DAT -> MISO (pullup with 1k owm to 3.3V)
+ * 2: CMD -> MOSI
+ * 3: 9V (for motor, if not use N.C.)
+ * 4: GND
+ * 5: 3.3V
+ * 6: Attention -> CS(SS)
+ * 7: SCK -> SCK
+ * 8: N.C.
+ * 9: ACK -> N.C.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+
+#define REVERSE_BIT(x) ((((x) & 0x80) >> 7) | (((x) & 0x40) >> 5) | \
+ (((x) & 0x20) >> 3) | (((x) & 0x10) >> 1) | (((x) & 0x08) << 1) | \
+ (((x) & 0x04) << 3) | (((x) & 0x02) << 5) | (((x) & 0x01) << 7))
+
+/* PlayStation 1/2 joypad command and response are LSBFIRST. */
+
+/*
+ * 0x01, 0x42, 0x00, 0x00, 0x00,
+ * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ */
+static const u8 PSX_CMD_POLL[] = {
+ 0x80, 0x42, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 0x01, 0x43, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 */
+static const u8 PSX_CMD_ENTER_CFG[] = {
+ 0x80, 0xC2, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+/* 0x01, 0x43, 0x00, 0x00, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A */
+static const u8 PSX_CMD_EXIT_CFG[] = {
+ 0x80, 0xC2, 0x00, 0x00, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A
+};
+/* 0x01, 0x4D, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF */
+static const u8 PSX_CMD_ENABLE_MOTOR[] = {
+ 0x80, 0xB2, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+struct psxpad {
+ struct spi_device *spi;
+ struct input_polled_dev *pdev;
+ char phys[0x20];
+ bool motor1enable;
+ bool motor2enable;
+ u8 motor1level;
+ u8 motor2level;
+ u8 sendbuf[0x20] ____cacheline_aligned;
+ u8 response[sizeof(PSX_CMD_POLL)] ____cacheline_aligned;
+};
+
+static int psxpad_command(struct psxpad *pad, const u8 sendcmdlen)
+{
+ struct spi_transfer xfers = {
+ .tx_buf = pad->sendbuf,
+ .rx_buf = pad->response,
+ .len = sendcmdlen,
+ };
+ int err;
+
+ err = spi_sync_transfer(pad->spi, &xfers, 1);
+ if (err) {
+ dev_err(&pad->spi->dev,
+ "%s: failed to SPI xfers mode: %d\n",
+ __func__, err);
+ return err;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_JOYSTICK_PSXPAD_SPI_FF
+static void psxpad_control_motor(struct psxpad *pad,
+ bool motor1enable, bool motor2enable)
+{
+ int err;
+
+ pad->motor1enable = motor1enable;
+ pad->motor2enable = motor2enable;
+
+ memcpy(pad->sendbuf, PSX_CMD_ENTER_CFG, sizeof(PSX_CMD_ENTER_CFG));
+ err = psxpad_command(pad, sizeof(PSX_CMD_ENTER_CFG));
+ if (err) {
+ dev_err(&pad->spi->dev,
+ "%s: failed to enter config mode: %d\n",
+ __func__, err);
+ return;
+ }
+
+ memcpy(pad->sendbuf, PSX_CMD_ENABLE_MOTOR,
+ sizeof(PSX_CMD_ENABLE_MOTOR));
+ pad->sendbuf[3] = pad->motor1enable ? 0x00 : 0xFF;
+ pad->sendbuf[4] = pad->motor2enable ? 0x80 : 0xFF;
+ err = psxpad_command(pad, sizeof(PSX_CMD_ENABLE_MOTOR));
+ if (err) {
+ dev_err(&pad->spi->dev,
+ "%s: failed to enable motor mode: %d\n",
+ __func__, err);
+ return;
+ }
+
+ memcpy(pad->sendbuf, PSX_CMD_EXIT_CFG, sizeof(PSX_CMD_EXIT_CFG));
+ err = psxpad_command(pad, sizeof(PSX_CMD_EXIT_CFG));
+ if (err) {
+ dev_err(&pad->spi->dev,
+ "%s: failed to exit config mode: %d\n",
+ __func__, err);
+ return;
+ }
+}
+
+static void psxpad_set_motor_level(struct psxpad *pad,
+ u8 motor1level, u8 motor2level)
+{
+ pad->motor1level = motor1level ? 0xFF : 0x00;
+ pad->motor2level = REVERSE_BIT(motor2level);
+}
+
+static int psxpad_spi_play_effect(struct input_dev *idev,
+ void *data, struct ff_effect *effect)
+{
+ struct input_polled_dev *pdev = input_get_drvdata(idev);
+ struct psxpad *pad = pdev->private;
+
+ switch (effect->type) {
+ case FF_RUMBLE:
+ psxpad_set_motor_level(pad,
+ (effect->u.rumble.weak_magnitude >> 8) & 0xFFU,
+ (effect->u.rumble.strong_magnitude >> 8) & 0xFFU);
+ break;
+ }
+
+ return 0;
+}
+
+static int psxpad_spi_init_ff(struct psxpad *pad)
+{
+ int err;
+
+ input_set_capability(pad->pdev->input, EV_FF, FF_RUMBLE);
+
+ err = input_ff_create_memless(pad->pdev->input, NULL,
+ psxpad_spi_play_effect);
+ if (err) {
+ dev_err(&pad->spi->dev,
+ "input_ff_create_memless() failed: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+#else /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
+
+static void psxpad_control_motor(struct psxpad *pad,
+ bool motor1enable, bool motor2enable)
+{
+}
+
+static void psxpad_set_motor_level(struct psxpad *pad,
+ u8 motor1level, u8 motor2level)
+{
+}
+
+static inline int psxpad_spi_init_ff(struct psxpad *pad)
+{
+ return 0;
+}
+#endif /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
+
+static void psxpad_spi_poll_open(struct input_polled_dev *pdev)
+{
+ struct psxpad *pad = pdev->private;
+
+ pm_runtime_get_sync(&pad->spi->dev);
+}
+
+static void psxpad_spi_poll_close(struct input_polled_dev *pdev)
+{
+ struct psxpad *pad = pdev->private;
+
+ pm_runtime_put_sync(&pad->spi->dev);
+}
+
+static void psxpad_spi_poll(struct input_polled_dev *pdev)
+{
+ struct psxpad *pad = pdev->private;
+ struct input_dev *input = pdev->input;
+ u8 b_rsp3, b_rsp4;
+ int err;
+
+ psxpad_control_motor(pad, true, true);
+
+ memcpy(pad->sendbuf, PSX_CMD_POLL, sizeof(PSX_CMD_POLL));
+ pad->sendbuf[3] = pad->motor1enable ? pad->motor1level : 0x00;
+ pad->sendbuf[4] = pad->motor2enable ? pad->motor2level : 0x00;
+ err = psxpad_command(pad, sizeof(PSX_CMD_POLL));
+ if (err) {
+ dev_err(&pad->spi->dev,
+ "%s: poll command failed mode: %d\n", __func__, err);
+ return;
+ }
+
+ switch (pad->response[1]) {
+ case 0xCE: /* 0x73 : analog 1 */
+ /* button data is inverted */
+ b_rsp3 = ~pad->response[3];
+ b_rsp4 = ~pad->response[4];
+
+ input_report_abs(input, ABS_X, REVERSE_BIT(pad->response[7]));
+ input_report_abs(input, ABS_Y, REVERSE_BIT(pad->response[8]));
+ input_report_abs(input, ABS_RX, REVERSE_BIT(pad->response[5]));
+ input_report_abs(input, ABS_RY, REVERSE_BIT(pad->response[6]));
+ input_report_key(input, BTN_DPAD_UP, b_rsp3 & BIT(3));
+ input_report_key(input, BTN_DPAD_DOWN, b_rsp3 & BIT(1));
+ input_report_key(input, BTN_DPAD_LEFT, b_rsp3 & BIT(0));
+ input_report_key(input, BTN_DPAD_RIGHT, b_rsp3 & BIT(2));
+ input_report_key(input, BTN_X, b_rsp4 & BIT(3));
+ input_report_key(input, BTN_A, b_rsp4 & BIT(2));
+ input_report_key(input, BTN_B, b_rsp4 & BIT(1));
+ input_report_key(input, BTN_Y, b_rsp4 & BIT(0));
+ input_report_key(input, BTN_TL, b_rsp4 & BIT(5));
+ input_report_key(input, BTN_TR, b_rsp4 & BIT(4));
+ input_report_key(input, BTN_TL2, b_rsp4 & BIT(7));
+ input_report_key(input, BTN_TR2, b_rsp4 & BIT(6));
+ input_report_key(input, BTN_THUMBL, b_rsp3 & BIT(6));
+ input_report_key(input, BTN_THUMBR, b_rsp3 & BIT(5));
+ input_report_key(input, BTN_SELECT, b_rsp3 & BIT(7));
+ input_report_key(input, BTN_START, b_rsp3 & BIT(4));
+ break;
+
+ case 0x82: /* 0x41 : digital */
+ /* button data is inverted */
+ b_rsp3 = ~pad->response[3];
+ b_rsp4 = ~pad->response[4];
+
+ input_report_abs(input, ABS_X, 0x80);
+ input_report_abs(input, ABS_Y, 0x80);
+ input_report_abs(input, ABS_RX, 0x80);
+ input_report_abs(input, ABS_RY, 0x80);
+ input_report_key(input, BTN_DPAD_UP, b_rsp3 & BIT(3));
+ input_report_key(input, BTN_DPAD_DOWN, b_rsp3 & BIT(1));
+ input_report_key(input, BTN_DPAD_LEFT, b_rsp3 & BIT(0));
+ input_report_key(input, BTN_DPAD_RIGHT, b_rsp3 & BIT(2));
+ input_report_key(input, BTN_X, b_rsp4 & BIT(3));
+ input_report_key(input, BTN_A, b_rsp4 & BIT(2));
+ input_report_key(input, BTN_B, b_rsp4 & BIT(1));
+ input_report_key(input, BTN_Y, b_rsp4 & BIT(0));
+ input_report_key(input, BTN_TL, b_rsp4 & BIT(5));
+ input_report_key(input, BTN_TR, b_rsp4 & BIT(4));
+ input_report_key(input, BTN_TL2, b_rsp4 & BIT(7));
+ input_report_key(input, BTN_TR2, b_rsp4 & BIT(6));
+ input_report_key(input, BTN_THUMBL, false);
+ input_report_key(input, BTN_THUMBR, false);
+ input_report_key(input, BTN_SELECT, b_rsp3 & BIT(7));
+ input_report_key(input, BTN_START, b_rsp3 & BIT(4));
+ break;
+ }
+
+ input_sync(input);
+}
+
+static int psxpad_spi_probe(struct spi_device *spi)
+{
+ struct psxpad *pad;
+ struct input_polled_dev *pdev;
+ struct input_dev *idev;
+ int err;
+
+ pad = devm_kzalloc(&spi->dev, sizeof(struct psxpad), GFP_KERNEL);
+ if (!pad)
+ return -ENOMEM;
+
+ pdev = devm_input_allocate_polled_device(&spi->dev);
+ if (!pdev) {
+ dev_err(&spi->dev, "failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ /* input poll device settings */
+ pad->pdev = pdev;
+ pad->spi = spi;
+
+ pdev->private = pad;
+ pdev->open = psxpad_spi_poll_open;
+ pdev->close = psxpad_spi_poll_close;
+ pdev->poll = psxpad_spi_poll;
+ /* poll interval is about 60fps */
+ pdev->poll_interval = 16;
+ pdev->poll_interval_min = 8;
+ pdev->poll_interval_max = 32;
+
+ /* input device settings */
+ idev = pdev->input;
+ idev->name = "PlayStation 1/2 joypad";
+ snprintf(pad->phys, sizeof(pad->phys), "%s/input", dev_name(&spi->dev));
+ idev->id.bustype = BUS_SPI;
+
+ /* key/value map settings */
+ input_set_abs_params(idev, ABS_X, 0, 255, 0, 0);
+ input_set_abs_params(idev, ABS_Y, 0, 255, 0, 0);
+ input_set_abs_params(idev, ABS_RX, 0, 255, 0, 0);
+ input_set_abs_params(idev, ABS_RY, 0, 255, 0, 0);
+ input_set_capability(idev, EV_KEY, BTN_DPAD_UP);
+ input_set_capability(idev, EV_KEY, BTN_DPAD_DOWN);
+ input_set_capability(idev, EV_KEY, BTN_DPAD_LEFT);
+ input_set_capability(idev, EV_KEY, BTN_DPAD_RIGHT);
+ input_set_capability(idev, EV_KEY, BTN_A);
+ input_set_capability(idev, EV_KEY, BTN_B);
+ input_set_capability(idev, EV_KEY, BTN_X);
+ input_set_capability(idev, EV_KEY, BTN_Y);
+ input_set_capability(idev, EV_KEY, BTN_TL);
+ input_set_capability(idev, EV_KEY, BTN_TR);
+ input_set_capability(idev, EV_KEY, BTN_TL2);
+ input_set_capability(idev, EV_KEY, BTN_TR2);
+ input_set_capability(idev, EV_KEY, BTN_THUMBL);
+ input_set_capability(idev, EV_KEY, BTN_THUMBR);
+ input_set_capability(idev, EV_KEY, BTN_SELECT);
+ input_set_capability(idev, EV_KEY, BTN_START);
+
+ err = psxpad_spi_init_ff(pad);
+ if (err)
+ return err;
+
+ /* SPI settings */
+ spi->mode = SPI_MODE_3;
+ spi->bits_per_word = 8;
+ /* (PlayStation 1/2 joypad might be possible works 250kHz/500kHz) */
+ spi->master->min_speed_hz = 125000;
+ spi->master->max_speed_hz = 125000;
+ spi_setup(spi);
+
+ /* pad settings */
+ psxpad_set_motor_level(pad, 0, 0);
+
+ /* register input poll device */
+ err = input_register_polled_device(pdev);
+ if (err) {
+ dev_err(&spi->dev,
+ "failed to register input poll device: %d\n", err);
+ return err;
+ }
+
+ pm_runtime_enable(&spi->dev);
+
+ return 0;
+}
+
+static int __maybe_unused psxpad_spi_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct psxpad *pad = spi_get_drvdata(spi);
+
+ psxpad_set_motor_level(pad, 0, 0);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(psxpad_spi_pm, psxpad_spi_suspend, NULL);
+
+static const struct spi_device_id psxpad_spi_id[] = {
+ { "psxpad-spi", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, psxpad_spi_id);
+
+static struct spi_driver psxpad_spi_driver = {
+ .driver = {
+ .name = "psxpad-spi",
+ .pm = &psxpad_spi_pm,
+ },
+ .id_table = psxpad_spi_id,
+ .probe = psxpad_spi_probe,
+};
+
+module_spi_driver(psxpad_spi_driver);
+
+MODULE_AUTHOR("Tomohiro Yoshidomi <sylph23k@gmail.com>");
+MODULE_DESCRIPTION("PlayStation 1/2 joypads via SPI interface Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/joystick/pxrc.c b/drivers/input/joystick/pxrc.c
new file mode 100644
index 000000000..ea2bf5951
--- /dev/null
+++ b/drivers/input/joystick/pxrc.c
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Phoenix RC Flight Controller Adapter
+ *
+ * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/mutex.h>
+#include <linux/input.h>
+
+#define PXRC_VENDOR_ID 0x1781
+#define PXRC_PRODUCT_ID 0x0898
+
+struct pxrc {
+ struct input_dev *input;
+ struct usb_interface *intf;
+ struct urb *urb;
+ struct mutex pm_mutex;
+ bool is_open;
+ char phys[64];
+};
+
+static void pxrc_usb_irq(struct urb *urb)
+{
+ struct pxrc *pxrc = urb->context;
+ u8 *data = urb->transfer_buffer;
+ int error;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ETIME:
+ /* this urb is timing out */
+ dev_dbg(&pxrc->intf->dev,
+ "%s - urb timed out - was the device unplugged?\n",
+ __func__);
+ return;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -EPIPE:
+ /* this urb is terminated, clean up */
+ dev_dbg(&pxrc->intf->dev, "%s - urb shutting down with status: %d\n",
+ __func__, urb->status);
+ return;
+ default:
+ dev_dbg(&pxrc->intf->dev, "%s - nonzero urb status received: %d\n",
+ __func__, urb->status);
+ goto exit;
+ }
+
+ if (urb->actual_length == 8) {
+ input_report_abs(pxrc->input, ABS_X, data[0]);
+ input_report_abs(pxrc->input, ABS_Y, data[2]);
+ input_report_abs(pxrc->input, ABS_RX, data[3]);
+ input_report_abs(pxrc->input, ABS_RY, data[4]);
+ input_report_abs(pxrc->input, ABS_RUDDER, data[5]);
+ input_report_abs(pxrc->input, ABS_THROTTLE, data[6]);
+ input_report_abs(pxrc->input, ABS_MISC, data[7]);
+
+ input_report_key(pxrc->input, BTN_A, data[1]);
+ }
+
+exit:
+ /* Resubmit to fetch new fresh URBs */
+ error = usb_submit_urb(urb, GFP_ATOMIC);
+ if (error && error != -EPERM)
+ dev_err(&pxrc->intf->dev,
+ "%s - usb_submit_urb failed with result: %d",
+ __func__, error);
+}
+
+static int pxrc_open(struct input_dev *input)
+{
+ struct pxrc *pxrc = input_get_drvdata(input);
+ int retval;
+
+ mutex_lock(&pxrc->pm_mutex);
+ retval = usb_submit_urb(pxrc->urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&pxrc->intf->dev,
+ "%s - usb_submit_urb failed, error: %d\n",
+ __func__, retval);
+ retval = -EIO;
+ goto out;
+ }
+
+ pxrc->is_open = true;
+
+out:
+ mutex_unlock(&pxrc->pm_mutex);
+ return retval;
+}
+
+static void pxrc_close(struct input_dev *input)
+{
+ struct pxrc *pxrc = input_get_drvdata(input);
+
+ mutex_lock(&pxrc->pm_mutex);
+ usb_kill_urb(pxrc->urb);
+ pxrc->is_open = false;
+ mutex_unlock(&pxrc->pm_mutex);
+}
+
+static void pxrc_free_urb(void *_pxrc)
+{
+ struct pxrc *pxrc = _pxrc;
+
+ usb_free_urb(pxrc->urb);
+}
+
+static int pxrc_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct pxrc *pxrc;
+ struct usb_endpoint_descriptor *epirq;
+ size_t xfer_size;
+ void *xfer_buf;
+ int error;
+
+ /*
+ * Locate the endpoint information. This device only has an
+ * interrupt endpoint.
+ */
+ error = usb_find_common_endpoints(intf->cur_altsetting,
+ NULL, NULL, &epirq, NULL);
+ if (error) {
+ dev_err(&intf->dev, "Could not find endpoint\n");
+ return error;
+ }
+
+ pxrc = devm_kzalloc(&intf->dev, sizeof(*pxrc), GFP_KERNEL);
+ if (!pxrc)
+ return -ENOMEM;
+
+ mutex_init(&pxrc->pm_mutex);
+ pxrc->intf = intf;
+
+ usb_set_intfdata(pxrc->intf, pxrc);
+
+ xfer_size = usb_endpoint_maxp(epirq);
+ xfer_buf = devm_kmalloc(&intf->dev, xfer_size, GFP_KERNEL);
+ if (!xfer_buf)
+ return -ENOMEM;
+
+ pxrc->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pxrc->urb)
+ return -ENOMEM;
+
+ error = devm_add_action_or_reset(&intf->dev, pxrc_free_urb, pxrc);
+ if (error)
+ return error;
+
+ usb_fill_int_urb(pxrc->urb, udev,
+ usb_rcvintpipe(udev, epirq->bEndpointAddress),
+ xfer_buf, xfer_size, pxrc_usb_irq, pxrc, 1);
+
+ pxrc->input = devm_input_allocate_device(&intf->dev);
+ if (!pxrc->input) {
+ dev_err(&intf->dev, "couldn't allocate input device\n");
+ return -ENOMEM;
+ }
+
+ pxrc->input->name = "PXRC Flight Controller Adapter";
+
+ usb_make_path(udev, pxrc->phys, sizeof(pxrc->phys));
+ strlcat(pxrc->phys, "/input0", sizeof(pxrc->phys));
+ pxrc->input->phys = pxrc->phys;
+
+ usb_to_input_id(udev, &pxrc->input->id);
+
+ pxrc->input->open = pxrc_open;
+ pxrc->input->close = pxrc_close;
+
+ input_set_capability(pxrc->input, EV_KEY, BTN_A);
+ input_set_abs_params(pxrc->input, ABS_X, 0, 255, 0, 0);
+ input_set_abs_params(pxrc->input, ABS_Y, 0, 255, 0, 0);
+ input_set_abs_params(pxrc->input, ABS_RX, 0, 255, 0, 0);
+ input_set_abs_params(pxrc->input, ABS_RY, 0, 255, 0, 0);
+ input_set_abs_params(pxrc->input, ABS_RUDDER, 0, 255, 0, 0);
+ input_set_abs_params(pxrc->input, ABS_THROTTLE, 0, 255, 0, 0);
+ input_set_abs_params(pxrc->input, ABS_MISC, 0, 255, 0, 0);
+
+ input_set_drvdata(pxrc->input, pxrc);
+
+ error = input_register_device(pxrc->input);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static void pxrc_disconnect(struct usb_interface *intf)
+{
+ /* All driver resources are devm-managed. */
+}
+
+static int pxrc_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct pxrc *pxrc = usb_get_intfdata(intf);
+
+ mutex_lock(&pxrc->pm_mutex);
+ if (pxrc->is_open)
+ usb_kill_urb(pxrc->urb);
+ mutex_unlock(&pxrc->pm_mutex);
+
+ return 0;
+}
+
+static int pxrc_resume(struct usb_interface *intf)
+{
+ struct pxrc *pxrc = usb_get_intfdata(intf);
+ int retval = 0;
+
+ mutex_lock(&pxrc->pm_mutex);
+ if (pxrc->is_open && usb_submit_urb(pxrc->urb, GFP_KERNEL) < 0)
+ retval = -EIO;
+
+ mutex_unlock(&pxrc->pm_mutex);
+ return retval;
+}
+
+static int pxrc_pre_reset(struct usb_interface *intf)
+{
+ struct pxrc *pxrc = usb_get_intfdata(intf);
+
+ mutex_lock(&pxrc->pm_mutex);
+ usb_kill_urb(pxrc->urb);
+ return 0;
+}
+
+static int pxrc_post_reset(struct usb_interface *intf)
+{
+ struct pxrc *pxrc = usb_get_intfdata(intf);
+ int retval = 0;
+
+ if (pxrc->is_open && usb_submit_urb(pxrc->urb, GFP_KERNEL) < 0)
+ retval = -EIO;
+
+ mutex_unlock(&pxrc->pm_mutex);
+
+ return retval;
+}
+
+static int pxrc_reset_resume(struct usb_interface *intf)
+{
+ return pxrc_resume(intf);
+}
+
+static const struct usb_device_id pxrc_table[] = {
+ { USB_DEVICE(PXRC_VENDOR_ID, PXRC_PRODUCT_ID) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, pxrc_table);
+
+static struct usb_driver pxrc_driver = {
+ .name = "pxrc",
+ .probe = pxrc_probe,
+ .disconnect = pxrc_disconnect,
+ .id_table = pxrc_table,
+ .suspend = pxrc_suspend,
+ .resume = pxrc_resume,
+ .pre_reset = pxrc_pre_reset,
+ .post_reset = pxrc_post_reset,
+ .reset_resume = pxrc_reset_resume,
+};
+
+module_usb_driver(pxrc_driver);
+
+MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
+MODULE_DESCRIPTION("PhoenixRC Flight Controller Adapter");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/joystick/sidewinder.c b/drivers/input/joystick/sidewinder.c
new file mode 100644
index 000000000..f46bf4d41
--- /dev/null
+++ b/drivers/input/joystick/sidewinder.c
@@ -0,0 +1,818 @@
+/*
+ * 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
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/gameport.h>
+#include <linux/jiffies.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;
+
+ switch (sw->type) {
+
+ case SW_ID_3DP:
+
+ if (sw_check(GB(0,64)) || (hat = (GB(6,1) << 3) | GB(60,3)) > 8)
+ return -1;
+
+ dev = sw->dev[0];
+
+ 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(sw->dev[i], ABS_X, GB(i*15+3,1) - GB(i*15+2,1));
+ input_report_abs(sw->dev[i], ABS_Y, GB(i*15+0,1) - GB(i*15+1,1));
+
+ for (j = 0; j < 10; j++)
+ input_report_key(sw->dev[i], sw_btn[SW_ID_GP][j], !GB(i*15+j+4,1));
+
+ input_sync(sw->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;
+
+ dev = sw->dev[0];
+ 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;
+
+ dev = sw->dev[0];
+ 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;
+
+ dev = sw->dev[0];
+ 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 = input_get_drvdata(dev);
+
+ gameport_start_polling(sw->gameport);
+ return 0;
+}
+
+static void sw_close(struct input_dev *dev)
+{
+ struct sw *sw = input_get_drvdata(dev);
+
+ 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, size_t size)
+{
+ 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;
+
+ snprintf(comment, size, " [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;
+ struct input_dev *input_dev;
+ int i, j, k, l;
+ int err = 0;
+ unsigned char *buf = NULL; /* [SW_LENGTH] */
+ unsigned char *idbuf = NULL; /* [SW_LENGTH] */
+ unsigned char m = 1;
+ char comment[40];
+
+ comment[0] = 0;
+
+ sw = kzalloc(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++; /* fall through */
+ 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++; /* fall through */
+ case 30:
+ sw->number++; /* fall through */
+ 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; /* fall through */
+ case 198:
+ sw->length = 22; /* fall through */
+ case 64:
+ sw->type = SW_ID_3DP;
+ if (j == 160)
+ sw_3dp_id(idbuf, comment, sizeof(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;
+
+ snprintf(sw->name, sizeof(sw->name),
+ "Microsoft SideWinder %s", sw_name[sw->type]);
+ snprintf(sw->phys[i], sizeof(sw->phys[i]),
+ "%s/input%d", gameport->phys, i);
+
+ sw->dev[i] = input_dev = input_allocate_device();
+ if (!input_dev) {
+ err = -ENOMEM;
+ goto fail3;
+ }
+
+ input_dev->name = sw->name;
+ input_dev->phys = sw->phys[i];
+ input_dev->id.bustype = BUS_GAMEPORT;
+ input_dev->id.vendor = GAMEPORT_ID_VENDOR_MICROSOFT;
+ input_dev->id.product = sw->type;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &gameport->dev;
+
+ input_set_drvdata(input_dev, sw);
+
+ input_dev->open = sw_open;
+ input_dev->close = sw_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ for (j = 0; (bits = sw_bit[sw->type][j]); j++) {
+ int min, max, fuzz, flat;
+
+ code = sw_abs[sw->type][j];
+ min = bits == 1 ? -1 : 0;
+ max = (1 << bits) - 1;
+ fuzz = (bits >> 1) >= 2 ? 1 << ((bits >> 1) - 2) : 0;
+ flat = code == ABS_THROTTLE || bits < 5 ?
+ 0 : 1 << (bits - 5);
+
+ input_set_abs_params(input_dev, code,
+ min, max, fuzz, flat);
+ }
+
+ for (j = 0; (code = sw_btn[sw->type][j]); j++)
+ __set_bit(code, input_dev->keybit);
+
+ dbg("%s%s [%d-bit id %d data %d]\n", sw->name, comment, m, l, k);
+
+ err = input_register_device(sw->dev[i]);
+ if (err)
+ goto fail4;
+ }
+
+ out: kfree(buf);
+ kfree(idbuf);
+
+ return err;
+
+ fail4: input_free_device(sw->dev[i]);
+ fail3: while (--i >= 0)
+ input_unregister_device(sw->dev[i]);
+ fail2: gameport_close(gameport);
+ fail1: gameport_set_drvdata(gameport, NULL);
+ kfree(sw);
+ goto out;
+}
+
+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",
+ .owner = THIS_MODULE,
+ },
+ .description = DRIVER_DESC,
+ .connect = sw_connect,
+ .disconnect = sw_disconnect,
+};
+
+module_gameport_driver(sw_drv);
diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c
new file mode 100644
index 000000000..197e0b5d5
--- /dev/null
+++ b/drivers/input/joystick/spaceball.c
@@ -0,0 +1,305 @@
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <asm/unaligned.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 9
+
+#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;
+ 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 input_dev *dev = spaceball->dev;
+ unsigned char *data = spaceball->data;
+ int i;
+
+ if (spaceball->idx < 2) return;
+
+ switch (spaceball->data[0]) {
+
+ case 'D': /* Ball data */
+ if (spaceball->idx != 15) return;
+ /*
+ * Skip first three bytes; read six axes worth of data.
+ * Axis values are signed 16-bit big-endian.
+ */
+ data += 3;
+ for (i = 0; i < ARRAY_SIZE(spaceball_axes); i++) {
+ input_report_abs(dev, spaceball_axes[i],
+ (__s16)get_unaligned_be16(&data[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 spaceball *spaceball = serio_get_drvdata(serio);
+
+ switch (data) {
+ case 0xd:
+ spaceball_process_packet(spaceball);
+ spaceball->idx = 0;
+ spaceball->escape = 0;
+ break;
+ case '^':
+ if (!spaceball->escape) {
+ spaceball->escape = 1;
+ break;
+ }
+ spaceball->escape = 0;
+ /* fall through */
+ case 'M':
+ case 'Q':
+ case 'S':
+ if (spaceball->escape) {
+ spaceball->escape = 0;
+ data &= 0x1f;
+ }
+ /* fall through */
+ 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);
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_unregister_device(spaceball->dev);
+ 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;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+ int i, id;
+
+ if ((id = serio->id.id) > SPACEBALL_MAX_ID)
+ return -ENODEV;
+
+ spaceball = kmalloc(sizeof(struct spaceball), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!spaceball || !input_dev)
+ goto fail1;
+
+ spaceball->dev = input_dev;
+ snprintf(spaceball->phys, sizeof(spaceball->phys), "%s/input0", serio->phys);
+
+ input_dev->name = spaceball_names[id];
+ input_dev->phys = spaceball->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_SPACEBALL;
+ input_dev->id.product = id;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ switch (id) {
+ case SPACEBALL_4000FLX:
+ case SPACEBALL_4000FLX_L:
+ input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_9);
+ input_dev->keybit[BIT_WORD(BTN_A)] |= BIT_MASK(BTN_A) |
+ BIT_MASK(BTN_B) | BIT_MASK(BTN_C) |
+ BIT_MASK(BTN_MODE);
+ /* fall through */
+ default:
+ input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_2) |
+ BIT_MASK(BTN_3) | BIT_MASK(BTN_4) |
+ BIT_MASK(BTN_5) | BIT_MASK(BTN_6) |
+ BIT_MASK(BTN_7) | BIT_MASK(BTN_8);
+ /* fall through */
+ case SPACEBALL_3003C:
+ input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_1) |
+ BIT_MASK(BTN_8);
+ }
+
+ for (i = 0; i < 3; i++) {
+ input_set_abs_params(input_dev, ABS_X + i, -8000, 8000, 8, 40);
+ input_set_abs_params(input_dev, ABS_RX + i, -1600, 1600, 2, 8);
+ }
+
+ serio_set_drvdata(serio, spaceball);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(spaceball->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(spaceball);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static const 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,
+};
+
+module_serio_driver(spaceball_drv);
diff --git a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c
new file mode 100644
index 000000000..20540ee71
--- /dev/null
+++ b/drivers/input/joystick/spaceorb.c
@@ -0,0 +1,235 @@
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.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 };
+
+/*
+ * Per-Orb data.
+ */
+
+struct spaceorb {
+ struct input_dev *dev;
+ 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 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;
+
+ 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] is %s\n",
+ dev->name, spaceorb->data + i, spaceorb->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 < 6; 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 "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 spaceorb* spaceorb = serio_get_drvdata(serio);
+
+ if (~data & 0x80) {
+ if (spaceorb->idx) spaceorb_process_packet(spaceorb);
+ 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);
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_unregister_device(spaceorb->dev);
+ 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;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+ int i;
+
+ spaceorb = kzalloc(sizeof(struct spaceorb), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!spaceorb || !input_dev)
+ goto fail1;
+
+ spaceorb->dev = input_dev;
+ snprintf(spaceorb->phys, sizeof(spaceorb->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "SpaceTec SpaceOrb 360 / Avenger";
+ input_dev->phys = spaceorb->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_SPACEORB;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ for (i = 0; i < 6; i++)
+ set_bit(spaceorb_buttons[i], input_dev->keybit);
+
+ for (i = 0; i < 6; i++)
+ input_set_abs_params(input_dev, spaceorb_axes[i], -508, 508, 0, 0);
+
+ serio_set_drvdata(serio, spaceorb);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(spaceorb->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(spaceorb);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static const 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,
+};
+
+module_serio_driver(spaceorb_drv);
diff --git a/drivers/input/joystick/stinger.c b/drivers/input/joystick/stinger.c
new file mode 100644
index 000000000..ba8579435
--- /dev/null
+++ b/drivers/input/joystick/stinger.c
@@ -0,0 +1,206 @@
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.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
+
+/*
+ * 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 input_dev *dev = stinger->dev;
+ unsigned char *data = stinger->data;
+
+ if (!stinger->idx) return;
+
+ 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 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);
+ 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);
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_unregister_device(stinger->dev);
+ 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;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+
+ stinger = kmalloc(sizeof(struct stinger), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!stinger || !input_dev)
+ goto fail1;
+
+ stinger->dev = input_dev;
+ snprintf(stinger->phys, sizeof(stinger->phys), "%s/serio0", serio->phys);
+
+ input_dev->name = "Gravis Stinger";
+ input_dev->phys = stinger->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_STINGER;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_A)] = BIT_MASK(BTN_A) | BIT_MASK(BTN_B) |
+ BIT_MASK(BTN_C) | BIT_MASK(BTN_X) | BIT_MASK(BTN_Y) |
+ BIT_MASK(BTN_Z) | BIT_MASK(BTN_TL) | BIT_MASK(BTN_TR) |
+ BIT_MASK(BTN_START) | BIT_MASK(BTN_SELECT);
+ input_set_abs_params(input_dev, ABS_X, -64, 64, 0, 4);
+ input_set_abs_params(input_dev, ABS_Y, -64, 64, 0, 4);
+
+ serio_set_drvdata(serio, stinger);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(stinger->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(stinger);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static const 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,
+};
+
+module_serio_driver(stinger_drv);
diff --git a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c
new file mode 100644
index 000000000..6f4a01cfe
--- /dev/null
+++ b/drivers/input/joystick/tmdc.c
@@ -0,0 +1,434 @@
+/*
+ * 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
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+#include <linux/jiffies.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 const unsigned char tmdc_byte_a[16] = { 0, 1, 3, 4, 6, 7 };
+static const unsigned char tmdc_byte_d[16] = { 2, 5, 8, 9 };
+
+static const signed char tmdc_abs[TMDC_ABS] =
+ { ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE, ABS_RX, ABS_RY, ABS_RZ };
+static const signed char tmdc_abs_hat[TMDC_ABS_HAT] =
+ { ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y };
+static const signed char tmdc_abs_at[TMDC_ABS] =
+ { ABS_X, ABS_Y, ABS_RUDDER, -1, ABS_THROTTLE };
+static const signed char tmdc_abs_fm[TMDC_ABS] =
+ { ABS_RX, ABS_RY, ABS_X, ABS_Y };
+
+static const 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 const 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 const 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 const 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 const struct {
+ int x;
+ int y;
+} tmdc_hat_to_axis[] = {{ 0, 0}, { 1, 0}, { 0,-1}, {-1, 0}, { 0, 1}};
+
+static const struct tmdc_model {
+ unsigned char id;
+ const char *name;
+ char abs;
+ char hats;
+ char btnc[4];
+ char btno[4];
+ const signed char *axes;
+ const short *buttons;
+} tmdc_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 }
+};
+
+
+struct tmdc_port {
+ struct input_dev *dev;
+ char name[64];
+ char phys[32];
+ int mode;
+ const signed char *abs;
+ const short *btn;
+ unsigned char absc;
+ unsigned char btnc[4];
+ unsigned char btno[4];
+};
+
+struct tmdc {
+ struct gameport *gameport;
+ struct tmdc_port *port[2];
+#if 0
+ 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];
+#endif
+ 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);
+}
+
+static int tmdc_parse_packet(struct tmdc_port *port, unsigned char *data)
+{
+ int i, k, l;
+
+ if (data[TMDC_BYTE_ID] != port->mode)
+ return -1;
+
+ for (i = 0; i < port->absc; i++) {
+ if (port->abs[i] < 0)
+ return 0;
+
+ input_report_abs(port->dev, port->abs[i], data[tmdc_byte_a[i]]);
+ }
+
+ switch (port->mode) {
+
+ case TMDC_MODE_M3DI:
+
+ i = tmdc_byte_d[0];
+ input_report_abs(port->dev, ABS_HAT0X, ((data[i] >> 3) & 1) - ((data[i] >> 1) & 1));
+ input_report_abs(port->dev, ABS_HAT0Y, ((data[i] >> 2) & 1) - ( data[i] & 1));
+ break;
+
+ case TMDC_MODE_AT:
+
+ i = tmdc_byte_a[3];
+ input_report_abs(port->dev, ABS_HAT0X, tmdc_hat_to_axis[(data[i] - 141) / 25].x);
+ input_report_abs(port->dev, ABS_HAT0Y, tmdc_hat_to_axis[(data[i] - 141) / 25].y);
+ break;
+
+ }
+
+ for (k = l = 0; k < 4; k++) {
+ for (i = 0; i < port->btnc[k]; i++)
+ input_report_key(port->dev, port->btn[i + l],
+ ((data[tmdc_byte_d[k]] >> (i + port->btno[k])) & 1));
+ l += port->btnc[k];
+ }
+
+ input_sync(port->dev);
+
+ return 0;
+}
+
+/*
+ * 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);
+ unsigned char r, bad = 0;
+ int i;
+
+ tmdc->reads++;
+
+ if ((r = tmdc_read_packet(tmdc->gameport, data)) != tmdc->exists)
+ bad = 1;
+ else {
+ for (i = 0; i < 2; i++) {
+ if (r & (1 << i) & tmdc->exists) {
+
+ if (tmdc_parse_packet(tmdc->port[i], data[i]))
+ bad = 1;
+ }
+ }
+ }
+
+ tmdc->bads += bad;
+}
+
+static int tmdc_open(struct input_dev *dev)
+{
+ struct tmdc *tmdc = input_get_drvdata(dev);
+
+ gameport_start_polling(tmdc->gameport);
+ return 0;
+}
+
+static void tmdc_close(struct input_dev *dev)
+{
+ struct tmdc *tmdc = input_get_drvdata(dev);
+
+ gameport_stop_polling(tmdc->gameport);
+}
+
+static int tmdc_setup_port(struct tmdc *tmdc, int idx, unsigned char *data)
+{
+ const struct tmdc_model *model;
+ struct tmdc_port *port;
+ struct input_dev *input_dev;
+ int i, j, b = 0;
+ int err;
+
+ tmdc->port[idx] = port = kzalloc(sizeof (struct tmdc_port), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!port || !input_dev) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ port->mode = data[TMDC_BYTE_ID];
+
+ for (model = tmdc_models; model->id && model->id != port->mode; model++)
+ /* empty */;
+
+ port->abs = model->axes;
+ port->btn = model->buttons;
+
+ if (!model->id) {
+ port->absc = data[TMDC_BYTE_DEF] >> 4;
+ for (i = 0; i < 4; i++)
+ port->btnc[i] = i < (data[TMDC_BYTE_DEF] & 0xf) ? 8 : 0;
+ } else {
+ port->absc = model->abs;
+ for (i = 0; i < 4; i++)
+ port->btnc[i] = model->btnc[i];
+ }
+
+ for (i = 0; i < 4; i++)
+ port->btno[i] = model->btno[i];
+
+ snprintf(port->name, sizeof(port->name), model->name,
+ port->absc, (data[TMDC_BYTE_DEF] & 0xf) << 3, port->mode);
+ snprintf(port->phys, sizeof(port->phys), "%s/input%d", tmdc->gameport->phys, i);
+
+ port->dev = input_dev;
+
+ input_dev->name = port->name;
+ input_dev->phys = port->phys;
+ input_dev->id.bustype = BUS_GAMEPORT;
+ input_dev->id.vendor = GAMEPORT_ID_VENDOR_THRUSTMASTER;
+ input_dev->id.product = model->id;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &tmdc->gameport->dev;
+
+ input_set_drvdata(input_dev, tmdc);
+
+ input_dev->open = tmdc_open;
+ input_dev->close = tmdc_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+ for (i = 0; i < port->absc && i < TMDC_ABS; i++)
+ if (port->abs[i] >= 0)
+ input_set_abs_params(input_dev, port->abs[i], 8, 248, 2, 4);
+
+ for (i = 0; i < model->hats && i < TMDC_ABS_HAT; i++)
+ input_set_abs_params(input_dev, tmdc_abs_hat[i], -1, 1, 0, 0);
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < port->btnc[i] && j < TMDC_BTN; j++)
+ set_bit(port->btn[j + b], input_dev->keybit);
+ b += port->btnc[i];
+ }
+
+ err = input_register_device(port->dev);
+ if (err)
+ goto fail;
+
+ return 0;
+
+ fail: input_free_device(input_dev);
+ kfree(port);
+ return err;
+}
+
+/*
+ * tmdc_probe() probes for ThrustMaster type joysticks.
+ */
+
+static int tmdc_connect(struct gameport *gameport, struct gameport_driver *drv)
+{
+ unsigned char data[2][TMDC_MAX_LENGTH];
+ struct tmdc *tmdc;
+ int i;
+ int err;
+
+ if (!(tmdc = kzalloc(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 (i = 0; i < 2; i++) {
+ if (tmdc->exists & (1 << i)) {
+
+ err = tmdc_setup_port(tmdc, i, data[i]);
+ if (err)
+ goto fail3;
+ }
+ }
+
+ return 0;
+
+ fail3: while (--i >= 0) {
+ if (tmdc->port[i]) {
+ input_unregister_device(tmdc->port[i]->dev);
+ kfree(tmdc->port[i]);
+ }
+ }
+ 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->port[i]) {
+ input_unregister_device(tmdc->port[i]->dev);
+ kfree(tmdc->port[i]);
+ }
+ }
+ gameport_close(gameport);
+ gameport_set_drvdata(gameport, NULL);
+ kfree(tmdc);
+}
+
+static struct gameport_driver tmdc_drv = {
+ .driver = {
+ .name = "tmdc",
+ .owner = THIS_MODULE,
+ },
+ .description = DRIVER_DESC,
+ .connect = tmdc_connect,
+ .disconnect = tmdc_disconnect,
+};
+
+module_gameport_driver(tmdc_drv);
diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c
new file mode 100644
index 000000000..bf2f9925e
--- /dev/null
+++ b/drivers/input/joystick/turbografx.c
@@ -0,0 +1,324 @@
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/parport.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("TurboGraFX parallel port interface driver");
+MODULE_LICENSE("GPL");
+
+#define TGFX_MAX_PORTS 3
+#define TGFX_MAX_DEVICES 7
+
+struct tgfx_config {
+ int args[TGFX_MAX_DEVICES + 1];
+ unsigned int nargs;
+};
+
+static struct tgfx_config tgfx_cfg[TGFX_MAX_PORTS];
+
+module_param_array_named(map, tgfx_cfg[0].args, int, &tgfx_cfg[0].nargs, 0);
+MODULE_PARM_DESC(map, "Describes first set of devices (<parport#>,<js1>,<js2>,..<js7>");
+module_param_array_named(map2, tgfx_cfg[1].args, int, &tgfx_cfg[1].nargs, 0);
+MODULE_PARM_DESC(map2, "Describes second set of devices");
+module_param_array_named(map3, tgfx_cfg[2].args, int, &tgfx_cfg[2].nargs, 0);
+MODULE_PARM_DESC(map3, "Describes third set of devices");
+
+#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 struct tgfx {
+ struct pardevice *pd;
+ struct timer_list timer;
+ struct input_dev *dev[TGFX_MAX_DEVICES];
+ char name[TGFX_MAX_DEVICES][64];
+ char phys[TGFX_MAX_DEVICES][32];
+ int sticks;
+ int used;
+ int parportno;
+ struct mutex sem;
+} *tgfx_base[TGFX_MAX_PORTS];
+
+/*
+ * tgfx_timer() reads and analyzes TurboGraFX joystick data.
+ */
+
+static void tgfx_timer(struct timer_list *t)
+{
+ struct tgfx *tgfx = from_timer(tgfx, t, timer);
+ 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 = input_get_drvdata(dev);
+ int err;
+
+ err = mutex_lock_interruptible(&tgfx->sem);
+ if (err)
+ return err;
+
+ if (!tgfx->used++) {
+ parport_claim(tgfx->pd);
+ parport_write_control(tgfx->pd->port, 0x04);
+ mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME);
+ }
+
+ mutex_unlock(&tgfx->sem);
+ return 0;
+}
+
+static void tgfx_close(struct input_dev *dev)
+{
+ struct tgfx *tgfx = input_get_drvdata(dev);
+
+ mutex_lock(&tgfx->sem);
+ if (!--tgfx->used) {
+ del_timer_sync(&tgfx->timer);
+ parport_write_control(tgfx->pd->port, 0x00);
+ parport_release(tgfx->pd);
+ }
+ mutex_unlock(&tgfx->sem);
+}
+
+
+
+/*
+ * tgfx_probe() probes for tg gamepads.
+ */
+
+static void tgfx_attach(struct parport *pp)
+{
+ struct tgfx *tgfx;
+ struct input_dev *input_dev;
+ struct pardevice *pd;
+ int i, j, port_idx;
+ int *n_buttons, n_devs;
+ struct pardev_cb tgfx_parport_cb;
+
+ for (port_idx = 0; port_idx < TGFX_MAX_PORTS; port_idx++) {
+ if (tgfx_cfg[port_idx].nargs == 0 ||
+ tgfx_cfg[port_idx].args[0] < 0)
+ continue;
+ if (tgfx_cfg[port_idx].args[0] == pp->number)
+ break;
+ }
+
+ if (port_idx == TGFX_MAX_PORTS) {
+ pr_debug("Not using parport%d.\n", pp->number);
+ return;
+ }
+ n_buttons = tgfx_cfg[port_idx].args + 1;
+ n_devs = tgfx_cfg[port_idx].nargs - 1;
+
+ memset(&tgfx_parport_cb, 0, sizeof(tgfx_parport_cb));
+ tgfx_parport_cb.flags = PARPORT_FLAG_EXCL;
+
+ pd = parport_register_dev_model(pp, "turbografx", &tgfx_parport_cb,
+ port_idx);
+ if (!pd) {
+ pr_err("parport busy already - lp.o loaded?\n");
+ return;
+ }
+
+ tgfx = kzalloc(sizeof(struct tgfx), GFP_KERNEL);
+ if (!tgfx) {
+ printk(KERN_ERR "turbografx.c: Not enough memory\n");
+ goto err_unreg_pardev;
+ }
+
+ mutex_init(&tgfx->sem);
+ tgfx->pd = pd;
+ tgfx->parportno = pp->number;
+ timer_setup(&tgfx->timer, tgfx_timer, 0);
+
+ for (i = 0; i < n_devs; i++) {
+ if (n_buttons[i] < 1)
+ continue;
+
+ if (n_buttons[i] > ARRAY_SIZE(tgfx_buttons)) {
+ printk(KERN_ERR "turbografx.c: Invalid number of buttons %d\n", n_buttons[i]);
+ goto err_unreg_devs;
+ }
+
+ tgfx->dev[i] = input_dev = input_allocate_device();
+ if (!input_dev) {
+ printk(KERN_ERR "turbografx.c: Not enough memory for input device\n");
+ goto err_unreg_devs;
+ }
+
+ tgfx->sticks |= (1 << i);
+ snprintf(tgfx->name[i], sizeof(tgfx->name[i]),
+ "TurboGraFX %d-button Multisystem joystick", n_buttons[i]);
+ snprintf(tgfx->phys[i], sizeof(tgfx->phys[i]),
+ "%s/input%d", tgfx->pd->port->name, i);
+
+ input_dev->name = tgfx->name[i];
+ input_dev->phys = tgfx->phys[i];
+ input_dev->id.bustype = BUS_PARPORT;
+ input_dev->id.vendor = 0x0003;
+ input_dev->id.product = n_buttons[i];
+ input_dev->id.version = 0x0100;
+
+ input_set_drvdata(input_dev, tgfx);
+
+ input_dev->open = tgfx_open;
+ input_dev->close = tgfx_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_set_abs_params(input_dev, ABS_X, -1, 1, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, -1, 1, 0, 0);
+
+ for (j = 0; j < n_buttons[i]; j++)
+ set_bit(tgfx_buttons[j], input_dev->keybit);
+
+ if (input_register_device(tgfx->dev[i]))
+ goto err_free_dev;
+ }
+
+ if (!tgfx->sticks) {
+ printk(KERN_ERR "turbografx.c: No valid devices specified\n");
+ goto err_free_tgfx;
+ }
+
+ tgfx_base[port_idx] = tgfx;
+ return;
+
+ err_free_dev:
+ input_free_device(tgfx->dev[i]);
+ err_unreg_devs:
+ while (--i >= 0)
+ if (tgfx->dev[i])
+ input_unregister_device(tgfx->dev[i]);
+ err_free_tgfx:
+ kfree(tgfx);
+ err_unreg_pardev:
+ parport_unregister_device(pd);
+}
+
+static void tgfx_detach(struct parport *port)
+{
+ int i;
+ struct tgfx *tgfx;
+
+ for (i = 0; i < TGFX_MAX_PORTS; i++) {
+ if (tgfx_base[i] && tgfx_base[i]->parportno == port->number)
+ break;
+ }
+
+ if (i == TGFX_MAX_PORTS)
+ return;
+
+ tgfx = tgfx_base[i];
+ tgfx_base[i] = NULL;
+
+ for (i = 0; i < TGFX_MAX_DEVICES; i++)
+ if (tgfx->dev[i])
+ input_unregister_device(tgfx->dev[i]);
+ parport_unregister_device(tgfx->pd);
+ kfree(tgfx);
+}
+
+static struct parport_driver tgfx_parport_driver = {
+ .name = "turbografx",
+ .match_port = tgfx_attach,
+ .detach = tgfx_detach,
+ .devmodel = true,
+};
+
+static int __init tgfx_init(void)
+{
+ int i;
+ int have_dev = 0;
+
+ for (i = 0; i < TGFX_MAX_PORTS; i++) {
+ if (tgfx_cfg[i].nargs == 0 || tgfx_cfg[i].args[0] < 0)
+ continue;
+
+ if (tgfx_cfg[i].nargs < 2) {
+ printk(KERN_ERR "turbografx.c: at least one joystick must be specified\n");
+ return -EINVAL;
+ }
+
+ have_dev = 1;
+ }
+
+ if (!have_dev)
+ return -ENODEV;
+
+ return parport_register_driver(&tgfx_parport_driver);
+}
+
+static void __exit tgfx_exit(void)
+{
+ parport_unregister_driver(&tgfx_parport_driver);
+}
+
+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 000000000..e60cb004c
--- /dev/null
+++ b/drivers/input/joystick/twidjoy.c
@@ -0,0 +1,259 @@
+/*
+ * 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>
+
+#define DRIVER_DESC "Handykey Twiddler keyboard as a joystick driver"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define TWIDJOY_MAX_LENGTH 5
+
+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 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);
+
+ 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);
+}
+
+/*
+ * 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 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);
+ 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);
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_unregister_device(twidjoy->dev);
+ 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;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+ int i;
+
+ twidjoy = kzalloc(sizeof(struct twidjoy), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!twidjoy || !input_dev)
+ goto fail1;
+
+ twidjoy->dev = input_dev;
+ snprintf(twidjoy->phys, sizeof(twidjoy->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "Handykey Twiddler";
+ input_dev->phys = twidjoy->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_TWIDJOY;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_set_abs_params(input_dev, ABS_X, -50, 50, 4, 4);
+ input_set_abs_params(input_dev, ABS_Y, -50, 50, 4, 4);
+
+ for (bp = twidjoy_buttons; bp->bitmask; bp++)
+ for (i = 0; i < bp->bitmask; i++)
+ set_bit(bp->buttons[i], input_dev->keybit);
+
+ serio_set_drvdata(serio, twidjoy);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(twidjoy->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(twidjoy);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static const 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,
+};
+
+module_serio_driver(twidjoy_drv);
diff --git a/drivers/input/joystick/walkera0701.c b/drivers/input/joystick/walkera0701.c
new file mode 100644
index 000000000..dce313dc2
--- /dev/null
+++ b/drivers/input/joystick/walkera0701.c
@@ -0,0 +1,314 @@
+/*
+ * Parallel port to Walkera WK-0701 TX joystick
+ *
+ * Copyright (c) 2008 Peter Popovec
+ *
+ * More about driver: <file:Documentation/input/devices/walkera0701.rst>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define RESERVE 20000
+#define SYNC_PULSE 1306000
+#define BIN0_PULSE 288000
+#define BIN1_PULSE 438000
+
+#define ANALOG_MIN_PULSE 318000
+#define ANALOG_MAX_PULSE 878000
+#define ANALOG_DELTA 80000
+
+#define BIN_SAMPLE ((BIN0_PULSE + BIN1_PULSE) / 2)
+
+#define NO_SYNC 25
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/parport.h>
+#include <linux/input.h>
+#include <linux/hrtimer.h>
+
+MODULE_AUTHOR("Peter Popovec <popovec@fei.tuke.sk>");
+MODULE_DESCRIPTION("Walkera WK-0701 TX as joystick");
+MODULE_LICENSE("GPL");
+
+static unsigned int walkera0701_pp_no;
+module_param_named(port, walkera0701_pp_no, int, 0);
+MODULE_PARM_DESC(port,
+ "Parallel port adapter for Walkera WK-0701 TX (default is 0)");
+
+/*
+ * For now, only one device is supported, if somebody need more devices, code
+ * can be expanded, one struct walkera_dev per device must be allocated and
+ * set up by walkera0701_connect (release of device by walkera0701_disconnect)
+ */
+
+struct walkera_dev {
+ unsigned char buf[25];
+ u64 irq_time, irq_lasttime;
+ int counter;
+ int ack;
+
+ struct input_dev *input_dev;
+ struct hrtimer timer;
+
+ struct parport *parport;
+ struct pardevice *pardevice;
+};
+
+static struct walkera_dev w_dev;
+
+static inline void walkera0701_parse_frame(struct walkera_dev *w)
+{
+ int i;
+ int val1, val2, val3, val4, val5, val6, val7, val8;
+ int magic, magic_bit;
+ int crc1, crc2;
+
+ for (crc1 = crc2 = i = 0; i < 10; i++) {
+ crc1 += w->buf[i] & 7;
+ crc2 += (w->buf[i] & 8) >> 3;
+ }
+ if ((w->buf[10] & 7) != (crc1 & 7))
+ return;
+ if (((w->buf[10] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1))
+ return;
+ for (crc1 = crc2 = 0, i = 11; i < 23; i++) {
+ crc1 += w->buf[i] & 7;
+ crc2 += (w->buf[i] & 8) >> 3;
+ }
+ if ((w->buf[23] & 7) != (crc1 & 7))
+ return;
+ if (((w->buf[23] & 8) >> 3) != (((crc1 >> 3) + crc2) & 1))
+ return;
+ val1 = ((w->buf[0] & 7) * 256 + w->buf[1] * 16 + w->buf[2]) >> 2;
+ val1 *= ((w->buf[0] >> 2) & 2) - 1; /* sign */
+ val2 = (w->buf[2] & 1) << 8 | (w->buf[3] << 4) | w->buf[4];
+ val2 *= (w->buf[2] & 2) - 1; /* sign */
+ val3 = ((w->buf[5] & 7) * 256 + w->buf[6] * 16 + w->buf[7]) >> 2;
+ val3 *= ((w->buf[5] >> 2) & 2) - 1; /* sign */
+ val4 = (w->buf[7] & 1) << 8 | (w->buf[8] << 4) | w->buf[9];
+ val4 *= (w->buf[7] & 2) - 1; /* sign */
+ val5 = ((w->buf[11] & 7) * 256 + w->buf[12] * 16 + w->buf[13]) >> 2;
+ val5 *= ((w->buf[11] >> 2) & 2) - 1; /* sign */
+ val6 = (w->buf[13] & 1) << 8 | (w->buf[14] << 4) | w->buf[15];
+ val6 *= (w->buf[13] & 2) - 1; /* sign */
+ val7 = ((w->buf[16] & 7) * 256 + w->buf[17] * 16 + w->buf[18]) >> 2;
+ val7 *= ((w->buf[16] >> 2) & 2) - 1; /*sign */
+ val8 = (w->buf[18] & 1) << 8 | (w->buf[19] << 4) | w->buf[20];
+ val8 *= (w->buf[18] & 2) - 1; /*sign */
+
+ magic = (w->buf[21] << 4) | w->buf[22];
+ magic_bit = (w->buf[24] & 8) >> 3;
+ pr_debug("%4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n",
+ val1, val2, val3, val4, val5, val6, val7, val8,
+ magic, magic_bit);
+
+ input_report_abs(w->input_dev, ABS_X, val2);
+ input_report_abs(w->input_dev, ABS_Y, val1);
+ input_report_abs(w->input_dev, ABS_Z, val6);
+ input_report_abs(w->input_dev, ABS_THROTTLE, val3);
+ input_report_abs(w->input_dev, ABS_RUDDER, val4);
+ input_report_abs(w->input_dev, ABS_MISC, val7);
+ input_report_key(w->input_dev, BTN_GEAR_DOWN, val5 > 0);
+}
+
+static inline int read_ack(struct pardevice *p)
+{
+ return parport_read_status(p->port) & 0x40;
+}
+
+/* falling edge, prepare to BIN value calculation */
+static void walkera0701_irq_handler(void *handler_data)
+{
+ u64 pulse_time;
+ struct walkera_dev *w = handler_data;
+
+ w->irq_time = ktime_to_ns(ktime_get());
+ pulse_time = w->irq_time - w->irq_lasttime;
+ w->irq_lasttime = w->irq_time;
+
+ /* cancel timer, if in handler or active do resync */
+ if (unlikely(0 != hrtimer_try_to_cancel(&w->timer))) {
+ w->counter = NO_SYNC;
+ return;
+ }
+
+ if (w->counter < NO_SYNC) {
+ if (w->ack) {
+ pulse_time -= BIN1_PULSE;
+ w->buf[w->counter] = 8;
+ } else {
+ pulse_time -= BIN0_PULSE;
+ w->buf[w->counter] = 0;
+ }
+ if (w->counter == 24) { /* full frame */
+ walkera0701_parse_frame(w);
+ w->counter = NO_SYNC;
+ if (abs(pulse_time - SYNC_PULSE) < RESERVE) /* new frame sync */
+ w->counter = 0;
+ } else {
+ if ((pulse_time > (ANALOG_MIN_PULSE - RESERVE)
+ && (pulse_time < (ANALOG_MAX_PULSE + RESERVE)))) {
+ pulse_time -= (ANALOG_MIN_PULSE - RESERVE);
+ pulse_time = (u32) pulse_time / ANALOG_DELTA; /* overtiping is safe, pulsetime < s32.. */
+ w->buf[w->counter++] |= (pulse_time & 7);
+ } else
+ w->counter = NO_SYNC;
+ }
+ } else if (abs(pulse_time - SYNC_PULSE - BIN0_PULSE) <
+ RESERVE + BIN1_PULSE - BIN0_PULSE) /* frame sync .. */
+ w->counter = 0;
+
+ hrtimer_start(&w->timer, BIN_SAMPLE, HRTIMER_MODE_REL);
+}
+
+static enum hrtimer_restart timer_handler(struct hrtimer
+ *handle)
+{
+ struct walkera_dev *w;
+
+ w = container_of(handle, struct walkera_dev, timer);
+ w->ack = read_ack(w->pardevice);
+
+ return HRTIMER_NORESTART;
+}
+
+static int walkera0701_open(struct input_dev *dev)
+{
+ struct walkera_dev *w = input_get_drvdata(dev);
+
+ if (parport_claim(w->pardevice))
+ return -EBUSY;
+
+ parport_enable_irq(w->parport);
+ return 0;
+}
+
+static void walkera0701_close(struct input_dev *dev)
+{
+ struct walkera_dev *w = input_get_drvdata(dev);
+
+ parport_disable_irq(w->parport);
+ hrtimer_cancel(&w->timer);
+
+ parport_release(w->pardevice);
+}
+
+static void walkera0701_attach(struct parport *pp)
+{
+ struct pardev_cb walkera0701_parport_cb;
+ struct walkera_dev *w = &w_dev;
+
+ if (pp->number != walkera0701_pp_no) {
+ pr_debug("Not using parport%d.\n", pp->number);
+ return;
+ }
+
+ if (pp->irq == -1) {
+ pr_err("parport %d does not have interrupt assigned\n",
+ pp->number);
+ return;
+ }
+
+ w->parport = pp;
+
+ memset(&walkera0701_parport_cb, 0, sizeof(walkera0701_parport_cb));
+ walkera0701_parport_cb.flags = PARPORT_FLAG_EXCL;
+ walkera0701_parport_cb.irq_func = walkera0701_irq_handler;
+ walkera0701_parport_cb.private = w;
+
+ w->pardevice = parport_register_dev_model(pp, "walkera0701",
+ &walkera0701_parport_cb, 0);
+
+ if (!w->pardevice) {
+ pr_err("failed to register parport device\n");
+ return;
+ }
+
+ if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT)) {
+ pr_err("failed to negotiate parport mode\n");
+ goto err_unregister_device;
+ }
+
+ hrtimer_init(&w->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ w->timer.function = timer_handler;
+
+ w->input_dev = input_allocate_device();
+ if (!w->input_dev) {
+ pr_err("failed to allocate input device\n");
+ goto err_unregister_device;
+ }
+
+ input_set_drvdata(w->input_dev, w);
+ w->input_dev->name = "Walkera WK-0701 TX";
+ w->input_dev->phys = w->parport->name;
+ w->input_dev->id.bustype = BUS_PARPORT;
+
+ /* TODO what id vendor/product/version ? */
+ w->input_dev->id.vendor = 0x0001;
+ w->input_dev->id.product = 0x0001;
+ w->input_dev->id.version = 0x0100;
+ w->input_dev->dev.parent = w->parport->dev;
+ w->input_dev->open = walkera0701_open;
+ w->input_dev->close = walkera0701_close;
+
+ w->input_dev->evbit[0] = BIT(EV_ABS) | BIT_MASK(EV_KEY);
+ w->input_dev->keybit[BIT_WORD(BTN_GEAR_DOWN)] = BIT_MASK(BTN_GEAR_DOWN);
+
+ input_set_abs_params(w->input_dev, ABS_X, -512, 512, 0, 0);
+ input_set_abs_params(w->input_dev, ABS_Y, -512, 512, 0, 0);
+ input_set_abs_params(w->input_dev, ABS_Z, -512, 512, 0, 0);
+ input_set_abs_params(w->input_dev, ABS_THROTTLE, -512, 512, 0, 0);
+ input_set_abs_params(w->input_dev, ABS_RUDDER, -512, 512, 0, 0);
+ input_set_abs_params(w->input_dev, ABS_MISC, -512, 512, 0, 0);
+
+ if (input_register_device(w->input_dev)) {
+ pr_err("failed to register input device\n");
+ goto err_free_input_dev;
+ }
+
+ return;
+
+err_free_input_dev:
+ input_free_device(w->input_dev);
+err_unregister_device:
+ parport_unregister_device(w->pardevice);
+}
+
+static void walkera0701_detach(struct parport *port)
+{
+ struct walkera_dev *w = &w_dev;
+
+ if (!w->pardevice || w->parport->number != port->number)
+ return;
+
+ input_unregister_device(w->input_dev);
+ parport_unregister_device(w->pardevice);
+ w->parport = NULL;
+}
+
+static struct parport_driver walkera0701_parport_driver = {
+ .name = "walkera0701",
+ .match_port = walkera0701_attach,
+ .detach = walkera0701_detach,
+ .devmodel = true,
+};
+
+static int __init walkera0701_init(void)
+{
+ return parport_register_driver(&walkera0701_parport_driver);
+}
+
+static void __exit walkera0701_exit(void)
+{
+ parport_unregister_driver(&walkera0701_parport_driver);
+}
+
+module_init(walkera0701_init);
+module_exit(walkera0701_exit);
diff --git a/drivers/input/joystick/warrior.c b/drivers/input/joystick/warrior.c
new file mode 100644
index 000000000..b60cab168
--- /dev/null
+++ b/drivers/input/joystick/warrior.c
@@ -0,0 +1,215 @@
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.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 };
+
+/*
+ * 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 input_dev *dev = warrior->dev;
+ unsigned char *data = warrior->data;
+
+ if (!warrior->idx) return;
+
+ 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 warrior *warrior = serio_get_drvdata(serio);
+
+ if (data & 0x80) {
+ if (warrior->idx) warrior_process_packet(warrior);
+ 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);
+ 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);
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_unregister_device(warrior->dev);
+ 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;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+
+ warrior = kzalloc(sizeof(struct warrior), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!warrior || !input_dev)
+ goto fail1;
+
+ warrior->dev = input_dev;
+ snprintf(warrior->phys, sizeof(warrior->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "Logitech WingMan Warrior";
+ input_dev->phys = warrior->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_WARRIOR;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) |
+ BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TRIGGER)] = BIT_MASK(BTN_TRIGGER) |
+ BIT_MASK(BTN_THUMB) | BIT_MASK(BTN_TOP) | BIT_MASK(BTN_TOP2);
+ input_dev->relbit[0] = BIT_MASK(REL_DIAL);
+ input_set_abs_params(input_dev, ABS_X, -64, 64, 0, 8);
+ input_set_abs_params(input_dev, ABS_Y, -64, 64, 0, 8);
+ input_set_abs_params(input_dev, ABS_THROTTLE, -112, 112, 0, 0);
+ input_set_abs_params(input_dev, ABS_HAT0X, -1, 1, 0, 0);
+ input_set_abs_params(input_dev, ABS_HAT0Y, -1, 1, 0, 0);
+
+ serio_set_drvdata(serio, warrior);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(warrior->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(warrior);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static const 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,
+};
+
+module_serio_driver(warrior_drv);
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
new file mode 100644
index 000000000..4423db71e
--- /dev/null
+++ b/drivers/input/joystick/xpad.c
@@ -0,0 +1,1989 @@
+/*
+ * X-Box gamepad driver
+ *
+ * Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de>
+ * 2004 Oliver Schwartz <Oliver.Schwartz@gmx.de>,
+ * Steven Toth <steve@toth.demon.co.uk>,
+ * Franz Lehner <franz@caos.at>,
+ * Ivan Hawkes <blackhawk@ivanhawkes.com>
+ * 2005 Dominic Cerquetti <binary1230@yahoo.com>
+ * 2006 Adam Buchbinder <adam.buchbinder@gmail.com>
+ * 2007 Jan Kratochvil <honza@jikos.cz>
+ * 2010 Christoph Fritz <chf.fritz@googlemail.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
+ *
+ *
+ * This driver is based on:
+ * - information from http://euc.jp/periphs/xbox-controller.ja.html
+ * - the iForce driver drivers/char/joystick/iforce.c
+ * - the skeleton-driver drivers/usb/usb-skeleton.c
+ * - Xbox 360 information http://www.free60.org/wiki/Gamepad
+ * - Xbox One information https://github.com/quantus/xbox-one-controller-protocol
+ *
+ * Thanks to:
+ * - ITO Takayuki for providing essential xpad information on his website
+ * - Vojtech Pavlik - iforce driver / input subsystem
+ * - Greg Kroah-Hartman - usb-skeleton driver
+ * - XBOX Linux project - extra USB id's
+ * - Pekka Pöyry (quantus) - Xbox One controller reverse engineering
+ *
+ * TODO:
+ * - fine tune axes (especially trigger axes)
+ * - fix "analog" buttons (reported as digital now)
+ * - get rumble working
+ * - need USB IDs for other dance pads
+ *
+ * History:
+ *
+ * 2002-06-27 - 0.0.1 : first version, just said "XBOX HID controller"
+ *
+ * 2002-07-02 - 0.0.2 : basic working version
+ * - all axes and 9 of the 10 buttons work (german InterAct device)
+ * - the black button does not work
+ *
+ * 2002-07-14 - 0.0.3 : rework by Vojtech Pavlik
+ * - indentation fixes
+ * - usb + input init sequence fixes
+ *
+ * 2002-07-16 - 0.0.4 : minor changes, merge with Vojtech's v0.0.3
+ * - verified the lack of HID and report descriptors
+ * - verified that ALL buttons WORK
+ * - fixed d-pad to axes mapping
+ *
+ * 2002-07-17 - 0.0.5 : simplified d-pad handling
+ *
+ * 2004-10-02 - 0.0.6 : DDR pad support
+ * - borrowed from the XBOX linux kernel
+ * - USB id's for commonly used dance pads are present
+ * - dance pads will map D-PAD to buttons, not axes
+ * - pass the module paramater 'dpad_to_buttons' to force
+ * the D-PAD to map to buttons if your pad is not detected
+ *
+ * Later changes can be tracked in SCM.
+ */
+
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/rcupdate.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+#include <linux/usb/quirks.h>
+
+#define XPAD_PKT_LEN 64
+
+/*
+ * xbox d-pads should map to buttons, as is required for DDR pads
+ * but we map them to axes when possible to simplify things
+ */
+#define MAP_DPAD_TO_BUTTONS (1 << 0)
+#define MAP_TRIGGERS_TO_BUTTONS (1 << 1)
+#define MAP_STICKS_TO_NULL (1 << 2)
+#define DANCEPAD_MAP_CONFIG (MAP_DPAD_TO_BUTTONS | \
+ MAP_TRIGGERS_TO_BUTTONS | MAP_STICKS_TO_NULL)
+
+#define XTYPE_XBOX 0
+#define XTYPE_XBOX360 1
+#define XTYPE_XBOX360W 2
+#define XTYPE_XBOXONE 3
+#define XTYPE_UNKNOWN 4
+
+static bool dpad_to_buttons;
+module_param(dpad_to_buttons, bool, S_IRUGO);
+MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads");
+
+static bool triggers_to_buttons;
+module_param(triggers_to_buttons, bool, S_IRUGO);
+MODULE_PARM_DESC(triggers_to_buttons, "Map triggers to buttons rather than axes for unknown pads");
+
+static bool sticks_to_null;
+module_param(sticks_to_null, bool, S_IRUGO);
+MODULE_PARM_DESC(sticks_to_null, "Do not map sticks at all for unknown pads");
+
+static bool auto_poweroff = true;
+module_param(auto_poweroff, bool, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(auto_poweroff, "Power off wireless controllers on suspend");
+
+static const struct xpad_device {
+ u16 idVendor;
+ u16 idProduct;
+ char *name;
+ u8 mapping;
+ u8 xtype;
+} xpad_device[] = {
+ { 0x0079, 0x18d4, "GPD Win 2 X-Box Controller", 0, XTYPE_XBOX360 },
+ { 0x044f, 0x0f00, "Thrustmaster Wheel", 0, XTYPE_XBOX },
+ { 0x044f, 0x0f03, "Thrustmaster Wheel", 0, XTYPE_XBOX },
+ { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX },
+ { 0x044f, 0x0f10, "Thrustmaster Modena GT Wheel", 0, XTYPE_XBOX },
+ { 0x044f, 0xb326, "Thrustmaster Gamepad GP XID", 0, XTYPE_XBOX360 },
+ { 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", 0, XTYPE_XBOX },
+ { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", 0, XTYPE_XBOX },
+ { 0x045e, 0x0287, "Microsoft Xbox Controller S", 0, XTYPE_XBOX },
+ { 0x045e, 0x0288, "Microsoft Xbox Controller S v2", 0, XTYPE_XBOX },
+ { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", 0, XTYPE_XBOX },
+ { 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 },
+ { 0x045e, 0x028f, "Microsoft X-Box 360 pad v2", 0, XTYPE_XBOX360 },
+ { 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
+ { 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE },
+ { 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE },
+ { 0x045e, 0x02e3, "Microsoft X-Box One Elite pad", 0, XTYPE_XBOXONE },
+ { 0x045e, 0x02ea, "Microsoft X-Box One S pad", 0, XTYPE_XBOXONE },
+ { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
+ { 0x046d, 0xc21d, "Logitech Gamepad F310", 0, XTYPE_XBOX360 },
+ { 0x046d, 0xc21e, "Logitech Gamepad F510", 0, XTYPE_XBOX360 },
+ { 0x046d, 0xc21f, "Logitech Gamepad F710", 0, XTYPE_XBOX360 },
+ { 0x046d, 0xc242, "Logitech Chillstream Controller", 0, XTYPE_XBOX360 },
+ { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", 0, XTYPE_XBOX },
+ { 0x046d, 0xca88, "Logitech Compact Controller for Xbox", 0, XTYPE_XBOX },
+ { 0x046d, 0xca8a, "Logitech Precision Vibration Feedback Wheel", 0, XTYPE_XBOX },
+ { 0x046d, 0xcaa3, "Logitech DriveFx Racing Wheel", 0, XTYPE_XBOX360 },
+ { 0x056e, 0x2004, "Elecom JC-U3613M", 0, XTYPE_XBOX360 },
+ { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", 0, XTYPE_XBOX },
+ { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", 0, XTYPE_XBOX },
+ { 0x05fe, 0x3030, "Chic Controller", 0, XTYPE_XBOX },
+ { 0x05fe, 0x3031, "Chic Controller", 0, XTYPE_XBOX },
+ { 0x062a, 0x0020, "Logic3 Xbox GamePad", 0, XTYPE_XBOX },
+ { 0x062a, 0x0033, "Competition Pro Steering Wheel", 0, XTYPE_XBOX },
+ { 0x06a3, 0x0200, "Saitek Racing Wheel", 0, XTYPE_XBOX },
+ { 0x06a3, 0x0201, "Saitek Adrenalin", 0, XTYPE_XBOX },
+ { 0x06a3, 0xf51a, "Saitek P3600", 0, XTYPE_XBOX360 },
+ { 0x0738, 0x4506, "Mad Catz 4506 Wireless Controller", 0, XTYPE_XBOX },
+ { 0x0738, 0x4516, "Mad Catz Control Pad", 0, XTYPE_XBOX },
+ { 0x0738, 0x4520, "Mad Catz Control Pad Pro", 0, XTYPE_XBOX },
+ { 0x0738, 0x4522, "Mad Catz LumiCON", 0, XTYPE_XBOX },
+ { 0x0738, 0x4526, "Mad Catz Control Pad Pro", 0, XTYPE_XBOX },
+ { 0x0738, 0x4530, "Mad Catz Universal MC2 Racing Wheel and Pedals", 0, XTYPE_XBOX },
+ { 0x0738, 0x4536, "Mad Catz MicroCON", 0, XTYPE_XBOX },
+ { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+ { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", 0, XTYPE_XBOX },
+ { 0x0738, 0x4586, "Mad Catz MicroCon Wireless Controller", 0, XTYPE_XBOX },
+ { 0x0738, 0x4588, "Mad Catz Blaster", 0, XTYPE_XBOX },
+ { 0x0738, 0x45ff, "Mad Catz Beat Pad (w/ Handle)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+ { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", 0, XTYPE_XBOX360 },
+ { 0x0738, 0x4718, "Mad Catz Street Fighter IV FightStick SE", 0, XTYPE_XBOX360 },
+ { 0x0738, 0x4726, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 },
+ { 0x0738, 0x4728, "Mad Catz Street Fighter IV FightPad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x0738, 0x4736, "Mad Catz MicroCon Gamepad", 0, XTYPE_XBOX360 },
+ { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x0738, 0x4740, "Mad Catz Beat Pad", 0, XTYPE_XBOX360 },
+ { 0x0738, 0x4743, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+ { 0x0738, 0x4758, "Mad Catz Arcade Game Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x0738, 0x4a01, "Mad Catz FightStick TE 2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
+ { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+ { 0x0738, 0x9871, "Mad Catz Portable Drum", 0, XTYPE_XBOX360 },
+ { 0x0738, 0xb726, "Mad Catz Xbox controller - MW2", 0, XTYPE_XBOX360 },
+ { 0x0738, 0xb738, "Mad Catz MVC2TE Stick 2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad", XTYPE_XBOX360 },
+ { 0x0738, 0xcb02, "Saitek Cyborg Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 },
+ { 0x0738, 0xcb03, "Saitek P3200 Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 },
+ { 0x0738, 0xcb29, "Saitek Aviator Stick AV8R02", 0, XTYPE_XBOX360 },
+ { 0x0738, 0xf738, "Super SFIV FightStick TE S", 0, XTYPE_XBOX360 },
+ { 0x07ff, 0xffff, "Mad Catz GamePad", 0, XTYPE_XBOX360 },
+ { 0x0c12, 0x0005, "Intec wireless", 0, XTYPE_XBOX },
+ { 0x0c12, 0x8801, "Nyko Xbox Controller", 0, XTYPE_XBOX },
+ { 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX },
+ { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", DANCEPAD_MAP_CONFIG, XTYPE_XBOX },
+ { 0x0c12, 0x880a, "Pelican Eclipse PL-2023", 0, XTYPE_XBOX },
+ { 0x0c12, 0x8810, "Zeroplus Xbox Controller", 0, XTYPE_XBOX },
+ { 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", 0, XTYPE_XBOX },
+ { 0x0d2f, 0x0002, "Andamiro Pump It Up pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+ { 0x0e4c, 0x1097, "Radica Gamester Controller", 0, XTYPE_XBOX },
+ { 0x0e4c, 0x1103, "Radica Gamester Reflex", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX },
+ { 0x0e4c, 0x2390, "Radica Games Jtech Controller", 0, XTYPE_XBOX },
+ { 0x0e4c, 0x3510, "Radica Gamester", 0, XTYPE_XBOX },
+ { 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller", 0, XTYPE_XBOX },
+ { 0x0e6f, 0x0005, "Eclipse wireless Controller", 0, XTYPE_XBOX },
+ { 0x0e6f, 0x0006, "Edge wireless Controller", 0, XTYPE_XBOX },
+ { 0x0e6f, 0x0008, "After Glow Pro Controller", 0, XTYPE_XBOX },
+ { 0x0e6f, 0x0105, "HSM3 Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x0e6f, 0x0113, "Afterglow AX.1 Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
+ { 0x0e6f, 0x011f, "Rock Candy Gamepad Wired Controller", 0, XTYPE_XBOX360 },
+ { 0x0e6f, 0x0131, "PDP EA Sports Controller", 0, XTYPE_XBOX360 },
+ { 0x0e6f, 0x0133, "Xbox 360 Wired Controller", 0, XTYPE_XBOX360 },
+ { 0x0e6f, 0x0139, "Afterglow Prismatic Wired Controller", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x013a, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x0146, "Rock Candy Wired Controller for Xbox One", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x0147, "PDP Marvel Xbox One Controller", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x015c, "PDP Xbox One Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
+ { 0x0e6f, 0x0161, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x0162, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x0163, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x0164, "PDP Battlefield One", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x0165, "PDP Titanfall 2", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 },
+ { 0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
+ { 0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
+ { 0x0e6f, 0x0246, "Rock Candy Gamepad for Xbox One 2015", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x02a0, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x02a1, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x02a2, "PDP Wired Controller for Xbox One - Crimson Red", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x02a4, "PDP Wired Controller for Xbox One - Stealth Series", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x02a6, "PDP Wired Controller for Xbox One - Camo Series", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x02a7, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x02a8, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x02ab, "PDP Controller for Xbox One", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x02ad, "PDP Wired Controller for Xbox One - Stealth Series", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x02b3, "Afterglow Prismatic Wired Controller", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x02b8, "Afterglow Prismatic Wired Controller", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x0301, "Logic3 Controller", 0, XTYPE_XBOX360 },
+ { 0x0e6f, 0x0346, "Rock Candy Gamepad for Xbox One 2016", 0, XTYPE_XBOXONE },
+ { 0x0e6f, 0x0401, "Logic3 Controller", 0, XTYPE_XBOX360 },
+ { 0x0e6f, 0x0413, "Afterglow AX.1 Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
+ { 0x0e6f, 0x0501, "PDP Xbox 360 Controller", 0, XTYPE_XBOX360 },
+ { 0x0e6f, 0xf900, "PDP Afterglow AX.1", 0, XTYPE_XBOX360 },
+ { 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", 0, XTYPE_XBOX },
+ { 0x0e8f, 0x3008, "Generic xbox control (dealextreme)", 0, XTYPE_XBOX },
+ { 0x0f0d, 0x000a, "Hori Co. DOA4 FightStick", 0, XTYPE_XBOX360 },
+ { 0x0f0d, 0x000c, "Hori PadEX Turbo", 0, XTYPE_XBOX360 },
+ { 0x0f0d, 0x000d, "Hori Fighting Stick EX2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x0f0d, 0x001b, "Hori Real Arcade Pro VX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x0f0d, 0x0063, "Hori Real Arcade Pro Hayabusa (USA) Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
+ { 0x0f0d, 0x0067, "HORIPAD ONE", 0, XTYPE_XBOXONE },
+ { 0x0f0d, 0x0078, "Hori Real Arcade Pro V Kai Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
+ { 0x0f30, 0x010b, "Philips Recoil", 0, XTYPE_XBOX },
+ { 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX },
+ { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX },
+ { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", 0, XTYPE_XBOX },
+ { 0x1038, 0x1430, "SteelSeries Stratus Duo", 0, XTYPE_XBOX360 },
+ { 0x1038, 0x1431, "SteelSeries Stratus Duo", 0, XTYPE_XBOX360 },
+ { 0x11c9, 0x55f0, "Nacon GC-100XF", 0, XTYPE_XBOX360 },
+ { 0x1209, 0x2882, "Ardwiino Controller", 0, XTYPE_XBOX360 },
+ { 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x12ab, 0x0301, "PDP AFTERGLOW AX.1", 0, XTYPE_XBOX360 },
+ { 0x12ab, 0x0303, "Mortal Kombat Klassic FightStick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+ { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", 0, XTYPE_XBOX360 },
+ { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+ { 0x1430, 0xf801, "RedOctane Controller", 0, XTYPE_XBOX360 },
+ { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 },
+ { 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 },
+ { 0x1532, 0x0a00, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
+ { 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE },
+ { 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 },
+ { 0x15e4, 0x3f0a, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
+ { 0x15e4, 0x3f10, "Batarang Xbox 360 controller", 0, XTYPE_XBOX360 },
+ { 0x162e, 0xbeef, "Joytech Neo-Se Take2", 0, XTYPE_XBOX360 },
+ { 0x1689, 0xfd00, "Razer Onza Tournament Edition", 0, XTYPE_XBOX360 },
+ { 0x1689, 0xfd01, "Razer Onza Classic Edition", 0, XTYPE_XBOX360 },
+ { 0x1689, 0xfe00, "Razer Sabertooth", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0x0130, "Ion Drum Rocker", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xf016, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xf018, "Mad Catz Street Fighter IV SE Fighting Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xf019, "Mad Catz Brawlstick for Xbox 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xf021, "Mad Cats Ghost Recon FS GamePad", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xf023, "MLG Pro Circuit Controller (Xbox)", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xf025, "Mad Catz Call Of Duty", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xf027, "Mad Catz FPS Pro", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xf028, "Street Fighter IV FightPad", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xf02e, "Mad Catz Fightpad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xf030, "Mad Catz Xbox 360 MC2 MicroCon Racing Wheel", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xf036, "Mad Catz MicroCon GamePad Pro", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xf038, "Street Fighter IV FightStick TE", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xf039, "Mad Catz MvC2 TE", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xf03a, "Mad Catz SFxT Fightstick Pro", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xf03d, "Street Fighter IV Arcade Stick TE - Chun Li", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xf03e, "Mad Catz MLG FightStick TE", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xf03f, "Mad Catz FightStick SoulCaliber", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xf042, "Mad Catz FightStick TES+", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xf080, "Mad Catz FightStick TE2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xf501, "HoriPad EX2 Turbo", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xf502, "Hori Real Arcade Pro.VX SA", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xf503, "Hori Fighting Stick VX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xf504, "Hori Real Arcade Pro. EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xf505, "Hori Fighting Stick EX2B", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xf506, "Hori Real Arcade Pro.EX Premium VLX", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xf900, "Harmonix Xbox 360 Controller", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xf901, "Gamestop Xbox 360 Controller", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xf903, "Tron Xbox 360 controller", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xf904, "PDP Versus Fighting Pad", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xf906, "MortalKombat FightStick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1bad, 0xfa01, "MadCatz GamePad", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xfd00, "Razer Onza TE", 0, XTYPE_XBOX360 },
+ { 0x1bad, 0xfd01, "Razer Onza", 0, XTYPE_XBOX360 },
+ { 0x20d6, 0x2001, "BDA Xbox Series X Wired Controller", 0, XTYPE_XBOXONE },
+ { 0x20d6, 0x2009, "PowerA Enhanced Wired Controller for Xbox Series X|S", 0, XTYPE_XBOXONE },
+ { 0x20d6, 0x281f, "PowerA Wired Controller For Xbox 360", 0, XTYPE_XBOX360 },
+ { 0x2e24, 0x0652, "Hyperkin Duke X-Box One pad", 0, XTYPE_XBOXONE },
+ { 0x24c6, 0x5000, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 },
+ { 0x24c6, 0x5303, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
+ { 0x24c6, 0x530a, "Xbox 360 Pro EX Controller", 0, XTYPE_XBOX360 },
+ { 0x24c6, 0x531a, "PowerA Pro Ex", 0, XTYPE_XBOX360 },
+ { 0x24c6, 0x5397, "FUS1ON Tournament Controller", 0, XTYPE_XBOX360 },
+ { 0x24c6, 0x541a, "PowerA Xbox One Mini Wired Controller", 0, XTYPE_XBOXONE },
+ { 0x24c6, 0x542a, "Xbox ONE spectra", 0, XTYPE_XBOXONE },
+ { 0x24c6, 0x543a, "PowerA Xbox One wired controller", 0, XTYPE_XBOXONE },
+ { 0x24c6, 0x5500, "Hori XBOX 360 EX 2 with Turbo", 0, XTYPE_XBOX360 },
+ { 0x24c6, 0x5501, "Hori Real Arcade Pro VX-SA", 0, XTYPE_XBOX360 },
+ { 0x24c6, 0x5502, "Hori Fighting Stick VX Alt", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x24c6, 0x5503, "Hori Fighting Edge", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 },
+ { 0x24c6, 0x550d, "Hori GEM Xbox controller", 0, XTYPE_XBOX360 },
+ { 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x24c6, 0x551a, "PowerA FUSION Pro Controller", 0, XTYPE_XBOXONE },
+ { 0x24c6, 0x561a, "PowerA FUSION Controller", 0, XTYPE_XBOXONE },
+ { 0x24c6, 0x5b00, "ThrustMaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 },
+ { 0x24c6, 0x5b02, "Thrustmaster, Inc. GPX Controller", 0, XTYPE_XBOX360 },
+ { 0x24c6, 0x5b03, "Thrustmaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 },
+ { 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 },
+ { 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
+ { 0x3285, 0x0607, "Nacon GC-100", 0, XTYPE_XBOX360 },
+ { 0x3767, 0x0101, "Fanatec Speedster 3 Forceshock Wheel", 0, XTYPE_XBOX },
+ { 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX },
+ { 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN }
+};
+
+/* buttons shared with xbox and xbox360 */
+static const signed short xpad_common_btn[] = {
+ BTN_A, BTN_B, BTN_X, BTN_Y, /* "analog" buttons */
+ BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */
+ -1 /* terminating entry */
+};
+
+/* original xbox controllers only */
+static const signed short xpad_btn[] = {
+ BTN_C, BTN_Z, /* "analog" buttons */
+ -1 /* terminating entry */
+};
+
+/* used when dpad is mapped to buttons */
+static const signed short xpad_btn_pad[] = {
+ BTN_TRIGGER_HAPPY1, BTN_TRIGGER_HAPPY2, /* d-pad left, right */
+ BTN_TRIGGER_HAPPY3, BTN_TRIGGER_HAPPY4, /* d-pad up, down */
+ -1 /* terminating entry */
+};
+
+/* used when triggers are mapped to buttons */
+static const signed short xpad_btn_triggers[] = {
+ BTN_TL2, BTN_TR2, /* triggers left/right */
+ -1
+};
+
+static const signed short xpad360_btn[] = { /* buttons for x360 controller */
+ BTN_TL, BTN_TR, /* Button LB/RB */
+ BTN_MODE, /* The big X button */
+ -1
+};
+
+static const signed short xpad_abs[] = {
+ ABS_X, ABS_Y, /* left stick */
+ ABS_RX, ABS_RY, /* right stick */
+ -1 /* terminating entry */
+};
+
+/* used when dpad is mapped to axes */
+static const signed short xpad_abs_pad[] = {
+ ABS_HAT0X, ABS_HAT0Y, /* d-pad axes */
+ -1 /* terminating entry */
+};
+
+/* used when triggers are mapped to axes */
+static const signed short xpad_abs_triggers[] = {
+ ABS_Z, ABS_RZ, /* triggers left/right */
+ -1
+};
+
+/*
+ * Xbox 360 has a vendor-specific class, so we cannot match it with only
+ * USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we
+ * match against vendor id as well. Wired Xbox 360 devices have protocol 1,
+ * wireless controllers have protocol 129.
+ */
+#define XPAD_XBOX360_VENDOR_PROTOCOL(vend, pr) \
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \
+ .idVendor = (vend), \
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
+ .bInterfaceSubClass = 93, \
+ .bInterfaceProtocol = (pr)
+#define XPAD_XBOX360_VENDOR(vend) \
+ { XPAD_XBOX360_VENDOR_PROTOCOL((vend), 1) }, \
+ { XPAD_XBOX360_VENDOR_PROTOCOL((vend), 129) }
+
+/* The Xbox One controller uses subclass 71 and protocol 208. */
+#define XPAD_XBOXONE_VENDOR_PROTOCOL(vend, pr) \
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \
+ .idVendor = (vend), \
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
+ .bInterfaceSubClass = 71, \
+ .bInterfaceProtocol = (pr)
+#define XPAD_XBOXONE_VENDOR(vend) \
+ { XPAD_XBOXONE_VENDOR_PROTOCOL((vend), 208) }
+
+static const struct usb_device_id xpad_table[] = {
+ { USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */
+ XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 Controller */
+ XPAD_XBOX360_VENDOR(0x044f), /* Thrustmaster X-Box 360 controllers */
+ XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */
+ XPAD_XBOXONE_VENDOR(0x045e), /* Microsoft X-Box One controllers */
+ XPAD_XBOX360_VENDOR(0x046d), /* Logitech X-Box 360 style controllers */
+ XPAD_XBOX360_VENDOR(0x056e), /* Elecom JC-U3613M */
+ XPAD_XBOX360_VENDOR(0x06a3), /* Saitek P3600 */
+ XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */
+ { USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */
+ XPAD_XBOXONE_VENDOR(0x0738), /* Mad Catz FightStick TE 2 */
+ XPAD_XBOX360_VENDOR(0x07ff), /* Mad Catz GamePad */
+ XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */
+ XPAD_XBOXONE_VENDOR(0x0e6f), /* 0x0e6f X-Box One controllers */
+ XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */
+ XPAD_XBOXONE_VENDOR(0x0f0d), /* Hori Controllers */
+ XPAD_XBOX360_VENDOR(0x1038), /* SteelSeries Controllers */
+ XPAD_XBOX360_VENDOR(0x11c9), /* Nacon GC100XF */
+ XPAD_XBOX360_VENDOR(0x1209), /* Ardwiino Controllers */
+ XPAD_XBOX360_VENDOR(0x12ab), /* X-Box 360 dance pads */
+ XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */
+ XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */
+ XPAD_XBOX360_VENDOR(0x1532), /* Razer Sabertooth */
+ XPAD_XBOXONE_VENDOR(0x1532), /* Razer Wildcat */
+ XPAD_XBOX360_VENDOR(0x15e4), /* Numark X-Box 360 controllers */
+ XPAD_XBOX360_VENDOR(0x162e), /* Joytech X-Box 360 controllers */
+ XPAD_XBOX360_VENDOR(0x1689), /* Razer Onza */
+ XPAD_XBOX360_VENDOR(0x1bad), /* Harminix Rock Band Guitar and Drums */
+ XPAD_XBOX360_VENDOR(0x20d6), /* PowerA Controllers */
+ XPAD_XBOXONE_VENDOR(0x20d6), /* PowerA Controllers */
+ XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */
+ XPAD_XBOXONE_VENDOR(0x24c6), /* PowerA Controllers */
+ XPAD_XBOXONE_VENDOR(0x2e24), /* Hyperkin Duke X-Box One pad */
+ XPAD_XBOX360_VENDOR(0x2f24), /* GameSir Controllers */
+ XPAD_XBOX360_VENDOR(0x3285), /* Nacon GC-100 */
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, xpad_table);
+
+struct xboxone_init_packet {
+ u16 idVendor;
+ u16 idProduct;
+ const u8 *data;
+ u8 len;
+};
+
+#define XBOXONE_INIT_PKT(_vid, _pid, _data) \
+ { \
+ .idVendor = (_vid), \
+ .idProduct = (_pid), \
+ .data = (_data), \
+ .len = ARRAY_SIZE(_data), \
+ }
+
+
+/*
+ * This packet is required for all Xbox One pads with 2015
+ * or later firmware installed (or present from the factory).
+ */
+static const u8 xboxone_fw2015_init[] = {
+ 0x05, 0x20, 0x00, 0x01, 0x00
+};
+
+/*
+ * This packet is required for Xbox One S (0x045e:0x02ea)
+ * and Xbox One Elite Series 2 (0x045e:0x0b00) pads to
+ * initialize the controller that was previously used in
+ * Bluetooth mode.
+ */
+static const u8 xboxone_s_init[] = {
+ 0x05, 0x20, 0x00, 0x0f, 0x06
+};
+
+/*
+ * This packet is required for the Titanfall 2 Xbox One pads
+ * (0x0e6f:0x0165) to finish initialization and for Hori pads
+ * (0x0f0d:0x0067) to make the analog sticks work.
+ */
+static const u8 xboxone_hori_init[] = {
+ 0x01, 0x20, 0x00, 0x09, 0x00, 0x04, 0x20, 0x3a,
+ 0x00, 0x00, 0x00, 0x80, 0x00
+};
+
+/*
+ * This packet is required for most (all?) of the PDP pads to start
+ * sending input reports. These pads include: (0x0e6f:0x02ab),
+ * (0x0e6f:0x02a4), (0x0e6f:0x02a6).
+ */
+static const u8 xboxone_pdp_init1[] = {
+ 0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14
+};
+
+/*
+ * This packet is required for most (all?) of the PDP pads to start
+ * sending input reports. These pads include: (0x0e6f:0x02ab),
+ * (0x0e6f:0x02a4), (0x0e6f:0x02a6).
+ */
+static const u8 xboxone_pdp_init2[] = {
+ 0x06, 0x20, 0x00, 0x02, 0x01, 0x00
+};
+
+/*
+ * A specific rumble packet is required for some PowerA pads to start
+ * sending input reports. One of those pads is (0x24c6:0x543a).
+ */
+static const u8 xboxone_rumblebegin_init[] = {
+ 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
+ 0x1D, 0x1D, 0xFF, 0x00, 0x00
+};
+
+/*
+ * A rumble packet with zero FF intensity will immediately
+ * terminate the rumbling required to init PowerA pads.
+ * This should happen fast enough that the motors don't
+ * spin up to enough speed to actually vibrate the gamepad.
+ */
+static const u8 xboxone_rumbleend_init[] = {
+ 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * This specifies the selection of init packets that a gamepad
+ * will be sent on init *and* the order in which they will be
+ * sent. The correct sequence number will be added when the
+ * packet is going to be sent.
+ */
+static const struct xboxone_init_packet xboxone_init_packets[] = {
+ XBOXONE_INIT_PKT(0x0e6f, 0x0165, xboxone_hori_init),
+ XBOXONE_INIT_PKT(0x0f0d, 0x0067, xboxone_hori_init),
+ XBOXONE_INIT_PKT(0x0000, 0x0000, xboxone_fw2015_init),
+ XBOXONE_INIT_PKT(0x045e, 0x02ea, xboxone_s_init),
+ XBOXONE_INIT_PKT(0x045e, 0x0b00, xboxone_s_init),
+ XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_init1),
+ XBOXONE_INIT_PKT(0x0e6f, 0x0000, xboxone_pdp_init2),
+ XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_rumblebegin_init),
+ XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumblebegin_init),
+ XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_rumblebegin_init),
+ XBOXONE_INIT_PKT(0x24c6, 0x541a, xboxone_rumbleend_init),
+ XBOXONE_INIT_PKT(0x24c6, 0x542a, xboxone_rumbleend_init),
+ XBOXONE_INIT_PKT(0x24c6, 0x543a, xboxone_rumbleend_init),
+};
+
+struct xpad_output_packet {
+ u8 data[XPAD_PKT_LEN];
+ u8 len;
+ bool pending;
+};
+
+#define XPAD_OUT_CMD_IDX 0
+#define XPAD_OUT_FF_IDX 1
+#define XPAD_OUT_LED_IDX (1 + IS_ENABLED(CONFIG_JOYSTICK_XPAD_FF))
+#define XPAD_NUM_OUT_PACKETS (1 + \
+ IS_ENABLED(CONFIG_JOYSTICK_XPAD_FF) + \
+ IS_ENABLED(CONFIG_JOYSTICK_XPAD_LEDS))
+
+struct usb_xpad {
+ struct input_dev *dev; /* input device interface */
+ struct input_dev __rcu *x360w_dev;
+ struct usb_device *udev; /* usb device */
+ struct usb_interface *intf; /* usb interface */
+
+ bool pad_present;
+ bool input_created;
+
+ struct urb *irq_in; /* urb for interrupt in report */
+ unsigned char *idata; /* input data */
+ dma_addr_t idata_dma;
+
+ struct urb *irq_out; /* urb for interrupt out report */
+ struct usb_anchor irq_out_anchor;
+ bool irq_out_active; /* we must not use an active URB */
+ u8 odata_serial; /* serial number for xbox one protocol */
+ unsigned char *odata; /* output data */
+ dma_addr_t odata_dma;
+ spinlock_t odata_lock;
+
+ struct xpad_output_packet out_packets[XPAD_NUM_OUT_PACKETS];
+ int last_out_packet;
+ int init_seq;
+
+#if defined(CONFIG_JOYSTICK_XPAD_LEDS)
+ struct xpad_led *led;
+#endif
+
+ char phys[64]; /* physical device path */
+
+ int mapping; /* map d-pad to buttons or to axes */
+ int xtype; /* type of xbox device */
+ int pad_nr; /* the order x360 pads were attached */
+ const char *name; /* name of the device */
+ struct work_struct work; /* init/remove device from callback */
+};
+
+static int xpad_init_input(struct usb_xpad *xpad);
+static void xpad_deinit_input(struct usb_xpad *xpad);
+static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num);
+
+/*
+ * xpad_process_packet
+ *
+ * Completes a request by converting the data into events for the
+ * input subsystem.
+ *
+ * The used report descriptor was taken from ITO Takayukis website:
+ * http://euc.jp/periphs/xbox-controller.ja.html
+ */
+static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
+{
+ struct input_dev *dev = xpad->dev;
+
+ if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
+ /* left stick */
+ input_report_abs(dev, ABS_X,
+ (__s16) le16_to_cpup((__le16 *)(data + 12)));
+ input_report_abs(dev, ABS_Y,
+ ~(__s16) le16_to_cpup((__le16 *)(data + 14)));
+
+ /* right stick */
+ input_report_abs(dev, ABS_RX,
+ (__s16) le16_to_cpup((__le16 *)(data + 16)));
+ input_report_abs(dev, ABS_RY,
+ ~(__s16) le16_to_cpup((__le16 *)(data + 18)));
+ }
+
+ /* triggers left/right */
+ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
+ input_report_key(dev, BTN_TL2, data[10]);
+ input_report_key(dev, BTN_TR2, data[11]);
+ } else {
+ input_report_abs(dev, ABS_Z, data[10]);
+ input_report_abs(dev, ABS_RZ, data[11]);
+ }
+
+ /* digital pad */
+ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
+ /* dpad as buttons (left, right, up, down) */
+ input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04);
+ input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08);
+ input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01);
+ input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02);
+ } else {
+ input_report_abs(dev, ABS_HAT0X,
+ !!(data[2] & 0x08) - !!(data[2] & 0x04));
+ input_report_abs(dev, ABS_HAT0Y,
+ !!(data[2] & 0x02) - !!(data[2] & 0x01));
+ }
+
+ /* start/back buttons and stick press left/right */
+ input_report_key(dev, BTN_START, data[2] & 0x10);
+ input_report_key(dev, BTN_SELECT, data[2] & 0x20);
+ input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
+ input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
+
+ /* "analog" buttons A, B, X, Y */
+ input_report_key(dev, BTN_A, data[4]);
+ input_report_key(dev, BTN_B, data[5]);
+ input_report_key(dev, BTN_X, data[6]);
+ input_report_key(dev, BTN_Y, data[7]);
+
+ /* "analog" buttons black, white */
+ input_report_key(dev, BTN_C, data[8]);
+ input_report_key(dev, BTN_Z, data[9]);
+
+ input_sync(dev);
+}
+
+/*
+ * xpad360_process_packet
+ *
+ * Completes a request by converting the data into events for the
+ * input subsystem. It is version for xbox 360 controller
+ *
+ * The used report descriptor was taken from:
+ * http://www.free60.org/wiki/Gamepad
+ */
+
+static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
+ u16 cmd, unsigned char *data)
+{
+ /* valid pad data */
+ if (data[0] != 0x00)
+ return;
+
+ /* digital pad */
+ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
+ /* dpad as buttons (left, right, up, down) */
+ input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04);
+ input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08);
+ input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01);
+ input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02);
+ }
+
+ /*
+ * This should be a simple else block. However historically
+ * xbox360w has mapped DPAD to buttons while xbox360 did not. This
+ * made no sense, but now we can not just switch back and have to
+ * support both behaviors.
+ */
+ if (!(xpad->mapping & MAP_DPAD_TO_BUTTONS) ||
+ xpad->xtype == XTYPE_XBOX360W) {
+ input_report_abs(dev, ABS_HAT0X,
+ !!(data[2] & 0x08) - !!(data[2] & 0x04));
+ input_report_abs(dev, ABS_HAT0Y,
+ !!(data[2] & 0x02) - !!(data[2] & 0x01));
+ }
+
+ /* start/back buttons */
+ input_report_key(dev, BTN_START, data[2] & 0x10);
+ input_report_key(dev, BTN_SELECT, data[2] & 0x20);
+
+ /* stick press left/right */
+ input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
+ input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
+
+ /* buttons A,B,X,Y,TL,TR and MODE */
+ input_report_key(dev, BTN_A, data[3] & 0x10);
+ input_report_key(dev, BTN_B, data[3] & 0x20);
+ input_report_key(dev, BTN_X, data[3] & 0x40);
+ input_report_key(dev, BTN_Y, data[3] & 0x80);
+ input_report_key(dev, BTN_TL, data[3] & 0x01);
+ input_report_key(dev, BTN_TR, data[3] & 0x02);
+ input_report_key(dev, BTN_MODE, data[3] & 0x04);
+
+ if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
+ /* left stick */
+ input_report_abs(dev, ABS_X,
+ (__s16) le16_to_cpup((__le16 *)(data + 6)));
+ input_report_abs(dev, ABS_Y,
+ ~(__s16) le16_to_cpup((__le16 *)(data + 8)));
+
+ /* right stick */
+ input_report_abs(dev, ABS_RX,
+ (__s16) le16_to_cpup((__le16 *)(data + 10)));
+ input_report_abs(dev, ABS_RY,
+ ~(__s16) le16_to_cpup((__le16 *)(data + 12)));
+ }
+
+ /* triggers left/right */
+ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
+ input_report_key(dev, BTN_TL2, data[4]);
+ input_report_key(dev, BTN_TR2, data[5]);
+ } else {
+ input_report_abs(dev, ABS_Z, data[4]);
+ input_report_abs(dev, ABS_RZ, data[5]);
+ }
+
+ input_sync(dev);
+}
+
+static void xpad_presence_work(struct work_struct *work)
+{
+ struct usb_xpad *xpad = container_of(work, struct usb_xpad, work);
+ int error;
+
+ if (xpad->pad_present) {
+ error = xpad_init_input(xpad);
+ if (error) {
+ /* complain only, not much else we can do here */
+ dev_err(&xpad->dev->dev,
+ "unable to init device: %d\n", error);
+ } else {
+ rcu_assign_pointer(xpad->x360w_dev, xpad->dev);
+ }
+ } else {
+ RCU_INIT_POINTER(xpad->x360w_dev, NULL);
+ synchronize_rcu();
+ /*
+ * Now that we are sure xpad360w_process_packet is not
+ * using input device we can get rid of it.
+ */
+ xpad_deinit_input(xpad);
+ }
+}
+
+/*
+ * xpad360w_process_packet
+ *
+ * Completes a request by converting the data into events for the
+ * input subsystem. It is version for xbox 360 wireless controller.
+ *
+ * Byte.Bit
+ * 00.1 - Status change: The controller or headset has connected/disconnected
+ * Bits 01.7 and 01.6 are valid
+ * 01.7 - Controller present
+ * 01.6 - Headset present
+ * 01.1 - Pad state (Bytes 4+) valid
+ *
+ */
+static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
+{
+ struct input_dev *dev;
+ bool present;
+
+ /* Presence change */
+ if (data[0] & 0x08) {
+ present = (data[1] & 0x80) != 0;
+
+ if (xpad->pad_present != present) {
+ xpad->pad_present = present;
+ schedule_work(&xpad->work);
+ }
+ }
+
+ /* Valid pad data */
+ if (data[1] != 0x1)
+ return;
+
+ rcu_read_lock();
+ dev = rcu_dereference(xpad->x360w_dev);
+ if (dev)
+ xpad360_process_packet(xpad, dev, cmd, &data[4]);
+ rcu_read_unlock();
+}
+
+/*
+ * xpadone_process_packet
+ *
+ * Completes a request by converting the data into events for the
+ * input subsystem. This version is for the Xbox One controller.
+ *
+ * The report format was gleaned from
+ * https://github.com/kylelemons/xbox/blob/master/xbox.go
+ */
+static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
+{
+ struct input_dev *dev = xpad->dev;
+
+ /* the xbox button has its own special report */
+ if (data[0] == 0X07) {
+ /*
+ * The Xbox One S controller requires these reports to be
+ * acked otherwise it continues sending them forever and
+ * won't report further mode button events.
+ */
+ if (data[1] == 0x30)
+ xpadone_ack_mode_report(xpad, data[2]);
+
+ input_report_key(dev, BTN_MODE, data[4] & 0x01);
+ input_sync(dev);
+ return;
+ }
+ /* check invalid packet */
+ else if (data[0] != 0X20)
+ return;
+
+ /* menu/view buttons */
+ input_report_key(dev, BTN_START, data[4] & 0x04);
+ input_report_key(dev, BTN_SELECT, data[4] & 0x08);
+
+ /* buttons A,B,X,Y */
+ input_report_key(dev, BTN_A, data[4] & 0x10);
+ input_report_key(dev, BTN_B, data[4] & 0x20);
+ input_report_key(dev, BTN_X, data[4] & 0x40);
+ input_report_key(dev, BTN_Y, data[4] & 0x80);
+
+ /* digital pad */
+ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
+ /* dpad as buttons (left, right, up, down) */
+ input_report_key(dev, BTN_TRIGGER_HAPPY1, data[5] & 0x04);
+ input_report_key(dev, BTN_TRIGGER_HAPPY2, data[5] & 0x08);
+ input_report_key(dev, BTN_TRIGGER_HAPPY3, data[5] & 0x01);
+ input_report_key(dev, BTN_TRIGGER_HAPPY4, data[5] & 0x02);
+ } else {
+ input_report_abs(dev, ABS_HAT0X,
+ !!(data[5] & 0x08) - !!(data[5] & 0x04));
+ input_report_abs(dev, ABS_HAT0Y,
+ !!(data[5] & 0x02) - !!(data[5] & 0x01));
+ }
+
+ /* TL/TR */
+ input_report_key(dev, BTN_TL, data[5] & 0x10);
+ input_report_key(dev, BTN_TR, data[5] & 0x20);
+
+ /* stick press left/right */
+ input_report_key(dev, BTN_THUMBL, data[5] & 0x40);
+ input_report_key(dev, BTN_THUMBR, data[5] & 0x80);
+
+ if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
+ /* left stick */
+ input_report_abs(dev, ABS_X,
+ (__s16) le16_to_cpup((__le16 *)(data + 10)));
+ input_report_abs(dev, ABS_Y,
+ ~(__s16) le16_to_cpup((__le16 *)(data + 12)));
+
+ /* right stick */
+ input_report_abs(dev, ABS_RX,
+ (__s16) le16_to_cpup((__le16 *)(data + 14)));
+ input_report_abs(dev, ABS_RY,
+ ~(__s16) le16_to_cpup((__le16 *)(data + 16)));
+ }
+
+ /* triggers left/right */
+ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
+ input_report_key(dev, BTN_TL2,
+ (__u16) le16_to_cpup((__le16 *)(data + 6)));
+ input_report_key(dev, BTN_TR2,
+ (__u16) le16_to_cpup((__le16 *)(data + 8)));
+ } else {
+ input_report_abs(dev, ABS_Z,
+ (__u16) le16_to_cpup((__le16 *)(data + 6)));
+ input_report_abs(dev, ABS_RZ,
+ (__u16) le16_to_cpup((__le16 *)(data + 8)));
+ }
+
+ input_sync(dev);
+}
+
+static void xpad_irq_in(struct urb *urb)
+{
+ struct usb_xpad *xpad = urb->context;
+ struct device *dev = &xpad->intf->dev;
+ int retval, status;
+
+ status = urb->status;
+
+ switch (status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dev_dbg(dev, "%s - urb shutting down with status: %d\n",
+ __func__, status);
+ return;
+ default:
+ dev_dbg(dev, "%s - nonzero urb status received: %d\n",
+ __func__, status);
+ goto exit;
+ }
+
+ switch (xpad->xtype) {
+ case XTYPE_XBOX360:
+ xpad360_process_packet(xpad, xpad->dev, 0, xpad->idata);
+ break;
+ case XTYPE_XBOX360W:
+ xpad360w_process_packet(xpad, 0, xpad->idata);
+ break;
+ case XTYPE_XBOXONE:
+ xpadone_process_packet(xpad, 0, xpad->idata);
+ break;
+ default:
+ xpad_process_packet(xpad, 0, xpad->idata);
+ }
+
+exit:
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
+ __func__, retval);
+}
+
+/* Callers must hold xpad->odata_lock spinlock */
+static bool xpad_prepare_next_init_packet(struct usb_xpad *xpad)
+{
+ const struct xboxone_init_packet *init_packet;
+
+ if (xpad->xtype != XTYPE_XBOXONE)
+ return false;
+
+ /* Perform initialization sequence for Xbox One pads that require it */
+ while (xpad->init_seq < ARRAY_SIZE(xboxone_init_packets)) {
+ init_packet = &xboxone_init_packets[xpad->init_seq++];
+
+ if (init_packet->idVendor != 0 &&
+ init_packet->idVendor != xpad->dev->id.vendor)
+ continue;
+
+ if (init_packet->idProduct != 0 &&
+ init_packet->idProduct != xpad->dev->id.product)
+ continue;
+
+ /* This packet applies to our device, so prepare to send it */
+ memcpy(xpad->odata, init_packet->data, init_packet->len);
+ xpad->irq_out->transfer_buffer_length = init_packet->len;
+
+ /* Update packet with current sequence number */
+ xpad->odata[2] = xpad->odata_serial++;
+ return true;
+ }
+
+ return false;
+}
+
+/* Callers must hold xpad->odata_lock spinlock */
+static bool xpad_prepare_next_out_packet(struct usb_xpad *xpad)
+{
+ struct xpad_output_packet *pkt, *packet = NULL;
+ int i;
+
+ /* We may have init packets to send before we can send user commands */
+ if (xpad_prepare_next_init_packet(xpad))
+ return true;
+
+ for (i = 0; i < XPAD_NUM_OUT_PACKETS; i++) {
+ if (++xpad->last_out_packet >= XPAD_NUM_OUT_PACKETS)
+ xpad->last_out_packet = 0;
+
+ pkt = &xpad->out_packets[xpad->last_out_packet];
+ if (pkt->pending) {
+ dev_dbg(&xpad->intf->dev,
+ "%s - found pending output packet %d\n",
+ __func__, xpad->last_out_packet);
+ packet = pkt;
+ break;
+ }
+ }
+
+ if (packet) {
+ memcpy(xpad->odata, packet->data, packet->len);
+ xpad->irq_out->transfer_buffer_length = packet->len;
+ packet->pending = false;
+ return true;
+ }
+
+ return false;
+}
+
+/* Callers must hold xpad->odata_lock spinlock */
+static int xpad_try_sending_next_out_packet(struct usb_xpad *xpad)
+{
+ int error;
+
+ if (!xpad->irq_out_active && xpad_prepare_next_out_packet(xpad)) {
+ usb_anchor_urb(xpad->irq_out, &xpad->irq_out_anchor);
+ error = usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+ if (error) {
+ dev_err(&xpad->intf->dev,
+ "%s - usb_submit_urb failed with result %d\n",
+ __func__, error);
+ usb_unanchor_urb(xpad->irq_out);
+ return -EIO;
+ }
+
+ xpad->irq_out_active = true;
+ }
+
+ return 0;
+}
+
+static void xpad_irq_out(struct urb *urb)
+{
+ struct usb_xpad *xpad = urb->context;
+ struct device *dev = &xpad->intf->dev;
+ int status = urb->status;
+ int error;
+ unsigned long flags;
+
+ spin_lock_irqsave(&xpad->odata_lock, flags);
+
+ switch (status) {
+ case 0:
+ /* success */
+ xpad->irq_out_active = xpad_prepare_next_out_packet(xpad);
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dev_dbg(dev, "%s - urb shutting down with status: %d\n",
+ __func__, status);
+ xpad->irq_out_active = false;
+ break;
+
+ default:
+ dev_dbg(dev, "%s - nonzero urb status received: %d\n",
+ __func__, status);
+ break;
+ }
+
+ if (xpad->irq_out_active) {
+ usb_anchor_urb(urb, &xpad->irq_out_anchor);
+ error = usb_submit_urb(urb, GFP_ATOMIC);
+ if (error) {
+ dev_err(dev,
+ "%s - usb_submit_urb failed with result %d\n",
+ __func__, error);
+ usb_unanchor_urb(urb);
+ xpad->irq_out_active = false;
+ }
+ }
+
+ spin_unlock_irqrestore(&xpad->odata_lock, flags);
+}
+
+static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad,
+ struct usb_endpoint_descriptor *ep_irq_out)
+{
+ int error;
+
+ if (xpad->xtype == XTYPE_UNKNOWN)
+ return 0;
+
+ init_usb_anchor(&xpad->irq_out_anchor);
+
+ xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN,
+ GFP_KERNEL, &xpad->odata_dma);
+ if (!xpad->odata)
+ return -ENOMEM;
+
+ spin_lock_init(&xpad->odata_lock);
+
+ xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL);
+ if (!xpad->irq_out) {
+ error = -ENOMEM;
+ goto err_free_coherent;
+ }
+
+ usb_fill_int_urb(xpad->irq_out, xpad->udev,
+ usb_sndintpipe(xpad->udev, ep_irq_out->bEndpointAddress),
+ xpad->odata, XPAD_PKT_LEN,
+ xpad_irq_out, xpad, ep_irq_out->bInterval);
+ xpad->irq_out->transfer_dma = xpad->odata_dma;
+ xpad->irq_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ return 0;
+
+err_free_coherent:
+ usb_free_coherent(xpad->udev, XPAD_PKT_LEN, xpad->odata, xpad->odata_dma);
+ return error;
+}
+
+static void xpad_stop_output(struct usb_xpad *xpad)
+{
+ if (xpad->xtype != XTYPE_UNKNOWN) {
+ if (!usb_wait_anchor_empty_timeout(&xpad->irq_out_anchor,
+ 5000)) {
+ dev_warn(&xpad->intf->dev,
+ "timed out waiting for output URB to complete, killing\n");
+ usb_kill_anchored_urbs(&xpad->irq_out_anchor);
+ }
+ }
+}
+
+static void xpad_deinit_output(struct usb_xpad *xpad)
+{
+ if (xpad->xtype != XTYPE_UNKNOWN) {
+ usb_free_urb(xpad->irq_out);
+ usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
+ xpad->odata, xpad->odata_dma);
+ }
+}
+
+static int xpad_inquiry_pad_presence(struct usb_xpad *xpad)
+{
+ struct xpad_output_packet *packet =
+ &xpad->out_packets[XPAD_OUT_CMD_IDX];
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&xpad->odata_lock, flags);
+
+ packet->data[0] = 0x08;
+ packet->data[1] = 0x00;
+ packet->data[2] = 0x0F;
+ packet->data[3] = 0xC0;
+ packet->data[4] = 0x00;
+ packet->data[5] = 0x00;
+ packet->data[6] = 0x00;
+ packet->data[7] = 0x00;
+ packet->data[8] = 0x00;
+ packet->data[9] = 0x00;
+ packet->data[10] = 0x00;
+ packet->data[11] = 0x00;
+ packet->len = 12;
+ packet->pending = true;
+
+ /* Reset the sequence so we send out presence first */
+ xpad->last_out_packet = -1;
+ retval = xpad_try_sending_next_out_packet(xpad);
+
+ spin_unlock_irqrestore(&xpad->odata_lock, flags);
+
+ return retval;
+}
+
+static int xpad_start_xbox_one(struct usb_xpad *xpad)
+{
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&xpad->odata_lock, flags);
+
+ /*
+ * Begin the init sequence by attempting to send a packet.
+ * We will cycle through the init packet sequence before
+ * sending any packets from the output ring.
+ */
+ xpad->init_seq = 0;
+ retval = xpad_try_sending_next_out_packet(xpad);
+
+ spin_unlock_irqrestore(&xpad->odata_lock, flags);
+
+ return retval;
+}
+
+static void xpadone_ack_mode_report(struct usb_xpad *xpad, u8 seq_num)
+{
+ unsigned long flags;
+ struct xpad_output_packet *packet =
+ &xpad->out_packets[XPAD_OUT_CMD_IDX];
+ static const u8 mode_report_ack[] = {
+ 0x01, 0x20, 0x00, 0x09, 0x00, 0x07, 0x20, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ spin_lock_irqsave(&xpad->odata_lock, flags);
+
+ packet->len = sizeof(mode_report_ack);
+ memcpy(packet->data, mode_report_ack, packet->len);
+ packet->data[2] = seq_num;
+ packet->pending = true;
+
+ /* Reset the sequence so we send out the ack now */
+ xpad->last_out_packet = -1;
+ xpad_try_sending_next_out_packet(xpad);
+
+ spin_unlock_irqrestore(&xpad->odata_lock, flags);
+}
+
+#ifdef CONFIG_JOYSTICK_XPAD_FF
+static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect)
+{
+ struct usb_xpad *xpad = input_get_drvdata(dev);
+ struct xpad_output_packet *packet = &xpad->out_packets[XPAD_OUT_FF_IDX];
+ __u16 strong;
+ __u16 weak;
+ int retval;
+ unsigned long flags;
+
+ if (effect->type != FF_RUMBLE)
+ return 0;
+
+ strong = effect->u.rumble.strong_magnitude;
+ weak = effect->u.rumble.weak_magnitude;
+
+ spin_lock_irqsave(&xpad->odata_lock, flags);
+
+ switch (xpad->xtype) {
+ case XTYPE_XBOX:
+ packet->data[0] = 0x00;
+ packet->data[1] = 0x06;
+ packet->data[2] = 0x00;
+ packet->data[3] = strong / 256; /* left actuator */
+ packet->data[4] = 0x00;
+ packet->data[5] = weak / 256; /* right actuator */
+ packet->len = 6;
+ packet->pending = true;
+ break;
+
+ case XTYPE_XBOX360:
+ packet->data[0] = 0x00;
+ packet->data[1] = 0x08;
+ packet->data[2] = 0x00;
+ packet->data[3] = strong / 256; /* left actuator? */
+ packet->data[4] = weak / 256; /* right actuator? */
+ packet->data[5] = 0x00;
+ packet->data[6] = 0x00;
+ packet->data[7] = 0x00;
+ packet->len = 8;
+ packet->pending = true;
+ break;
+
+ case XTYPE_XBOX360W:
+ packet->data[0] = 0x00;
+ packet->data[1] = 0x01;
+ packet->data[2] = 0x0F;
+ packet->data[3] = 0xC0;
+ packet->data[4] = 0x00;
+ packet->data[5] = strong / 256;
+ packet->data[6] = weak / 256;
+ packet->data[7] = 0x00;
+ packet->data[8] = 0x00;
+ packet->data[9] = 0x00;
+ packet->data[10] = 0x00;
+ packet->data[11] = 0x00;
+ packet->len = 12;
+ packet->pending = true;
+ break;
+
+ case XTYPE_XBOXONE:
+ packet->data[0] = 0x09; /* activate rumble */
+ packet->data[1] = 0x00;
+ packet->data[2] = xpad->odata_serial++;
+ packet->data[3] = 0x09;
+ packet->data[4] = 0x00;
+ packet->data[5] = 0x0F;
+ packet->data[6] = 0x00;
+ packet->data[7] = 0x00;
+ packet->data[8] = strong / 512; /* left actuator */
+ packet->data[9] = weak / 512; /* right actuator */
+ packet->data[10] = 0xFF; /* on period */
+ packet->data[11] = 0x00; /* off period */
+ packet->data[12] = 0xFF; /* repeat count */
+ packet->len = 13;
+ packet->pending = true;
+ break;
+
+ default:
+ dev_dbg(&xpad->dev->dev,
+ "%s - rumble command sent to unsupported xpad type: %d\n",
+ __func__, xpad->xtype);
+ retval = -EINVAL;
+ goto out;
+ }
+
+ retval = xpad_try_sending_next_out_packet(xpad);
+
+out:
+ spin_unlock_irqrestore(&xpad->odata_lock, flags);
+ return retval;
+}
+
+static int xpad_init_ff(struct usb_xpad *xpad)
+{
+ if (xpad->xtype == XTYPE_UNKNOWN)
+ return 0;
+
+ input_set_capability(xpad->dev, EV_FF, FF_RUMBLE);
+
+ return input_ff_create_memless(xpad->dev, NULL, xpad_play_effect);
+}
+
+#else
+static int xpad_init_ff(struct usb_xpad *xpad) { return 0; }
+#endif
+
+#if defined(CONFIG_JOYSTICK_XPAD_LEDS)
+#include <linux/leds.h>
+#include <linux/idr.h>
+
+static DEFINE_IDA(xpad_pad_seq);
+
+struct xpad_led {
+ char name[16];
+ struct led_classdev led_cdev;
+ struct usb_xpad *xpad;
+};
+
+/**
+ * set the LEDs on Xbox360 / Wireless Controllers
+ * @param command
+ * 0: off
+ * 1: all blink, then previous setting
+ * 2: 1/top-left blink, then on
+ * 3: 2/top-right blink, then on
+ * 4: 3/bottom-left blink, then on
+ * 5: 4/bottom-right blink, then on
+ * 6: 1/top-left on
+ * 7: 2/top-right on
+ * 8: 3/bottom-left on
+ * 9: 4/bottom-right on
+ * 10: rotate
+ * 11: blink, based on previous setting
+ * 12: slow blink, based on previous setting
+ * 13: rotate with two lights
+ * 14: persistent slow all blink
+ * 15: blink once, then previous setting
+ */
+static void xpad_send_led_command(struct usb_xpad *xpad, int command)
+{
+ struct xpad_output_packet *packet =
+ &xpad->out_packets[XPAD_OUT_LED_IDX];
+ unsigned long flags;
+
+ command %= 16;
+
+ spin_lock_irqsave(&xpad->odata_lock, flags);
+
+ switch (xpad->xtype) {
+ case XTYPE_XBOX360:
+ packet->data[0] = 0x01;
+ packet->data[1] = 0x03;
+ packet->data[2] = command;
+ packet->len = 3;
+ packet->pending = true;
+ break;
+
+ case XTYPE_XBOX360W:
+ packet->data[0] = 0x00;
+ packet->data[1] = 0x00;
+ packet->data[2] = 0x08;
+ packet->data[3] = 0x40 + command;
+ packet->data[4] = 0x00;
+ packet->data[5] = 0x00;
+ packet->data[6] = 0x00;
+ packet->data[7] = 0x00;
+ packet->data[8] = 0x00;
+ packet->data[9] = 0x00;
+ packet->data[10] = 0x00;
+ packet->data[11] = 0x00;
+ packet->len = 12;
+ packet->pending = true;
+ break;
+ }
+
+ xpad_try_sending_next_out_packet(xpad);
+
+ spin_unlock_irqrestore(&xpad->odata_lock, flags);
+}
+
+/*
+ * Light up the segment corresponding to the pad number on
+ * Xbox 360 Controllers.
+ */
+static void xpad_identify_controller(struct usb_xpad *xpad)
+{
+ led_set_brightness(&xpad->led->led_cdev, (xpad->pad_nr % 4) + 2);
+}
+
+static void xpad_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct xpad_led *xpad_led = container_of(led_cdev,
+ struct xpad_led, led_cdev);
+
+ xpad_send_led_command(xpad_led->xpad, value);
+}
+
+static int xpad_led_probe(struct usb_xpad *xpad)
+{
+ struct xpad_led *led;
+ struct led_classdev *led_cdev;
+ int error;
+
+ if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX360W)
+ return 0;
+
+ xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ xpad->pad_nr = ida_simple_get(&xpad_pad_seq, 0, 0, GFP_KERNEL);
+ if (xpad->pad_nr < 0) {
+ error = xpad->pad_nr;
+ goto err_free_mem;
+ }
+
+ snprintf(led->name, sizeof(led->name), "xpad%d", xpad->pad_nr);
+ led->xpad = xpad;
+
+ led_cdev = &led->led_cdev;
+ led_cdev->name = led->name;
+ led_cdev->brightness_set = xpad_led_set;
+ led_cdev->flags = LED_CORE_SUSPENDRESUME;
+
+ error = led_classdev_register(&xpad->udev->dev, led_cdev);
+ if (error)
+ goto err_free_id;
+
+ xpad_identify_controller(xpad);
+
+ return 0;
+
+err_free_id:
+ ida_simple_remove(&xpad_pad_seq, xpad->pad_nr);
+err_free_mem:
+ kfree(led);
+ xpad->led = NULL;
+ return error;
+}
+
+static void xpad_led_disconnect(struct usb_xpad *xpad)
+{
+ struct xpad_led *xpad_led = xpad->led;
+
+ if (xpad_led) {
+ led_classdev_unregister(&xpad_led->led_cdev);
+ ida_simple_remove(&xpad_pad_seq, xpad->pad_nr);
+ kfree(xpad_led);
+ }
+}
+#else
+static int xpad_led_probe(struct usb_xpad *xpad) { return 0; }
+static void xpad_led_disconnect(struct usb_xpad *xpad) { }
+#endif
+
+static int xpad_start_input(struct usb_xpad *xpad)
+{
+ int error;
+
+ if (usb_submit_urb(xpad->irq_in, GFP_KERNEL))
+ return -EIO;
+
+ if (xpad->xtype == XTYPE_XBOXONE) {
+ error = xpad_start_xbox_one(xpad);
+ if (error) {
+ usb_kill_urb(xpad->irq_in);
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+static void xpad_stop_input(struct usb_xpad *xpad)
+{
+ usb_kill_urb(xpad->irq_in);
+}
+
+static void xpad360w_poweroff_controller(struct usb_xpad *xpad)
+{
+ unsigned long flags;
+ struct xpad_output_packet *packet =
+ &xpad->out_packets[XPAD_OUT_CMD_IDX];
+
+ spin_lock_irqsave(&xpad->odata_lock, flags);
+
+ packet->data[0] = 0x00;
+ packet->data[1] = 0x00;
+ packet->data[2] = 0x08;
+ packet->data[3] = 0xC0;
+ packet->data[4] = 0x00;
+ packet->data[5] = 0x00;
+ packet->data[6] = 0x00;
+ packet->data[7] = 0x00;
+ packet->data[8] = 0x00;
+ packet->data[9] = 0x00;
+ packet->data[10] = 0x00;
+ packet->data[11] = 0x00;
+ packet->len = 12;
+ packet->pending = true;
+
+ /* Reset the sequence so we send out poweroff now */
+ xpad->last_out_packet = -1;
+ xpad_try_sending_next_out_packet(xpad);
+
+ spin_unlock_irqrestore(&xpad->odata_lock, flags);
+}
+
+static int xpad360w_start_input(struct usb_xpad *xpad)
+{
+ int error;
+
+ error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
+ if (error)
+ return -EIO;
+
+ /*
+ * Send presence packet.
+ * This will force the controller to resend connection packets.
+ * This is useful in the case we activate the module after the
+ * adapter has been plugged in, as it won't automatically
+ * send us info about the controllers.
+ */
+ error = xpad_inquiry_pad_presence(xpad);
+ if (error) {
+ usb_kill_urb(xpad->irq_in);
+ return error;
+ }
+
+ return 0;
+}
+
+static void xpad360w_stop_input(struct usb_xpad *xpad)
+{
+ usb_kill_urb(xpad->irq_in);
+
+ /* Make sure we are done with presence work if it was scheduled */
+ flush_work(&xpad->work);
+}
+
+static int xpad_open(struct input_dev *dev)
+{
+ struct usb_xpad *xpad = input_get_drvdata(dev);
+
+ return xpad_start_input(xpad);
+}
+
+static void xpad_close(struct input_dev *dev)
+{
+ struct usb_xpad *xpad = input_get_drvdata(dev);
+
+ xpad_stop_input(xpad);
+}
+
+static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
+{
+ struct usb_xpad *xpad = input_get_drvdata(input_dev);
+
+ switch (abs) {
+ case ABS_X:
+ case ABS_Y:
+ case ABS_RX:
+ case ABS_RY: /* the two sticks */
+ input_set_abs_params(input_dev, abs, -32768, 32767, 16, 128);
+ break;
+ case ABS_Z:
+ case ABS_RZ: /* the triggers (if mapped to axes) */
+ if (xpad->xtype == XTYPE_XBOXONE)
+ input_set_abs_params(input_dev, abs, 0, 1023, 0, 0);
+ else
+ input_set_abs_params(input_dev, abs, 0, 255, 0, 0);
+ break;
+ case ABS_HAT0X:
+ case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */
+ input_set_abs_params(input_dev, abs, -1, 1, 0, 0);
+ break;
+ default:
+ input_set_abs_params(input_dev, abs, 0, 0, 0, 0);
+ break;
+ }
+}
+
+static void xpad_deinit_input(struct usb_xpad *xpad)
+{
+ if (xpad->input_created) {
+ xpad->input_created = false;
+ xpad_led_disconnect(xpad);
+ input_unregister_device(xpad->dev);
+ }
+}
+
+static int xpad_init_input(struct usb_xpad *xpad)
+{
+ struct input_dev *input_dev;
+ int i, error;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ xpad->dev = input_dev;
+ input_dev->name = xpad->name;
+ input_dev->phys = xpad->phys;
+ usb_to_input_id(xpad->udev, &input_dev->id);
+
+ if (xpad->xtype == XTYPE_XBOX360W) {
+ /* x360w controllers and the receiver have different ids */
+ input_dev->id.product = 0x02a1;
+ }
+
+ input_dev->dev.parent = &xpad->intf->dev;
+
+ input_set_drvdata(input_dev, xpad);
+
+ if (xpad->xtype != XTYPE_XBOX360W) {
+ input_dev->open = xpad_open;
+ input_dev->close = xpad_close;
+ }
+
+ if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
+ /* set up axes */
+ for (i = 0; xpad_abs[i] >= 0; i++)
+ xpad_set_up_abs(input_dev, xpad_abs[i]);
+ }
+
+ /* set up standard buttons */
+ for (i = 0; xpad_common_btn[i] >= 0; i++)
+ input_set_capability(input_dev, EV_KEY, xpad_common_btn[i]);
+
+ /* set up model-specific ones */
+ if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W ||
+ xpad->xtype == XTYPE_XBOXONE) {
+ for (i = 0; xpad360_btn[i] >= 0; i++)
+ input_set_capability(input_dev, EV_KEY, xpad360_btn[i]);
+ } else {
+ for (i = 0; xpad_btn[i] >= 0; i++)
+ input_set_capability(input_dev, EV_KEY, xpad_btn[i]);
+ }
+
+ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
+ for (i = 0; xpad_btn_pad[i] >= 0; i++)
+ input_set_capability(input_dev, EV_KEY,
+ xpad_btn_pad[i]);
+ }
+
+ /*
+ * This should be a simple else block. However historically
+ * xbox360w has mapped DPAD to buttons while xbox360 did not. This
+ * made no sense, but now we can not just switch back and have to
+ * support both behaviors.
+ */
+ if (!(xpad->mapping & MAP_DPAD_TO_BUTTONS) ||
+ xpad->xtype == XTYPE_XBOX360W) {
+ for (i = 0; xpad_abs_pad[i] >= 0; i++)
+ xpad_set_up_abs(input_dev, xpad_abs_pad[i]);
+ }
+
+ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
+ for (i = 0; xpad_btn_triggers[i] >= 0; i++)
+ input_set_capability(input_dev, EV_KEY,
+ xpad_btn_triggers[i]);
+ } else {
+ for (i = 0; xpad_abs_triggers[i] >= 0; i++)
+ xpad_set_up_abs(input_dev, xpad_abs_triggers[i]);
+ }
+
+ error = xpad_init_ff(xpad);
+ if (error)
+ goto err_free_input;
+
+ error = xpad_led_probe(xpad);
+ if (error)
+ goto err_destroy_ff;
+
+ error = input_register_device(xpad->dev);
+ if (error)
+ goto err_disconnect_led;
+
+ xpad->input_created = true;
+ return 0;
+
+err_disconnect_led:
+ xpad_led_disconnect(xpad);
+err_destroy_ff:
+ input_ff_destroy(input_dev);
+err_free_input:
+ input_free_device(input_dev);
+ return error;
+}
+
+static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_xpad *xpad;
+ struct usb_endpoint_descriptor *ep_irq_in, *ep_irq_out;
+ int i, error;
+
+ if (intf->cur_altsetting->desc.bNumEndpoints != 2)
+ return -ENODEV;
+
+ for (i = 0; xpad_device[i].idVendor; i++) {
+ if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
+ (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct))
+ break;
+ }
+
+ xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL);
+ if (!xpad)
+ return -ENOMEM;
+
+ usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
+ strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
+
+ xpad->idata = usb_alloc_coherent(udev, XPAD_PKT_LEN,
+ GFP_KERNEL, &xpad->idata_dma);
+ if (!xpad->idata) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL);
+ if (!xpad->irq_in) {
+ error = -ENOMEM;
+ goto err_free_idata;
+ }
+
+ xpad->udev = udev;
+ xpad->intf = intf;
+ xpad->mapping = xpad_device[i].mapping;
+ xpad->xtype = xpad_device[i].xtype;
+ xpad->name = xpad_device[i].name;
+ INIT_WORK(&xpad->work, xpad_presence_work);
+
+ if (xpad->xtype == XTYPE_UNKNOWN) {
+ if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
+ if (intf->cur_altsetting->desc.bInterfaceProtocol == 129)
+ xpad->xtype = XTYPE_XBOX360W;
+ else if (intf->cur_altsetting->desc.bInterfaceProtocol == 208)
+ xpad->xtype = XTYPE_XBOXONE;
+ else
+ xpad->xtype = XTYPE_XBOX360;
+ } else {
+ xpad->xtype = XTYPE_XBOX;
+ }
+
+ if (dpad_to_buttons)
+ xpad->mapping |= MAP_DPAD_TO_BUTTONS;
+ if (triggers_to_buttons)
+ xpad->mapping |= MAP_TRIGGERS_TO_BUTTONS;
+ if (sticks_to_null)
+ xpad->mapping |= MAP_STICKS_TO_NULL;
+ }
+
+ if (xpad->xtype == XTYPE_XBOXONE &&
+ intf->cur_altsetting->desc.bInterfaceNumber != 0) {
+ /*
+ * The Xbox One controller lists three interfaces all with the
+ * same interface class, subclass and protocol. Differentiate by
+ * interface number.
+ */
+ error = -ENODEV;
+ goto err_free_in_urb;
+ }
+
+ ep_irq_in = ep_irq_out = NULL;
+
+ for (i = 0; i < 2; i++) {
+ struct usb_endpoint_descriptor *ep =
+ &intf->cur_altsetting->endpoint[i].desc;
+
+ if (usb_endpoint_xfer_int(ep)) {
+ if (usb_endpoint_dir_in(ep))
+ ep_irq_in = ep;
+ else
+ ep_irq_out = ep;
+ }
+ }
+
+ if (!ep_irq_in || !ep_irq_out) {
+ error = -ENODEV;
+ goto err_free_in_urb;
+ }
+
+ error = xpad_init_output(intf, xpad, ep_irq_out);
+ if (error)
+ goto err_free_in_urb;
+
+ usb_fill_int_urb(xpad->irq_in, udev,
+ usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress),
+ xpad->idata, XPAD_PKT_LEN, xpad_irq_in,
+ xpad, ep_irq_in->bInterval);
+ xpad->irq_in->transfer_dma = xpad->idata_dma;
+ xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_set_intfdata(intf, xpad);
+
+ if (xpad->xtype == XTYPE_XBOX360W) {
+ /*
+ * Submit the int URB immediately rather than waiting for open
+ * because we get status messages from the device whether
+ * or not any controllers are attached. In fact, it's
+ * exactly the message that a controller has arrived that
+ * we're waiting for.
+ */
+ error = xpad360w_start_input(xpad);
+ if (error)
+ goto err_deinit_output;
+ /*
+ * Wireless controllers require RESET_RESUME to work properly
+ * after suspend. Ideally this quirk should be in usb core
+ * quirk list, but we have too many vendors producing these
+ * controllers and we'd need to maintain 2 identical lists
+ * here in this driver and in usb core.
+ */
+ udev->quirks |= USB_QUIRK_RESET_RESUME;
+ } else {
+ error = xpad_init_input(xpad);
+ if (error)
+ goto err_deinit_output;
+ }
+ return 0;
+
+err_deinit_output:
+ xpad_deinit_output(xpad);
+err_free_in_urb:
+ usb_free_urb(xpad->irq_in);
+err_free_idata:
+ usb_free_coherent(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
+err_free_mem:
+ kfree(xpad);
+ return error;
+}
+
+static void xpad_disconnect(struct usb_interface *intf)
+{
+ struct usb_xpad *xpad = usb_get_intfdata(intf);
+
+ if (xpad->xtype == XTYPE_XBOX360W)
+ xpad360w_stop_input(xpad);
+
+ xpad_deinit_input(xpad);
+
+ /*
+ * Now that both input device and LED device are gone we can
+ * stop output URB.
+ */
+ xpad_stop_output(xpad);
+
+ xpad_deinit_output(xpad);
+
+ usb_free_urb(xpad->irq_in);
+ usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
+ xpad->idata, xpad->idata_dma);
+
+ kfree(xpad);
+
+ usb_set_intfdata(intf, NULL);
+}
+
+static int xpad_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct usb_xpad *xpad = usb_get_intfdata(intf);
+ struct input_dev *input = xpad->dev;
+
+ if (xpad->xtype == XTYPE_XBOX360W) {
+ /*
+ * Wireless controllers always listen to input so
+ * they are notified when controller shows up
+ * or goes away.
+ */
+ xpad360w_stop_input(xpad);
+
+ /*
+ * The wireless adapter is going off now, so the
+ * gamepads are going to become disconnected.
+ * Unless explicitly disabled, power them down
+ * so they don't just sit there flashing.
+ */
+ if (auto_poweroff && xpad->pad_present)
+ xpad360w_poweroff_controller(xpad);
+ } else {
+ mutex_lock(&input->mutex);
+ if (input->users)
+ xpad_stop_input(xpad);
+ mutex_unlock(&input->mutex);
+ }
+
+ xpad_stop_output(xpad);
+
+ return 0;
+}
+
+static int xpad_resume(struct usb_interface *intf)
+{
+ struct usb_xpad *xpad = usb_get_intfdata(intf);
+ struct input_dev *input = xpad->dev;
+ int retval = 0;
+
+ if (xpad->xtype == XTYPE_XBOX360W) {
+ retval = xpad360w_start_input(xpad);
+ } else {
+ mutex_lock(&input->mutex);
+ if (input->users) {
+ retval = xpad_start_input(xpad);
+ } else if (xpad->xtype == XTYPE_XBOXONE) {
+ /*
+ * Even if there are no users, we'll send Xbox One pads
+ * the startup sequence so they don't sit there and
+ * blink until somebody opens the input device again.
+ */
+ retval = xpad_start_xbox_one(xpad);
+ }
+ mutex_unlock(&input->mutex);
+ }
+
+ return retval;
+}
+
+static struct usb_driver xpad_driver = {
+ .name = "xpad",
+ .probe = xpad_probe,
+ .disconnect = xpad_disconnect,
+ .suspend = xpad_suspend,
+ .resume = xpad_resume,
+ .reset_resume = xpad_resume,
+ .id_table = xpad_table,
+};
+
+module_usb_driver(xpad_driver);
+
+MODULE_AUTHOR("Marko Friedemann <mfr@bmx-chemnitz.de>");
+MODULE_DESCRIPTION("X-Box pad driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/joystick/zhenhua.c b/drivers/input/joystick/zhenhua.c
new file mode 100644
index 000000000..5c6d5de74
--- /dev/null
+++ b/drivers/input/joystick/zhenhua.c
@@ -0,0 +1,218 @@
+/*
+ * derived from "twidjoy.c"
+ *
+ * Copyright (c) 2008 Martin Kebert
+ * Copyright (c) 2001 Arndt Schoenewald
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ * Copyright (c) 2000 Mark Fletcher
+ *
+ */
+
+/*
+ * Driver to use 4CH RC transmitter using Zhen Hua 5-byte protocol (Walkera Lama,
+ * EasyCopter etc.) as a joystick under Linux.
+ *
+ * RC transmitters using Zhen Hua 5-byte protocol are cheap four channels
+ * transmitters for control a RC planes or RC helicopters with possibility to
+ * connect on a serial port.
+ * Data coming from transmitter is in this order:
+ * 1. byte = synchronisation byte
+ * 2. byte = X axis
+ * 3. byte = Y axis
+ * 4. byte = RZ axis
+ * 5. byte = Z axis
+ * (and this is repeated)
+ *
+ * For questions or feedback regarding this driver module please contact:
+ * Martin Kebert <gkmarty@gmail.com> - but I am not a C-programmer nor kernel
+ * coder :-(
+ */
+
+/*
+ * 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/bitrev.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "RC transmitter with 5-byte Zhen Hua protocol joystick driver"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define ZHENHUA_MAX_LENGTH 5
+
+/*
+ * Zhen Hua data.
+ */
+
+struct zhenhua {
+ struct input_dev *dev;
+ int idx;
+ unsigned char data[ZHENHUA_MAX_LENGTH];
+ char phys[32];
+};
+
+/*
+ * zhenhua_process_packet() decodes packets the driver receives from the
+ * RC transmitter. It updates the data accordingly.
+ */
+
+static void zhenhua_process_packet(struct zhenhua *zhenhua)
+{
+ struct input_dev *dev = zhenhua->dev;
+ unsigned char *data = zhenhua->data;
+
+ input_report_abs(dev, ABS_Y, data[1]);
+ input_report_abs(dev, ABS_X, data[2]);
+ input_report_abs(dev, ABS_RZ, data[3]);
+ input_report_abs(dev, ABS_Z, data[4]);
+
+ input_sync(dev);
+}
+
+/*
+ * zhenhua_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 zhenhua_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
+{
+ struct zhenhua *zhenhua = serio_get_drvdata(serio);
+
+ /* All Zhen Hua packets are 5 bytes. The fact that the first byte
+ * is allways 0xf7 and all others are in range 0x32 - 0xc8 (50-200)
+ * can be used to check and regain sync. */
+
+ if (data == 0xef)
+ zhenhua->idx = 0; /* this byte starts a new packet */
+ else if (zhenhua->idx == 0)
+ return IRQ_HANDLED; /* wrong MSB -- ignore this byte */
+
+ if (zhenhua->idx < ZHENHUA_MAX_LENGTH)
+ zhenhua->data[zhenhua->idx++] = bitrev8(data);
+
+ if (zhenhua->idx == ZHENHUA_MAX_LENGTH) {
+ zhenhua_process_packet(zhenhua);
+ zhenhua->idx = 0;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * zhenhua_disconnect() is the opposite of zhenhua_connect()
+ */
+
+static void zhenhua_disconnect(struct serio *serio)
+{
+ struct zhenhua *zhenhua = serio_get_drvdata(serio);
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_unregister_device(zhenhua->dev);
+ kfree(zhenhua);
+}
+
+/*
+ * zhenhua_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 zhenhua_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct zhenhua *zhenhua;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+
+ zhenhua = kzalloc(sizeof(struct zhenhua), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!zhenhua || !input_dev)
+ goto fail1;
+
+ zhenhua->dev = input_dev;
+ snprintf(zhenhua->phys, sizeof(zhenhua->phys), "%s/input0", serio->phys);
+
+ input_dev->name = "Zhen Hua 5-byte device";
+ input_dev->phys = zhenhua->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_ZHENHUA;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT(EV_ABS);
+ input_set_abs_params(input_dev, ABS_X, 50, 200, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 50, 200, 0, 0);
+ input_set_abs_params(input_dev, ABS_Z, 50, 200, 0, 0);
+ input_set_abs_params(input_dev, ABS_RZ, 50, 200, 0, 0);
+
+ serio_set_drvdata(serio, zhenhua);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(zhenhua->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(zhenhua);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static const struct serio_device_id zhenhua_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_ZHENHUA,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, zhenhua_serio_ids);
+
+static struct serio_driver zhenhua_drv = {
+ .driver = {
+ .name = "zhenhua",
+ },
+ .description = DRIVER_DESC,
+ .id_table = zhenhua_serio_ids,
+ .interrupt = zhenhua_interrupt,
+ .connect = zhenhua_connect,
+ .disconnect = zhenhua_disconnect,
+};
+
+module_serio_driver(zhenhua_drv);