# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#                  ChangeSet    1.636   -> 1.637  
#       drivers/usb/input/hid.h 1.14    -> 1.15   
#       drivers/usb/input/Makefile      1.6     -> 1.7    
#       drivers/usb/input/hid-input.c   1.5     -> 1.6    
#       drivers/usb/input/hiddev.c      1.14    -> 1.15   
#       drivers/usb/input/hid-core.c    1.25    -> 1.26   
#       drivers/usb/input/Config.help   1.3     -> 1.4    
#       drivers/usb/input/Config.in     1.4     -> 1.5    
#                      (new)            -> 1.1     drivers/usb/input/hid-lgff.c
#                      (new)            -> 1.1     drivers/usb/input/hid-lg3dff.c
#                      (new)            -> 1.1     drivers/usb/input/powermate.c
#                      (new)            -> 1.1     drivers/usb/input/fixp-arith.h
#                      (new)            -> 1.1     drivers/usb/input/pid.c
#                      (new)            -> 1.1     drivers/usb/input/pid.h
#                      (new)            -> 1.1     drivers/usb/input/hid-ff.c
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/07/12      [EMAIL PROTECTED] 1.637
# [PATCH] Big HID update
# 
# This cset is update of the HID drivers to the latest version, as a part
# of the Input merge. It finally includes ForceFeedback support by Johann
# Deneux, enabling ForceFeedback on new Logitech and Microsoft devices.
# --------------------------------------------
#
diff -Nru a/drivers/usb/input/Config.help b/drivers/usb/input/Config.help
--- a/drivers/usb/input/Config.help     Fri Jul 12 11:45:38 2002
+++ b/drivers/usb/input/Config.help     Fri Jul 12 11:45:38 2002
@@ -23,6 +23,23 @@
 
   If unsure, say Y.
 
+CONFIG_HID_FF
+  Say Y here is you want force feedback support for a few hid devices. See
+  below for a list of supported devices.
+  See Documentation/input/ff.txt for a description of the force feedback API.
+
+  If unsure, say N.
+
+CONFIG_LOGITECH_RUMBLE
+  Say Y here if you have a Logitech WingMan Cordless rumble pad and if you
+  want to enable force feedback. Note: if you say N here, this device will
+  still be supported, but without force feedback.
+
+CONFIG_HID_PID
+  Say Y yes if you have a PID-compliant joystick and wish to enable force
+  feedback for it. The Microsoft Sidewinder Force Feedback 2 is one such
+  device.
+
 CONFIG_USB_HIDDEV
   Say Y here if you want to support HID devices (from the USB
   specification standpoint) that aren't strictly user interface
@@ -82,4 +99,18 @@
   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 wacom.o.  If you want to compile it as a
+  module, say M here and read <file:Documentation/modules.txt>.
+
+CONFIG_USB_POWERMATE
+  Say Y here if you want to use  Griffin PowerMate or Contour Jog devices.
+  These are stainless steel dials which can measure clockwise and
+  anticlockwise rotation. The dial also acts as a pushbutton. The base
+  contains an LED which can be instructed to pulse or to switch to a
+  particular intensity.
+
+  You can download userspace tools from http://sowerbutts.com/powermate/
+
+  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 powermate.o. If you want to compile it as a
   module, say M here and read <file:Documentation/modules.txt>.
diff -Nru a/drivers/usb/input/Config.in b/drivers/usb/input/Config.in
--- a/drivers/usb/input/Config.in       Fri Jul 12 11:45:38 2002
+++ b/drivers/usb/input/Config.in       Fri Jul 12 11:45:38 2002
@@ -3,10 +3,16 @@
 #
 comment 'USB Human Interface Devices (HID)'
 dep_tristate '  USB Human Interface Device (full HID) support' CONFIG_USB_HID 
$CONFIG_USB
+
 if [ "$CONFIG_INPUT" = "n" ]; then
    comment '    Input core support is needed for USB HID input layer or HIDBP support'
 fi
+
 dep_mbool '    HID input layer support' CONFIG_USB_HIDINPUT $CONFIG_INPUT 
$CONFIG_USB_HID
+dep_mbool '      Force feedback support' CONFIG_HID_FF $CONFIG_USB_HIDINPUT
+dep_mbool '        PID Devices' CONFIG_HID_PID $CONFIG_USB_HID $CONFIG_HID_FF
+dep_mbool '        Logitech RumblePad support' CONFIG_LOGITECH_RUMBLE $CONFIG_USB_HID 
+$CONFIG_HID_FF
+dep_mbool '        Logitech WingMan Force 3D support' CONFIG_LOGITECH_3D 
+$CONFIG_USB_HID $CONFIG_HID_FF
 dep_mbool '    /dev/hiddev raw HID device support' CONFIG_USB_HIDDEV $CONFIG_USB_HID
 
 if [ "$CONFIG_USB_HID" != "y" ]; then
@@ -16,4 +22,5 @@
 
 dep_tristate '  Aiptek 6000U/8000U tablet support' CONFIG_USB_AIPTEK $CONFIG_USB 
$CONFIG_INPUT
 dep_tristate '  Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB 
$CONFIG_INPUT
-   
+dep_tristate '  Griffin PowerMate and Contour Jog support' CONFIG_USB_POWERMATE 
+$CONFIG_USB $CONFIG_INPUT
+
diff -Nru a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile
--- a/drivers/usb/input/Makefile        Fri Jul 12 11:45:38 2002
+++ b/drivers/usb/input/Makefile        Fri Jul 12 11:45:38 2002
@@ -12,12 +12,27 @@
 ifeq ($(CONFIG_USB_HIDINPUT),y)
        hid-objs        += hid-input.o
 endif
+ifeq ($(CONFIG_HID_PID),y)
+       hid-objs        += pid.o
+endif
+
+ifeq ($(CONFIG_LOGITECH_RUMBLE),y)
+       hid-objs        += hid-lgff.o
+endif
+
+ifeq ($(CONFIG_LOGITECH_3D),y)
+       hid-objs        += hid-lg3dff.o
+endif
+
+ifeq ($(CONFIG_HID_FF),y)
+       hid-objs        += hid-ff.o
+endif
 
 obj-$(CONFIG_USB_AIPTEK)       += aiptek.o
 obj-$(CONFIG_USB_HID)          += hid.o
 obj-$(CONFIG_USB_KBD)          += usbkbd.o
 obj-$(CONFIG_USB_MOUSE)                += usbmouse.o
 obj-$(CONFIG_USB_WACOM)                += wacom.o
-
+obj-$(CONFIG_USB_POWERMATE)    += powermate.o
 
 include $(TOPDIR)/Rules.make
diff -Nru a/drivers/usb/input/fixp-arith.h b/drivers/usb/input/fixp-arith.h
--- /dev/null   Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/input/fixp-arith.h    Fri Jul 12 11:45:38 2002
@@ -0,0 +1,84 @@
+#ifndef _FIXP_ARITH_H
+#define _FIXP_ARITH_H
+
+/*
+ * $$
+ *
+ * Simplistic fixed-point arithmetics.
+ * Hmm, I'm probably duplicating some code :(
+ *
+ * Copyright (c) 2002 Johann Deneux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so by
+ * e-mail - mail your message to <[EMAIL PROTECTED]>
+ */
+
+#include <linux/types.h>
+
+// The type representing fixed-point values
+typedef s16 fixp_t;
+
+#define FRAC_N 8
+#define FRAC_MASK ((1<<FRAC_N)-1)
+
+// Not to be used directly. Use fixp_{cos,sin}
+fixp_t cos_table[45] = {
+       0x0100, 0x00FF, 0x00FF, 0x00FE, 0x00FD, 0x00FC, 0x00FA, 0x00F8,
+       0x00F6, 0x00F3, 0x00F0, 0x00ED, 0x00E9, 0x00E6, 0x00E2, 0x00DD,
+       0x00D9, 0x00D4, 0x00CF, 0x00C9, 0x00C4, 0x00BE, 0x00B8, 0x00B1,
+       0x00AB, 0x00A4, 0x009D, 0x0096, 0x008F, 0x0087, 0x0080, 0x0078,
+       0x0070, 0x0068, 0x005F, 0x0057, 0x004F, 0x0046, 0x003D, 0x0035,
+       0x002C, 0x0023, 0x001A, 0x0011, 0x0008
+};
+
+
+/* a: 123 -> 123.0 */
+inline fixp_t fixp_new(s16 a)
+{
+       return a<<FRAC_N;
+}
+
+/* a: 0xFFFF -> -1.0
+      0x8000 -> 1.0
+      0x0000 -> 0.0
+*/
+inline fixp_t fixp_new16(s16 a)
+{
+       return ((s32)a)>>(16-FRAC_N);
+}
+
+inline fixp_t fixp_cos(unsigned int degrees)
+{
+       int quadrant = (degrees / 90) & 3;
+       unsigned int i = (degrees % 90) >> 1;
+
+       return (quadrant == 1 || quadrant == 2)? -cos_table[i] : cos_table[i];
+}
+
+inline fixp_t fixp_sin(unsigned int degrees)
+{
+       return -fixp_cos(degrees + 90);
+}
+
+inline fixp_t fixp_mult(fixp_t a, fixp_t b)
+{
+       return ((s32)(a*b))>>FRAC_N;
+}
+
+#endif
diff -Nru a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
--- a/drivers/usb/input/hid-core.c      Fri Jul 12 11:45:38 2002
+++ b/drivers/usb/input/hid-core.c      Fri Jul 12 11:45:38 2002
@@ -1,5 +1,5 @@
 /*
- * $Id: hid-core.c,v 1.42 2002/01/27 00:22:46 vojtech Exp $
+ * $Id: hid-core.c,v 1.6 2002/06/09 17:34:55 jdeneux Exp $
  *
  *  Copyright (c) 1999 Andreas Gal
  *  Copyright (c) 2000-2001 Vojtech Pavlik
@@ -108,11 +108,10 @@
        memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
                + values * sizeof(unsigned));
 
-       report->field[report->maxfield] = field;
+       report->field[report->maxfield++] = field;
        field->usage = (struct hid_usage *)(field + 1);
        field->value = (unsigned *)(field->usage + usages);
        field->report = report;
-       field->index = report->maxfield++;
 
        return field;
 }
@@ -518,6 +517,8 @@
 {
        unsigned i,j;
 
+       hid_ff_exit(device);
+
        for (i = 0; i < HID_REPORT_TYPES; i++) {
                struct hid_report_enum *report_enum = device->report_enum + i;
 
@@ -1171,8 +1172,8 @@
        set_current_state(TASK_UNINTERRUPTIBLE);
        add_wait_queue(&hid->wait, &wait);
 
-       while (timeout && test_bit(HID_CTRL_RUNNING, &hid->iofl) &&
-                         test_bit(HID_OUT_RUNNING, &hid->iofl))
+       while (timeout && (test_bit(HID_CTRL_RUNNING, &hid->iofl) ||
+                          test_bit(HID_OUT_RUNNING, &hid->iofl)))
                timeout = schedule_timeout(timeout);
 
        set_current_state(TASK_RUNNING);
@@ -1223,6 +1224,7 @@
        struct hid_report *report;
        struct list_head *list;
        int len;
+       int err, ret;
 
        report_enum = hid->report_enum + HID_INPUT_REPORT;
        list = report_enum->report_list.next;
@@ -1240,7 +1242,16 @@
                list = list->next;
        }
 
-       if (hid_wait_io(hid)) {
+       err = 0;
+       while ((ret = hid_wait_io(hid))) {
+               err |= ret;
+               if (test_bit(HID_CTRL_RUNNING, &hid->iofl))
+                       usb_unlink_urb(hid->urbctrl);
+               if (test_bit(HID_OUT_RUNNING, &hid->iofl))
+                       usb_unlink_urb(hid->urbout);
+       }
+
+       if (err) {
                warn("timeout initializing reports\n");
                return;
        }
@@ -1299,7 +1310,7 @@
        struct hid_descriptor *hdesc;
        struct hid_device *hid;
        unsigned quirks = 0, rsize = 0;
-       char *buf;
+       char *buf, *rdesc;
        int n;
 
        for (n = 0; hid_blacklist[n].idVendor; n++)
@@ -1325,27 +1336,31 @@
                return NULL;
        }
 
-       {
-               __u8 rdesc[rsize];
+       if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
+               dbg("couldn't allocate rdesc memory");
+               return NULL;
+       }
 
-               if ((n = hid_get_class_descriptor(dev, interface->bInterfaceNumber, 
HID_DT_REPORT, rdesc, rsize)) < 0) {
-                       dbg("reading report descriptor failed");
-                       return NULL;
-               }
+       if ((n = hid_get_class_descriptor(dev, interface->bInterfaceNumber, 
+HID_DT_REPORT, rdesc, rsize)) < 0) {
+               dbg("reading report descriptor failed");
+               kfree(rdesc);
+               return NULL;
+       }
 
 #ifdef DEBUG_DATA
-               printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = 
", rsize, n);
-               for (n = 0; n < rsize; n++)
-                       printk(" %02x", (unsigned) rdesc[n]);
-               printk("\n");
+       printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, 
+n);
+       for (n = 0; n < rsize; n++)
+               printk(" %02x", (unsigned) rdesc[n]);
+       printk("\n");
 #endif
 
-               if (!(hid = hid_parse_report(rdesc, rsize))) {
-                       dbg("parsing report descriptor failed");
-                       return NULL;
-               }
+       if (!(hid = hid_parse_report(rdesc, rsize))) {
+               dbg("parsing report descriptor failed");
+               kfree(rdesc);
+               return NULL;
        }
 
+       kfree(rdesc);
        hid->quirks = quirks;
 
        for (n = 0; n < interface->bNumEndpoints; n++) {
@@ -1439,6 +1454,8 @@
        hid_init_reports(hid);
        hid_dump_device(hid);
 
+       hid_ff_init(hid);
+
        if (!hidinput_connect(hid))
                hid->claimed |= HID_CLAIMED_INPUT;
        if (!hiddev_connect(hid))
@@ -1477,20 +1494,20 @@
 {
        struct hid_device *hid = ptr;
 
-       dbg("cleanup called");
        usb_unlink_urb(hid->urbin);
        usb_unlink_urb(hid->urbout);
        usb_unlink_urb(hid->urbctrl);
 
+       if (hid->claimed & HID_CLAIMED_INPUT)
+               hidinput_disconnect(hid);
+       if (hid->claimed & HID_CLAIMED_HIDDEV)
+               hiddev_disconnect(hid);
+
        usb_free_urb(hid->urbin);
        usb_free_urb(hid->urbctrl);
        if (hid->urbout)
                usb_free_urb(hid->urbout);
 
-       if (hid->claimed & HID_CLAIMED_INPUT)
-               hidinput_disconnect(hid);
-       if (hid->claimed & HID_CLAIMED_HIDDEV)
-               hiddev_disconnect(hid);
        hid_free_device(hid);
 }
 
diff -Nru a/drivers/usb/input/hid-ff.c b/drivers/usb/input/hid-ff.c
--- /dev/null   Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/input/hid-ff.c        Fri Jul 12 11:45:38 2002
@@ -0,0 +1,90 @@
+/*
+ * $Id: hid-ff.c,v 1.3 2002/06/09 11:06:38 jdeneux Exp $
+ *
+ *  Force feedback support for hid devices.
+ *  Not all hid devices use the same protocol. For example, some use PID,
+ *  other use their own proprietary procotol.
+ *
+ *  Copyright (c) 2002 Johann Deneux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so by
+ * e-mail - mail your message to <[EMAIL PROTECTED]>
+ */
+
+#include <linux/input.h>
+
+#define DEBUG
+#include <linux/usb.h>
+
+#include "hid.h"
+
+/* Drivers' initializing functions */
+extern int hid_lgff_init(struct hid_device* hid);
+extern int hid_lg3d_init(struct hid_device* hid);
+extern int hid_pid_init(struct hid_device* hid);
+
+/*
+ * This table contains pointers to initializers. To add support for new
+ * devices, you need to add the USB vendor and product ids here.
+ */
+struct hid_ff_initializer {
+       __u16 idVendor;
+       __u16 idProduct;
+       int (*init)(struct hid_device*);
+};
+
+static struct hid_ff_initializer inits[] = {
+#ifdef CONFIG_LOGITECH_RUMBLE
+       {0x46d, 0xc211, hid_lgff_init},
+#endif
+#ifdef CONFIG_LOGITECH_3D
+       {0x46d, 0xc283, hid_lg3d_init},
+#endif
+#ifdef CONFIG_HID_PID
+       {0x45e, 0x001b, hid_pid_init},
+#endif
+       {0, 0, NULL} /* Terminating entry */
+};
+
+static struct hid_ff_initializer *hid_get_ff_init(__u16 idVendor,
+                                                 __u16 idProduct)
+{
+       struct hid_ff_initializer *init;
+       for (init = inits;
+            init->idVendor
+                    && !(init->idVendor == idVendor
+                         && init->idProduct == idProduct);
+            init++);
+
+       return init->idVendor? init : NULL;
+}
+
+int hid_ff_init(struct hid_device* hid)
+{
+       struct hid_ff_initializer *init;
+
+       init = hid_get_ff_init(hid->dev->descriptor.idVendor,
+                              hid->dev->descriptor.idProduct);
+
+       if (!init) {
+               warn("hid_ff_init could not find initializer");
+               return -ENOSYS;
+       }
+       return init->init(hid);
+}
diff -Nru a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c
--- a/drivers/usb/input/hid-input.c     Fri Jul 12 11:45:38 2002
+++ b/drivers/usb/input/hid-input.c     Fri Jul 12 11:45:38 2002
@@ -1,5 +1,5 @@
 /*
- * $Id: hid-input.c,v 1.18 2001/11/07 09:01:18 vojtech Exp $
+ * $Id: hid-input.c,v 1.2 2002/04/23 00:59:25 rdamazio Exp $
  *
  *  Copyright (c) 2000-2001 Vojtech Pavlik
  *
@@ -273,9 +273,53 @@
 
                        usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX;
                        break;
+                       
+               case HID_UP_PID:
 
+                       usage->type = EV_FF; bit = input->ffbit; max = FF_MAX;
+                       
+                       switch(usage->hid & HID_USAGE) {
+                               case 0x26: set_bit(FF_CONSTANT, input->ffbit); break;
+                               case 0x27: set_bit(FF_RAMP,     input->ffbit); break;
+                               case 0x28: set_bit(FF_CUSTOM,   input->ffbit); break;
+                               case 0x30: set_bit(FF_SQUARE,   input->ffbit);
+                                          set_bit(FF_PERIODIC, input->ffbit); break;
+                               case 0x31: set_bit(FF_SINE,     input->ffbit);
+                                          set_bit(FF_PERIODIC, input->ffbit); break;
+                               case 0x32: set_bit(FF_TRIANGLE, input->ffbit);
+                                          set_bit(FF_PERIODIC, input->ffbit); break;
+                               case 0x33: set_bit(FF_SAW_UP,   input->ffbit);
+                                          set_bit(FF_PERIODIC, input->ffbit); break;
+                               case 0x34: set_bit(FF_SAW_DOWN, input->ffbit);
+                                          set_bit(FF_PERIODIC, input->ffbit); break;
+                               case 0x40: set_bit(FF_SPRING,   input->ffbit); break;
+                               case 0x41: set_bit(FF_DAMPER,   input->ffbit); break;
+                               case 0x42: set_bit(FF_INERTIA , input->ffbit); break;
+                               case 0x43: set_bit(FF_FRICTION, input->ffbit); break;
+                               case 0x7e: usage->code = FF_GAIN;       break;
+                               case 0x83:  /* Simultaneous Effects Max */
+                                       input->ff_effects_max = (field->value[0]);
+                                       dbg("Maximum Effects - 
+%d",input->ff_effects_max);
+                                       break;
+                               case 0x98:  /* Device Control */
+                                       usage->code = FF_AUTOCENTER;    break;
+                               case 0xa4:  /* Safety Switch */
+                                       usage->code = BTN_DEAD;
+                                       bit = input->keybit;
+                                       usage->type = EV_KEY;
+                                       max = KEY_MAX;
+                                       dbg("Safety Switch Report\n");
+                                       break;
+                               case 0x9f: /* Device Paused */
+                               case 0xa0: /* Actuators Enabled */
+                                       dbg("Not telling the input API about ");
+                                       resolv_usage(usage->hid);
+                                       return;
+                       }
+                       break;
                default:
                unknown:
+                       resolv_usage(usage->hid);
 
                        if (field->report_size == 1) {
 
@@ -365,6 +409,16 @@
                input_event(input, EV_KEY, BTN_TOUCH, value > a + ((b - a) >> 3));
        }
 
+       if (usage->hid == (HID_UP_PID | 0x83UL)) { /* Simultaneous Effects Max */
+               input->ff_effects_max = value;
+               dbg("Maximum Effects - %d",input->ff_effects_max);
+               return;
+       }
+       if (usage->hid == (HID_UP_PID | 0x7fUL)) {
+               dbg("PID Pool Report\n");
+               return;
+       }
+
        if((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", 
not KEY_UKNOWN */
                return;
 
@@ -379,6 +433,9 @@
        struct hid_device *hid = dev->private;
        struct hid_field *field = NULL;
        int offset;
+
+       if (type == EV_FF)
+               return hid_ff_event(hid, dev, type, code, value);
 
        if ((offset = hid_find_field(hid, type, code, &field)) == -1) {
                warn("event field not found");
diff -Nru a/drivers/usb/input/hid-lg3dff.c b/drivers/usb/input/hid-lg3dff.c
--- /dev/null   Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/input/hid-lg3dff.c    Fri Jul 12 11:45:38 2002
@@ -0,0 +1,444 @@
+/*
+ * $$
+ *
+ *  Force feedback support for hid-compliant devices of the Logitech *3D family
+ *
+ *  Copyright (c) 2002 Johann Deneux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so by
+ * e-mail - mail your message to <[EMAIL PROTECTED]>
+ */
+
+#include <linux/input.h>
+#include <linux/sched.h>
+
+#define DEBUG
+#include <linux/usb.h>
+
+#include <linux/circ_buf.h>
+
+#include "hid.h"
+#include "fixp-arith.h"
+
+#define RUN_AT(t) (jiffies + (t))
+
+/* Periodicity of the update */
+#define PERIOD (HZ/10)
+
+/* Effect status: lg3d_effect::flags */
+#define EFFECT_STARTED 0     /* Effect is going to play after some time
+                               (ff_replay.delay) */
+#define EFFECT_PLAYING 1     /* Effect is being played */
+#define EFFECT_USED    2
+
+/* Check that the current process can access an effect */
+#define CHECK_OWNERSHIP(i, l) \
+        (i>=0 && i<N_EFFECTS \
+        && test_bit(EFFECT_USED, l->effects[i].flags) \
+        && (current->pid == 0 \
+            || l->effects[i].owner == current->pid))
+
+#define N_EFFECTS 8
+
+struct lg3d_effect {
+       pid_t owner;
+
+       struct ff_effect effect;   /* Description of the effect */
+
+       unsigned int count;        /* Number of times left to play */
+       unsigned long flags[1];
+
+       unsigned long started_at;  /* When the effect started to play */
+};
+
+// For lg3d_device::flags
+#define DEVICE_USB_XMIT 0          /* An URB is being sent */
+#define DEVICE_CLOSING 1           /* The driver is being unitialised */
+
+struct lg3d_device {
+       struct hid_device* hid;
+
+       struct urb* urbffout;        /* Output URB used to send ff commands */
+       struct usb_ctrlrequest ffcr; /* ff commands use control URBs */
+       char buf[8];
+
+       struct lg3d_effect effects[N_EFFECTS];
+       spinlock_t lock;             /* device-level lock. Having locks on
+                                       a per-effect basis could be nice, but
+                                       isn't really necessary */
+       struct timer_list timer;
+       unsigned long last_time;     /* Last time the timer handler was
+                                       executed */
+
+       unsigned long flags[1];      /* Contains various information about the
+                                       state of the driver for this device */
+};
+
+static void hid_lg3d_ctrl_out(struct urb *urb);
+static void hid_lg3d_exit(struct hid_device* hid);
+static int hid_lg3d_event(struct hid_device *hid, struct input_dev *input,
+                         unsigned int type, unsigned int code, int value);
+static int hid_lg3d_flush(struct input_dev *input, struct file *file);
+static int hid_lg3d_upload_effect(struct input_dev *input,
+                                 struct ff_effect *effect);
+static int hid_lg3d_erase(struct input_dev *input, int id);
+static void hid_lg3d_timer(unsigned long timer_data);
+
+
+int hid_lg3d_init(struct hid_device* hid)
+{
+       struct lg3d_device *private;
+
+       /* Private data */
+       private = kmalloc(sizeof(struct lg3d_device), GFP_KERNEL);
+       if (!private) return -1;
+
+       memset(private, 0, sizeof(struct lg3d_device));
+
+       hid->ff_private = private;
+
+       private->hid = hid;
+       spin_lock_init(&private->lock);
+
+       /* Timer for the periodic update task */
+       init_timer(&private->timer);
+       private->timer.data = (unsigned long)private;
+       private->timer.function = hid_lg3d_timer;
+
+       /* Event and exit callbacks */
+       hid->ff_exit = hid_lg3d_exit;
+       hid->ff_event = hid_lg3d_event;
+
+       /* USB init */
+       if (!(private->urbffout = usb_alloc_urb(0, GFP_KERNEL))) {
+               kfree(hid->ff_private);
+               return -1;
+       }
+
+       usb_fill_control_urb(private->urbffout, hid->dev, 0,
+                            (void*) &private->ffcr, private->buf, 8,
+                            hid_lg3d_ctrl_out, hid);
+       dbg("Created ff output control urb");
+
+       /* Input init */
+       hid->input.upload_effect = hid_lg3d_upload_effect;
+       hid->input.flush = hid_lg3d_flush;
+       set_bit(FF_CONSTANT, hid->input.ffbit);
+       set_bit(EV_FF, hid->input.evbit);
+       hid->input.ff_effects_max = N_EFFECTS;
+
+       printk(KERN_INFO "Force feedback for Logitech *3D devices by Johann Deneux 
+<[EMAIL PROTECTED]>\n");
+
+       /* Start the update task */
+       private->timer.expires = RUN_AT(PERIOD);
+       add_timer(&private->timer);  /*TODO: only run the timer when at least
+                                      one effect is playing */
+
+       return 0;
+}
+
+static void hid_lg3d_exit(struct hid_device* hid)
+{
+       struct lg3d_device *lg3d = hid->ff_private;
+       unsigned long flags;
+       
+       spin_lock_irqsave(&lg3d->lock, flags);
+       set_bit(DEVICE_CLOSING, lg3d->flags);
+       spin_unlock_irqrestore(&lg3d->lock, flags);
+
+       del_timer_sync(&lg3d->timer);
+
+       if (lg3d->urbffout) {
+               usb_unlink_urb(lg3d->urbffout);
+               usb_free_urb(lg3d->urbffout);
+       }
+
+       kfree(lg3d);
+}
+
+static int hid_lg3d_event(struct hid_device *hid, struct input_dev* input,
+                         unsigned int type, unsigned int code, int value)
+{
+       struct lg3d_device *lg3d = hid->ff_private;
+       struct lg3d_effect *effect = lg3d->effects + code;
+       unsigned long flags;
+
+       if (type != EV_FF)                return -EINVAL;
+               if (!CHECK_OWNERSHIP(code, lg3d)) return -EACCES;
+       if (value < 0)                    return -EINVAL;
+
+       spin_lock_irqsave(&lg3d->lock, flags);
+       
+       if (value > 0) {
+               if (test_bit(EFFECT_STARTED, effect->flags)) {
+                       spin_unlock_irqrestore(&lg3d->lock, flags);
+                       return -EBUSY;
+               }
+               if (test_bit(EFFECT_PLAYING, effect->flags)) {
+                       spin_unlock_irqrestore(&lg3d->lock, flags);
+                       return -EBUSY;
+               }
+
+               effect->count = value;
+
+               if (effect->effect.replay.delay) {
+                       set_bit(EFFECT_STARTED, effect->flags);
+               } else {
+                       set_bit(EFFECT_PLAYING, effect->flags);
+               }
+               effect->started_at = jiffies;
+       }
+       else { /* value == 0 */
+               clear_bit(EFFECT_STARTED, effect->flags);
+               clear_bit(EFFECT_PLAYING, effect->flags);
+       }
+
+       spin_unlock_irqrestore(&lg3d->lock, flags);
+
+       return 0;
+}
+
+/* Erase all effects this process owns */
+static int hid_lg3d_flush(struct input_dev *dev, struct file *file)
+{
+       struct hid_device *hid = dev->private;
+       struct lg3d_device *lg3d = hid->ff_private;
+       int i;
+
+       for (i=0; i<dev->ff_effects_max; ++i) {
+
+               /*NOTE: no need to lock here. The only times EFFECT_USED is
+                 modified is when effects are uploaded or when an effect is
+                 erased. But a process cannot close its dev/input/eventX fd
+                 and perform ioctls on the same fd all at the same time */
+               if ( current->pid == lg3d->effects[i].owner
+                    && test_bit(EFFECT_USED, lg3d->effects[i].flags)) {
+                       
+                       if (hid_lg3d_erase(dev, i))
+                               warn("erase effect %d failed", i);
+               }
+
+       }
+
+       return 0;
+}
+
+static int hid_lg3d_erase(struct input_dev *dev, int id)
+{
+       struct hid_device *hid = dev->private;
+       struct lg3d_device *lg3d = hid->ff_private;
+       unsigned long flags;
+
+       if (!CHECK_OWNERSHIP(id, lg3d)) return -EACCES;
+
+       spin_lock_irqsave(&lg3d->lock, flags);
+       lg3d->effects[id].flags[0] = 0;
+       spin_unlock_irqrestore(&lg3d->lock, flags);
+
+       return 0;
+}
+
+static int hid_lg3d_upload_effect(struct input_dev* input,
+                                 struct ff_effect* effect)
+{
+       struct hid_device *hid = input->private;
+       struct lg3d_device *lg3d = hid->ff_private;
+       struct lg3d_effect new;
+       int id;
+       unsigned long flags;
+       
+       dbg("ioctl upload");
+
+       if (!test_bit(effect->type, input->ffbit)) return -EINVAL;
+
+       if (effect->type != FF_CONSTANT) return -EINVAL;
+
+       spin_lock_irqsave(&lg3d->lock, flags);
+
+       if (effect->id == -1) {
+               int i;
+
+               for (i=0; i<N_EFFECTS && test_bit(EFFECT_USED, 
+lg3d->effects[i].flags); ++i);
+               if (i >= N_EFFECTS) {
+                       spin_unlock_irqrestore(&lg3d->lock, flags);
+                       return -ENOSPC;
+               }
+
+               effect->id = i;
+               lg3d->effects[i].owner = current->pid;
+               lg3d->effects[i].flags[0] = 0;
+               set_bit(EFFECT_USED, lg3d->effects[i].flags);
+       }
+       else if (!CHECK_OWNERSHIP(effect->id, lg3d)) {
+               spin_unlock_irqrestore(&lg3d->lock, flags);
+               return -EACCES;
+       }
+
+       id = effect->id;
+       new = lg3d->effects[id];
+
+       new.effect = *effect;
+       new.effect.replay = effect->replay;
+
+       if (test_bit(EFFECT_STARTED, lg3d->effects[id].flags)
+           || test_bit(EFFECT_STARTED, lg3d->effects[id].flags)) {
+
+               /* Changing replay parameters is not allowed (for the time
+                  being) */
+               if (new.effect.replay.delay != lg3d->effects[id].effect.replay.delay
+                   || new.effect.replay.length != 
+lg3d->effects[id].effect.replay.length) {
+                       spin_unlock_irqrestore(&lg3d->lock, flags);
+                       return -ENOSYS;
+               }
+
+               lg3d->effects[id] = new;
+
+       } else {
+               lg3d->effects[id] = new;
+       }
+
+       spin_unlock_irqrestore(&lg3d->lock, flags);
+       return 0;
+}
+
+static void hid_lg3d_ctrl_out(struct urb *urb)
+{
+       struct hid_device *hid = urb->context;
+       struct lg3d_device *lg3d = hid->ff_private;
+       unsigned long flags;
+
+       spin_lock_irqsave(&lg3d->lock, flags);
+
+       if (urb->status)
+               warn("hid_irq_ffout status %d received", urb->status);
+       clear_bit(DEVICE_USB_XMIT, lg3d->flags);
+       dbg("xmit = 0");
+
+       spin_unlock_irqrestore(&lg3d->lock, flags);
+}
+
+static void hid_lg3d_timer(unsigned long timer_data)
+{
+       struct lg3d_device *lg3d = (struct lg3d_device*)timer_data;
+       struct hid_device *hid = lg3d->hid;
+       unsigned long flags;
+       int x, y;
+       int i;
+       int err;
+
+       spin_lock_irqsave(&lg3d->lock, flags);
+
+       if (test_bit(DEVICE_USB_XMIT, lg3d->flags)) {
+               if (lg3d->urbffout->status != -EINPROGRESS) {
+                       warn("xmit *not* in progress");
+               }
+               else {
+                       dbg("xmit in progress");
+               }
+
+               spin_unlock_irqrestore(&lg3d->lock, flags);
+
+               lg3d->timer.expires = RUN_AT(PERIOD);
+               add_timer(&lg3d->timer);
+
+               return;
+       }
+
+       x = 0x7f;
+       y = 0x7f;
+
+       for (i=0; i<N_EFFECTS; ++i) {
+               struct lg3d_effect* effect = lg3d->effects +i;
+
+               if (test_bit(EFFECT_PLAYING, effect->flags)) {
+
+                       if (effect->effect.type == FF_CONSTANT) {
+                               //TODO: handle envelopes
+                               int degrees = effect->effect.direction * 360 >> 16;
+                               x += fixp_mult(fixp_sin(degrees),
+                                              
+fixp_new16(effect->effect.u.constant.level));
+                               y += fixp_mult(-fixp_cos(degrees),
+                                              
+fixp_new16(effect->effect.u.constant.level));
+                       }
+
+                       /* One run of the effect is finished playing */
+                       if (time_after(jiffies,
+                                       effect->started_at
+                                       + effect->effect.replay.delay*HZ/1000
+                                       + effect->effect.replay.length*HZ/1000)) {
+                               dbg("Finished playing once");
+                               if (--effect->count <= 0) {
+                                       dbg("Stopped");
+                                       clear_bit(EFFECT_PLAYING, effect->flags);
+                               }
+                               else {
+                                       dbg("Start again");
+                                       if (effect->effect.replay.length != 0) {
+                                               clear_bit(EFFECT_PLAYING, 
+effect->flags);
+                                               set_bit(EFFECT_STARTED, effect->flags);
+                                       }
+                                       effect->started_at = jiffies;
+                               }
+                       }
+
+               } else if (test_bit(EFFECT_STARTED, lg3d->effects[i].flags)) {
+                       dbg("Started");
+                       /* Check if we should start playing the effect */
+                       if (time_after(jiffies,
+                                       lg3d->effects[i].started_at
+                                       + 
+lg3d->effects[i].effect.replay.delay*HZ/1000)) {
+                               dbg("Now playing");
+                               clear_bit(EFFECT_STARTED, lg3d->effects[i].flags);
+                               set_bit(EFFECT_PLAYING, lg3d->effects[i].flags);
+                       }
+               }
+       }
+
+       if (x < 0) x = 0;
+       if (x > 0xff) x = 0xff;
+       if (y < 0) y = 0;
+       if (y > 0xff) y = 0xff;
+       
+       lg3d->urbffout->pipe = usb_sndctrlpipe(hid->dev, 0);
+       lg3d->ffcr.bRequestType = USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE;
+       lg3d->urbffout->transfer_buffer_length = lg3d->ffcr.wLength = 8;
+       lg3d->ffcr.bRequest = 9;
+       lg3d->ffcr.wValue = 0x0200;    /*NOTE: Potential problem with 
+                                        little/big endian */
+       lg3d->ffcr.wIndex = 0;
+       
+       lg3d->urbffout->dev = hid->dev;
+       
+       lg3d->buf[0] = 0x51;
+       lg3d->buf[1] = 0x08;
+       lg3d->buf[2] = x;
+       lg3d->buf[3] = y;
+
+       if ((err=usb_submit_urb(lg3d->urbffout, GFP_ATOMIC)))
+               warn("usb_submit_urb returned %d", err);
+       else
+               set_bit(DEVICE_USB_XMIT, lg3d->flags);
+
+       if (!test_bit(DEVICE_CLOSING, lg3d->flags)) {
+               lg3d->timer.expires = RUN_AT(PERIOD);
+               add_timer(&lg3d->timer);
+       }
+
+       spin_unlock_irqrestore(&lg3d->lock, flags);
+}
diff -Nru a/drivers/usb/input/hid-lgff.c b/drivers/usb/input/hid-lgff.c
--- /dev/null   Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/input/hid-lgff.c      Fri Jul 12 11:45:38 2002
@@ -0,0 +1,495 @@
+/*
+ * $$
+ *
+ * Force feedback support for hid-compliant for some of the devices from
+ * Logitech, namely:
+ * - WingMan Cordless RumblePad
+ *
+ *  Copyright (c) 2002 Johann Deneux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so by
+ * e-mail - mail your message to <[EMAIL PROTECTED]>
+ */
+
+#include <linux/input.h>
+#include <linux/sched.h>
+
+#define DEBUG
+#include <linux/usb.h>
+
+#include <linux/circ_buf.h>
+
+#include "hid.h"
+
+#define RUN_AT(t) (jiffies + (t))
+
+/* Transmition state */
+#define XMIT_RUNNING 0
+
+/* Effect status */
+#define EFFECT_STARTED 0     /* Effect is going to play after some time
+                               (ff_replay.delay) */
+#define EFFECT_PLAYING 1     /* Effect is being played */
+#define EFFECT_USED    2
+
+/* Check that the current process can access an effect */
+#define CHECK_OWNERSHIP(effect) (current->pid == 0 \
+        || effect.owner == current->pid)
+
+/* **************************************************************************/
+/* Implements the protocol used by the Logitech WingMan Cordless RumblePad */
+/* **************************************************************************/
+
+#define LGFF_CHECK_OWNERSHIP(i, l) \
+        (i>=0 && i<LGFF_EFFECTS \
+        && test_bit(EFFECT_USED, l->effects[i].flags) \
+        && CHECK_OWNERSHIP(l->effects[i]))
+
+#define LGFF_BUFFER_SIZE 64
+#define LGFF_EFFECTS 8
+
+struct lgff_magnitudes {
+       unsigned char left;
+       unsigned char right;
+};
+
+struct lgff_effect {
+       int id;
+       struct hid_ff_logitech* lgff;
+
+       pid_t owner;
+       unsigned char left;        /* Magnitude of vibration for left motor */
+       unsigned char right;       /* Magnitude of vibration for right motor */
+       struct ff_replay replay;
+       unsigned int count;        /* Number of times to play */
+       struct timer_list timer;
+       unsigned long flags[1];
+};
+
+struct hid_ff_logitech {
+       struct hid_device* hid;
+
+       struct urb* urbffout;        /* Output URB used to send ff commands */
+       struct usb_ctrlrequest ffcr; /* ff commands use control URBs */
+       char buf[8];
+
+       spinlock_t xmit_lock;
+       unsigned int xmit_head, xmit_tail;
+       struct lgff_magnitudes xmit_data[LGFF_BUFFER_SIZE];
+       long xmit_flags[1];
+
+       struct lgff_effect effects[LGFF_EFFECTS];
+       spinlock_t lock;             /* device-level lock. Having locks on
+                                       a per-effect basis could be nice, but
+                                       isn't really necessary */
+};
+
+static void hid_lgff_ctrl_out(struct urb *urb);
+static void hid_lgff_exit(struct hid_device* hid);
+static int hid_lgff_event(struct hid_device *hid, struct input_dev *input,
+                         unsigned int type, unsigned int code, int value);
+static void hid_lgff_make_rumble(struct hid_device* hid);
+
+static int hid_lgff_flush(struct input_dev *input, struct file *file);
+static int hid_lgff_upload_effect(struct input_dev *input,
+                                 struct ff_effect *effect);
+static int hid_lgff_erase(struct input_dev *input, int id);
+static void hid_lgff_ctrl_playback(struct hid_device* hid, struct lgff_effect*,
+                                  int play);
+static void hid_lgff_timer(unsigned long timer_data);
+
+
+int hid_lgff_init(struct hid_device* hid)
+{
+       struct hid_ff_logitech *private;
+       int i;
+
+       /* Private data */
+       private = kmalloc(sizeof(struct hid_ff_logitech), GFP_KERNEL);
+       if (!private) return -1;
+
+       memset(private, 0, sizeof(struct hid_ff_logitech));
+
+       hid->ff_private = private;
+
+       private->hid = hid;
+       spin_lock_init(&private->lock);
+       spin_lock_init(&private->xmit_lock);
+
+       private->buf[0] = 0x03;
+       private->buf[1] = 0x42;
+
+       for (i=0; i<LGFF_EFFECTS; ++i) {
+               struct lgff_effect* effect = &private->effects[i];
+               struct timer_list* timer = &effect->timer;
+
+               init_timer(timer);
+               effect->id = i;
+               effect->lgff = private;
+               timer->data = (unsigned long)effect;
+               timer->function = hid_lgff_timer;
+       }
+
+       /* Event and exit callbacks */
+       hid->ff_exit = hid_lgff_exit;
+       hid->ff_event = hid_lgff_event;
+
+       /* USB init */
+       if (!(private->urbffout = usb_alloc_urb(0, GFP_KERNEL))) {
+               kfree(hid->ff_private);
+               return -1;
+       }
+
+       usb_fill_control_urb(private->urbffout, hid->dev, 0,
+                            (void*) &private->ffcr, private->buf, 8,
+                            hid_lgff_ctrl_out, hid);
+       dbg("Created ff output control urb");
+
+       /* Input init */
+       hid->input.upload_effect = hid_lgff_upload_effect;
+       hid->input.flush = hid_lgff_flush;
+       set_bit(FF_RUMBLE, hid->input.ffbit);
+       set_bit(EV_FF, hid->input.evbit);
+       hid->input.ff_effects_max = LGFF_EFFECTS;
+
+       printk(KERN_INFO "Force feedback for Logitech rumble devices by Johann Deneux 
+<[EMAIL PROTECTED]>\n");
+
+       return 0;
+}
+
+static void hid_lgff_exit(struct hid_device* hid)
+{
+       struct hid_ff_logitech *lgff = hid->ff_private;
+
+       if (lgff->urbffout) {
+               usb_unlink_urb(lgff->urbffout);
+               usb_free_urb(lgff->urbffout);
+       }
+}
+
+static int hid_lgff_event(struct hid_device *hid, struct input_dev* input,
+                         unsigned int type, unsigned int code, int value)
+{
+       struct hid_ff_logitech *lgff = hid->ff_private;
+       struct lgff_effect *effect = lgff->effects + code;
+       unsigned long flags;
+
+       if (type != EV_FF)                     return -EINVAL;
+               if (!LGFF_CHECK_OWNERSHIP(code, lgff)) return -EACCES;
+       if (value < 0)                         return -EINVAL;
+
+       spin_lock_irqsave(&lgff->lock, flags);
+       
+       if (value > 0) {
+               if (test_bit(EFFECT_STARTED, effect->flags)) {
+                       spin_unlock_irqrestore(&lgff->lock, flags);
+                       return -EBUSY;
+               }
+               if (test_bit(EFFECT_PLAYING, effect->flags)) {
+                       spin_unlock_irqrestore(&lgff->lock, flags);
+                       return -EBUSY;
+               }
+
+               effect->count = value;
+
+               if (effect->replay.delay) {
+                       set_bit(EFFECT_STARTED, effect->flags);
+                       effect->timer.expires = RUN_AT(effect->replay.delay * HZ / 
+1000);
+               } else {
+                       hid_lgff_ctrl_playback(hid, effect, value);
+                       effect->timer.expires = RUN_AT(effect->replay.length * HZ / 
+1000);
+               }
+
+               add_timer(&effect->timer);
+       }
+       else { /* value == 0 */
+               if (test_and_clear_bit(EFFECT_STARTED, effect->flags)) {
+                       del_timer(&effect->timer);
+
+               } else if (test_and_clear_bit(EFFECT_PLAYING, effect->flags)) {
+                       del_timer(&effect->timer);
+                       hid_lgff_ctrl_playback(hid, effect, value);
+               }
+
+               if (test_bit(EFFECT_PLAYING, effect->flags))
+                       warn("Effect %d still playing", code);
+       }
+
+       spin_unlock_irqrestore(&lgff->lock, flags);
+
+       return 0;
+
+}
+
+/* Erase all effects this process owns */
+static int hid_lgff_flush(struct input_dev *dev, struct file *file)
+{
+       struct hid_device *hid = dev->private;
+       struct hid_ff_logitech *lgff = hid->ff_private;
+       int i;
+
+       for (i=0; i<dev->ff_effects_max; ++i) {
+
+               /*NOTE: no need to lock here. The only times EFFECT_USED is
+                 modified is when effects are uploaded or when an effect is
+                 erased. But a process cannot close its dev/input/eventX fd
+                 and perform ioctls on the same fd all at the same time */
+               if ( current->pid == lgff->effects[i].owner
+                    && test_bit(EFFECT_USED, lgff->effects[i].flags)) {
+                       
+                       if (hid_lgff_erase(dev, i))
+                               warn("erase effect %d failed", i);
+               }
+
+       }
+
+       return 0;
+}
+
+static int hid_lgff_erase(struct input_dev *dev, int id)
+{
+       struct hid_device *hid = dev->private;
+       struct hid_ff_logitech *lgff = hid->ff_private;
+       unsigned long flags;
+
+       if (!LGFF_CHECK_OWNERSHIP(id, lgff)) return -EACCES;
+
+       spin_lock_irqsave(&lgff->lock, flags);
+       hid_lgff_ctrl_playback(hid, lgff->effects + id, 0);
+       lgff->effects[id].flags[0] = 0;
+       spin_unlock_irqrestore(&lgff->lock, flags);
+
+       return 0;
+}
+
+static int hid_lgff_upload_effect(struct input_dev* input,
+                                 struct ff_effect* effect)
+{
+       struct hid_device *hid = input->private;
+       struct hid_ff_logitech *lgff = hid->ff_private;
+       struct lgff_effect new;
+       int id;
+       unsigned long flags;
+       
+       dbg("ioctl rumble");
+
+       if (!test_bit(effect->type, input->ffbit)) return -EINVAL;
+
+       if (effect->type != FF_RUMBLE) return -EINVAL;
+
+       spin_lock_irqsave(&lgff->lock, flags);
+
+       if (effect->id == -1) {
+               int i;
+
+               for (i=0; i<LGFF_EFFECTS && test_bit(EFFECT_USED, 
+lgff->effects[i].flags); ++i);
+               if (i >= LGFF_EFFECTS) {
+                       spin_unlock_irqrestore(&lgff->lock, flags);
+                       return -ENOSPC;
+               }
+
+               effect->id = i;
+               lgff->effects[i].owner = current->pid;
+               lgff->effects[i].flags[0] = 0;
+               set_bit(EFFECT_USED, lgff->effects[i].flags);
+       }
+       else if (!LGFF_CHECK_OWNERSHIP(effect->id, lgff)) {
+               spin_unlock_irqrestore(&lgff->lock, flags);
+               return -EACCES;
+       }
+
+       id = effect->id;
+       new = lgff->effects[id];
+
+       new.right = effect->u.rumble.strong_magnitude >> 9;
+       new.left = effect->u.rumble.weak_magnitude >> 9;
+       new.replay = effect->replay;
+
+       /* If we updated an effect that was being played, we need to remake
+          the rumble effect */
+       if (test_bit(EFFECT_STARTED, lgff->effects[id].flags)
+           || test_bit(EFFECT_STARTED, lgff->effects[id].flags)) {
+
+               /* Changing replay parameters is not allowed (for the time
+                  being) */
+               if (new.replay.delay != lgff->effects[id].replay.delay
+                   || new.replay.length != lgff->effects[id].replay.length) {
+                       spin_unlock_irqrestore(&lgff->lock, flags);
+                       return -ENOSYS;
+               }
+
+               lgff->effects[id] = new;
+               hid_lgff_make_rumble(hid);
+
+       } else {
+               lgff->effects[id] = new;
+       }
+
+       spin_unlock_irqrestore(&lgff->lock, flags);
+       return 0;
+}
+
+static void hid_lgff_xmit(struct hid_device* hid)
+{
+       struct hid_ff_logitech *lgff = hid->ff_private;
+       int err;
+       int tail;
+       unsigned long flags;
+
+       spin_lock_irqsave(&lgff->xmit_lock, flags);
+
+       tail = lgff->xmit_tail;
+       if (lgff->xmit_head == tail) {
+               clear_bit(XMIT_RUNNING, lgff->xmit_flags);
+               spin_unlock_irqrestore(&lgff->xmit_lock, flags);
+               return;
+       }
+       lgff->buf[3] = lgff->xmit_data[tail].left;
+       lgff->buf[4] = lgff->xmit_data[tail].right;
+       tail++; tail &= LGFF_BUFFER_SIZE -1;
+       lgff->xmit_tail = tail;
+
+       spin_unlock_irqrestore(&lgff->xmit_lock, flags);
+
+       lgff->urbffout->pipe = usb_sndctrlpipe(hid->dev, 0);
+       lgff->ffcr.bRequestType = USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE;
+       lgff->urbffout->transfer_buffer_length = lgff->ffcr.wLength = 8;
+       lgff->ffcr.bRequest = 9;
+       lgff->ffcr.wValue = 0x0203;    /*NOTE: Potential problem with 
+                                        little/big endian */
+       lgff->ffcr.wIndex = 0;
+       
+       lgff->urbffout->dev = hid->dev;
+       
+       if ((err=usb_submit_urb(lgff->urbffout, GFP_ATOMIC)))
+               warn("usb_submit_urb returned %d", err);
+}
+
+static void hid_lgff_make_rumble(struct hid_device* hid)
+{
+       struct hid_ff_logitech *lgff = hid->ff_private;
+       int left = 0, right = 0;
+       int i;
+       int head, tail;
+       unsigned long flags;
+
+       for (i=0; i<LGFF_EFFECTS; ++i) {
+               if (test_bit(EFFECT_USED, lgff->effects[i].flags)
+                   && test_bit(EFFECT_PLAYING, lgff->effects[i].flags)) {
+                       left += lgff->effects[i].left;
+                       right += lgff->effects[i].right;
+               }
+       }
+
+       spin_lock_irqsave(&lgff->xmit_lock, flags);
+
+       head = lgff->xmit_head;
+       tail = lgff->xmit_tail; 
+
+       if (CIRC_SPACE(head, tail, LGFF_BUFFER_SIZE) < 1) {
+               warn("not enough space in xmit buffer to send new packet");
+               spin_unlock_irqrestore(&lgff->xmit_lock, flags);
+               return;
+       }
+
+       lgff->xmit_data[head].left = left > 0x7f ? 0x7f : left;
+       lgff->xmit_data[head].right = right > 0x7f ? 0x7f : right;
+       head++; head &= LGFF_BUFFER_SIZE -1;
+       lgff->xmit_head = head;
+
+       if (test_and_set_bit(XMIT_RUNNING, lgff->xmit_flags))
+               spin_unlock_irqrestore(&lgff->xmit_lock, flags);
+       else {
+               spin_unlock_irqrestore(&lgff->xmit_lock, flags);
+               hid_lgff_xmit(hid);
+       }
+}
+
+static void hid_lgff_ctrl_out(struct urb *urb)
+{
+       struct hid_device *hid = urb->context;
+
+       if (urb->status)
+               warn("hid_irq_ffout status %d received", urb->status);
+
+       hid_lgff_xmit(hid);
+}
+
+/* Lock must be held by caller */
+static void hid_lgff_ctrl_playback(struct hid_device *hid,
+                                  struct lgff_effect *effect, int play)
+{
+       if (play) {
+               set_bit(EFFECT_PLAYING, effect->flags);
+               hid_lgff_make_rumble(hid);
+
+       } else {
+               clear_bit(EFFECT_PLAYING, effect->flags);
+               hid_lgff_make_rumble(hid);
+       }
+}
+
+static void hid_lgff_timer(unsigned long timer_data)
+{
+       struct lgff_effect *effect = (struct lgff_effect*) timer_data;
+       struct hid_ff_logitech* lgff = effect->lgff;
+       int id = effect->id;
+
+       unsigned long flags;
+
+       dbg("in hid_lgff_timer");
+
+       if (id < 0 || id >= LGFF_EFFECTS) {
+               warn("Bad effect id %d", id);
+               return;
+       }
+
+       effect = lgff->effects + id;
+
+       spin_lock_irqsave(&lgff->lock, flags);
+
+       if (!test_bit(EFFECT_USED, effect->flags)) {
+               warn("Unused effect id %d", id);
+
+       } else if (test_bit(EFFECT_STARTED, effect->flags)) {
+               clear_bit(EFFECT_STARTED, effect->flags);
+               set_bit(EFFECT_PLAYING, effect->flags);
+               hid_lgff_ctrl_playback(lgff->hid, effect, 1);
+               effect->timer.expires = RUN_AT(effect->replay.length * HZ / 1000);
+               add_timer(&effect->timer);
+
+               dbg("Effect %d starts playing", id);
+       } else if (test_bit(EFFECT_PLAYING, effect->flags)) {
+               clear_bit(EFFECT_PLAYING, effect->flags);
+               hid_lgff_ctrl_playback(lgff->hid, effect, 0);
+               if (--effect->count > 0) {
+                       /*TODO: check that replay.delay is non-null */
+                       set_bit(EFFECT_STARTED, effect->flags);
+                       effect->timer.expires = RUN_AT(effect->replay.delay * HZ / 
+1000);
+                       add_timer(&effect->timer);
+                       dbg("Effect %d restarted", id);
+               } else {
+                       dbg("Effect %d stopped", id);
+               }
+       } else {
+               warn("Effect %d is not started nor playing", id);
+       }
+
+       spin_unlock_irqrestore(&lgff->lock, flags);
+}
diff -Nru a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h
--- a/drivers/usb/input/hid.h   Fri Jul 12 11:45:38 2002
+++ b/drivers/usb/input/hid.h   Fri Jul 12 11:45:38 2002
@@ -359,6 +359,11 @@
        char name[128];                                                 /* Device name 
*/
        char phys[64];                                                  /* Device 
physical location */
        char uniq[64];                                                  /* Device 
unique identifier (serial #) */
+
+       void *ff_private;                                               /* Private 
+data for the force-feedback driver */
+       void (*ff_exit)(struct hid_device*);                            /* Called by 
+hid_exit_ff(hid) */
+       int (*ff_event)(struct hid_device *hid, struct input_dev *input,
+                       unsigned int type, unsigned int code, int value);
 };
 
 #define HID_GLOBAL_STACK_SIZE 4
@@ -395,6 +400,7 @@
 #define hid_dump_input(a,b)    do { } while (0)
 #define hid_dump_device(c)     do { } while (0)
 #define hid_dump_field(a,b)    do { } while (0)
+#define resolv_usage(a)                do { } while (0)
 #endif
 
 #endif
@@ -419,3 +425,23 @@
 int hid_set_field(struct hid_field *, unsigned, __s32);
 void hid_submit_report(struct hid_device *, struct hid_report *, unsigned char dir);
 void hid_init_reports(struct hid_device *hid);
+int hid_find_report_by_usage(struct hid_device *hid, __u32 wanted_usage, struct 
+hid_report **report, int type);
+
+
+#ifdef CONFIG_HID_FF
+int hid_ff_init(struct hid_device *hid);
+#else
+static inline int hid_ff_init(struct hid_device *hid) { return -1; }
+#endif
+static inline void hid_ff_exit(struct hid_device *hid)
+{
+       if (hid->ff_exit)
+               hid->ff_exit(hid);
+}
+static inline int hid_ff_event(struct hid_device *hid, struct input_dev *input,
+                       unsigned int type, unsigned int code, int value)
+{
+       if (hid->ff_event)
+               return hid->ff_event(hid, input, type, code, value);
+       return -ENOSYS;
+}
diff -Nru a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c
--- a/drivers/usb/input/hiddev.c        Fri Jul 12 11:45:38 2002
+++ b/drivers/usb/input/hiddev.c        Fri Jul 12 11:45:38 2002
@@ -389,9 +389,7 @@
                dinfo.product = dev->descriptor.idProduct;
                dinfo.version = dev->descriptor.bcdDevice;
                dinfo.num_applications = hid->maxapplication;
-               if (copy_to_user((void *) arg, &dinfo, sizeof(dinfo)))
-                       return -EFAULT;
-               return 0;
+               return copy_to_user((void *) arg, &dinfo, sizeof(dinfo));
        }
 
        case HIDIOCGFLAG:
@@ -482,9 +480,7 @@
 
                rinfo.num_fields = report->maxfield;
 
-               if (copy_to_user((void *) arg, &rinfo, sizeof(rinfo)))
-                       return -EFAULT;
-               return 0;
+               return copy_to_user((void *) arg, &rinfo, sizeof(rinfo));
 
        case HIDIOCGFIELDINFO:
        {
@@ -516,9 +512,7 @@
                finfo.unit_exponent = field->unit_exponent;
                finfo.unit = field->unit;
 
-               if (copy_to_user((void *) arg, &finfo, sizeof(finfo)))
-                       return -EFAULT;
-               return 0;
+               return copy_to_user((void *) arg, &finfo, sizeof(finfo));
        }
 
        case HIDIOCGUCODE:
@@ -539,17 +533,39 @@
 
                uref.usage_code = field->usage[uref.usage_index].hid;
 
-               if (copy_to_user((void *) arg, &uref, sizeof(uref)))
-                       return -EFAULT;
-               return 0;
+               return copy_to_user((void *) arg, &uref, sizeof(uref));
 
        case HIDIOCGUSAGE:
+               if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
+                       return -EFAULT;
+
+               if (uref.report_id == HID_REPORT_ID_UNKNOWN) {
+                       field = hiddev_lookup_usage(hid, &uref);
+                       if (field == NULL)
+                               return -EINVAL;
+               } else {
+                       rinfo.report_type = uref.report_type;
+                       rinfo.report_id = uref.report_id;
+                       if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+                               return -EINVAL;
+
+                       if (uref.field_index >= report->maxfield)
+                               return -EINVAL;
+
+                       field = report->field[uref.field_index];
+                       if (uref.usage_index >= field->maxusage)
+                               return -EINVAL;
+               }
+
+               uref.value = field->value[uref.usage_index];
+
+               return copy_to_user((void *) arg, &uref, sizeof(uref));
+
        case HIDIOCSUSAGE:
                if (copy_from_user(&uref, (void *) arg, sizeof(uref)))
                        return -EFAULT;
 
-               if (cmd == HIDIOCSUSAGE &&
-                   uref.report_type != HID_REPORT_TYPE_OUTPUT)
+               if (uref.report_type == HID_REPORT_TYPE_INPUT)
                        return -EINVAL;
 
                if (uref.report_id == HID_REPORT_ID_UNKNOWN) {
@@ -570,14 +586,7 @@
                                return -EINVAL;
                }
 
-               if (cmd == HIDIOCGUSAGE) {
-                       uref.value = field->value[uref.usage_index];
-                       if (copy_to_user((void *) arg, &uref, sizeof(uref)))
-                               return -EFAULT;
-                       return 0;
-               } else {
-                       field->value[uref.usage_index] = uref.value;
-               }
+               field->value[uref.usage_index] = uref.value;
 
                return 0;
 
@@ -626,9 +635,9 @@
        if (i == hid->maxapplication)
                return -1;
 
-       retval = usb_register_dev (&hiddev_fops, HIDDEV_MINOR_BASE, 1, &minor);
+       retval = usb_register_dev(&hiddev_fops, HIDDEV_MINOR_BASE, 1, &minor);
        if (retval) {
-               err ("Not able to get a minor for this device.");
+               err("Not able to get a minor for this device.");
                return -1;
        }
 
diff -Nru a/drivers/usb/input/pid.c b/drivers/usb/input/pid.c
--- /dev/null   Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/input/pid.c   Fri Jul 12 11:45:38 2002
@@ -0,0 +1,330 @@
+/*
+ *  PID Force feedback support for hid devices.
+ *
+ *  Copyright (c) 2002 Rodrigo Damazio.
+ *  Portions by Johann Deneux and Bjorn Augustson
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so by
+ * e-mail - mail your message to <[EMAIL PROTECTED]>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+#include "hid.h"
+#include "pid.h"
+
+#define DEBUG
+
+MODULE_AUTHOR("Rodrigo Damazio <[EMAIL PROTECTED]>");
+MODULE_DESCRIPTION("USB PID(Physical Interface Device) Driver");
+MODULE_LICENSE("GPL");
+
+#define CHECK_OWNERSHIP(i, hid_pid)    \
+       ((i) < FF_EFFECTS_MAX && i >= 0 && \
+       test_bit(FF_PID_FLAGS_USED, &hid_pid->effects[(i)].flags) && \
+       (current->pid == 0 || \
+       (hid_pid)->effects[(i)].owner == current->pid))
+
+/* Called when a transfer is completed */
+static void hid_pid_ctrl_out(struct urb *u)
+{
+#ifdef DEBUG
+    printk("hid_pid_ctrl_out - Transfer Completed\n");
+#endif
+}
+
+static void hid_pid_exit(struct hid_device* hid)
+{
+    struct hid_ff_pid *private = hid->ff_private;
+    
+    if (private->urbffout) {
+       usb_unlink_urb(private->urbffout);
+       usb_free_urb(private->urbffout);
+    }
+}
+
+static int pid_upload_periodic(struct hid_ff_pid *pid, struct ff_effect *effect, int 
+is_update) {
+    
+    printk("Requested periodic force upload\n");
+    return 0;
+}
+
+static int pid_upload_constant(struct hid_ff_pid *pid, struct ff_effect *effect, int 
+is_update) {
+    printk("Requested constant force upload\n");
+    return 0;
+}
+
+static int pid_upload_condition(struct hid_ff_pid *pid, struct ff_effect *effect, int 
+is_update) {
+    printk("Requested Condition force upload\n");
+    return 0;
+}
+
+static int pid_upload_ramp(struct hid_ff_pid *pid, struct ff_effect *effect, int 
+is_update) {
+    printk("Request ramp force upload\n");
+    return 0;
+}
+
+static int hid_pid_event(struct hid_device *hid, struct input_dev *input,
+                         unsigned int type, unsigned int code, int value)
+{
+#ifdef DEBUG
+    printk ("PID event received: type=%d,code=%d,value=%d.\n",type,code,value);
+#endif
+
+    if (type != EV_FF)
+       return -1;
+
+    
+
+    return 0;
+}
+
+/* Lock must be held by caller */
+static void hid_pid_ctrl_playback(struct hid_device *hid,
+                                  struct hid_pid_effect *effect, int play)
+{
+       if (play) {
+               set_bit(FF_PID_FLAGS_PLAYING, &effect->flags);
+
+       } else {
+               clear_bit(FF_PID_FLAGS_PLAYING, &effect->flags);
+       }
+}
+
+
+static int hid_pid_erase(struct input_dev *dev, int id)
+{
+       struct hid_device *hid = dev->private;
+       struct hid_field* field;
+       struct hid_report* report;
+       struct hid_ff_pid *pid = hid->ff_private;
+       unsigned long flags;
+       unsigned wanted_report = HID_UP_PID | FF_PID_USAGE_BLOCK_FREE;  /*  PID Block 
+Free Report */
+       int ret;
+
+       if (!CHECK_OWNERSHIP(id, pid)) return -EACCES;
+
+       /* Find report */
+       ret =  hid_find_report_by_usage(hid, wanted_report, &report, 
+HID_OUTPUT_REPORT);
+       if(!ret) {
+               printk("Couldn't find report\n");
+               return ret;
+       }
+
+       /* Find field */
+       field = (struct hid_field *) kmalloc(sizeof(struct hid_field), GFP_KERNEL);
+       ret = hid_set_field(field, ret, pid->effects[id].device_id);
+       if(!ret) {
+               printk("Couldn't set field\n");
+               return ret;
+       }
+
+       hid_submit_report(hid, report, USB_DIR_OUT);
+
+       spin_lock_irqsave(&pid->lock, flags);
+       hid_pid_ctrl_playback(hid, pid->effects + id, 0);
+       pid->effects[id].flags = 0;
+       spin_unlock_irqrestore(&pid->lock, flags);
+
+       return ret;
+}
+
+/* Erase all effects this process owns */
+static int hid_pid_flush(struct input_dev *dev, struct file *file)
+{
+       struct hid_device *hid = dev->private;
+       struct hid_ff_pid *pid = hid->ff_private;
+       int i;
+
+       /*NOTE: no need to lock here. The only times EFFECT_USED is
+         modified is when effects are uploaded or when an effect is
+         erased. But a process cannot close its dev/input/eventX fd
+         and perform ioctls on the same fd all at the same time */
+       for (i=0; i<dev->ff_effects_max; ++i)
+               if ( current->pid == pid->effects[i].owner
+                    && test_bit(FF_PID_FLAGS_USED, &pid->effects[i].flags))
+                       if (hid_pid_erase(dev, i))
+                               warn("erase effect %d failed", i);
+
+       return 0;
+}
+
+
+static int hid_pid_upload_effect(struct input_dev *dev,
+                                 struct ff_effect *effect)
+{
+       struct hid_ff_pid* pid_private  = (struct hid_ff_pid*)(dev->private);
+       int ret;
+       int is_update;
+       int flags=0;
+
+#ifdef DEBUG
+        printk("Upload effect called: effect_type=%x\n",effect->type);
+#endif
+       /* Check this effect type is supported by this device */
+       if (!test_bit(effect->type, dev->ffbit)) {
+#ifdef DEBUG
+               printk("Invalid kind of effect requested.\n");
+#endif
+               return -EINVAL;
+       }
+
+       /*
+        * If we want to create a new effect, get a free id
+        */
+       if (effect->id == -1) {
+               int id=0;
+
+               // Spinlock so we don`t get a race condition when choosing IDs
+               spin_lock_irqsave(&pid_private->lock,flags);
+
+               while(id < FF_EFFECTS_MAX)
+                       if (!test_and_set_bit(FF_PID_FLAGS_USED, 
+&pid_private->effects[id++].flags)) 
+                           break;
+
+               if ( id == FF_EFFECTS_MAX) {
+// TEMP - We need to get ff_effects_max correctly first:  || id >= 
+dev->ff_effects_max) {
+#ifdef DEBUG
+                       printk("Not enough device memory\n");
+#endif
+                       return -ENOMEM;
+               }
+
+               effect->id = id;
+#ifdef DEBUG
+               printk("Effect ID is %d\n.",id);
+#endif
+               pid_private->effects[id].owner = current->pid;
+               pid_private->effects[id].flags = (1<<FF_PID_FLAGS_USED);
+               spin_unlock_irqrestore(&pid_private->lock,flags);
+
+               is_update = FF_PID_FALSE;
+       }
+       else {
+               /* We want to update an effect */
+               if (!CHECK_OWNERSHIP(effect->id, pid_private)) return -EACCES;
+
+               /* Parameter type cannot be updated */
+               if (effect->type != pid_private->effects[effect->id].effect.type)
+                       return -EINVAL;
+
+               /* Check the effect is not already being updated */
+               if (test_bit(FF_PID_FLAGS_UPDATING, 
+&pid_private->effects[effect->id].flags)) {
+                       return -EAGAIN;
+               }
+
+               is_update = FF_PID_TRUE;
+       }
+
+       /*
+        * Upload the effect
+        */
+       switch (effect->type) {
+               case FF_PERIODIC:
+                       ret = pid_upload_periodic(pid_private, effect, is_update);
+                       break;
+
+               case FF_CONSTANT:
+                       ret = pid_upload_constant(pid_private, effect, is_update);
+                       break;
+
+               case FF_SPRING:
+               case FF_FRICTION:
+               case FF_DAMPER:
+               case FF_INERTIA:
+                       ret = pid_upload_condition(pid_private, effect, is_update);
+                       break;
+
+               case FF_RAMP:
+                       ret = pid_upload_ramp(pid_private, effect, is_update);
+                       break;
+
+               default:
+#ifdef DEBUG
+                       printk("Invalid type of effect requested - %x.\n", 
+effect->type);
+#endif
+                       return -EINVAL;
+       }
+       /* If a packet was sent, forbid new updates until we are notified
+        * that the packet was updated
+        */
+       if (ret == 0)
+               set_bit(FF_PID_FLAGS_UPDATING, 
+&pid_private->effects[effect->id].flags);
+       pid_private->effects[effect->id].effect = *effect;
+       return ret;
+}
+
+int hid_pid_init(struct hid_device *hid)
+{
+    struct hid_ff_pid *private;
+    
+    private = hid->ff_private = kmalloc(sizeof(struct hid_ff_pid), GFP_KERNEL);
+    if (!private) return -1;
+    
+    memset(private,0,sizeof(struct hid_ff_pid));
+
+    hid->ff_private = private; /* 'cause memset can move the block away */
+
+    private->hid = hid;
+    
+    hid->ff_exit = hid_pid_exit;
+    hid->ff_event = hid_pid_event;
+    
+    /* Open output URB */
+    if (!(private->urbffout = usb_alloc_urb(0, GFP_KERNEL))) {
+       kfree(private);
+       return -1;
+    }
+
+    usb_fill_control_urb(private->urbffout, hid->dev,0,(void *) 
+&private->ffcr,private->ctrl_buffer,8,hid_pid_ctrl_out,hid);
+    hid->input.upload_effect = hid_pid_upload_effect;
+    hid->input.flush = hid_pid_flush;
+    hid->input.ff_effects_max = 8;  // A random default
+    set_bit(EV_FF, hid->input.evbit);
+    set_bit(EV_FF_STATUS, hid->input.evbit);
+
+    spin_lock_init(&private->lock);
+
+    printk(KERN_INFO "Force feedback driver for PID devices by Rodrigo Damazio 
+<[EMAIL PROTECTED]>.\n");
+    
+    return 0;    
+}
+
+static int __init hid_pid_modinit(void)
+{
+    return 0;
+}
+
+static void __exit hid_pid_modexit(void)
+{
+
+}
+
+module_init(hid_pid_modinit);
+module_exit(hid_pid_modexit);
+
+
diff -Nru a/drivers/usb/input/pid.h b/drivers/usb/input/pid.h
--- /dev/null   Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/input/pid.h   Fri Jul 12 11:45:38 2002
@@ -0,0 +1,62 @@
+/*
+ *  PID Force feedback support for hid devices.
+ *
+ *  Copyright (c) 2002 Rodrigo Damazio.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so by
+ * e-mail - mail your message to <[EMAIL PROTECTED]>
+ */
+
+#define FF_EFFECTS_MAX 64
+
+#define FF_PID_FLAGS_USED      1  /* If the effect exists */
+#define FF_PID_FLAGS_UPDATING  2  /* If the effect is being updated */
+#define FF_PID_FLAGS_PLAYING   3  /* If the effect is currently being played */
+
+#define FF_PID_FALSE   0
+#define FF_PID_TRUE    1
+
+struct hid_pid_effect {
+    unsigned long flags;
+    pid_t owner;
+    unsigned int device_id;  // The device-assigned ID
+    struct ff_effect effect;
+};
+
+struct hid_ff_pid {
+    struct hid_device *hid;
+    unsigned long int gain;
+
+    struct urb *urbffout;
+    struct usb_ctrlrequest ffcr;
+    spinlock_t lock;
+
+    char ctrl_buffer[8];
+
+    struct hid_pid_effect effects[FF_EFFECTS_MAX];
+};
+
+/*
+ * Constants from the PID usage table (still far from complete)
+ */
+
+#define FF_PID_USAGE_BLOCK_LOAD        0x89UL
+#define FF_PID_USAGE_BLOCK_FREE                0x90UL
+#define FF_PID_USAGE_NEW_EFFECT        0xABUL
+#define FF_PID_USAGE_POOL_REPORT       0x7FUL
diff -Nru a/drivers/usb/input/powermate.c b/drivers/usb/input/powermate.c
--- /dev/null   Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/input/powermate.c     Fri Jul 12 11:45:38 2002
@@ -0,0 +1,360 @@
+/*
+ * A driver for the Griffin Technology, Inc. "PowerMate" USB controller dial.
+ *
+ * v1.0, (c)2002 William R Sowerbutts <[EMAIL PROTECTED]>
+ *
+ * This device is a stainless steel knob which connects over USB. It can measure
+ * clockwise and anticlockwise rotation. The dial also acts as a pushbutton with
+ * a spring for automatic release. The base contains a pair of LEDs which illuminate
+ * the translucent base. It rotates without limit and reports its relative rotation
+ * back to the host when polled by the USB controller.
+ *
+ * Testing with the knob I have has shown that it measures approximately 94 "clicks"
+ * for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was 
+ * a variable speed cordless electric drill) has shown that the device can measure
+ * speeds of up to 7 clicks either clockwise or anticlockwise between pollings from
+ * the host. If it counts more than 7 clicks before it is polled, it will wrap back
+ * to zero and start counting again. This was at quite high speed, however, almost
+ * certainly faster than the human hand could turn it.
+ *
+ * The device's microcontroller can be programmed to set the LED to either a constant
+ * intensity, or to a rhythmic pulsing. Several patterns and speeds are available.
+ *
+ * Griffin were very happy to provide documentation and free hardware for development.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+
+#define POWERMATE_VENDOR       0x077d  /* Griffin Technology, Inc. */
+#define POWERMATE_PRODUCT_NEW  0x0410  /* Griffin PowerMate */
+#define POWERMATE_PRODUCT_OLD  0x04AA  /* Griffin soundKnob */
+
+#define CONTOUR_VENDOR         0x05f3  /* Contour Design, Inc. */
+#define CONTOUR_JOG            0x0240  /* Jog and Shuttle */
+
+/* these are the command codes we send to the device */
+#define SET_STATIC_BRIGHTNESS  0x01
+#define SET_PULSE_ASLEEP       0x02
+#define SET_PULSE_AWAKE        0x03
+#define SET_PULSE_MODE         0x04
+
+/* these refer to bits in the powermate_device's requires_update field. */
+#define UPDATE_STATIC_BRIGHTNESS (1<<0)
+#define UPDATE_PULSE_ASLEEP      (1<<1)
+#define UPDATE_PULSE_AWAKE       (1<<2)
+#define UPDATE_PULSE_MODE        (1<<3)
+
+#define POWERMATE_PAYLOAD_SIZE 3
+struct powermate_device {
+       signed char data[POWERMATE_PAYLOAD_SIZE];
+       struct urb irq, config;
+       struct usb_ctrlrequest configcr;
+       struct usb_device *udev;
+       struct input_dev input;
+       struct semaphore lock;
+       int static_brightness;
+       int pulse_speed;
+       int pulse_table;
+       int pulse_asleep;
+       int pulse_awake;
+       int requires_update; // physical settings which are out of sync
+       char phys[64];
+};
+
+static char pm_name_powermate[] = "Griffin PowerMate";
+static char pm_name_soundknob[] = "Griffin SoundKnob";
+
+static void powermate_config_complete(struct urb *urb); /* forward declararation of 
+callback */
+
+/* Callback for data arriving from the PowerMate over the USB interrupt pipe */
+static void powermate_irq(struct urb *urb)
+{
+       struct powermate_device *pm = urb->context;
+
+       if(urb->status)
+               return;
+
+       /* handle updates to device state */
+       input_report_key(&pm->input, BTN_0, pm->data[0] & 0x01);
+       input_report_rel(&pm->input, REL_DIAL, pm->data[1]);
+}
+
+/* Decide if we need to issue a control message and do so. Must be called with 
+pm->lock down */
+static void powermate_sync_state(struct powermate_device *pm)
+{
+       if(pm->requires_update == 0) 
+               return; /* no updates are required */
+       if(pm->config.status == -EINPROGRESS) 
+               return; /* an update is already in progress; it'll issue this update 
+when it completes */
+
+       if(pm->requires_update & UPDATE_STATIC_BRIGHTNESS){
+               pm->configcr.wValue = cpu_to_le16( SET_STATIC_BRIGHTNESS );
+               pm->configcr.wIndex = cpu_to_le16( pm->static_brightness );
+               pm->requires_update &= ~UPDATE_STATIC_BRIGHTNESS;
+       }else if(pm->requires_update & UPDATE_PULSE_ASLEEP){
+               pm->configcr.wValue = cpu_to_le16( SET_PULSE_ASLEEP );
+               pm->configcr.wIndex = cpu_to_le16( pm->pulse_asleep ? 1 : 0 );
+               pm->requires_update &= ~UPDATE_PULSE_ASLEEP;
+       }else if(pm->requires_update & UPDATE_PULSE_AWAKE){
+               pm->configcr.wValue = cpu_to_le16( SET_PULSE_AWAKE );
+               pm->configcr.wIndex = cpu_to_le16( pm->pulse_awake ? 1 : 0 );
+               pm->requires_update &= ~UPDATE_PULSE_AWAKE;
+       }else if(pm->requires_update & UPDATE_PULSE_MODE){
+               int op, arg;
+               /* the powermate takes an operation and an argument for its pulse 
+algorithm.
+                  the operation can be:
+                  0: divide the speed
+                  1: pulse at normal speed
+                  2: multiply the speed
+                  the argument only has an effect for operations 0 and 2, and ranges 
+between
+                  1 (least effect) to 255 (maximum effect).
+       
+                  thus, several states are equivalent and are coalesced into one 
+state.
+
+                  we map this onto a range from 0 to 510, with:
+                  0 -- 254    -- use divide (0 = slowest)
+                  255         -- use normal speed
+                  256 -- 510  -- use multiple (510 = fastest).
+
+                  Only values of 'arg' quite close to 255 are particularly 
+useful/spectacular.
+               */    
+               if(pm->pulse_speed < 255){
+                       op = 0;                   // divide
+                       arg = 255 - pm->pulse_speed;
+               }else if(pm->pulse_speed > 255){
+                       op = 2;                   // multiply
+                       arg = pm->pulse_speed - 255;
+               }else{
+                       op = 1;                   // normal speed
+                       arg = 0;                  // can be any value
+               }
+               pm->configcr.wValue = cpu_to_le16( (pm->pulse_table << 8) | 
+SET_PULSE_MODE );
+               pm->configcr.wIndex = cpu_to_le16( (arg << 8) | op );
+               pm->requires_update &= ~UPDATE_PULSE_MODE;
+       }else{
+               printk(KERN_ERR "powermate: unknown update required");
+               pm->requires_update = 0; /* fudge the bug */
+               return;
+       }
+
+/*     printk("powermate: %04x %04x\n", pm->configcr.wValue, pm->configcr.wIndex); */
+
+       pm->config.dev = pm->udev; /* is this necessary? */
+       pm->configcr.bRequestType = 0x41; /* vendor request */
+       pm->configcr.bRequest = 0x01;
+       pm->configcr.wLength = 0;
+
+        FILL_CONTROL_URB(&pm->config, pm->udev, usb_sndctrlpipe(pm->udev, 0), 
+                        (void*)&pm->configcr, 0, 0, powermate_config_complete, pm);   
+ 
+
+       if(usb_submit_urb(&pm->config, GFP_ATOMIC))
+               printk(KERN_ERR "powermate: usb_submit_urb(config) failed");
+}
+
+/* Called when our asynchronous control message completes. We may need to issue 
+another immediately */
+static void powermate_config_complete(struct urb *urb)
+{
+       struct powermate_device *pm = urb->context;
+
+       if(urb->status)
+               printk(KERN_ERR "powermate: config urb returned %d\n", urb->status);
+       
+       down(&pm->lock);
+       powermate_sync_state(pm);
+       up(&pm->lock);
+}
+
+/* Set the LED up as described and begin the sync with the hardware if required */
+static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, 
+int pulse_speed, 
+                               int pulse_table, int pulse_asleep, int pulse_awake)
+{
+       if(pulse_speed < 0)
+               pulse_speed = 0;
+       if(pulse_table < 0)
+               pulse_table = 0;
+       if(pulse_speed > 510)
+               pulse_speed = 510;
+       if(pulse_table > 2)
+               pulse_table = 2;
+
+       pulse_asleep = !!pulse_asleep;
+       pulse_awake = !!pulse_awake;
+
+       down(&pm->lock);
+
+       /* mark state updates which are required */
+       if(static_brightness != pm->static_brightness){
+               pm->static_brightness = static_brightness;
+               pm->requires_update |= UPDATE_STATIC_BRIGHTNESS;                
+       }
+       if(pulse_asleep != pm->pulse_asleep){
+               pm->pulse_asleep = pulse_asleep;
+               pm->requires_update |= UPDATE_PULSE_ASLEEP;
+       }
+       if(pulse_awake != pm->pulse_awake){
+               pm->pulse_awake = pulse_awake;
+               pm->requires_update |= UPDATE_PULSE_AWAKE;
+       }
+       if(pulse_speed != pm->pulse_speed || pulse_table != pm->pulse_table){
+               pm->pulse_speed = pulse_speed;
+               pm->pulse_table = pulse_table;
+               pm->requires_update |= UPDATE_PULSE_MODE;
+       }
+
+       powermate_sync_state(pm);
+   
+       up(&pm->lock);
+}
+
+/* Callback from the Input layer when an event arrives from userspace to configure 
+the LED */
+static int powermate_input_event(struct input_dev *dev, unsigned int type, unsigned 
+int code, int _value)
+{
+       unsigned int command = (unsigned int)_value;
+       struct powermate_device *pm = dev->private;
+
+       if(type == EV_MSC && code == MSC_PULSELED){
+               /*  
+                   bits  0- 7: 8 bits: LED brightness
+                   bits  8-16: 9 bits: pulsing speed modifier (0 ... 510); 0-254 = 
+slower, 255 = standard, 256-510 = faster.
+                   bits 17-18: 2 bits: pulse table (0, 1, 2 valid)
+                   bit     19: 1 bit : pulse whilst asleep?
+                   bit     20: 1 bit : pulse constantly?
+               */  
+               int static_brightness = command & 0xFF;   // bits 0-7
+               int pulse_speed = (command >> 8) & 0x1FF; // bits 8-16
+               int pulse_table = (command >> 17) & 0x3;  // bits 17-18
+               int pulse_asleep = (command >> 19) & 0x1; // bit 19
+               int pulse_awake  = (command >> 20) & 0x1; // bit 20
+  
+               powermate_pulse_led(pm, static_brightness, pulse_speed, pulse_table, 
+pulse_asleep, pulse_awake);
+       }
+
+       return 0;
+}
+
+/* Called whenever a USB device matching one in our supported devices table is 
+connected */
+static void *powermate_probe(struct usb_device *udev, unsigned int ifnum, const 
+struct usb_device_id *id)
+{
+       struct usb_interface_descriptor *interface;
+       struct usb_endpoint_descriptor *endpoint;
+       struct powermate_device *pm;
+       int pipe, maxp;
+       char path[64];
+
+       interface = udev->config[0].interface[ifnum].altsetting + 0;
+       endpoint = interface->endpoint + 0;
+       if (!(endpoint->bEndpointAddress & 0x80)) return NULL;
+       if ((endpoint->bmAttributes & 3) != 3) return NULL;
+
+       usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+               0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+               0, interface->bInterfaceNumber, NULL, 0,
+               HZ * USB_CTRL_SET_TIMEOUT);
+
+       if (!(pm = kmalloc(sizeof(struct powermate_device), GFP_KERNEL)))
+               return NULL;
+
+       memset(pm, 0, sizeof(struct powermate_device));
+       pm->udev = udev;
+
+       init_MUTEX(&pm->lock);
+
+       /* get a handle to the interrupt data pipe */
+       pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+       maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+
+       if(maxp != POWERMATE_PAYLOAD_SIZE)
+               printk("powermate: Expected payload of %d bytes, found %d bytes!\n", 
+POWERMATE_PAYLOAD_SIZE, maxp);
+
+       FILL_INT_URB(&pm->irq, udev, pipe, pm->data, POWERMATE_PAYLOAD_SIZE, 
+powermate_irq, pm, endpoint->bInterval);
+
+       /* register our interrupt URB with the USB system */
+       if(usb_submit_urb(&pm->irq, GFP_KERNEL)) {
+               kfree(pm);
+               return NULL; /* failure */
+       }
+
+       switch (udev->descriptor.idProduct) {
+       case POWERMATE_PRODUCT_NEW: pm->input.name = pm_name_powermate; break;
+       case POWERMATE_PRODUCT_OLD: pm->input.name = pm_name_soundknob; break;
+       default: 
+         pm->input.name = pm_name_soundknob;
+         printk(KERN_WARNING "powermate: unknown product id %04x\n", 
+udev->descriptor.idProduct);
+       }
+
+       pm->input.private = pm;
+       pm->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_MSC);
+       pm->input.keybit[LONG(BTN_0)] = BIT(BTN_0);
+       pm->input.relbit[LONG(REL_DIAL)] = BIT(REL_DIAL);
+       pm->input.mscbit[LONG(MSC_PULSELED)] = BIT(MSC_PULSELED);
+       pm->input.idbus = BUS_USB;
+       pm->input.idvendor = udev->descriptor.idVendor;
+       pm->input.idproduct = udev->descriptor.idProduct;
+       pm->input.idversion = udev->descriptor.bcdDevice;
+       pm->input.event = powermate_input_event;
+
+       input_register_device(&pm->input);
+
+       usb_make_path(udev, path, 64);
+       snprintf(pm->phys, 64, "%s/input0", path);
+       printk(KERN_INFO "input: %s on %s\n", pm->input.name, pm->input.phys);
+       
+       /* force an update of everything */
+       pm->requires_update = UPDATE_PULSE_ASLEEP | UPDATE_PULSE_AWAKE | 
+UPDATE_PULSE_MODE | UPDATE_STATIC_BRIGHTNESS;
+       powermate_pulse_led(pm, 0x80, 255, 0, 1, 0); // set default pulse parameters
+  
+       return pm;
+}
+
+/* Called when a USB device we've accepted ownership of is removed */
+static void powermate_disconnect(struct usb_device *dev, void *ptr)
+{
+       struct powermate_device *pm = ptr;
+       down(&pm->lock);
+       pm->requires_update = 0;
+       usb_unlink_urb(&pm->irq);  
+       input_unregister_device(&pm->input);
+  
+       kfree(pm);
+}
+
+static struct usb_device_id powermate_devices [] = {
+       { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_NEW) },
+       { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_OLD) },
+       { USB_DEVICE(CONTOUR_VENDOR, CONTOUR_JOG) },
+       { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, powermate_devices);
+
+static struct usb_driver powermate_driver = {
+        name:           "powermate",
+        probe:          powermate_probe,
+        disconnect:     powermate_disconnect,
+        id_table:       powermate_devices,
+};
+
+int powermate_init(void)
+{
+       if (usb_register(&powermate_driver) < 0)
+               return -1;
+       return 0;
+}
+
+void powermate_cleanup(void)
+{
+       usb_deregister(&powermate_driver);
+}
+
+module_init(powermate_init);
+module_exit(powermate_cleanup);
+
+MODULE_AUTHOR( "William R Sowerbutts" );
+MODULE_DESCRIPTION( "Griffin Technology, Inc PowerMate driver" );
+MODULE_LICENSE("GPL");


-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Gadgets, caffeine, t-shirts, fun stuff.
http://thinkgeek.com/sf
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to