# 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	vojtech@suse.cz	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 <deneux@ifrance.com>
+ */
+
+#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 <deneux@ifrance.com>
+ */
+
+#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 <deneux@ifrance.com>
+ */
+
+#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 <deneux@ifrance.com>\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 <deneux@ifrance.com>
+ */
+
+#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 <deneux@ifrance.com>\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 <rdamazio@lsi.usp.br>
+ */
+
+#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 <rdamazio@lsi.usp.br>");
+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 <rdamazio@lsi.usp.br>.\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 <rdamazio@lsi.usp.br>
+ */
+
+#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 <will@sowerbutts.com>
+ *
+ * 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");
