]> git.hungrycats.org Git - linux/commitdiff
Update the iforce driver to the latest revision (it now lives in a
authorVojtech Pavlik <vojtech@twilight.ucw.cz>
Wed, 3 Jul 2002 16:49:42 +0000 (18:49 +0200)
committerVojtech Pavlik <vojtech@twilight.ucw.cz>
Wed, 3 Jul 2002 16:49:42 +0000 (18:49 +0200)
separate directory), add twiddler and guillemot drivers.

13 files changed:
drivers/input/joystick/Config.help
drivers/input/joystick/Config.in
drivers/input/joystick/Makefile
drivers/input/joystick/guillemot.c [new file with mode: 0644]
drivers/input/joystick/iforce.c [deleted file]
drivers/input/joystick/iforce/Makefile [new file with mode: 0644]
drivers/input/joystick/iforce/iforce-ff.c [new file with mode: 0644]
drivers/input/joystick/iforce/iforce-main.c [new file with mode: 0644]
drivers/input/joystick/iforce/iforce-packets.c [new file with mode: 0644]
drivers/input/joystick/iforce/iforce-serio.c [new file with mode: 0644]
drivers/input/joystick/iforce/iforce-usb.c [new file with mode: 0644]
drivers/input/joystick/iforce/iforce.h [new file with mode: 0644]
drivers/input/joystick/twidjoy.c [new file with mode: 0644]

index 1b4187bbb8a37bf2aac32e18f2c7aff0073efe1c..a8998c103900072be0ff344bf6f06f15b9ae8c50 100644 (file)
@@ -67,6 +67,15 @@ CONFIG_JOYSTICK_GRIP
   The module will be called grip.o. If you want to compile it as a
   module, say M here and read <file:Documentation/modules.txt>.
 
+CONFIG_JOYSTICK_GUILLEMOT
+  Say Y here if you have a Guillemot joystick using a digital
+  protocol over the PC gameport. 
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called guillemot.o. If you want to compile it as a
+  module, say M here and read <file:Documentation/modules.txt>.
+
 CONFIG_JOYSTICK_INTERACT
   Say Y here if you have an InterAct gameport or joystick
   communicating digitally over the gameport.
@@ -158,6 +167,15 @@ CONFIG_JOYSTICK_STINGER
   The module will be called stinger.o. If you want to compile it as a
   module, say M here and read <file:Documentation/modules.txt>.
 
+CONFIG_JOYSTICK_TWIDDLER
+  Say Y here if you have a Handykey Twiddler connected to your
+  computer's serial port and want to use it as a joystick.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called twidjoy.o. If you want to compile it as a
+  module, say M here and read <file:Documentation/modules.txt>.
+
 CONFIG_JOYSTICK_DB9
   Say Y here if you have a Sega Master System gamepad, Sega Genesis
   gamepad, Sega Saturn gamepad, or a Multisystem -- Atari, Amiga,
index afd07b89dff37b963ddf97e456c2000c1069e20e..25a293ddee3e10c4fc21dc68b1dd79afa1d1950b 100644 (file)
@@ -10,6 +10,7 @@ dep_tristate '  Logitech ADI digital joysticks and gamepads' CONFIG_JOYSTICK_ADI
 dep_tristate '  Creative Labs Blaster Cobra gamepad' CONFIG_JOYSTICK_COBRA $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
 dep_tristate '  Genius Flight2000 Digital joysticks and gamepads' CONFIG_JOYSTICK_GF2K $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
 dep_tristate '  Gravis GrIP joysticks and gamepads' CONFIG_JOYSTICK_GRIP $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
+dep_tristate '  Guillemot joysticks and gamepads' CONFIG_JOYSTICK_GUILLEMOT $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
 dep_tristate '  InterAct digital joysticks and gamepads' CONFIG_JOYSTICK_INTERACT $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
 dep_tristate '  Microsoft SideWinder digital joysticks and gamepads' CONFIG_JOYSTICK_SIDEWINDER $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
 dep_tristate '  ThrustMaster DirectConnect joysticks and gamepads' CONFIG_JOYSTICK_TMDC $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_GAMEPORT
@@ -21,6 +22,7 @@ dep_tristate '  LogiCad3d Magellan/SpaceMouse 6dof controllers' CONFIG_JOYSTICK_
 dep_tristate '  SpaceTec SpaceOrb/Avenger 6dof controllers' CONFIG_JOYSTICK_SPACEORB $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO
 dep_tristate '  SpaceTec SpaceBall 6dof controllers' CONFIG_JOYSTICK_SPACEBALL $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO
 dep_tristate '  Gravis Stinger gamepad' CONFIG_JOYSTICK_STINGER $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO
+dep_tristate '  Twiddler as as joystick' CONFIG_JOYSTICK_TWIDDLER $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_SERIO
 
 dep_tristate '  Multisystem, Sega Genesis, Saturn joysticks and gamepads' CONFIG_JOYSTICK_DB9 $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_PARPORT
 dep_tristate '  Multisystem, NES, SNES, N64, PSX joysticks and gamepads' CONFIG_JOYSTICK_GAMECON $CONFIG_INPUT $CONFIG_INPUT_JOYSTICK $CONFIG_PARPORT
index c70244633703b24061ea6b8abfc0a01e450e44f6..b06e9d878828e0f3d3f40dfa456e877d26b602bb 100644 (file)
@@ -2,24 +2,6 @@
 # Makefile for the input core drivers.
 #
 
-# I-Force may need both USB and RS-232
-
-CONFIG_JOYSTICK_IFORCE := n
-
-ifeq ($(CONFIG_JOYSTICK_IFORCE_232),y)
-       ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),y)
-               CONFIG_JOYSTICK_IFORCE := y
-       endif
-endif
-
-ifeq ($(CONFIG_JOYSTICK_IFORCE_232),m)
-       CONFIG_JOYSTICK_IFORCE := m
-endif
-
-ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),m)
-       CONFIG_JOYSTICK_IFORCE := m
-endif
-
 # Each configuration option enables a list of files.
 
 obj-$(CONFIG_JOYSTICK_A3D)             += a3d.o
@@ -31,8 +13,9 @@ 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_IFORCE)          += iforce.o
+obj-$(CONFIG_JOYSTICK_GUILLEMOT)       += guillemot.o
 obj-$(CONFIG_JOYSTICK_INTERACT)                += interact.o
+obj-$(CONFIG_JOYSTICK_JOYDUMP)         += joydump.o
 obj-$(CONFIG_JOYSTICK_MAGELLAN)                += magellan.o
 obj-$(CONFIG_JOYSTICK_SIDEWINDER)      += sidewinder.o
 obj-$(CONFIG_JOYSTICK_SPACEBALL)       += spaceball.o
@@ -40,8 +23,33 @@ 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
 
+# I-Force may need both USB and RS-232
+
+CONFIG_JOYSTICK_IFORCE := n
+
+ifeq ($(CONFIG_JOYSTICK_IFORCE_232),y)
+       ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),y)
+               CONFIG_JOYSTICK_IFORCE := y
+       endif
+endif
+
+ifeq ($(CONFIG_JOYSTICK_IFORCE_232),m)
+       CONFIG_JOYSTICK_IFORCE := m
+endif
+
+ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),m)
+       CONFIG_JOYSTICK_IFORCE := m
+endif
+
+subdir-$(CONFIG_JOYSTICK_IFORCE)       += iforce
+
+ifeq ($(CONFIG_JOYSTICK_IFORCE),y)
+       obj-y += iforce/iforce-drv.o
+endif
+
 # The global Rules.make.
 
 include $(TOPDIR)/Rules.make
diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c
new file mode 100644 (file)
index 0000000..53c4672
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * $Id: guillemot.c,v 1.10 2002/01/22 20:28:12 vojtech Exp $
+ *
+ *  Copyright (c) 2001 Vojtech Pavlik
+ */
+
+/*
+ * Guillemot Digital Interface Protocol driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/gameport.h>
+#include <linux/input.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Guillemot Digital joystick driver");
+MODULE_LICENSE("GPL");
+
+#define GUILLEMOT_MAX_START    600     /* 600 us */
+#define GUILLEMOT_MAX_STROBE   60      /* 60 us */
+#define GUILLEMOT_MAX_LENGTH   17      /* 17 bytes */
+#define GUILLEMOT_REFRESH_TIME HZ/50   /* 20 ms */
+
+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;
+       struct timer_list timer;
+       int used;
+       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);
+
+       __save_flags(flags);
+       __cli();
+       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;
+               }
+       }
+
+       __restore_flags(flags);
+
+       return i;
+}
+
+/*
+ * guillemot_timer() reads and analyzes Guillemot joystick data.
+ */
+
+static void guillemot_timer(unsigned long private)
+{
+       struct guillemot *guillemot = (struct guillemot *) private;
+       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);
+       }
+
+       mod_timer(&guillemot->timer, jiffies + GUILLEMOT_REFRESH_TIME);
+}
+
+/*
+ * guillemot_open() is a callback from the input open routine.
+ */
+
+static int guillemot_open(struct input_dev *dev)
+{
+       struct guillemot *guillemot = dev->private;
+       if (!guillemot->used++)
+               mod_timer(&guillemot->timer, jiffies + GUILLEMOT_REFRESH_TIME); 
+       return 0;
+}
+
+/*
+ * guillemot_close() is a callback from the input close routine.
+ */
+
+static void guillemot_close(struct input_dev *dev)
+{
+       struct guillemot *guillemot = dev->private;
+       if (!--guillemot->used)
+               del_timer(&guillemot->timer);
+}
+
+/*
+ * guillemot_connect() probes for Guillemot joysticks.
+ */
+
+static void guillemot_connect(struct gameport *gameport, struct gameport_dev *dev)
+{
+       struct guillemot *guillemot;
+       u8 data[GUILLEMOT_MAX_LENGTH];
+       int i, t;
+
+       if (!(guillemot = kmalloc(sizeof(struct guillemot), GFP_KERNEL)))
+               return;
+       memset(guillemot, 0, sizeof(struct guillemot));
+
+       gameport->private = guillemot;
+
+       guillemot->gameport = gameport;
+       init_timer(&guillemot->timer);
+       guillemot->timer.data = (long) guillemot;
+       guillemot->timer.function = guillemot_timer;
+
+       if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW))
+               goto fail1;
+
+       i = guillemot_read_packet(gameport, data);
+
+       if (i != GUILLEMOT_MAX_LENGTH * 8 || data[0] != 0x55 || data[16] != 0xaa)
+               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]);
+               goto fail2;     
+       }
+
+       sprintf(guillemot->phys, "%s/input0", gameport->phys);
+
+       guillemot->type = guillemot_type + i;
+
+       guillemot->dev.private = guillemot;
+       guillemot->dev.open = guillemot_open;
+       guillemot->dev.close = guillemot_close;
+
+       guillemot->dev.name = guillemot_type[i].name;
+       guillemot->dev.phys = guillemot->phys;
+       guillemot->dev.idbus = BUS_GAMEPORT;
+       guillemot->dev.idvendor = GAMEPORT_ID_VENDOR_GUILLEMOT;
+       guillemot->dev.idproduct = guillemot_type[i].id;
+       guillemot->dev.idversion = (int)data[14] << 8 | data[15];
+
+       guillemot->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+       for (i = 0; (t = guillemot->type->abs[i]) >= 0; i++) {
+               set_bit(t, guillemot->dev.absbit);
+               guillemot->dev.absmin[t] = 0;
+               guillemot->dev.absmax[t] = 255;
+       }
+
+       if (guillemot->type->hat) 
+               for (i = 0; i < 2; i++) {
+                       t = ABS_HAT0X + i;
+                       set_bit(t, guillemot->dev.absbit);
+                       guillemot->dev.absmin[t] = -1;
+                       guillemot->dev.absmax[t] = 1;
+               }
+
+       for (i = 0; (t = guillemot->type->btn[i]) >= 0; i++)
+               set_bit(t, guillemot->dev.keybit);
+
+       input_register_device(&guillemot->dev);
+       printk(KERN_INFO "input: %s ver %d.%02d on %s\n",
+               guillemot->type->name, data[14], data[15], gameport->phys);
+
+       return;
+fail2: gameport_close(gameport);
+fail1:  kfree(guillemot);
+}
+
+static void guillemot_disconnect(struct gameport *gameport)
+{
+       struct guillemot *guillemot = gameport->private;
+       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_dev guillemot_dev = {
+       connect:        guillemot_connect,
+       disconnect:     guillemot_disconnect,
+};
+
+int __init guillemot_init(void)
+{
+       gameport_register_device(&guillemot_dev);
+       return 0;
+}
+
+void __exit guillemot_exit(void)
+{
+       gameport_unregister_device(&guillemot_dev);
+}
+
+module_init(guillemot_init);
+module_exit(guillemot_exit);
diff --git a/drivers/input/joystick/iforce.c b/drivers/input/joystick/iforce.c
deleted file mode 100644 (file)
index 172c5d9..0000000
+++ /dev/null
@@ -1,1224 +0,0 @@
-/*
- * $Id: iforce.c,v 1.56 2001/05/27 14:41:26 jdeneux Exp $
- *
- *  Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@suse.cz>
- *  Copyright (c) 2001 Johann Deneux <deneux@ifrance.com>
- *
- *  USB/RS232 I-Force joysticks and wheels.
- *
- *  Sponsored by SuSE
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Should you need to contact me, the author, you can do so either by
- * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
- * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/input.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/usb.h>
-#include <linux/serio.h>
-#include <linux/config.h>
-
-/* FF: 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>
-
-MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>, Johann Deneux <deneux@ifrance.com>");
-MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver");
-MODULE_LICENSE("GPL");
-
-#define IFORCE_MAX_LENGTH      16
-
-#if defined(CONFIG_JOYSTICK_IFORCE_232) || defined(CONFIG_JOYSTICK_IFORCE_232_MODULE)
-#define IFORCE_232     1
-#endif
-#if defined(CONFIG_JOYSTICK_IFORCE_USB) || defined(CONFIG_JOYSTICK_IFORCE_USB_MODULE)
-#define IFORCE_USB     2
-#endif
-
-#define FF_EFFECTS_MAX 32
-
-/* Each force feedback effect is made of one core effect, which can be
- * associated to at most to effect modifiers
- */
-#define FF_MOD1_IS_USED                0
-#define FF_MOD2_IS_USED                1
-#define FF_CORE_IS_USED                2
-#define FF_CORE_IS_PLAYED      3
-#define FF_MODCORE_MAX         3
-
-struct iforce_core_effect {
-       /* Information about where modifiers are stored in the device's memory */
-       struct resource mod1_chunk;
-       struct resource mod2_chunk;
-       unsigned long flags[NBITS(FF_MODCORE_MAX)];
-};
-
-#define FF_CMD_EFFECT          0x010e
-#define FF_CMD_SHAPE           0x0208
-#define FF_CMD_MAGNITUDE       0x0303
-#define FF_CMD_PERIOD          0x0407
-#define FF_CMD_INTERACT                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
-
-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_wheel[] =    { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
-       BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 };
-static signed short abs_joystick[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -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_FRICTION,
-       FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, FF_SAW_DOWN, FF_GAIN, FF_AUTOCENTER, -1 };
-
-static struct iforce_device {
-       u16 idvendor;
-       u16 idproduct;
-       char *name;
-       signed short *btn;
-       signed short *abs;
-       signed short *ff;
-} iforce_device[] = {
-       { 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, abs_joystick, ff_iforce },
-       { 0x05ef, 0x8884, "AVB Mag Turbo Force",                        btn_wheel, abs_wheel, ff_iforce },
-       { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback",       btn_wheel, abs_wheel, ff_iforce },
-       { 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]",         btn_joystick, abs_joystick, ff_iforce }
-};
-
-struct iforce {
-       struct input_dev dev;           /* Input device interface */
-       struct iforce_device *type;
-       char name[64];
-       int open;
-       int bus;
-
-       unsigned char data[IFORCE_MAX_LENGTH];
-       unsigned char edata[IFORCE_MAX_LENGTH];
-       u16 ecmd;
-       u16 expect_packet;
-
-#ifdef IFORCE_232
-       struct serio *serio;            /* RS232 transfer */
-       int idx, pkt, len, id;
-       unsigned char csum;
-#endif
-#ifdef IFORCE_USB
-       struct usb_device *usbdev;      /* USB transfer */
-       struct urb *irq, *out, *ctrl;
-       struct usb_ctrlrequest dr;
-#endif
-                                       /* Force Feedback */
-       wait_queue_head_t wait;
-       struct resource device_memory;
-       struct iforce_core_effect core_effects[FF_EFFECTS_MAX];
-};
-
-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}};
-
-/* Get hi and low bytes of a 16-bits int */
-#define HI(a)  ((unsigned char)((a) >> 8))
-#define LO(a)  ((unsigned char)((a) & 0xff))
-
-/* Encode a time value */
-#define TIME_SCALE(a)  ((a) == 0xffff ? 0xffff : (a) * 1000 / 256)
-
-static void dump_packet(char *msg, u16 cmd, unsigned char *data)
-{
-       int i;
-
-       printk(KERN_DEBUG "iforce.c: %s ( cmd = %04x, data = ", msg, cmd);
-       for (i = 0; i < LO(cmd); i++)
-               printk("%02x ", data[i]);
-       printk(")\n");
-}
-
-/*
- * Send a packet of bytes to the device
- */
-static void send_packet(struct iforce *iforce, u16 cmd, unsigned char* data)
-{
-       switch (iforce->bus) {
-
-#ifdef IFORCE_232
-               case IFORCE_232: {
-
-                       int i;
-                       unsigned char csum = 0x2b ^ HI(cmd) ^ LO(cmd);
-
-                       serio_write(iforce->serio, 0x2b);
-                       serio_write(iforce->serio, HI(cmd));
-                       serio_write(iforce->serio, LO(cmd));
-
-                       for (i = 0; i < LO(cmd); i++) {
-                               serio_write(iforce->serio, data[i]);
-                               csum = csum ^ data[i];
-                       }
-
-                       serio_write(iforce->serio, csum);
-                       return;
-               }
-#endif
-#ifdef IFORCE_USB
-               case IFORCE_USB: {
-
-                       DECLARE_WAITQUEUE(wait, current);
-                       int timeout = HZ; /* 1 second */
-
-                       memcpy(iforce->out->transfer_buffer + 1, data, LO(cmd));
-                       ((char*)iforce->out->transfer_buffer)[0] = HI(cmd);
-                       iforce->out->transfer_buffer_length = LO(cmd) + 2;
-                       iforce->out->dev = iforce->usbdev;
-
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       add_wait_queue(&iforce->wait, &wait);
-
-                       if (usb_submit_urb(iforce->out, GFP_ATOMIC)) {
-                               set_current_state(TASK_RUNNING);
-                               remove_wait_queue(&iforce->wait, &wait);
-                               return;
-                       }
-
-                       while (timeout && iforce->out->status == -EINPROGRESS)
-                               timeout = schedule_timeout(timeout);
-
-                       set_current_state(TASK_RUNNING);
-                       remove_wait_queue(&iforce->wait, &wait);
-
-                       if (!timeout)
-                               usb_unlink_urb(iforce->out);
-
-                       return;
-               }
-#endif
-       }
-}
-
-static void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data)
-{
-       struct input_dev *dev = &iforce->dev;
-       int i;
-
-#ifdef IFORCE_232
-       if (HI(iforce->expect_packet) == HI(cmd)) {
-               iforce->expect_packet = 0;
-               iforce->ecmd = cmd;
-               memcpy(iforce->edata, data, IFORCE_MAX_LENGTH);
-               if (waitqueue_active(&iforce->wait))
-                       wake_up(&iforce->wait);
-       }
-#endif
-
-       if (!iforce->type)
-               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]);
-                       } 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)));
-
-                       break;
-
-               case 0x02:      /* status report */
-
-                       input_report_key(dev, BTN_DEAD, data[0] & 0x02);
-                       break;
-       }
-}
-
-static int get_id_packet(struct iforce *iforce, char *packet)
-{
-       DECLARE_WAITQUEUE(wait, current);
-       int timeout = HZ; /* 1 second */
-
-       switch (iforce->bus) {
-
-#ifdef IFORCE_USB
-               case IFORCE_USB:
-
-                       iforce->dr.bRequest = packet[0];
-                       iforce->ctrl->dev = iforce->usbdev;
-
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       add_wait_queue(&iforce->wait, &wait);
-
-                       if (usb_submit_urb(iforce->ctrl, GFP_ATOMIC)) {
-                               set_current_state(TASK_RUNNING);
-                               remove_wait_queue(&iforce->wait, &wait);
-                               return -1;
-                       }
-
-                       while (timeout && iforce->ctrl->status == -EINPROGRESS)
-                               timeout = schedule_timeout(timeout);
-
-                       set_current_state(TASK_RUNNING);
-                       remove_wait_queue(&iforce->wait, &wait);
-
-                       if (!timeout) {
-                               usb_unlink_urb(iforce->ctrl);
-                               return -1;
-                       }
-
-                       break;
-#endif
-#ifdef IFORCE_232
-               case IFORCE_232:
-
-                       iforce->expect_packet = FF_CMD_QUERY;
-                       send_packet(iforce, FF_CMD_QUERY, packet);
-
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       add_wait_queue(&iforce->wait, &wait);
-
-                       while (timeout && iforce->expect_packet)
-                               timeout = schedule_timeout(timeout);
-
-                       set_current_state(TASK_RUNNING);
-                       remove_wait_queue(&iforce->wait, &wait);
-
-                       if (!timeout) {
-                               iforce->expect_packet = 0;
-                               return -1;
-                       }
-
-                       break;
-#endif
-       }
-
-       return -(iforce->edata[0] != packet[0]);
-}
-
-static int iforce_open(struct input_dev *dev)
-{
-       struct iforce *iforce = dev->private;
-
-       switch (iforce->bus) {
-#ifdef IFORCE_USB
-               case IFORCE_USB:
-                       if (iforce->open++)
-                               break;
-                       iforce->irq->dev = iforce->usbdev;
-                       if (usb_submit_urb(iforce->irq, GFP_KERNEL))
-                                       return -EIO;
-                       break;
-#endif
-       }
-       return 0;
-}
-
-static void iforce_close(struct input_dev *dev)
-{
-       struct iforce *iforce = dev->private;
-
-       switch (iforce->bus) {
-#ifdef IFORCE_USB
-               case IFORCE_USB:
-                       if (!--iforce->open)
-                               usb_unlink_urb(iforce->irq);
-                       break;
-#endif
-       }
-}
-
-/*
- * Start or stop playing an effect
- */
-
-static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
-{
-       struct iforce* iforce = (struct iforce*)(dev->private);
-       unsigned char data[3];
-
-       printk(KERN_DEBUG "iforce.c: input_event(type = %d, code = %d, value = %d)\n", type, code, value);
-
-       if (type != EV_FF)
-               return -1;
-
-       switch (code) {
-
-               case FF_GAIN:
-
-                       data[0] = value >> 9;
-                       send_packet(iforce, FF_CMD_GAIN, data);
-
-                       return 0;
-
-               case FF_AUTOCENTER:
-
-                       data[0] = 0x03;
-                       data[1] = value >> 9;
-                       send_packet(iforce, FF_CMD_AUTOCENTER, data);
-
-                       data[0] = 0x04;
-                       data[1] = 0x01;
-                       send_packet(iforce, FF_CMD_AUTOCENTER, data);
-
-                       return 0;
-
-               default: /* Play an effect */
-
-                       if (code >= iforce->dev.ff_effects_max)
-                               return -1;
-
-                       data[0] = LO(code);
-                       data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0;
-                       data[2] = LO(value);
-                       send_packet(iforce, FF_CMD_PLAY, data);
-
-                       return 0;
-       }
-
-       return -1;
-}
-
-/*
- * 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, __s16 level)
-{
-       unsigned char data[3];
-
-       if (allocate_resource(&(iforce->device_memory), mod_chunk, 2,
-               iforce->device_memory.start, iforce->device_memory.end, 2L,
-               NULL, NULL)) {
-               return -ENOMEM;
-       }
-
-       data[0] = LO(mod_chunk->start);
-       data[1] = HI(mod_chunk->start);
-       data[2] = HI(level);
-
-       send_packet(iforce, 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,
-       __s16 magnitude, __s16 offset, u16 period, u16 phase)
-{
-       unsigned char data[7];
-
-       period = TIME_SCALE(period);
-
-       if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c,
-               iforce->device_memory.start, iforce->device_memory.end, 2L,
-               NULL, NULL)) {
-               return -ENOMEM;
-       }
-
-       data[0] = LO(mod_chunk->start);
-       data[1] = HI(mod_chunk->start);
-
-       data[2] = HI(magnitude);
-       data[3] = HI(offset);
-       data[4] = HI(phase);
-
-       data[5] = LO(period);
-       data[6] = HI(period);
-
-       send_packet(iforce, FF_CMD_PERIOD, data);
-
-       return 0;
-}
-
-/*
- * Uploads the part of an effect setting the shape of the force
- */
-
-static int make_shape_modifier(struct iforce* iforce, struct resource* mod_chunk,
-       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 (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e,
-               iforce->device_memory.start, iforce->device_memory.end, 2L,
-               NULL, NULL)) {
-               return -ENOMEM;
-       }
-
-       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);
-
-       send_packet(iforce, FF_CMD_SHAPE, data);
-
-       return 0;
-}
-
-/*
- * Component of spring, friction, inertia... effects
- */
-
-static int make_interactive_modifier(struct iforce* iforce,
-       struct resource* mod_chunk,
-       __s16 rsat, __s16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center)
-{
-       unsigned char data[10];
-
-       if (allocate_resource(&(iforce->device_memory), mod_chunk, 8,
-               iforce->device_memory.start, iforce->device_memory.end, 2L,
-               NULL, NULL)) {
-               return -ENOMEM;
-       }
-
-       data[0] = LO(mod_chunk->start);
-       data[1] = HI(mod_chunk->start);
-
-       data[2] = HI(rk);
-       data[3] = HI(lk);
-
-       data[4] = LO(center);
-       data[5] = HI(center);
-
-       data[6] = LO(db);
-       data[7] = HI(db);
-
-       data[8] = HI(rsat);
-       data[9] = HI(lsat);
-
-       send_packet(iforce, FF_CMD_INTERACT, 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;
-}
-
-/*
- * 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);
-
-       send_packet(iforce, FF_CMD_EFFECT, data);
-
-       return 0;
-}
-
-/*
- * Upload a periodic effect to the device
- */
-
-static int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect)
-{
-       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 err = 0;
-
-       err = make_period_modifier(iforce, mod1_chunk,
-               effect->u.periodic.magnitude, effect->u.periodic.offset,
-               effect->u.periodic.period, effect->u.periodic.phase);
-       if (err) return err;
-       set_bit(FF_MOD1_IS_USED, core_effect->flags);
-
-       err = make_shape_modifier(iforce, mod2_chunk,
-               effect->u.periodic.shape.attack_length,
-               effect->u.periodic.shape.attack_level,
-               effect->u.periodic.shape.fade_length,
-               effect->u.periodic.shape.fade_level);
-       if (err) return 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;
-       }
-
-       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);
-
-       return err;
-}
-
-/*
- * Upload a constant force effect
- */
-static int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect)
-{
-       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 err = 0;
-
-       printk(KERN_DEBUG "iforce.c: make constant effect\n");
-
-       err = make_magnitude_modifier(iforce, mod1_chunk, effect->u.constant.level);
-       if (err) return err;
-       set_bit(FF_MOD1_IS_USED, core_effect->flags);
-
-       err = make_shape_modifier(iforce, mod2_chunk,
-               effect->u.constant.shape.attack_length,
-               effect->u.constant.shape.attack_level,
-               effect->u.constant.shape.fade_length,
-               effect->u.constant.shape.fade_level);
-       if (err) return err;
-       set_bit(FF_MOD2_IS_USED, core_effect->flags);
-
-       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);
-
-       return err;
-}
-
-/*
- * Upload an interactive effect. Those are for example friction, inertia, springs...
- */
-static int iforce_upload_interactive(struct iforce* iforce, struct ff_effect* effect)
-{
-       int core_id = effect->id;
-       struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
-       struct resource* mod_chunk = &(core_effect->mod1_chunk);
-       u8 type, axes;
-       u16 mod1, mod2, direction;
-       int err = 0;
-
-       printk(KERN_DEBUG "iforce.c: make interactive effect\n");
-
-       switch (effect->type) {
-               case FF_SPRING:         type = 0x40; break;
-               case FF_FRICTION:       type = 0x41; break;
-               default: return -1;
-       }
-
-       err = make_interactive_modifier(iforce, mod_chunk,
-               effect->u.interactive.right_saturation,
-               effect->u.interactive.left_saturation,
-               effect->u.interactive.right_coeff,
-               effect->u.interactive.left_coeff,
-               effect->u.interactive.deadband,
-               effect->u.interactive.center);
-       if (err) return err;
-       set_bit(FF_MOD1_IS_USED, core_effect->flags);
-
-       switch ((test_bit(ABS_X, &effect->u.interactive.axis) ||
-               test_bit(ABS_WHEEL, &effect->u.interactive.axis)) |
-               (!!test_bit(ABS_Y, &effect->u.interactive.axis) << 1)) {
-
-               case 0: /* Only one axis, choose orientation */
-                       mod1 = mod_chunk->start;
-                       mod2 = 0xffff;
-                       direction = effect->direction;
-                       axes = 0x20;
-                       break;
-
-               case 1: /* Only X axis */
-                       mod1 = mod_chunk->start;
-                       mod2 = 0xffff;
-                       direction = 0x5a00;
-                       axes = 0x40;
-                       break;
-
-               case 2: /* Only Y axis */
-                       mod1 = 0xffff;
-                       mod2 = mod_chunk->start;
-                       direction = 0xb400;
-                       axes = 0x80;
-                       break;
-
-               case 3: /* Both X and Y axes */
-                       /* TODO: same setting for both axes is not mandatory */
-                       mod1 = mod_chunk->start;
-                       mod2 = mod_chunk->start;
-                       direction = 0x6000;
-                       axes = 0xc0;
-                       break;
-
-               default:
-                       return -1;
-       }
-
-       err = make_core(iforce, effect->id,
-               mod1, mod2,
-               type, axes,
-               effect->replay.length, effect->replay.delay,
-               effect->trigger.button, effect->trigger.interval,
-               direction);
-
-       return err;
-}
-
-/*
- * Function called when an ioctl is performed on the event dev entry.
- * It uploads an effect to the device
- */
-static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
-{
-       struct iforce* iforce = (struct iforce*)(dev->private);
-       int id;
-
-       printk(KERN_DEBUG "iforce.c: upload effect\n");
-
-/*
- * Get a free id
- */
-
-       for (id=0; id < FF_EFFECTS_MAX; ++id)
-               if (!test_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags)) break;
-
-       if ( id == FF_EFFECTS_MAX || id >= iforce->dev.ff_effects_max)
-               return -ENOMEM;
-
-       effect->id = id;
-       set_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags);
-
-/*
- * Upload the effect
- */
-
-       switch (effect->type) {
-
-               case FF_PERIODIC:
-                       return iforce_upload_periodic(iforce, effect);
-
-               case FF_CONSTANT:
-                       return iforce_upload_constant(iforce, effect);
-
-               case FF_SPRING:
-               case FF_FRICTION:
-                       return iforce_upload_interactive(iforce, effect);
-
-               default:
-                       return -1;
-       }
-}
-
-/*
- * Erases an effect: it frees the effect id and mark as unused the memory
- * allocated for the parameters
- */
-static int iforce_erase_effect(struct input_dev *dev, int effect_id)
-{
-       struct iforce* iforce = (struct iforce*)(dev->private);
-       int err = 0;
-       struct iforce_core_effect* core_effect;
-
-       printk(KERN_DEBUG "iforce.c: erase effect %d\n", effect_id);
-
-       if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX)
-               return -EINVAL;
-
-       core_effect = iforce->core_effects + effect_id;
-
-       if (test_bit(FF_MOD1_IS_USED, core_effect->flags))
-               err = release_resource(&(iforce->core_effects[effect_id].mod1_chunk));
-
-       if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags))
-               err = release_resource(&(iforce->core_effects[effect_id].mod2_chunk));
-
-       /*TODO: remember to change that if more FF_MOD* bits are added */
-       core_effect->flags[0] = 0;
-
-       return err;
-}
-static int iforce_init_device(struct iforce *iforce)
-{
-       unsigned char c[] = "CEOV";
-       int i;
-
-       init_waitqueue_head(&iforce->wait);
-       iforce->dev.ff_effects_max = 10;
-
-/*
- * Input device fields.
- */
-
-       iforce->dev.idbus = BUS_USB;
-       iforce->dev.private = iforce;
-       iforce->dev.name = iforce->name;
-       iforce->dev.open = iforce_open;
-       iforce->dev.close = iforce_close;
-       iforce->dev.event = iforce_input_event;
-       iforce->dev.upload_effect = iforce_upload_effect;
-       iforce->dev.erase_effect = iforce_erase_effect;
-
-/*
- * On-device memory allocation.
- */
-
-       iforce->device_memory.name = "I-Force device effect memory";
-       iforce->device_memory.start = 0;
-       iforce->device_memory.end = 200;
-       iforce->device_memory.flags = IORESOURCE_MEM;
-       iforce->device_memory.parent = NULL;
-       iforce->device_memory.child = NULL;
-       iforce->device_memory.sibling = NULL;
-
-/*
- * Wait until device ready - until it sends its first response.
- */
-
-       for (i = 0; i < 20; i++)
-               if (!get_id_packet(iforce, "O"))
-                       break;
-
-       if (i == 20) { /* 5 seconds */
-               printk(KERN_ERR "iforce.c: Timeout waiting for response from device.\n");
-               iforce_close(&iforce->dev);
-               return -1;
-       }
-
-/*
- * Get device info.
- */
-
-       if (!get_id_packet(iforce, "M"))
-               iforce->dev.idvendor = (iforce->edata[2] << 8) | iforce->edata[1];
-       if (!get_id_packet(iforce, "P"))
-               iforce->dev.idproduct = (iforce->edata[2] << 8) | iforce->edata[1];
-       if (!get_id_packet(iforce, "B"))
-               iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1];
-       if (!get_id_packet(iforce, "N"))
-               iforce->dev.ff_effects_max = iforce->edata[1];
-
-/*
- * Display additional info.
- */
-
-       for (i = 0; c[i]; i++)
-               if (!get_id_packet(iforce, c + i))
-                       dump_packet("info", iforce->ecmd, iforce->edata);
-
-/*
- * Disable spring, enable force feedback.
- * FIXME: We should use iforce_set_autocenter() et al here.
- */
-
-       send_packet(iforce, FF_CMD_AUTOCENTER, "\004\000");
-       send_packet(iforce, FF_CMD_ENABLE, "\004");
-
-/*
- * Find appropriate device entry
- */
-
-       for (i = 0; iforce_device[i].idvendor; i++)
-               if (iforce_device[i].idvendor == iforce->dev.idvendor &&
-                   iforce_device[i].idproduct == iforce->dev.idproduct)
-                       break;
-
-       iforce->type = iforce_device + i;
-
-       sprintf(iforce->name, iforce->type->name,
-               iforce->dev.idproduct, iforce->dev.idvendor);
-
-/*
- * Set input device bitfields and ranges.
- */
-
-       iforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF);
-
-       for (i = 0; iforce->type->btn[i] >= 0; i++) {
-               signed short t = iforce->type->btn[i];
-               set_bit(t, iforce->dev.keybit);
-               if (t != BTN_DEAD)
-                       set_bit(FF_BTN(t), iforce->dev.ffbit);
-       }
-
-       for (i = 0; iforce->type->abs[i] >= 0; i++) {
-
-               signed short t = iforce->type->abs[i];
-               set_bit(t, iforce->dev.absbit);
-
-               switch (t) {
-
-                       case ABS_X:
-                       case ABS_Y:
-                       case ABS_WHEEL:
-
-                               iforce->dev.absmax[t] =  1920;
-                               iforce->dev.absmin[t] = -1920;
-                               iforce->dev.absflat[t] = 128;
-                               iforce->dev.absfuzz[t] = 16;
-
-                               set_bit(FF_ABS(t), iforce->dev.ffbit);
-                               break;
-
-                       case ABS_THROTTLE:
-                       case ABS_GAS:
-                       case ABS_BRAKE:
-
-                               iforce->dev.absmax[t] = 255;
-                               iforce->dev.absmin[t] = 0;
-                               break;
-
-                       case ABS_HAT0X:
-                       case ABS_HAT0Y:
-                               iforce->dev.absmax[t] =  1;
-                               iforce->dev.absmin[t] = -1;
-                               break;
-               }
-       }
-
-       for (i = 0; iforce->type->ff[i] >= 0; i++)
-               set_bit(iforce->type->ff[i], iforce->dev.ffbit);
-
-/*
- * Register input device.
- */
-
-       input_register_device(&iforce->dev);
-
-       return 0;
-}
-
-#ifdef IFORCE_USB
-
-static void iforce_usb_irq(struct urb *urb)
-{
-       struct iforce *iforce = urb->context;
-       if (urb->status) return;
-       iforce_process_packet(iforce,
-               (iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1);
-}
-
-static void iforce_usb_out(struct urb *urb)
-{
-       struct iforce *iforce = urb->context;
-       if (urb->status) return;
-       if (waitqueue_active(&iforce->wait))
-               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;
-       if (waitqueue_active(&iforce->wait))
-               wake_up(&iforce->wait);
-}
-
-static void *iforce_usb_probe(struct usb_device *dev, unsigned int ifnum,
-                               const struct usb_device_id *id)
-{
-       struct usb_endpoint_descriptor *epirq, *epout;
-       struct iforce *iforce;
-
-       epirq = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
-       epout = dev->config[0].interface[ifnum].altsetting[0].endpoint + 1;
-
-       if (!(iforce = kmalloc(sizeof(struct iforce) + 32, GFP_KERNEL))) return NULL;
-       memset(iforce, 0, sizeof(struct iforce));
-
-       iforce->irq = usb_alloc_urb(0, GFP_KERNEL);
-       if (!iforce->irq) {
-               kfree(iforce);
-               return NULL;
-       }
-       iforce->out = usb_alloc_urb(0, GFP_KERNEL);
-       if (!iforce->out) {
-               usb_free_urb(iforce->irq);
-               kfree(iforce);
-               return NULL;
-       }
-       iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL);
-       if (!iforce->ctrl) {
-               usb_free_urb(iforce->out);
-               usb_free_urb(iforce->irq);
-               kfree(iforce);
-               return NULL;
-       }
-
-       iforce->bus = IFORCE_USB;
-       iforce->usbdev = dev;
-
-       iforce->dr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE;
-       iforce->dr.wIndex = 0;
-       iforce->dr.wLength = 16;
-
-       FILL_INT_URB(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress),
-                       iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval);
-
-       FILL_BULK_URB(iforce->out, dev, usb_sndbulkpipe(dev, epout->bEndpointAddress),
-                       iforce + 1, 32, iforce_usb_out, iforce);
-
-       FILL_CONTROL_URB(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0),
-                       (void*) &iforce->dr, iforce->edata, 16, iforce_usb_ctrl, iforce);
-
-       if (iforce_init_device(iforce)) {
-               usb_free_urb(iforce->ctrl);
-               usb_free_urb(iforce->out);
-               usb_free_urb(iforce->irq);
-               kfree(iforce);
-               return NULL;
-       }
-
-       printk(KERN_INFO "input%d: %s [%d effects, %ld bytes memory] on usb%d:%d.%d\n",
-               iforce->dev.number, iforce->dev.name, iforce->dev.ff_effects_max,
-               iforce->device_memory.end, dev->bus->busnum, dev->devnum, ifnum);
-
-       return iforce;
-}
-
-static void iforce_usb_disconnect(struct usb_device *dev, void *ptr)
-{
-       struct iforce *iforce = ptr;
-       usb_unlink_urb(iforce->irq);
-       input_unregister_device(&iforce->dev);
-       usb_free_urb(iforce->ctrl);
-       usb_free_urb(iforce->out);
-       usb_free_urb(iforce->irq);
-       kfree(iforce);
-}
-
-static struct usb_device_id iforce_usb_ids [] = {
-       { 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(0x06f8, 0x0001) },         /* Guillemot Race Leader Force Feedback */
-       { }                                     /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, iforce_usb_ids);
-
-static struct usb_driver iforce_usb_driver = {
-       name:           "iforce",
-       probe:          iforce_usb_probe,
-       disconnect:     iforce_usb_disconnect,
-       id_table:       iforce_usb_ids,
-};
-
-#endif
-
-#ifdef IFORCE_232
-
-static void iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags)
-{
-       struct iforce* iforce = serio->private;
-
-       if (!iforce->pkt) {
-               if (data != 0x2b) {
-                       return;
-               }
-               iforce->pkt = 1;
-               return;
-       }
-
-       if (!iforce->id) {
-               if (data > 3 && data != 0xff) {
-                       iforce->pkt = 0;
-                       return;
-               }
-               iforce->id = data;
-               return;
-       }
-
-       if (!iforce->len) {
-               if (data > IFORCE_MAX_LENGTH) {
-                       iforce->pkt = 0;
-                       iforce->id = 0;
-                       return;
-               }
-               iforce->len = data;
-               return;
-       }
-
-       if (iforce->idx < iforce->len) {
-               iforce->csum += iforce->data[iforce->idx++] = data;
-               return;
-       }
-
-       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;
-       }
-}
-
-static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev)
-{
-       struct iforce *iforce;
-       if (serio->type != (SERIO_RS232 | SERIO_IFORCE))
-               return;
-
-       if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return;
-       memset(iforce, 0, sizeof(struct iforce));
-
-       iforce->bus = IFORCE_232;
-       iforce->serio = serio;
-       serio->private = iforce;
-
-       if (serio_open(serio, dev)) {
-               kfree(iforce);
-               return;
-       }
-
-       if (iforce_init_device(iforce)) {
-               serio_close(serio);
-               kfree(iforce);
-               return;
-       }
-
-       printk(KERN_INFO "input%d: %s [%d effects, %ld bytes memory] on serio%d\n",
-               iforce->dev.number, iforce->dev.name, iforce->dev.ff_effects_max,
-               iforce->device_memory.end, serio->number);
-}
-
-static void iforce_serio_disconnect(struct serio *serio)
-{
-       struct iforce* iforce = serio->private;
-
-       input_unregister_device(&iforce->dev);
-       serio_close(serio);
-       kfree(iforce);
-}
-
-static struct serio_dev iforce_serio_dev = {
-       interrupt:      iforce_serio_irq,
-       connect:        iforce_serio_connect,
-       disconnect:     iforce_serio_disconnect,
-};
-
-#endif
-
-static int __init iforce_init(void)
-{
-#ifdef IFORCE_USB
-       usb_register(&iforce_usb_driver);
-#endif
-#ifdef IFORCE_232
-       serio_register_device(&iforce_serio_dev);
-#endif
-       return 0;
-}
-
-static void __exit iforce_exit(void)
-{
-#ifdef IFORCE_USB
-       usb_deregister(&iforce_usb_driver);
-#endif
-#ifdef IFORCE_232
-       serio_unregister_device(&iforce_serio_dev);
-#endif
-}
-
-module_init(iforce_init);
-module_exit(iforce_exit);
diff --git a/drivers/input/joystick/iforce/Makefile b/drivers/input/joystick/iforce/Makefile
new file mode 100644 (file)
index 0000000..9960f93
--- /dev/null
@@ -0,0 +1,41 @@
+#
+# Makefile for the I-Force driver
+#
+
+O_TARGET := iforce-drv.o
+
+# I-Force may need both USB and RS-232
+
+CONFIG_JOYSTICK_IFORCE := n
+
+ifeq ($(CONFIG_JOYSTICK_IFORCE_232),y)
+       ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),y)
+               CONFIG_JOYSTICK_IFORCE := y
+       endif
+endif
+
+ifeq ($(CONFIG_JOYSTICK_IFORCE_232),m)
+       CONFIG_JOYSTICK_IFORCE := m
+endif
+
+ifeq ($(CONFIG_JOYSTICK_IFORCE_USB),m)
+       CONFIG_JOYSTICK_IFORCE := m
+endif
+
+obj-$(CONFIG_JOYSTICK_IFORCE)  += iforce.o 
+
+include $(TOPDIR)/Rules.make
+
+IFORCEOBJS = iforce-ff.o iforce-main.o iforce-packets.o
+
+ifneq ($(CONFIG_JOYSTICK_IFORCE_232),)
+       IFORCEOBJS += iforce-serio.o 
+endif
+
+ifneq ($(CONFIG_JOYSTICK_IFORCE_USB),)
+       IFORCEOBJS += iforce-usb.o 
+endif
+
+iforce.o: $(IFORCEOBJS) 
+       $(LD) -i $(IFORCEOBJS) -o $@
+
diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c
new file mode 100644 (file)
index 0000000..267f92f
--- /dev/null
@@ -0,0 +1,543 @@
+/*
+ * $Id: iforce-ff.c,v 1.9 2002/02/02 19:28:35 jdeneux Exp $
+ *
+ *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include "iforce.h"
+
+/*
+ * Set the magnitude of a constant force effect
+ * Return error code
+ *
+ * Note: caller must ensure exclusive access to device
+ */
+
+static int make_magnitude_modifier(struct iforce* iforce,
+       struct resource* mod_chunk, int no_alloc, __s16 level)
+{
+       unsigned char data[3];
+
+       if (!no_alloc) {
+               down(&iforce->mem_mutex);
+               if (allocate_resource(&(iforce->device_memory), mod_chunk, 2,
+                       iforce->device_memory.start, iforce->device_memory.end, 2L,
+                       NULL, NULL)) {
+                       up(&iforce->mem_mutex);
+                       return -ENOMEM;
+               }
+               up(&iforce->mem_mutex);
+       }
+
+       data[0] = LO(mod_chunk->start);
+       data[1] = HI(mod_chunk->start);
+       data[2] = HIFIX80(level);
+
+       iforce_send_packet(iforce, FF_CMD_MAGNITUDE, data);
+
+       iforce_dump_packet("magnitude: ", FF_CMD_MAGNITUDE, data);
+       return 0;
+}
+
+/*
+ * Upload the component of an effect dealing with the period, phase and magnitude
+ */
+
+static int make_period_modifier(struct iforce* iforce,
+       struct resource* mod_chunk, int no_alloc,
+       __s16 magnitude, __s16 offset, u16 period, u16 phase)
+{
+       unsigned char data[7];
+
+       period = TIME_SCALE(period);
+
+       if (!no_alloc) {
+               down(&iforce->mem_mutex);
+               if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c,
+                       iforce->device_memory.start, iforce->device_memory.end, 2L,
+                       NULL, NULL)) {
+                       up(&iforce->mem_mutex);
+                       return -ENOMEM;
+               }
+               up(&iforce->mem_mutex);
+       }
+
+       data[0] = LO(mod_chunk->start);
+       data[1] = HI(mod_chunk->start);
+
+       data[2] = HIFIX80(magnitude);
+       data[3] = HIFIX80(offset);
+       data[4] = HI(phase);
+
+       data[5] = LO(period);
+       data[6] = HI(period);
+
+       iforce_send_packet(iforce, FF_CMD_PERIOD, data);
+
+       return 0;
+}
+
+/*
+ * Uploads the part of an effect setting the envelope of the force
+ */
+
+static int make_envelope_modifier(struct iforce* iforce,
+       struct resource* mod_chunk, int no_alloc,
+       u16 attack_duration, __s16 initial_level,
+       u16 fade_duration, __s16 final_level)
+{
+       unsigned char data[8];
+
+       attack_duration = TIME_SCALE(attack_duration);
+       fade_duration = TIME_SCALE(fade_duration);
+
+       if (!no_alloc) {
+               down(&iforce->mem_mutex);
+               if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e,
+                       iforce->device_memory.start, iforce->device_memory.end, 2L,
+                       NULL, NULL)) {
+                       up(&iforce->mem_mutex);
+                       return -ENOMEM;
+               }
+               up(&iforce->mem_mutex);
+       }
+
+       data[0] = LO(mod_chunk->start);
+       data[1] = HI(mod_chunk->start);
+
+       data[2] = LO(attack_duration);
+       data[3] = HI(attack_duration);
+       data[4] = HI(initial_level);
+
+       data[5] = LO(fade_duration);
+       data[6] = HI(fade_duration);
+       data[7] = HI(final_level);
+
+       iforce_send_packet(iforce, FF_CMD_ENVELOPE, data);
+
+       return 0;
+}
+
+/*
+ * Component of spring, friction, inertia... effects
+ */
+
+static int make_condition_modifier(struct iforce* iforce,
+       struct resource* mod_chunk, int no_alloc,
+       __u16 rsat, __u16 lsat, __s16 rk, __s16 lk, u16 db, __s16 center)
+{
+       unsigned char data[10];
+
+       if (!no_alloc) {
+               down(&iforce->mem_mutex);
+               if (allocate_resource(&(iforce->device_memory), mod_chunk, 8,
+                       iforce->device_memory.start, iforce->device_memory.end, 2L,
+                       NULL, NULL)) {
+                       up(&iforce->mem_mutex);
+                       return -ENOMEM;
+               }
+               up(&iforce->mem_mutex);
+       }
+
+       data[0] = LO(mod_chunk->start);
+       data[1] = HI(mod_chunk->start);
+
+       data[2] = (100*rk)>>15; /* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */
+       data[3] = (100*lk)>>15; /* This code is incorrect on cpus lacking arith shift */
+
+       center = (500*center)>>15;
+       data[4] = LO(center);
+       data[5] = HI(center);
+
+       db = (1000*db)>>16;
+       data[6] = LO(db);
+       data[7] = HI(db);
+
+       data[8] = (100*rsat)>>16;
+       data[9] = (100*lsat)>>16;
+
+       iforce_send_packet(iforce, FF_CMD_CONDITION, data);
+       iforce_dump_packet("condition", FF_CMD_CONDITION, data);
+
+       return 0;
+}
+
+static unsigned char find_button(struct iforce *iforce, signed short button)
+{
+       int i;
+       for (i = 1; iforce->type->btn[i] >= 0; i++)
+               if (iforce->type->btn[i] == button)
+                       return i + 1;
+       return 0;
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send an condition 
+ * parameter packet
+ */
+static int need_condition_modifier(struct iforce* iforce, struct ff_effect* new)
+{
+       int id = new->id;
+       struct ff_effect* old = &iforce->core_effects[id].effect;
+       int ret=0;
+       int i;
+
+       if (new->type != FF_SPRING && new->type != FF_FRICTION) {
+               printk(KERN_WARNING "iforce.c: bad effect type in need_condition_modifier\n");
+               return FALSE;
+       }
+
+       for(i=0; i<2; i++) {
+               ret |= old->u.condition[i].right_saturation != new->u.condition[i].right_saturation
+                       || old->u.condition[i].left_saturation != new->u.condition[i].left_saturation
+                       || old->u.condition[i].right_coeff != new->u.condition[i].right_coeff
+                       || old->u.condition[i].left_coeff != new->u.condition[i].left_coeff
+                       || old->u.condition[i].deadband != new->u.condition[i].deadband
+                       || old->u.condition[i].center != new->u.condition[i].center;
+       }
+       return ret;
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send a magnitude
+ * parameter packet
+ */
+static int need_magnitude_modifier(struct iforce* iforce, struct ff_effect* effect)
+{
+       int id = effect->id;
+       struct ff_effect* old = &iforce->core_effects[id].effect;
+
+       if (effect->type != FF_CONSTANT) {
+               printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n");
+               return FALSE;
+       }
+
+       return (old->u.constant.level != effect->u.constant.level);
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send an envelope
+ * parameter packet
+ */
+static int need_envelope_modifier(struct iforce* iforce, struct ff_effect* effect)
+{
+       int id = effect->id;
+       struct ff_effect* old = &iforce->core_effects[id].effect;
+
+       switch (effect->type) {
+       case FF_CONSTANT:
+               if (old->u.constant.envelope.attack_length != effect->u.constant.envelope.attack_length
+               || old->u.constant.envelope.attack_level != effect->u.constant.envelope.attack_level
+               || old->u.constant.envelope.fade_length != effect->u.constant.envelope.fade_length
+               || old->u.constant.envelope.fade_level != effect->u.constant.envelope.fade_level)
+                       return TRUE;
+               break;
+
+       case FF_PERIODIC:
+               if (old->u.periodic.envelope.attack_length != effect->u.periodic.envelope.attack_length
+               || old->u.periodic.envelope.attack_level != effect->u.periodic.envelope.attack_level
+               || old->u.periodic.envelope.fade_length != effect->u.periodic.envelope.fade_length
+               || old->u.periodic.envelope.fade_level != effect->u.periodic.envelope.fade_level)
+                       return TRUE;
+               break;
+
+       default:
+               printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n");
+       }
+
+       return FALSE;
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send a periodic
+ * parameter effect
+ */
+static int need_period_modifier(struct iforce* iforce, struct ff_effect* new)
+{
+       int id = new->id;
+       struct ff_effect* old = &iforce->core_effects[id].effect;
+
+       if (new->type != FF_PERIODIC) {
+               printk(KERN_WARNING "iforce.c: bad effect type in need_periodic_modifier\n");
+               return FALSE;
+       }
+
+       return (old->u.periodic.period != new->u.periodic.period
+               || old->u.periodic.magnitude != new->u.periodic.magnitude
+               || old->u.periodic.offset != new->u.periodic.offset
+               || old->u.periodic.phase != new->u.periodic.phase);
+}
+
+/*
+ * Analyse the changes in an effect, and tell if we need to send an effect
+ * packet
+ */
+static int need_core(struct iforce* iforce, struct ff_effect* new)
+{
+       int id = new->id;
+       struct ff_effect* old = &iforce->core_effects[id].effect;
+
+       if (old->direction != new->direction
+               || old->trigger.button != new->trigger.button
+               || old->trigger.interval != new->trigger.interval
+               || old->replay.length != new->replay.length
+               || old->replay.delay != new->replay.delay)
+               return TRUE;
+
+       return FALSE;
+}
+/*
+ * Send the part common to all effects to the device
+ */
+static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2,
+       u8 effect_type, u8 axes, u16 duration, u16 delay, u16 button,
+       u16 interval, u16 direction)
+{
+       unsigned char data[14];
+
+       duration = TIME_SCALE(duration);
+       delay    = TIME_SCALE(delay);
+       interval = TIME_SCALE(interval);
+
+       data[0]  = LO(id);
+       data[1]  = effect_type;
+       data[2]  = LO(axes) | find_button(iforce, button);
+
+       data[3]  = LO(duration);
+       data[4]  = HI(duration);
+
+       data[5]  = HI(direction);
+
+       data[6]  = LO(interval);
+       data[7]  = HI(interval);
+
+       data[8]  = LO(mod_id1);
+       data[9]  = HI(mod_id1);
+       data[10] = LO(mod_id2);
+       data[11] = HI(mod_id2);
+
+       data[12] = LO(delay);
+       data[13] = HI(delay);
+
+       /* Stop effect */
+/*     iforce_control_playback(iforce, id, 0);*/
+
+       iforce_send_packet(iforce, FF_CMD_EFFECT, data);
+
+       /* If needed, restart effect */
+       if (test_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[id].flags)) {
+               /* BUG: perhaps we should replay n times, instead of 1. But we do not know n */
+               iforce_control_playback(iforce, id, 1);
+       }
+
+       return 0;
+}
+
+/*
+ * Upload a periodic effect to the device
+ * See also iforce_upload_constant.
+ */
+int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int is_update)
+{
+       u8 wave_code;
+       int core_id = effect->id;
+       struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
+       struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
+       struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
+       int param1_err = 1;
+       int param2_err = 1;
+       int core_err = 0;
+
+       if (!is_update || need_period_modifier(iforce, effect)) {
+               param1_err = make_period_modifier(iforce, mod1_chunk, 
+                       is_update,
+                       effect->u.periodic.magnitude, effect->u.periodic.offset,
+                       effect->u.periodic.period, effect->u.periodic.phase);
+               if (param1_err) return param1_err;
+               set_bit(FF_MOD1_IS_USED, core_effect->flags);
+       }
+
+       if (!is_update || need_envelope_modifier(iforce, effect)) {
+               param2_err = make_envelope_modifier(iforce, mod2_chunk,
+                       is_update,
+                       effect->u.periodic.envelope.attack_length,
+                       effect->u.periodic.envelope.attack_level,
+                       effect->u.periodic.envelope.fade_length,
+                       effect->u.periodic.envelope.fade_level);
+               if (param2_err) return param2_err;
+               set_bit(FF_MOD2_IS_USED, core_effect->flags);
+       }
+
+       switch (effect->u.periodic.waveform) {
+               case FF_SQUARE:         wave_code = 0x20; break;
+               case FF_TRIANGLE:       wave_code = 0x21; break;
+               case FF_SINE:           wave_code = 0x22; break;
+               case FF_SAW_UP:         wave_code = 0x23; break;
+               case FF_SAW_DOWN:       wave_code = 0x24; break;
+               default:                wave_code = 0x20; break;
+       }
+
+       if (!is_update || need_core(iforce, effect)) {
+               core_err = make_core(iforce, effect->id,
+                       mod1_chunk->start,
+                       mod2_chunk->start,
+                       wave_code,
+                       0x20,
+                       effect->replay.length,
+                       effect->replay.delay,
+                       effect->trigger.button,
+                       effect->trigger.interval,
+                       effect->direction);
+       }
+
+       /* If one of the parameter creation failed, we already returned an
+        * error code.
+        * If the core creation failed, we return its error code.
+        * Else: if one parameter at least was created, we return 0
+        *       else we return 1;
+        */
+       return core_err < 0 ? core_err : (param1_err && param2_err);
+}
+
+/*
+ * Upload a constant force effect
+ * Return value:
+ *  <0 Error code
+ *  0 Ok, effect created or updated
+ *  1 effect did not change since last upload, and no packet was therefore sent
+ */
+int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect, int is_update)
+{
+       int core_id = effect->id;
+       struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
+       struct resource* mod1_chunk = &(iforce->core_effects[core_id].mod1_chunk);
+       struct resource* mod2_chunk = &(iforce->core_effects[core_id].mod2_chunk);
+       int param1_err = 1;
+       int param2_err = 1;
+       int core_err = 0;
+
+       if (!is_update || need_magnitude_modifier(iforce, effect)) {
+               param1_err = make_magnitude_modifier(iforce, mod1_chunk,
+                       is_update,
+                       effect->u.constant.level);
+               if (param1_err) return param1_err;
+               set_bit(FF_MOD1_IS_USED, core_effect->flags);
+       }
+
+       if (!is_update || need_envelope_modifier(iforce, effect)) {
+               param2_err = make_envelope_modifier(iforce, mod2_chunk,
+                       is_update,
+                       effect->u.constant.envelope.attack_length,
+                       effect->u.constant.envelope.attack_level,
+                       effect->u.constant.envelope.fade_length,
+                       effect->u.constant.envelope.fade_level);
+               if (param2_err) return param2_err;
+               set_bit(FF_MOD2_IS_USED, core_effect->flags);
+       }
+
+       if (!is_update || need_core(iforce, effect)) {
+               core_err = make_core(iforce, effect->id,
+                       mod1_chunk->start,
+                       mod2_chunk->start,
+                       0x00,
+                       0x20,
+                       effect->replay.length,
+                       effect->replay.delay,
+                       effect->trigger.button,
+                       effect->trigger.interval,
+                       effect->direction);
+       }
+
+       /* If one of the parameter creation failed, we already returned an
+        * error code.
+        * If the core creation failed, we return its error code.
+        * Else: if one parameter at least was created, we return 0
+        *       else we return 1;
+        */
+       return core_err < 0 ? core_err : (param1_err && param2_err);
+}
+
+/*
+ * Upload an condition effect. Those are for example friction, inertia, springs...
+ */
+int iforce_upload_condition(struct iforce* iforce, struct ff_effect* effect, int is_update)
+{
+       int core_id = effect->id;
+       struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
+       struct resource* mod1_chunk = &(core_effect->mod1_chunk);
+       struct resource* mod2_chunk = &(core_effect->mod2_chunk);
+       u8 type;
+       int param_err = 1;
+       int core_err = 0;
+
+       switch (effect->type) {
+               case FF_SPRING:         type = 0x40; break;
+               case FF_DAMPER:         type = 0x41; break;
+               default: return -1;
+       }
+
+       if (!is_update || need_condition_modifier(iforce, effect)) {
+               param_err = make_condition_modifier(iforce, mod1_chunk,
+                       is_update,
+                       effect->u.condition[0].right_saturation,
+                       effect->u.condition[0].left_saturation,
+                       effect->u.condition[0].right_coeff,
+                       effect->u.condition[0].left_coeff,
+                       effect->u.condition[0].deadband,
+                       effect->u.condition[0].center);
+               if (param_err) return param_err;
+               set_bit(FF_MOD1_IS_USED, core_effect->flags);
+
+               param_err = make_condition_modifier(iforce, mod2_chunk,
+                       is_update,
+                       effect->u.condition[1].right_saturation,
+                       effect->u.condition[1].left_saturation,
+                       effect->u.condition[1].right_coeff,
+                       effect->u.condition[1].left_coeff,
+                       effect->u.condition[1].deadband,
+                       effect->u.condition[1].center);
+               if (param_err) return param_err;
+               set_bit(FF_MOD2_IS_USED, core_effect->flags);
+
+       }
+
+       if (!is_update || need_core(iforce, effect)) {
+               core_err = make_core(iforce, effect->id,
+                       mod1_chunk->start, mod2_chunk->start,
+                       type, 0xc0,
+                       effect->replay.length, effect->replay.delay,
+                       effect->trigger.button, effect->trigger.interval,
+                       effect->direction);
+       }
+
+       /* If the parameter creation failed, we already returned an
+        * error code.
+        * If the core creation failed, we return its error code.
+        * Else: if a parameter  was created, we return 0
+        *       else we return 1;
+        */
+       return core_err < 0 ? core_err : param_err;
+}
diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c
new file mode 100644 (file)
index 0000000..45b7c00
--- /dev/null
@@ -0,0 +1,543 @@
+/*
+ * $Id: iforce-main.c,v 1.18 2002/06/09 11:03:03 jdeneux Exp $
+ *
+ *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include "iforce.h"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <deneux@ifrance.com>");
+MODULE_DESCRIPTION("USB/RS232 I-Force joysticks and wheels driver");
+MODULE_LICENSE("GPL");
+
+static signed short btn_joystick[] =
+{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
+  BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 };
+
+static signed short btn_avb_pegasus[] =
+{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
+  BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 };
+
+static signed short btn_wheel[] =
+{ BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE,
+  BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_A, BTN_B, BTN_C, -1 };
+
+static signed short btn_avb_tw[] =
+{ BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE,
+  BTN_BASE2, BTN_BASE3, BTN_BASE4, -1 };
+
+static signed short btn_avb_wheel[] =
+{ BTN_GEAR_DOWN, BTN_GEAR_UP, BTN_BASE, BTN_BASE2, BTN_BASE3,
+  BTN_BASE4, BTN_BASE5, BTN_BASE6, -1 };
+
+static signed short abs_joystick[] =
+{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 };
+
+static signed short abs_avb_pegasus[] =
+{ ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_HAT0X, ABS_HAT0Y,
+  ABS_HAT1X, ABS_HAT1Y, -1 };
+
+static signed short abs_wheel[] =
+{ ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, -1 };
+
+static signed short ff_iforce[] =
+{ FF_PERIODIC, FF_CONSTANT, FF_SPRING, FF_DAMPER,
+  FF_SQUARE, FF_TRIANGLE, FF_SINE, FF_SAW_UP, FF_SAW_DOWN, FF_GAIN,
+  FF_AUTOCENTER, -1 };
+
+static struct iforce_device iforce_device[] = {
+       { 0x044f, 0xa01c, "Thrustmaster Motor Sport GT",                btn_wheel, abs_wheel, ff_iforce },
+       { 0x046d, 0xc281, "Logitech WingMan Force",                     btn_joystick, abs_joystick, ff_iforce },
+       { 0x046d, 0xc291, "Logitech WingMan Formula Force",             btn_wheel, abs_wheel, ff_iforce },
+       { 0x05ef, 0x020a, "AVB Top Shot Pegasus",                       btn_avb_pegasus, abs_avb_pegasus, ff_iforce },
+       { 0x05ef, 0x8884, "AVB Mag Turbo Force",                        btn_avb_wheel, abs_wheel, ff_iforce },
+       { 0x05ef, 0x8888, "AVB Top Shot Force Feedback Racing Wheel",   btn_avb_tw, abs_wheel, ff_iforce }, //?
+       { 0x061c, 0xc0a4, "ACT LABS Force RS",                          btn_wheel, abs_wheel, ff_iforce }, //?
+       { 0x06f8, 0x0001, "Guillemot Race Leader Force Feedback",       btn_wheel, abs_wheel, ff_iforce }, //?
+       { 0x06f8, 0x0004, "Guillemot Force Feedback Racing Wheel",      btn_wheel, abs_wheel, ff_iforce }, //?
+       { 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]",         btn_joystick, abs_joystick, ff_iforce }
+};
+
+
+
+static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+       struct iforce* iforce = (struct iforce*)(dev->private);
+       unsigned char data[3];
+
+       if (type != EV_FF)
+               return -1;
+
+       switch (code) {
+
+               case FF_GAIN:
+
+                       data[0] = value >> 9;
+                       iforce_send_packet(iforce, FF_CMD_GAIN, data);
+
+                       return 0;
+
+               case FF_AUTOCENTER:
+
+                       data[0] = 0x03;
+                       data[1] = value >> 9;
+                       iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
+
+                       data[0] = 0x04;
+                       data[1] = 0x01;
+                       iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
+
+                       return 0;
+
+               default: /* Play or stop an effect */
+
+                       if (!CHECK_OWNERSHIP(code, iforce)) {
+                               return -1;
+                       }
+                       if (value > 0) {
+                               set_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags);
+                       }
+                       else {
+                               clear_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags);
+                       }
+
+                       iforce_control_playback(iforce, code, value);
+                       return 0;
+       }
+
+       return -1;
+}
+
+/*
+ * Function called when an ioctl is performed on the event dev entry.
+ * It uploads an effect to the device
+ */
+static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
+{
+       struct iforce* iforce = (struct iforce*)(dev->private);
+       int id;
+       int ret;
+       int is_update;
+
+/* Check this effect type is supported by this device */
+       if (!test_bit(effect->type, iforce->dev.ffbit))
+               return -EINVAL;
+
+/*
+ * If we want to create a new effect, get a free id
+ */
+       if (effect->id == -1) {
+
+               for (id=0; id < FF_EFFECTS_MAX; ++id)
+                       if (!test_and_set_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags)) break;
+
+               if ( id == FF_EFFECTS_MAX || id >= iforce->dev.ff_effects_max)
+                       return -ENOMEM;
+
+               effect->id = id;
+               iforce->core_effects[id].owner = current->pid;
+               iforce->core_effects[id].flags[0] = (1<<FF_CORE_IS_USED);       /* Only IS_USED bit must be set */
+
+               is_update = FALSE;
+       }
+       else {
+               /* We want to update an effect */
+               if (!CHECK_OWNERSHIP(effect->id, iforce)) return -EACCES;
+               
+               /* Parameter type cannot be updated */
+               if (effect->type != iforce->core_effects[effect->id].effect.type)
+                       return -EINVAL;
+
+               /* Check the effect is not already being updated */
+               if (test_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags)) {
+                       return -EAGAIN;
+               }
+
+               is_update = TRUE;
+       }
+
+/*
+ * Upload the effect
+ */
+       switch (effect->type) {
+
+               case FF_PERIODIC:
+                       ret = iforce_upload_periodic(iforce, effect, is_update);
+                       break;
+
+               case FF_CONSTANT:
+                       ret = iforce_upload_constant(iforce, effect, is_update);
+                       break;
+
+               case FF_SPRING:
+               case FF_DAMPER:
+                       ret = iforce_upload_condition(iforce, effect, is_update);
+                       break;
+
+               default:
+                       return -EINVAL;
+       }
+       if (ret == 0) {
+               /* A packet was sent, forbid new updates until we are notified
+                * that the packet was updated
+                */
+               set_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags);
+       }
+       iforce->core_effects[effect->id].effect = *effect;
+       return ret;
+}
+
+/*
+ * Erases an effect: it frees the effect id and mark as unused the memory
+ * allocated for the parameters
+ */
+static int iforce_erase_effect(struct input_dev *dev, int effect_id)
+{
+       struct iforce* iforce = (struct iforce*)(dev->private);
+       int err = 0;
+       struct iforce_core_effect* core_effect;
+
+       /* Check who is trying to erase this effect */
+       if (iforce->core_effects[effect_id].owner != current->pid) {
+               printk(KERN_WARNING "iforce-main.c: %d tried to erase an effect belonging to %d\n", current->pid, iforce->core_effects[effect_id].owner);
+               return -EACCES;
+       }
+
+       if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX)
+               return -EINVAL;
+
+       core_effect = iforce->core_effects + effect_id;
+
+       if (test_bit(FF_MOD1_IS_USED, core_effect->flags))
+               err = release_resource(&(iforce->core_effects[effect_id].mod1_chunk));
+
+       if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags))
+               err = release_resource(&(iforce->core_effects[effect_id].mod2_chunk));
+
+       /*TODO: remember to change that if more FF_MOD* bits are added */
+       core_effect->flags[0] = 0;
+
+       return err;
+}
+
+static int iforce_open(struct input_dev *dev)
+{
+       struct iforce *iforce = dev->private;
+
+       switch (iforce->bus) {
+#ifdef IFORCE_USB
+               case IFORCE_USB:
+                       iforce->irq->dev = iforce->usbdev;
+                       if (usb_submit_urb(iforce->irq, GFP_KERNEL))
+                               return -EIO;
+                       break;
+#endif
+       }
+
+       /* Enable force feedback */
+       iforce_send_packet(iforce, FF_CMD_ENABLE, "\004");
+
+       return 0;
+}
+
+static int iforce_flush(struct input_dev *dev, struct file *file)
+{
+       struct iforce *iforce = dev->private;
+       int i;
+
+       /* Erase all effects this process owns */
+       for (i=0; i<dev->ff_effects_max; ++i) {
+
+               if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) &&
+                       current->pid == iforce->core_effects[i].owner) {
+                       
+                       /* Stop effect */
+                       input_report_ff(dev, i, 0);
+
+                       /* Free ressources assigned to effect */
+                       if (iforce_erase_effect(dev, i)) {
+                               printk(KERN_WARNING "iforce_flush: erase effect %d failed\n", i);
+                       }
+               }
+
+       }
+       return 0;
+}
+
+static void iforce_release(struct input_dev *dev)
+{
+       struct iforce *iforce = dev->private;
+       int i;
+
+       /* Check: no effect should be present in memory */
+       for (i=0; i<dev->ff_effects_max; ++i) {
+               if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags))
+                       break;
+       }
+       if (i<dev->ff_effects_max) {
+               printk(KERN_WARNING "iforce_release: Device still owns effects\n");
+       }
+
+       /* Disable force feedback playback */
+       iforce_send_packet(iforce, FF_CMD_ENABLE, "\001");
+
+       switch (iforce->bus) {
+#ifdef IFORCE_USB
+               case IFORCE_USB:
+                       usb_unlink_urb(iforce->irq);
+
+                       /* The device was unplugged before the file
+                        * was released */
+                       if (iforce->usbdev == NULL) {
+                               iforce_delete_device(iforce);
+                               kfree(iforce);
+                       }
+               break;
+#endif
+       }
+}
+
+void iforce_delete_device(struct iforce *iforce)
+{
+       switch (iforce->bus) {
+#ifdef IFORCE_USB
+       case IFORCE_USB:
+               iforce_usb_delete(iforce);
+               break;
+#endif
+#ifdef IFORCE_232
+       case IFORCE_232:
+               //TODO: Wait for the last packets to be sent
+               break;
+#endif
+       }
+}
+
+int iforce_init_device(struct iforce *iforce)
+{
+       unsigned char c[] = "CEOV";
+       int i;
+
+       init_waitqueue_head(&iforce->wait);
+       spin_lock_init(&iforce->xmit_lock);
+       init_MUTEX(&iforce->mem_mutex);
+       iforce->xmit.buf = iforce->xmit_data;
+
+       iforce->dev.ff_effects_max = 10;
+
+/*
+ * Input device fields.
+ */
+
+       iforce->dev.idbus = BUS_USB;
+       iforce->dev.private = iforce;
+       iforce->dev.name = "Unknown I-Force device";
+       iforce->dev.open = iforce_open;
+       iforce->dev.close = iforce_release;
+       iforce->dev.flush = iforce_flush;
+       iforce->dev.event = iforce_input_event;
+       iforce->dev.upload_effect = iforce_upload_effect;
+       iforce->dev.erase_effect = iforce_erase_effect;
+
+/*
+ * On-device memory allocation.
+ */
+
+       iforce->device_memory.name = "I-Force device effect memory";
+       iforce->device_memory.start = 0;
+       iforce->device_memory.end = 200;
+       iforce->device_memory.flags = IORESOURCE_MEM;
+       iforce->device_memory.parent = NULL;
+       iforce->device_memory.child = NULL;
+       iforce->device_memory.sibling = NULL;
+
+/*
+ * Wait until device ready - until it sends its first response.
+ */
+
+       for (i = 0; i < 20; i++)
+               if (!iforce_get_id_packet(iforce, "O"))
+                       break;
+
+       if (i == 20) { /* 5 seconds */
+               printk(KERN_ERR "iforce-main.c: Timeout waiting for response from device.\n");
+               return -1;
+       }
+
+/*
+ * Get device info.
+ */
+
+       if (!iforce_get_id_packet(iforce, "M"))
+               iforce->dev.idvendor = (iforce->edata[2] << 8) | iforce->edata[1];
+       else
+               printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet M\n");
+
+       if (!iforce_get_id_packet(iforce, "P"))
+               iforce->dev.idproduct = (iforce->edata[2] << 8) | iforce->edata[1];
+       else
+               printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet P\n");
+
+       if (!iforce_get_id_packet(iforce, "B"))
+               iforce->device_memory.end = (iforce->edata[2] << 8) | iforce->edata[1];
+       else
+               printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet B\n");
+
+       if (!iforce_get_id_packet(iforce, "N"))
+               iforce->dev.ff_effects_max = iforce->edata[1];
+       else
+               printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet N\n");
+
+       /* Check if the device can store more effects than the driver can really handle */
+       if (iforce->dev.ff_effects_max > FF_EFFECTS_MAX) {
+               printk(KERN_WARNING "input??: Device can handle %d effects, but N_EFFECTS_MAX is set to %d in iforce.h\n",
+                       iforce->dev.ff_effects_max, FF_EFFECTS_MAX);
+               iforce->dev.ff_effects_max = FF_EFFECTS_MAX;
+       }
+
+/*
+ * Display additional info.
+ */
+
+       for (i = 0; c[i]; i++)
+               if (!iforce_get_id_packet(iforce, c + i))
+                       iforce_dump_packet("info", iforce->ecmd, iforce->edata);
+
+/*
+ * Disable spring, enable force feedback.
+ * FIXME: We should use iforce_set_autocenter() et al here.
+ */
+
+       iforce_send_packet(iforce, FF_CMD_AUTOCENTER, "\004\000");
+
+/*
+ * Find appropriate device entry
+ */
+
+       for (i = 0; iforce_device[i].idvendor; i++)
+               if (iforce_device[i].idvendor == iforce->dev.idvendor &&
+                   iforce_device[i].idproduct == iforce->dev.idproduct)
+                       break;
+
+       iforce->type = iforce_device + i;
+       iforce->dev.name = iforce->type->name;
+
+/*
+ * Set input device bitfields and ranges.
+ */
+
+       iforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF) | BIT(EV_FF_STATUS);
+
+       for (i = 0; iforce->type->btn[i] >= 0; i++) {
+               signed short t = iforce->type->btn[i];
+               set_bit(t, iforce->dev.keybit);
+       }
+       set_bit(BTN_DEAD, iforce->dev.keybit);
+
+       for (i = 0; iforce->type->abs[i] >= 0; i++) {
+
+               signed short t = iforce->type->abs[i];
+               set_bit(t, iforce->dev.absbit);
+
+               switch (t) {
+
+                       case ABS_X:
+                       case ABS_Y:
+                       case ABS_WHEEL:
+
+                               iforce->dev.absmax[t] =  1920;
+                               iforce->dev.absmin[t] = -1920;
+                               iforce->dev.absflat[t] = 128;
+                               iforce->dev.absfuzz[t] = 16;
+
+                               set_bit(t, iforce->dev.ffbit);
+                               break;
+
+                       case ABS_THROTTLE:
+                       case ABS_GAS:
+                       case ABS_BRAKE:
+
+                               iforce->dev.absmax[t] = 255;
+                               iforce->dev.absmin[t] = 0;
+                               break;
+
+                       case ABS_RUDDER:
+
+                               iforce->dev.absmax[t] = 127;
+                               iforce->dev.absmin[t] = -128;
+                               break;
+
+                       case ABS_HAT0X:
+                       case ABS_HAT0Y:
+                       case ABS_HAT1X:
+                       case ABS_HAT1Y:
+                               iforce->dev.absmax[t] =  1;
+                               iforce->dev.absmin[t] = -1;
+                               break;
+               }
+       }
+
+       for (i = 0; iforce->type->ff[i] >= 0; i++)
+               set_bit(iforce->type->ff[i], iforce->dev.ffbit);
+
+/*
+ * Register input device.
+ */
+
+       input_register_device(&iforce->dev);
+
+       printk(KERN_DEBUG "iforce->dev.open = %p\n", iforce->dev.open);
+
+       printk(KERN_INFO "input: %s [%d effects, %ld bytes memory]\n",
+               iforce->dev.name, iforce->dev.ff_effects_max,
+               iforce->device_memory.end);
+
+       return 0;
+}
+
+static int __init iforce_init(void)
+{
+#ifdef IFORCE_USB
+       usb_register(&iforce_usb_driver);
+#endif
+#ifdef IFORCE_232
+       serio_register_device(&iforce_serio_dev);
+#endif
+       return 0;
+}
+
+static void __exit iforce_exit(void)
+{
+#ifdef IFORCE_USB
+       usb_deregister(&iforce_usb_driver);
+#endif
+#ifdef IFORCE_232
+       serio_unregister_device(&iforce_serio_dev);
+#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 (file)
index 0000000..922894b
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * $Id: iforce-packets.c,v 1.15 2002/06/09 11:08:04 jdeneux Exp $
+ *
+ *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include "iforce.h"
+
+static struct {
+       __s32 x;
+       __s32 y;
+} iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+
+void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data)
+{
+       int i;
+
+       printk(KERN_DEBUG "iforce.c: %s ( cmd = %04x, data = ", msg, cmd);
+       for (i = 0; i < LO(cmd); i++)
+               printk("%02x ", data[i]);
+       printk(")\n");
+}
+
+/*
+ * Send a packet of bytes to the device
+ */
+int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data)
+{
+       /* Copy data to buffer */
+       int n = LO(cmd);
+       int c;
+       int empty;
+       int head, tail;
+       unsigned long flags;
+                       
+/*
+ * Update head and tail of xmit buffer
+ */
+       spin_lock_irqsave(&iforce->xmit_lock, flags);
+
+       head = iforce->xmit.head;
+       tail = iforce->xmit.tail;
+
+       if (CIRC_SPACE(head, tail, XMIT_SIZE) < n+2) {
+               printk(KERN_WARNING "iforce.c: not enough space in xmit buffer to send new packet\n");
+               spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+               return -1;
+       }
+
+       empty = head == tail;
+       XMIT_INC(iforce->xmit.head, n+2);
+
+/*
+ * Store packet in xmit buffer
+ */
+       iforce->xmit.buf[head] = HI(cmd);
+       XMIT_INC(head, 1);
+       iforce->xmit.buf[head] = LO(cmd);
+       XMIT_INC(head, 1);
+
+       c = CIRC_SPACE_TO_END(head, tail, XMIT_SIZE);
+       if (n < c) c=n;
+
+       memcpy(&iforce->xmit.buf[head],
+              data,
+              c);
+       if (n != c) {
+               memcpy(&iforce->xmit.buf[0],
+                      data + c,
+                      n - c);
+       }
+       XMIT_INC(head, n);
+
+       spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+/*
+ * If necessary, start the transmission
+ */
+       switch (iforce->bus) {
+
+#ifdef IFORCE_232
+               case IFORCE_232:
+               if (empty)
+                       iforce_serial_xmit(iforce);
+               break;
+#endif
+#ifdef IFORCE_USB
+               case IFORCE_USB: 
+
+               if (iforce->usbdev && empty &&
+                       !test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) {
+
+                       iforce_usb_xmit(iforce);
+               }
+               break;
+#endif
+       }
+       return 0;
+}
+
+/* Start or stop an effect */
+int iforce_control_playback(struct iforce* iforce, u16 id, unsigned int value)
+{
+       unsigned char data[3];
+
+printk(KERN_DEBUG "iforce-packets.c: control_playback %d %d\n", id, value);
+
+       data[0] = LO(id);
+       data[1] = (value > 0) ? ((value > 1) ? 0x41 : 0x01) : 0;
+       data[2] = LO(value);
+       return iforce_send_packet(iforce, FF_CMD_PLAY, data);
+}
+
+/* Mark an effect that was being updated as ready. That means it can be updated
+ * again */
+static int mark_core_as_ready(struct iforce *iforce, unsigned short addr)
+{
+       int i;
+       for (i=0; i<iforce->dev.ff_effects_max; ++i) {
+               if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) &&
+                   (iforce->core_effects[i].mod1_chunk.start == addr ||
+                    iforce->core_effects[i].mod2_chunk.start == addr)) {
+                       clear_bit(FF_CORE_UPDATE, iforce->core_effects[i].flags);
+                       return 0;
+               }
+       }
+       printk(KERN_WARNING "iforce-packets.c: unused effect %04x updated !!!\n", addr);
+       return -1;
+}
+
+void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data)
+{
+       struct input_dev *dev = &iforce->dev;
+       int i;
+       static int being_used = 0;
+
+       if (being_used)
+               printk(KERN_WARNING "iforce-packets.c: re-entrant call to iforce_process %d\n", being_used);
+       being_used++;
+
+#ifdef IFORCE_232
+       if (HI(iforce->expect_packet) == HI(cmd)) {
+               iforce->expect_packet = 0;
+               iforce->ecmd = cmd;
+               memcpy(iforce->edata, data, IFORCE_MAX_LENGTH);
+               if (waitqueue_active(&iforce->wait))
+                       wake_up(&iforce->wait);
+       }
+#endif
+
+       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);
+                               }
+                       }
+
+                       break;
+
+               case 0x02:      /* status report */
+                       input_report_key(dev, BTN_DEAD, data[0] & 0x02);
+
+                       /* Check if an effect was just started or stopped */
+                       i = data[1] & 0x7f;
+                       if (data[1] & 0x80) {
+                               if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
+                               /* Report play event */
+                               input_report_ff_status(dev, i, FF_STATUS_PLAYING);
+                               }
+                       }
+                       else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
+                               /* Report stop event */
+                               input_report_ff_status(dev, i, FF_STATUS_STOPPED);
+                       }
+                       if (LO(cmd) > 3) {
+                               int j;
+                               for (j=3; j<LO(cmd); j+=2) {
+                                       mark_core_as_ready(iforce, data[j] | (data[j+1]<<8));
+                               }
+                       }
+                       break;
+       }
+       being_used--;
+}
+
+int iforce_get_id_packet(struct iforce *iforce, char *packet)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       int timeout = HZ; /* 1 second */
+
+       switch (iforce->bus) {
+
+       case IFORCE_USB:
+
+#ifdef IFORCE_USB
+               iforce->cr.bRequest = packet[0];
+               iforce->ctrl->dev = iforce->usbdev;
+
+               set_current_state(TASK_INTERRUPTIBLE);
+               add_wait_queue(&iforce->wait, &wait);
+
+               if (usb_submit_urb(iforce->ctrl, GFP_KERNEL)) {
+                       set_current_state(TASK_RUNNING);
+                       remove_wait_queue(&iforce->wait, &wait);
+                       return -1;
+               }
+
+               while (timeout && iforce->ctrl->status == -EINPROGRESS)
+                       timeout = schedule_timeout(timeout);
+
+               set_current_state(TASK_RUNNING);
+               remove_wait_queue(&iforce->wait, &wait);
+
+               if (!timeout) {
+                       usb_unlink_urb(iforce->ctrl);
+                       return -1;
+               }
+#else
+               printk(KERN_ERR "iforce_get_id_packet: iforce->bus = USB!\n");
+#endif
+               break;
+
+       case IFORCE_232:
+
+#ifdef IFORCE_232
+               iforce->expect_packet = FF_CMD_QUERY;
+               iforce_send_packet(iforce, FF_CMD_QUERY, packet);
+
+               set_current_state(TASK_INTERRUPTIBLE);
+               add_wait_queue(&iforce->wait, &wait);
+
+               while (timeout && iforce->expect_packet)
+                       timeout = schedule_timeout(timeout);
+
+               set_current_state(TASK_RUNNING);
+               remove_wait_queue(&iforce->wait, &wait);
+
+               if (!timeout) {
+                       iforce->expect_packet = 0;
+                       return -1;
+               }
+#else
+               printk(KERN_ERR "iforce_get_id_packet: iforce->bus = SERIO!\n");
+#endif
+               break;
+
+       default:
+               printk(KERN_ERR "iforce_get_id_packet: iforce->bus = %d\n",
+                      iforce->bus);
+               break;
+       }
+
+       return -(iforce->edata[0] != packet[0]);
+}
+
diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c
new file mode 100644 (file)
index 0000000..31e21fb
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * $Id: iforce-serio.c,v 1.4 2002/01/28 22:45:00 jdeneux Exp $
+ *
+ *  Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001 Johann Deneux <deneux@ifrance.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include "iforce.h"
+
+void iforce_serial_xmit(struct iforce *iforce)
+{
+       unsigned char cs;
+       int i;
+       unsigned long flags;
+
+       if (test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags)) {
+               set_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags);
+               return;
+       }
+
+       spin_lock_irqsave(&iforce->xmit_lock, flags);
+
+again:
+       if (iforce->xmit.head == iforce->xmit.tail) {
+               clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+               spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+               return;
+       }
+
+       cs = 0x2b;
+
+       serio_write(iforce->serio, 0x2b);
+
+       serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
+       cs ^= iforce->xmit.buf[iforce->xmit.tail];
+       XMIT_INC(iforce->xmit.tail, 1);
+
+       for (i=iforce->xmit.buf[iforce->xmit.tail]; i >= 0; --i) {
+               serio_write(iforce->serio, iforce->xmit.buf[iforce->xmit.tail]);
+               cs ^= iforce->xmit.buf[iforce->xmit.tail];
+               XMIT_INC(iforce->xmit.tail, 1);
+       }
+       
+       serio_write(iforce->serio, cs);
+
+       if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags))
+               goto again;
+
+       clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+
+       spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+}
+
+static void iforce_serio_write_wakeup(struct serio *serio)
+{
+       iforce_serial_xmit((struct iforce *)serio->private);
+}
+
+static void iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags)
+{
+       struct iforce* iforce = serio->private;
+
+       if (!iforce->pkt) {
+               if (data != 0x2b) {
+                       return;
+               }
+               iforce->pkt = 1;
+               return;
+       }
+
+       if (!iforce->id) {
+               if (data > 3 && data != 0xff) {
+                       iforce->pkt = 0;
+                       return;
+               }
+               iforce->id = data;
+               return;
+       }
+
+       if (!iforce->len) {
+               if (data > IFORCE_MAX_LENGTH) {
+                       iforce->pkt = 0;
+                       iforce->id = 0;
+                       return;
+               }
+               iforce->len = data;
+               return;
+       }
+
+       if (iforce->idx < iforce->len) {
+               iforce->csum += iforce->data[iforce->idx++] = data;
+               return;
+       }
+
+       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;
+       }
+}
+
+static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev)
+{
+       struct iforce *iforce;
+       if (serio->type != (SERIO_RS232 | SERIO_IFORCE))
+               return;
+
+       if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return;
+       memset(iforce, 0, sizeof(struct iforce));
+
+       iforce->bus = IFORCE_232;
+       iforce->serio = serio;
+       serio->private = iforce;
+
+       if (serio_open(serio, dev)) {
+               kfree(iforce);
+               return;
+       }
+
+       if (iforce_init_device(iforce)) {
+               serio_close(serio);
+               kfree(iforce);
+               return;
+       }
+}
+
+static void iforce_serio_disconnect(struct serio *serio)
+{
+       struct iforce* iforce = serio->private;
+
+       input_unregister_device(&iforce->dev);
+       serio_close(serio);
+       kfree(iforce);
+}
+
+struct serio_dev iforce_serio_dev = {
+       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 (file)
index 0000000..760534a
--- /dev/null
@@ -0,0 +1,214 @@
+ /*
+ * $Id: iforce-usb.c,v 1.16 2002/06/09 11:08:04 jdeneux Exp $
+ *
+ *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include "iforce.h"
+
+void iforce_usb_xmit(struct iforce *iforce)
+{
+       int n, c;
+       unsigned long flags;
+
+       spin_lock_irqsave(&iforce->xmit_lock, flags);
+
+       if (iforce->xmit.head == iforce->xmit.tail) {
+               clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+               spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+               return;
+       }
+
+       ((char *)iforce->out->transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail];
+       XMIT_INC(iforce->xmit.tail, 1);
+       n = iforce->xmit.buf[iforce->xmit.tail];
+       XMIT_INC(iforce->xmit.tail, 1);
+
+       iforce->out->transfer_buffer_length = n + 1;
+       iforce->out->dev = iforce->usbdev;
+
+       /* Copy rest of data then */
+       c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE);
+       if (n < c) c=n;
+
+       memcpy(iforce->out->transfer_buffer + 1,
+              &iforce->xmit.buf[iforce->xmit.tail],
+              c);
+       if (n != c) {
+               memcpy(iforce->out->transfer_buffer + 1 + c,
+                      &iforce->xmit.buf[0],
+                      n-c);
+       }
+       XMIT_INC(iforce->xmit.tail, n);
+
+       if ( (n=usb_submit_urb(iforce->out, GFP_ATOMIC)) ) {
+               printk(KERN_WARNING "iforce-usb.c: iforce_usb_xmit: usb_submit_urb failed %d\n", n);
+       }
+
+       /* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended.
+        * As long as the urb completion handler is not called, the transmiting
+        * is considered to be running */
+       spin_unlock_irqrestore(&iforce->xmit_lock, flags);
+}
+
+static void iforce_usb_irq(struct urb *urb)
+{
+       struct iforce *iforce = urb->context;
+       if (urb->status) return;
+       iforce_process_packet(iforce,
+               (iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1);
+}
+
+static void iforce_usb_out(struct urb *urb)
+{
+       struct iforce *iforce = urb->context;
+
+       if (urb->status) {
+               printk(KERN_DEBUG "iforce_usb_out: urb->status %d, exiting", urb->status);
+               return;
+       }
+
+       iforce_usb_xmit(iforce);
+
+       if (waitqueue_active(&iforce->wait))
+               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;
+       if (waitqueue_active(&iforce->wait))
+               wake_up(&iforce->wait);
+}
+
+static void *iforce_usb_probe(struct usb_device *dev, unsigned int ifnum,
+                               const struct usb_device_id *id)
+{
+       struct usb_endpoint_descriptor *epirq, *epout;
+       struct iforce *iforce;
+
+       epirq = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0;
+       epout = dev->config[0].interface[ifnum].altsetting[0].endpoint + 1;
+
+       if (!(iforce = kmalloc(sizeof(struct iforce) + 32, GFP_KERNEL)))
+               goto fail;
+
+       memset(iforce, 0, sizeof(struct iforce));
+
+       if (!(iforce->irq = usb_alloc_urb(0, GFP_KERNEL))) {
+               goto fail;
+       }
+
+       if (!(iforce->out = usb_alloc_urb(0, GFP_KERNEL))) {
+               goto fail;
+       }
+
+       if (!(iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL))) {
+               goto fail;
+       }
+
+       iforce->bus = IFORCE_USB;
+       iforce->usbdev = dev;
+
+       iforce->cr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE;
+       iforce->cr.wIndex = 0;
+       iforce->cr.wLength = 16;
+
+       usb_fill_int_urb(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress),
+                       iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval);
+
+       usb_fill_bulk_urb(iforce->out, dev, usb_sndbulkpipe(dev, epout->bEndpointAddress),
+                       iforce + 1, 32, iforce_usb_out, iforce);
+
+       usb_fill_control_urb(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0),
+                       (void*) &iforce->cr, iforce->edata, 16, iforce_usb_ctrl, iforce);
+
+       if (iforce_init_device(iforce)) goto fail;
+
+       return iforce;
+
+fail:
+       if (iforce) {
+               if (iforce->irq) usb_free_urb(iforce->irq);
+               if (iforce->out) usb_free_urb(iforce->out);
+               if (iforce->ctrl) usb_free_urb(iforce->ctrl);
+               kfree(iforce);
+       }
+
+       return NULL;
+}
+
+/* Called by iforce_delete() */
+void iforce_usb_delete(struct iforce* iforce)
+{
+       usb_unlink_urb(iforce->irq);
+/* Is it ok to unlink those ? */
+       usb_unlink_urb(iforce->out);
+       usb_unlink_urb(iforce->ctrl);
+
+       usb_free_urb(iforce->irq);
+       usb_free_urb(iforce->out);
+       usb_free_urb(iforce->ctrl);
+}
+
+static void iforce_usb_disconnect(struct usb_device *dev, void *ptr)
+{
+       struct iforce *iforce = ptr;
+       int open = iforce->dev.handle->open;
+
+       iforce->usbdev = NULL;
+       input_unregister_device(&iforce->dev);
+
+       if (!open) {
+               iforce_delete_device(iforce);
+               kfree(iforce);
+       }
+}
+
+static struct usb_device_id iforce_usb_ids [] = {
+       { USB_DEVICE(0x044f, 0xa01c) },         /* Thrustmaster Motor Sport GT */
+       { USB_DEVICE(0x046d, 0xc281) },         /* Logitech WingMan Force */
+       { USB_DEVICE(0x046d, 0xc291) },         /* Logitech WingMan Formula Force */
+       { USB_DEVICE(0x05ef, 0x020a) },         /* AVB Top Shot Pegasus */
+       { USB_DEVICE(0x05ef, 0x8884) },         /* AVB Mag Turbo Force */
+       { USB_DEVICE(0x05ef, 0x8888) },         /* AVB Top Shot FFB Racing Wheel */
+       { USB_DEVICE(0x061c, 0xc0a4) },         /* ACT LABS Force RS */
+       { USB_DEVICE(0x06f8, 0x0001) },         /* Guillemot Race Leader Force Feedback */
+       { USB_DEVICE(0x06f8, 0x0004) },         /* Guillemot Force Feedback Racing Wheel */
+       { }                                     /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, iforce_usb_ids);
+
+struct usb_driver iforce_usb_driver = {
+       owner:          THIS_MODULE,
+       name:           "iforce",
+       probe:          iforce_usb_probe,
+       disconnect:     iforce_usb_disconnect,
+       id_table:       iforce_usb_ids,
+};
diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h
new file mode 100644 (file)
index 0000000..fec1b25
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * $Id: iforce.h,v 1.12 2002/06/09 11:08:04 jdeneux Exp $
+ *
+ *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
+ *  Copyright (c) 2001-2002 Johann Deneux <deneux@ifrance.com>
+ *
+ *  USB/RS232 I-Force joysticks and wheels.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/serio.h>
+#include <linux/config.h>
+#include <linux/circ_buf.h>
+#include <asm/semaphore.h>
+
+/* FF: 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
+
+#if defined(CONFIG_JOYSTICK_IFORCE_232)
+#define IFORCE_232     1
+#endif
+#if defined(CONFIG_JOYSTICK_IFORCE_USB)
+#define IFORCE_USB     2
+#endif
+
+#define FALSE 0
+#define TRUE 1
+
+#define FF_EFFECTS_MAX 32
+
+/* Each force feedback effect is made of one core effect, which can be
+ * associated to at most to effect modifiers
+ */
+#define FF_MOD1_IS_USED                0
+#define FF_MOD2_IS_USED                1
+#define FF_CORE_IS_USED                2
+#define FF_CORE_IS_PLAYED      3       /* Effect is currently being played */
+#define FF_CORE_SHOULD_PLAY    4       /* User wants the effect to be played */
+#define FF_CORE_UPDATE         5       /* Effect is being updated */
+#define FF_MODCORE_MAX         5
+
+#define CHECK_OWNERSHIP(i, iforce)     \
+       ((i) < FF_EFFECTS_MAX && i >= 0 && \
+       test_bit(FF_CORE_IS_USED, (iforce)->core_effects[(i)].flags) && \
+       (current->pid == 0 || \
+       (iforce)->core_effects[(i)].owner == current->pid))
+
+struct iforce_core_effect {
+       /* Information about where modifiers are stored in the device's memory */
+       struct resource mod1_chunk;
+       struct resource mod2_chunk;
+       unsigned long flags[NBITS(FF_MODCORE_MAX)];
+       pid_t owner;
+       /* Used to keep track of parameters of an effect. They are needed
+        * to know what parts of an effect changed in an update operation.
+        * We try to send only parameter packets if possible, as sending
+        * effect parameter requires the effect to be stoped and restarted
+        */
+       struct ff_effect effect;
+};
+
+#define FF_CMD_EFFECT          0x010e
+#define FF_CMD_ENVELOPE                0x0208
+#define FF_CMD_MAGNITUDE       0x0303
+#define FF_CMD_PERIOD          0x0407
+#define FF_CMD_CONDITION       0x050a
+
+#define FF_CMD_AUTOCENTER      0x4002
+#define FF_CMD_PLAY            0x4103
+#define FF_CMD_ENABLE          0x4201
+#define FF_CMD_GAIN            0x4301
+
+#define FF_CMD_QUERY           0xff01
+
+/* Buffer for async write */
+#define XMIT_SIZE              256
+#define XMIT_INC(var, n)       (var)+=n; (var)&= XMIT_SIZE -1
+/* iforce::xmit_flags */
+#define IFORCE_XMIT_RUNNING    0
+#define IFORCE_XMIT_AGAIN      1
+
+struct iforce_device {
+       u16 idvendor;
+       u16 idproduct;
+       char *name;
+       signed short *btn;
+       signed short *abs;
+       signed short *ff;
+};
+
+struct iforce {
+       struct input_dev dev;           /* Input device interface */
+       struct iforce_device *type;
+       int bus;
+
+       unsigned char data[IFORCE_MAX_LENGTH];
+       unsigned char edata[IFORCE_MAX_LENGTH];
+       u16 ecmd;
+       u16 expect_packet;
+
+#ifdef IFORCE_232
+       struct serio *serio;            /* RS232 transfer */
+       int idx, pkt, len, id;
+       unsigned char csum;
+#endif
+#ifdef IFORCE_USB
+       struct usb_device *usbdev;      /* USB transfer */
+       struct urb *irq, *out, *ctrl;
+       struct usb_ctrlrequest cr;
+#endif
+       spinlock_t xmit_lock;
+       /* Buffer used for asynchronous sending of bytes to the device */
+       struct circ_buf xmit;
+       unsigned char xmit_data[XMIT_SIZE];
+       long xmit_flags[1];
+       
+                                       /* Force Feedback */
+       wait_queue_head_t wait;
+       struct resource device_memory;
+       struct iforce_core_effect core_effects[FF_EFFECTS_MAX];
+       struct semaphore mem_mutex;
+};
+
+/* Get hi and low bytes of a 16-bits int */
+#define HI(a)  ((unsigned char)((a) >> 8))
+#define LO(a)  ((unsigned char)((a) & 0xff))
+
+/* For many parameters, it seems that 0x80 is a special value that should
+ * be avoided. Instead, we replace this value by 0x7f
+ */
+#define HIFIX80(a) ((unsigned char)(((a)<0? (a)+255 : (a))>>8))
+
+/* Encode a time value */
+#define TIME_SCALE(a)  (a)
+
+
+/* Public functions */
+/* iforce-serio.c */
+void iforce_serial_xmit(struct iforce *iforce);
+
+/* iforce-usb.c */
+void iforce_usb_xmit(struct iforce *iforce);
+void iforce_usb_delete(struct iforce *iforce);
+
+/* iforce-main.c */
+int iforce_init_device(struct iforce *iforce);
+void iforce_delete_device(struct iforce *iforce);
+
+/* iforce-packets.c */
+int iforce_control_playback(struct iforce*, u16 id, unsigned int);
+void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data);
+int iforce_send_packet(struct iforce *iforce, u16 cmd, unsigned char* data);
+void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data) ;
+int iforce_get_id_packet(struct iforce *iforce, char *packet);
+
+/* iforce-ff.c */
+int iforce_upload_periodic(struct iforce*, struct ff_effect*, int is_update);
+int iforce_upload_constant(struct iforce*, struct ff_effect*, int is_update);
+int iforce_upload_condition(struct iforce*, struct ff_effect*, int is_update);
+
+/* Public variables */
+extern struct serio_dev iforce_serio_dev;
+extern struct usb_driver iforce_usb_driver;
diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c
new file mode 100644 (file)
index 0000000..70da80e
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * $Id: twidjoy.c,v 1.5 2002/01/22 20:31:53 vojtech Exp $
+ *
+ *  derived from CVS-ID "stinger.c,v 1.5 2001/05/29 12:57:18 vojtech Exp"
+ *
+ *  Copyright (c) 2001 Arndt Schoenewald
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *  Copyright (c) 2000 Mark Fletcher
+ *
+ *  Sponsored by Quelltext AG (http://www.quelltext-ag.de), Dortmund, Germany
+ */
+
+/*
+ * Driver to use Handykey's Twiddler (the first edition, i.e. the one with
+ * the RS232 interface) as a joystick under Linux
+ *
+ * The Twiddler is a one-handed chording keyboard featuring twelve buttons on
+ * the front, six buttons on the top, and a built-in tilt sensor. The buttons
+ * on the front, which are grouped as four rows of three buttons, are pressed
+ * by the four fingers (this implies only one button per row can be held down
+ * at the same time) and the buttons on the top are for the thumb. The tilt
+ * sensor delivers X and Y axis data depending on how the Twiddler is held.
+ * Additional information can be found at http://www.handykey.com.
+ *
+ * This driver does not use the Twiddler for its intended purpose, i.e. as
+ * a chording keyboard, but as a joystick: pressing and releasing a button
+ * immediately sends a corresponding button event, and tilting it generates
+ * corresponding ABS_X and ABS_Y events. This turns the Twiddler into a game
+ * controller with amazing 18 buttons :-)
+ *
+ * Note: The Twiddler2 (the successor of the Twiddler that connects directly
+ * to the PS/2 keyboard and mouse ports) is NOT supported by this driver!
+ *
+ * For questions or feedback regarding this driver module please contact:
+ * Arndt Schoenewald <arndt@quelltext.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+/*
+ * Constants.
+ */
+
+#define TWIDJOY_MAX_LENGTH 5
+
+static char *twidjoy_name = "Handykey Twiddler";
+
+static struct twidjoy_button_spec {
+       int bitshift;
+       int bitmask;
+       int buttons[3];
+}
+twidjoy_buttons[] = {
+       {  0, 3, { BTN_A,      BTN_B,     BTN_C    } },
+       {  2, 3, { BTN_X,      BTN_Y,     BTN_Z    } },
+       {  4, 3, { BTN_TL,     BTN_TR,    BTN_TR2  } },
+       {  6, 3, { BTN_SELECT, BTN_START, BTN_MODE } },
+       {  8, 1, { BTN_BASE5                       } },
+       {  9, 1, { BTN_BASE                        } },
+       { 10, 1, { BTN_BASE3                       } },
+       { 11, 1, { BTN_BASE4                       } },
+       { 12, 1, { BTN_BASE2                       } },
+       { 13, 1, { BTN_BASE6                       } },
+       { 0,  0, { 0                               } }
+};
+
+/*
+ * Per-Twiddler data.
+ */
+
+struct twidjoy {
+       struct input_dev dev;
+       int idx;
+       unsigned char data[TWIDJOY_MAX_LENGTH];
+       char phys[32];
+};
+
+/*
+ * twidjoy_process_packet() decodes packets the driver receives from the
+ * Twiddler. It updates the data accordingly.
+ */
+
+static void twidjoy_process_packet(struct twidjoy *twidjoy)
+{
+       if (twidjoy->idx == TWIDJOY_MAX_LENGTH) {
+               struct input_dev *dev = &twidjoy->dev;
+               unsigned char *data = twidjoy->data;
+               struct twidjoy_button_spec *bp;
+               int button_bits, abs_x, abs_y;
+
+               button_bits = ((data[1] & 0x7f) << 7) | (data[0] & 0x7f);
+
+               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);
+       }
+
+       return;
+}
+
+/*
+ * twidjoy_interrupt() is called by the low level driver when characters
+ * are ready for us. We then buffer them for further processing, or call the
+ * packet processing routine.
+ */
+
+static void twidjoy_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
+{
+       struct twidjoy *twidjoy = serio->private;
+
+       /* 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;                 /* 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;
+}
+
+/*
+ * twidjoy_disconnect() is the opposite of twidjoy_connect()
+ */
+
+static void twidjoy_disconnect(struct serio *serio)
+{
+       struct twidjoy *twidjoy = serio->private;
+       input_unregister_device(&twidjoy->dev);
+       serio_close(serio);
+       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 void twidjoy_connect(struct serio *serio, struct serio_dev *dev)
+{
+       struct twidjoy_button_spec *bp;
+       struct twidjoy *twidjoy;
+       int i;
+
+       if (serio->type != (SERIO_RS232 | SERIO_TWIDJOY))
+               return;
+
+       if (!(twidjoy = kmalloc(sizeof(struct twidjoy), GFP_KERNEL)))
+               return;
+
+       memset(twidjoy, 0, sizeof(struct twidjoy));
+
+       sprintf(twidjoy->phys, "%s/input0", serio->phys);
+
+       twidjoy->dev.name = twidjoy_name;
+       twidjoy->dev.phys = twidjoy->phys;
+       twidjoy->dev.idbus = BUS_RS232;
+       twidjoy->dev.idvendor = SERIO_TWIDJOY;
+       twidjoy->dev.idproduct = 0x0001;
+       twidjoy->dev.idversion = 0x0100;
+
+       twidjoy->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);      
+
+       for (bp = twidjoy_buttons; bp->bitmask; bp++) {
+               for (i = 0; i < bp->bitmask; i++)
+                       set_bit(bp->buttons[i], twidjoy->dev.keybit);
+       }
+
+       twidjoy->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+
+       for (i = 0; i < 2; i++) {
+               twidjoy->dev.absmax[ABS_X+i] =  50;     
+               twidjoy->dev.absmin[ABS_X+i] = -50;     
+
+               /* TODO: arndt 20010708: Are these values appropriate? */
+               twidjoy->dev.absfuzz[ABS_X+i] = 4;
+               twidjoy->dev.absflat[ABS_X+i] = 4;
+       }
+
+       twidjoy->dev.private = twidjoy;
+       
+       serio->private = twidjoy;
+
+       if (serio_open(serio, dev)) {
+               kfree(twidjoy);
+               return;
+       }
+
+       input_register_device(&twidjoy->dev);
+
+       printk(KERN_INFO "input: %s on %s\n", twidjoy_name, serio->phys);
+}
+
+/*
+ * The serio device structure.
+ */
+
+static struct serio_dev twidjoy_dev = {
+       interrupt:      twidjoy_interrupt,
+       connect:        twidjoy_connect,
+       disconnect:     twidjoy_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+int __init twidjoy_init(void)
+{
+       serio_register_device(&twidjoy_dev);
+       return 0;
+}
+
+void __exit twidjoy_exit(void)
+{
+       serio_unregister_device(&twidjoy_dev);
+}
+
+module_init(twidjoy_init);
+module_exit(twidjoy_exit);