Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig
new file mode 100644
index 0000000..d28e7ea
--- /dev/null
+++ b/drivers/usb/input/Kconfig
@@ -0,0 +1,237 @@
+#
+# USB Input driver configuration
+#
+comment "USB Input Devices"
+	depends on USB
+
+config USB_HID
+	tristate "USB Human Interface Device (full HID) support"
+	depends on USB
+	---help---
+	  Say Y here if you want full HID support to connect keyboards,
+	  mice, joysticks, graphic tablets, or any other HID based devices
+	  to your computer via USB. You also need to select HID Input layer
+	  support (below) if you want to use keyboards, mice, joysticks and
+	  the like ... as well as Uninterruptible Power Supply (UPS) and
+	  monitor control devices.
+
+	  You can't use this driver and the HIDBP (Boot Protocol) keyboard
+	  and mouse drivers at the same time. More information is available:
+	  <file:Documentation/input/input.txt>.
+
+	  If unsure, say Y.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbhid.
+
+comment "Input core support is needed for USB HID input layer or HIDBP support"
+	depends on USB_HID && INPUT=n
+
+config USB_HIDINPUT
+	bool "HID input layer support"
+	default y
+	depends on INPUT && USB_HID
+	help
+	  Say Y here if you want to use a USB keyboard, mouse or joystick,
+	  or any other HID input device.
+
+	  If unsure, say Y.
+
+config HID_FF
+	bool "Force feedback support (EXPERIMENTAL)"
+	depends on USB_HIDINPUT && EXPERIMENTAL
+	help
+	  Say Y here is you want force feedback support for a few HID devices.
+	  See below for a list of supported devices.
+
+	  See <file:Documentation/input/ff.txt> for a description of the force
+	  feedback API.
+
+	  If unsure, say N.
+
+config HID_PID
+	bool "PID Devices (Microsoft Sidewinder Force Feedback 2)"
+	depends on HID_FF
+	help
+	  Say Y here 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 LOGITECH_FF
+	bool "Logitech WingMan *3D support"
+	depends on HID_FF
+	help
+	  Say Y here if you have one of these devices:
+	  - Logitech WingMan Cordless RumblePad
+	  - Logitech WingMan Force 3D
+	  and if you want to enable force feedback for them.
+	  Note: if you say N here, this device will still be supported, but without
+	  force feedback.
+
+config THRUSTMASTER_FF
+	bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)"
+	depends on HID_FF && EXPERIMENTAL
+	help
+	  Say Y here if you have a THRUSTMASTER FireStore Dual Power 2,
+	  and want to enable force feedback support for it.
+	  Note: if you say N here, this device will still be supported, but without
+	  force feedback.
+
+config USB_HIDDEV
+	bool "/dev/hiddev raw HID device support"
+	depends on USB_HID
+	help
+	  Say Y here if you want to support HID devices (from the USB
+	  specification standpoint) that aren't strictly user interface
+	  devices, like monitor controls and Uninterruptable Power Supplies.
+
+	  This module supports these devices separately using a separate
+	  event interface on /dev/usb/hiddevX (char 180:96 to 180:111).
+
+	  If unsure, say Y.
+
+menu "USB HID Boot Protocol drivers"
+	depends on USB!=n && USB_HID!=y
+
+config USB_KBD
+	tristate "USB HIDBP Keyboard (simple Boot) support"
+	depends on USB && INPUT
+	---help---
+	  Say Y here only if you are absolutely sure that you don't want
+	  to use the generic HID driver for your USB keyboard and prefer
+	  to use the keyboard in its limited Boot Protocol mode instead.
+
+	  This is almost certainly not what you want.  This is mostly
+	  useful for embedded applications or simple keyboards.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbkbd.
+
+	  If even remotely unsure, say N.
+
+config USB_MOUSE
+	tristate "USB HIDBP Mouse (simple Boot) support"
+	depends on USB && INPUT
+	---help---
+	  Say Y here only if you are absolutely sure that you don't want
+	  to use the generic HID driver for your USB mouse and prefer
+	  to use the mouse in its limited Boot Protocol mode instead.
+
+	  This is almost certainly not what you want.  This is mostly
+	  useful for embedded applications or simple mice.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbmouse.
+
+	  If even remotely unsure, say N.
+
+endmenu
+
+config USB_AIPTEK
+	tristate "Aiptek 6000U/8000U tablet support"
+	depends on USB && INPUT
+	help
+	  Say Y here if you want to use the USB version of the Aiptek 6000U
+	  or Aiptek 8000U tablet.  Make sure to say Y to "Mouse support"
+	  (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
+	  (CONFIG_INPUT_EVDEV) as well.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called aiptek.
+
+config USB_WACOM
+	tristate "Wacom Intuos/Graphire tablet support"
+	depends on USB && INPUT
+	help
+	  Say Y here if you want to use the USB version of the Wacom Intuos
+	  or Graphire tablet.  Make sure to say Y to "Mouse support"
+	  (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
+	  (CONFIG_INPUT_EVDEV) as well.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called wacom.
+
+config USB_KBTAB
+	tristate "KB Gear JamStudio tablet support"
+	depends on USB && INPUT
+	help
+	  Say Y here if you want to use the USB version of the KB Gear
+	  JamStudio tablet.  Make sure to say Y to "Mouse support"
+	  (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
+	  (CONFIG_INPUT_EVDEV) as well.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called kbtab.
+
+config USB_POWERMATE
+	tristate "Griffin PowerMate and Contour Jog support"
+	depends on USB && INPUT
+	---help---
+	  Say Y here if you want to use Griffin PowerMate or Contour Jog devices.
+	  These are aluminum 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/>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called powermate.
+
+config USB_MTOUCH
+	tristate "MicroTouch USB Touchscreen Driver"
+	depends on USB && INPUT
+	---help---
+	  Say Y here if you want to use a MicroTouch (Now 3M) USB 
+	  Touchscreen controller.
+
+	  See <file:Documentation/usb/mtouch.txt> for additional information.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mtouchusb.
+
+config USB_EGALAX
+	tristate "eGalax TouchKit USB Touchscreen Driver"
+	depends on USB && INPUT
+	---help---
+	  Say Y here if you want to use a eGalax TouchKit USB
+	  Touchscreen controller.
+
+	  The driver has been tested on a Xenarc 700TSV monitor
+	  with eGalax touchscreen.
+
+	  Have a look at <http://linux.chapter7.ch/touchkit/> for
+	  a usage description and the required user-space stuff.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called touchkitusb.
+
+config USB_XPAD
+	tristate "X-Box gamepad support"
+	depends on USB && INPUT
+	---help---
+	  Say Y here if you want to use the X-Box pad with your computer.
+	  Make sure to say Y to "Joystick support" (CONFIG_INPUT_JOYDEV)
+	  and/or "Event interface support" (CONFIG_INPUT_EVDEV) as well.
+
+	  For information about how to connect the X-Box pad to USB, see
+	  <file:Documentation/input/xpad.txt>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called xpad.
+	  
+config USB_ATI_REMOTE
+	tristate "ATI / X10 USB RF remote control"
+	depends on USB && INPUT
+	---help---
+	  Say Y here if you want to use an ATI or X10 "Lola" USB remote control.
+	  These are RF remotes with USB receivers. 
+	  The ATI remote comes with many of ATI's All-In-Wonder video cards.
+	  The X10 "Lola" remote is available at:
+	     <http://www.x10.com/products/lola_sg1.htm>
+	  This driver provides mouse pointer, left and right mouse buttons, 
+	  and maps all the other remote buttons to keypress events.
+	  
+	  To compile this driver as a module, choose M here: the module will be
+	  called ati_remote.
+
diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile
new file mode 100644
index 0000000..6bcedd1
--- /dev/null
+++ b/drivers/usb/input/Makefile
@@ -0,0 +1,39 @@
+#
+# Makefile for the USB input drivers
+#
+
+# Multipart objects.
+usbhid-objs	:= hid-core.o
+
+# Optional parts of multipart objects.
+
+ifeq ($(CONFIG_USB_HIDDEV),y)
+	usbhid-objs	+= hiddev.o
+endif
+ifeq ($(CONFIG_USB_HIDINPUT),y)
+	usbhid-objs	+= hid-input.o
+endif
+ifeq ($(CONFIG_HID_PID),y)
+	usbhid-objs	+= pid.o
+endif
+ifeq ($(CONFIG_LOGITECH_FF),y)
+	usbhid-objs	+= hid-lgff.o
+endif
+ifeq ($(CONFIG_THRUSTMASTER_FF),y)
+	usbhid-objs	+= hid-tmff.o
+endif
+ifeq ($(CONFIG_HID_FF),y)
+	usbhid-objs	+= hid-ff.o
+endif
+
+obj-$(CONFIG_USB_AIPTEK)	+= aiptek.o
+obj-$(CONFIG_USB_ATI_REMOTE)	+= ati_remote.o
+obj-$(CONFIG_USB_HID)		+= usbhid.o
+obj-$(CONFIG_USB_KBD)		+= usbkbd.o
+obj-$(CONFIG_USB_KBTAB)		+= kbtab.o
+obj-$(CONFIG_USB_MOUSE)		+= usbmouse.o
+obj-$(CONFIG_USB_MTOUCH)	+= mtouchusb.o
+obj-$(CONFIG_USB_EGALAX)	+= touchkitusb.o
+obj-$(CONFIG_USB_POWERMATE)	+= powermate.o
+obj-$(CONFIG_USB_WACOM)		+= wacom.o
+obj-$(CONFIG_USB_XPAD)		+= xpad.o
diff --git a/drivers/usb/input/aiptek.c b/drivers/usb/input/aiptek.c
new file mode 100644
index 0000000..d7fea9e
--- /dev/null
+++ b/drivers/usb/input/aiptek.c
@@ -0,0 +1,2283 @@
+/*
+ *  Native support for the Aiptek HyperPen USB Tablets
+ *  (4000U/5000U/6000U/8000U/12000U)
+ *  
+ *  Copyright (c) 2001      Chris Atenasio   <chris@crud.net>
+ *  Copyright (c) 2002-2004 Bryan W. Headley <bwheadley@earthlink.net>
+ *
+ *  based on wacom.c by
+ *     Vojtech Pavlik      <vojtech@suse.cz>
+ *     Andreas Bach Aaen   <abach@stofanet.dk>
+ *     Clifford Wolf       <clifford@clifford.at>
+ *     Sam Mosel           <sam.mosel@computer.org>
+ *     James E. Blair      <corvus@gnu.org>
+ *     Daniel Egger        <egger@suse.de>
+ *
+ *  Many thanks to Oliver Kuechemann for his support.
+ *
+ *  ChangeLog:
+ *      v0.1 - Initial release
+ *      v0.2 - Hack to get around fake event 28's. (Bryan W. Headley)
+ *      v0.3 - Make URB dynamic (Bryan W. Headley, Jun-8-2002)
+ *             Released to Linux 2.4.19 and 2.5.x
+ *      v0.4 - Rewrote substantial portions of the code to deal with
+ *             corrected control sequences, timing, dynamic configuration,
+ *             support of 6000U - 12000U, procfs, and macro key support
+ *             (Jan-1-2003 - Feb-5-2003, Bryan W. Headley)
+ *      v1.0 - Added support for diagnostic messages, count of messages
+ *             received from URB - Mar-8-2003, Bryan W. Headley
+ *      v1.1 - added support for tablet resolution, changed DV and proximity
+ *             some corrections - Jun-22-2003, martin schneebacher
+ *           - Added support for the sysfs interface, deprecating the
+ *             procfs interface for 2.5.x kernel. Also added support for
+ *             Wheel command. Bryan W. Headley July-15-2003.
+ *      v1.2 - Reworked jitter timer as a kernel thread. 
+ *             Bryan W. Headley November-28-2003/Jan-10-2004.
+ *      v1.3 - Repaired issue of kernel thread going nuts on single-processor
+ *             machines, introduced programmableDelay as a command line
+ *             parameter. Feb 7 2004, Bryan W. Headley.
+ *      v1.4 - Re-wire jitter so it does not require a thread. Courtesy of
+ *             Rene van Paassen. Added reporting of physical pointer device
+ *             (e.g., stylus, mouse in reports 2, 3, 4, 5. We don't know
+ *             for reports 1, 6.)
+ *             what physical device reports for reports 1, 6.) Also enabled
+ *             MOUSE and LENS tool button modes. Renamed "rubber" to "eraser".
+ *             Feb 20, 2004, Bryan W. Headley.
+ *      v1.5 - Added previousJitterable, so we don't do jitter delay when the
+ *             user is holding a button down for periods of time.
+ *
+ * NOTE:
+ *      This kernel driver is augmented by the "Aiptek" XFree86 input
+ *      driver for your X server, as well as the Gaiptek GUI Front-end
+ *      "Tablet Manager". 
+ *      These three products are highly interactive with one another, 
+ *      so therefore it's easier to document them all as one subsystem.
+ *      Please visit the project's "home page", located at, 
+ *      http://aiptektablet.sourceforge.net.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.5 (May-15-2004)"
+#define DRIVER_AUTHOR  "Bryan W. Headley/Chris Atenasio"
+#define DRIVER_DESC    "Aiptek HyperPen USB Tablet Driver (Linux 2.6.x)"
+
+/*
+ * Aiptek status packet:
+ *
+ * (returned as Report 1 - relative coordinates from mouse and stylus)
+ *
+ *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
+ * byte0   0     0     0     0     0     0     0     1
+ * byte1   0     0     0     0     0    BS2   BS    Tip
+ * byte2  X7    X6    X5    X4    X3    X2    X1    X0
+ * byte3  Y7    Y6    Y5    Y4    Y3    Y2    Y1    Y0
+ *
+ * (returned as Report 2 - absolute coordinates from the stylus)
+ *
+ *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
+ * byte0   0     0     0     0     0     0     1     0
+ * byte1  X7    X6    X5    X4    X3    X2    X1    X0
+ * byte2  X15   X14   X13   X12   X11   X10   X9    X8
+ * byte3  Y7    Y6    Y5    Y4    Y3    Y2    Y1    Y0
+ * byte4  Y15   Y14   Y13   Y12   Y11   Y10   Y9    Y8
+ * byte5   *     *     *    BS2   BS1   Tip   IR    DV
+ * byte6  P7    P6    P5    P4    P3    P2    P1    P0
+ * byte7  P15   P14   P13   P12   P11   P10   P9    P8
+ *
+ * (returned as Report 3 - absolute coordinates from the mouse)
+ *
+ *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
+ * byte0   0     0     0     0     0     0     1     0
+ * byte1  X7    X6    X5    X4    X3    X2    X1    X0
+ * byte2  X15   X14   X13   X12   X11   X10   X9    X8
+ * byte3  Y7    Y6    Y5    Y4    Y3    Y2    Y1    Y0
+ * byte4  Y15   Y14   Y13   Y12   Y11   Y10   Y9    Y8
+ * byte5   *     *     *    BS2   BS1   Tip   IR    DV
+ * byte6  P7    P6    P5    P4    P3    P2    P1    P0
+ * byte7  P15   P14   P13   P12   P11   P10   P9    P8
+ *
+ * (returned as Report 4 - macrokeys from the stylus)
+ *
+ *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
+ * byte0   0     0     0     0     0     1     0     0
+ * byte1   0     0     0    BS2   BS    Tip   IR    DV
+ * byte2   0     0     0     0     0     0     1     0
+ * byte3   0     0     0    K4    K3    K2    K1    K0
+ * byte4  P7    P6    P5    P4    P3    P2    P1    P0
+ * byte5  P15   P14   P13   P12   P11   P10   P9    P8
+ *
+ * (returned as Report 5 - macrokeys from the mouse)
+ *
+ *        bit7  bit6  bit5  bit4  bit3  bit2  bit1  bit0
+ * byte0   0     0     0     0     0     1     0     0
+ * byte1   0     0     0    BS2   BS    Tip   IR    DV
+ * byte2   0     0     0     0     0     0     1     0
+ * byte3   0     0     0    K4    K3    K2    K1    K0
+ * byte4  P7    P6    P5    P4    P3    P2    P1    P0
+ * byte5  P15   P14   P13   P12   P11   P10   P9    P8
+ *
+ * IR: In Range = Proximity on
+ * DV = Data Valid
+ * BS = Barrel Switch (as in, macro keys)
+ * BS2 also referred to as Tablet Pick
+ *
+ * Command Summary:
+ *
+ * Use report_type CONTROL (3)
+ * Use report_id   2
+ *
+ * Command/Data    Description     Return Bytes    Return Value
+ * 0x10/0x00       SwitchToMouse       0
+ * 0x10/0x01       SwitchToTablet      0
+ * 0x18/0x04       SetResolution       0 
+ * 0x12/0xFF       AutoGainOn          0
+ * 0x17/0x00       FilterOn            0
+ * 0x01/0x00       GetXExtension       2           MaxX
+ * 0x01/0x01       GetYExtension       2           MaxY
+ * 0x02/0x00       GetModelCode        2           ModelCode = LOBYTE
+ * 0x03/0x00       GetODMCode          2           ODMCode
+ * 0x08/0x00       GetPressureLevels   2           =512
+ * 0x04/0x00       GetFirmwareVersion  2           Firmware Version
+ * 0x11/0x02       EnableMacroKeys     0
+ *
+ * To initialize the tablet:
+ *
+ * (1) Send Resolution500LPI (Command)
+ * (2) Query for Model code (Option Report)
+ * (3) Query for ODM code (Option Report)
+ * (4) Query for firmware (Option Report)
+ * (5) Query for GetXExtension (Option Report)
+ * (6) Query for GetYExtension (Option Report)
+ * (7) Query for GetPressureLevels (Option Report)
+ * (8) SwitchToTablet for Absolute coordinates, or
+ *     SwitchToMouse for Relative coordinates (Command)
+ * (9) EnableMacroKeys (Command)
+ * (10) FilterOn (Command)
+ * (11) AutoGainOn (Command)
+ *
+ * (Step 9 can be omitted, but you'll then have no function keys.)
+ */
+
+#define USB_VENDOR_ID_AIPTEK				0x08ca
+#define USB_REQ_GET_REPORT				0x01
+#define USB_REQ_SET_REPORT				0x09
+
+	/* PointerMode codes
+	 */
+#define AIPTEK_POINTER_ONLY_MOUSE_MODE			0
+#define AIPTEK_POINTER_ONLY_STYLUS_MODE			1
+#define AIPTEK_POINTER_EITHER_MODE			2
+
+#define AIPTEK_POINTER_ALLOW_MOUSE_MODE(a)		\
+	(a == AIPTEK_POINTER_ONLY_MOUSE_MODE ||		\
+	 a == AIPTEK_POINTER_EITHER_MODE)
+#define AIPTEK_POINTER_ALLOW_STYLUS_MODE(a)		\
+	(a == AIPTEK_POINTER_ONLY_STYLUS_MODE ||	\
+	 a == AIPTEK_POINTER_EITHER_MODE)
+
+	/* CoordinateMode code
+	 */
+#define AIPTEK_COORDINATE_RELATIVE_MODE			0
+#define AIPTEK_COORDINATE_ABSOLUTE_MODE			1
+
+       /* XTilt and YTilt values
+        */
+#define AIPTEK_TILT_MIN					(-128)
+#define AIPTEK_TILT_MAX					127
+#define AIPTEK_TILT_DISABLE				(-10101)
+
+	/* Wheel values
+	 */
+#define AIPTEK_WHEEL_MIN				0
+#define AIPTEK_WHEEL_MAX				1024
+#define AIPTEK_WHEEL_DISABLE				(-10101)
+
+	/* ToolCode values, which BTW are 0x140 .. 0x14f
+	 * We have things set up such that if TOOL_BUTTON_FIRED_BIT is
+	 * not set, we'll send one instance of AIPTEK_TOOL_BUTTON_xxx.
+	 *
+	 * Whenever the user resets the value, TOOL_BUTTON_FIRED_BIT will
+	 * get reset.
+	 */
+#define TOOL_BUTTON(x)					((x) & 0x14f)
+#define TOOL_BUTTON_FIRED(x)				((x) & 0x200)
+#define TOOL_BUTTON_FIRED_BIT				0x200
+	/* toolMode codes
+	 */
+#define AIPTEK_TOOL_BUTTON_PEN_MODE			BTN_TOOL_PEN
+#define AIPTEK_TOOL_BUTTON_PEN_MODE			BTN_TOOL_PEN
+#define AIPTEK_TOOL_BUTTON_PENCIL_MODE			BTN_TOOL_PENCIL
+#define AIPTEK_TOOL_BUTTON_BRUSH_MODE			BTN_TOOL_BRUSH
+#define AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE		BTN_TOOL_AIRBRUSH
+#define AIPTEK_TOOL_BUTTON_ERASER_MODE			BTN_TOOL_RUBBER
+#define AIPTEK_TOOL_BUTTON_MOUSE_MODE			BTN_TOOL_MOUSE
+#define AIPTEK_TOOL_BUTTON_LENS_MODE			BTN_TOOL_LENS
+
+	/* Diagnostic message codes
+	 */
+#define AIPTEK_DIAGNOSTIC_NA				0
+#define AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE	1
+#define AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE	2
+#define AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED		3
+
+	/* Time to wait (in ms) to help mask hand jittering 
+	 * when pressing the stylus buttons.
+	 */
+#define AIPTEK_JITTER_DELAY_DEFAULT			50
+
+	/* Time to wait (in ms) in-between sending the tablet
+	 * a command and beginning the process of reading the return
+	 * sequence from the tablet.
+	 */
+#define AIPTEK_PROGRAMMABLE_DELAY_25		25
+#define AIPTEK_PROGRAMMABLE_DELAY_50		50
+#define AIPTEK_PROGRAMMABLE_DELAY_100		100
+#define AIPTEK_PROGRAMMABLE_DELAY_200		200
+#define AIPTEK_PROGRAMMABLE_DELAY_300		300
+#define AIPTEK_PROGRAMMABLE_DELAY_400		400
+#define AIPTEK_PROGRAMMABLE_DELAY_DEFAULT	AIPTEK_PROGRAMMABLE_DELAY_400
+
+	/* Mouse button programming
+	 */
+#define AIPTEK_MOUSE_LEFT_BUTTON		0x01
+#define AIPTEK_MOUSE_RIGHT_BUTTON		0x02
+#define AIPTEK_MOUSE_MIDDLE_BUTTON		0x04
+
+	/* Stylus button programming
+	 */
+#define AIPTEK_STYLUS_LOWER_BUTTON		0x08
+#define AIPTEK_STYLUS_UPPER_BUTTON		0x10
+
+	/* Length of incoming packet from the tablet
+	 */
+#define AIPTEK_PACKET_LENGTH			8
+
+	/* We report in EV_MISC both the proximity and
+	 * whether the report came from the stylus, tablet mouse
+	 * or "unknown" -- Unknown when the tablet is in relative
+	 * mode, because we only get report 1's.
+	 */
+#define AIPTEK_REPORT_TOOL_UNKNOWN		0x10
+#define AIPTEK_REPORT_TOOL_STYLUS		0x20
+#define AIPTEK_REPORT_TOOL_MOUSE		0x40
+
+static int programmableDelay = AIPTEK_PROGRAMMABLE_DELAY_DEFAULT;
+static int jitterDelay = AIPTEK_JITTER_DELAY_DEFAULT;
+
+struct aiptek_features {
+	int odmCode;		/* Tablet manufacturer code       */
+	int modelCode;		/* Tablet model code (not unique) */
+	int firmwareCode;	/* prom/eeprom version            */
+	char usbPath[64 + 1];	/* device's physical usb path     */
+	char inputPath[64 + 1];	/* input device path              */
+};
+
+struct aiptek_settings {
+	int pointerMode;	/* stylus-, mouse-only or either */
+	int coordinateMode;	/* absolute/relative coords      */
+	int toolMode;		/* pen, pencil, brush, etc. tool */
+	int xTilt;		/* synthetic xTilt amount        */
+	int yTilt;		/* synthetic yTilt amount        */
+	int wheel;		/* synthetic wheel amount        */
+	int stylusButtonUpper;	/* stylus upper btn delivers...  */
+	int stylusButtonLower;	/* stylus lower btn delivers...  */
+	int mouseButtonLeft;	/* mouse left btn delivers...    */
+	int mouseButtonMiddle;	/* mouse middle btn delivers...  */
+	int mouseButtonRight;	/* mouse right btn delivers...   */
+	int programmableDelay;	/* delay for tablet programming  */
+	int jitterDelay;	/* delay for hand jittering      */
+};
+
+struct aiptek {
+	struct input_dev inputdev;		/* input device struct           */
+	struct usb_device *usbdev;		/* usb device struct             */
+	struct urb *urb;			/* urb for incoming reports      */
+	dma_addr_t data_dma;			/* our dma stuffage              */
+	struct aiptek_features features;	/* tablet's array of features    */
+	struct aiptek_settings curSetting;	/* tablet's current programmable */
+	struct aiptek_settings newSetting;	/* ... and new param settings    */
+	unsigned int ifnum;			/* interface number for IO       */
+	int openCount;				/* module use counter            */
+	int diagnostic;				/* tablet diagnostic codes       */
+	unsigned long eventCount;		/* event count                   */
+	int inDelay;				/* jitter: in jitter delay?      */
+	unsigned long endDelay;			/* jitter: time when delay ends  */
+	int previousJitterable;			/* jitterable prev value     */
+	unsigned char *data;			/* incoming packet data          */
+};
+
+/*
+ * Permit easy lookup of keyboard events to send, versus
+ * the bitmap which comes from the tablet. This hides the
+ * issue that the F_keys are not sequentially numbered.
+ */
+static int macroKeyEvents[] = {
+	KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
+	KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11,
+	KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17,
+	KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23,
+	KEY_F24, KEY_STOP, KEY_AGAIN, KEY_PROPS, KEY_UNDO,
+	KEY_FRONT, KEY_COPY, KEY_OPEN, KEY_PASTE, 0
+};
+
+/***********************************************************************
+ * Relative reports deliver values in 2's complement format to
+ * deal with negative offsets.
+ */
+static int aiptek_convert_from_2s_complement(unsigned char c)
+{
+	int ret;
+	unsigned char b = c;
+	int negate = 0;
+
+	if ((b & 0x80) != 0) {
+		b = ~b;
+		b--;
+		negate = 1;
+	}
+	ret = b;
+	ret = (negate == 1) ? -ret : ret;
+	return ret;
+}
+
+/***********************************************************************
+ * aiptek_irq can receive one of six potential reports.
+ * The documentation for each is in the body of the function.
+ *
+ * The tablet reports on several attributes per invocation of
+ * aiptek_irq. Because the Linux Input Event system allows the
+ * transmission of ONE attribute per input_report_xxx() call,
+ * collation has to be done on the other end to reconstitute
+ * a complete tablet report. Further, the number of Input Event reports
+ * submitted varies, depending on what USB report type, and circumstance.
+ * To deal with this, EV_MSC is used to indicate an 'end-of-report'
+ * message. This has been an undocumented convention understood by the kernel
+ * tablet driver and clients such as gpm and XFree86's tablet drivers.
+ *
+ * Of the information received from the tablet, the one piece I
+ * cannot transmit is the proximity bit (without resorting to an EV_MSC
+ * convention above.) I therefore have taken over REL_MISC and ABS_MISC
+ * (for relative and absolute reports, respectively) for communicating
+ * Proximity. Why two events? I thought it interesting to know if the
+ * Proximity event occured while the tablet was in absolute or relative
+ * mode.
+ *
+ * Other tablets use the notion of a certain minimum stylus pressure
+ * to infer proximity. While that could have been done, that is yet
+ * another 'by convention' behavior, the documentation for which
+ * would be spread between two (or more) pieces of software.
+ *
+ * EV_MSC usage was terminated for this purpose in Linux 2.5.x, and
+ * replaced with the input_sync() method (which emits EV_SYN.)
+ */
+
+static void aiptek_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct aiptek *aiptek = urb->context;
+	unsigned char *data = aiptek->data;
+	struct input_dev *inputdev = &aiptek->inputdev;
+	int jitterable = 0;
+	int retval, macro, x, y, z, left, right, middle, p, dv, tip, bs, pck;
+
+	switch (urb->status) {
+	case 0:
+		/* Success */
+		break;
+
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* This urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d",
+		    __FUNCTION__, urb->status);
+		return;
+
+	default:
+		dbg("%s - nonzero urb status received: %d",
+		    __FUNCTION__, urb->status);
+		goto exit;
+	}
+
+	/* See if we are in a delay loop -- throw out report if true.
+	 */
+	if (aiptek->inDelay == 1 && time_after(aiptek->endDelay, jiffies)) {
+		goto exit;
+	}
+
+	aiptek->inDelay = 0;
+	aiptek->eventCount++;
+
+	/* Report 1 delivers relative coordinates with either a stylus
+	 * or the mouse. You do not know, however, which input
+	 * tool generated the event.
+	 */
+	if (data[0] == 1) {
+		if (aiptek->curSetting.coordinateMode ==
+		    AIPTEK_COORDINATE_ABSOLUTE_MODE) {
+			aiptek->diagnostic =
+			    AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE;
+		} else {
+			input_regs(inputdev, regs);
+
+			x = aiptek_convert_from_2s_complement(data[2]);
+			y = aiptek_convert_from_2s_complement(data[3]);
+
+			/* jitterable keeps track of whether any button has been pressed.
+			 * We're also using it to remap the physical mouse button mask
+			 * to pseudo-settings. (We don't specifically care about it's
+			 * value after moving/transposing mouse button bitmasks, except
+			 * that a non-zero value indicates that one or more
+			 * mouse button was pressed.)
+			 */
+			jitterable = data[5] & 0x07;
+
+			left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
+			right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
+			middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
+
+			input_report_key(inputdev, BTN_LEFT, left);
+			input_report_key(inputdev, BTN_MIDDLE, middle);
+			input_report_key(inputdev, BTN_RIGHT, right);
+			input_report_rel(inputdev, REL_X, x);
+			input_report_rel(inputdev, REL_Y, y);
+			input_report_rel(inputdev, REL_MISC, 1 | AIPTEK_REPORT_TOOL_UNKNOWN);
+
+			/* Wheel support is in the form of a single-event
+			 * firing.
+			 */
+			if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) {
+				input_report_rel(inputdev, REL_WHEEL,
+						 aiptek->curSetting.wheel);
+				aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
+			}
+			input_sync(inputdev);
+		}
+	}
+	/* Report 2 is delivered only by the stylus, and delivers
+	 * absolute coordinates.
+	 */
+	else if (data[0] == 2) {
+		if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) {
+			aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE;
+		} else if (!AIPTEK_POINTER_ALLOW_STYLUS_MODE
+			    (aiptek->curSetting.pointerMode)) {
+				aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED;
+		} else {
+			input_regs(inputdev, regs);
+
+			x = le16_to_cpu(get_unaligned((__le16 *) (data + 1)));
+			y = le16_to_cpu(get_unaligned((__le16 *) (data + 3)));
+			z = le16_to_cpu(get_unaligned((__le16 *) (data + 6)));
+
+			p = (data[5] & 0x01) != 0 ? 1 : 0;
+			dv = (data[5] & 0x02) != 0 ? 1 : 0;
+			tip = (data[5] & 0x04) != 0 ? 1 : 0;
+
+			/* Use jitterable to re-arrange button masks
+			 */
+			jitterable = data[5] & 0x18;
+
+			bs = (data[5] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0;
+			pck = (data[5] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0;
+
+			/* dv indicates 'data valid' (e.g., the tablet is in sync
+			 * and has delivered a "correct" report) We will ignore
+			 * all 'bad' reports...
+			 */
+			if (dv != 0) {
+				/* If we've not already sent a tool_button_?? code, do
+				 * so now. Then set FIRED_BIT so it won't be resent unless
+				 * the user forces FIRED_BIT off.
+				 */
+				if (TOOL_BUTTON_FIRED
+				    (aiptek->curSetting.toolMode) == 0) {
+					input_report_key(inputdev,
+							 TOOL_BUTTON(aiptek->curSetting.toolMode),
+							 1);
+					aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
+				}
+
+				if (p != 0) {
+					input_report_abs(inputdev, ABS_X, x);
+					input_report_abs(inputdev, ABS_Y, y);
+					input_report_abs(inputdev, ABS_PRESSURE, z);
+
+					input_report_key(inputdev, BTN_TOUCH, tip);
+					input_report_key(inputdev, BTN_STYLUS, bs);
+					input_report_key(inputdev, BTN_STYLUS2, pck);
+
+					if (aiptek->curSetting.xTilt !=
+					    AIPTEK_TILT_DISABLE) {
+						input_report_abs(inputdev,
+								 ABS_TILT_X,
+								 aiptek->curSetting.xTilt);
+					}
+					if (aiptek->curSetting.yTilt != AIPTEK_TILT_DISABLE) {
+						input_report_abs(inputdev,
+								 ABS_TILT_Y,
+								 aiptek->curSetting.yTilt);
+					}
+
+					/* Wheel support is in the form of a single-event
+					 * firing.
+					 */
+					if (aiptek->curSetting.wheel !=
+					    AIPTEK_WHEEL_DISABLE) {
+						input_report_abs(inputdev,
+								 ABS_WHEEL,
+								 aiptek->curSetting.wheel);
+						aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
+					}
+				}
+				input_report_abs(inputdev, ABS_MISC, p | AIPTEK_REPORT_TOOL_STYLUS);
+				input_sync(inputdev);
+			}
+		}
+	}
+	/* Report 3's come from the mouse in absolute mode.
+	 */
+	else if (data[0] == 3) {
+		if (aiptek->curSetting.coordinateMode == AIPTEK_COORDINATE_RELATIVE_MODE) {
+			aiptek->diagnostic = AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE;
+		} else if (!AIPTEK_POINTER_ALLOW_MOUSE_MODE
+			(aiptek->curSetting.pointerMode)) {
+			aiptek->diagnostic = AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED;
+		} else {
+			input_regs(inputdev, regs);
+			x = le16_to_cpu(get_unaligned((__le16 *) (data + 1)));
+			y = le16_to_cpu(get_unaligned((__le16 *) (data + 3)));
+
+			jitterable = data[5] & 0x1c;
+
+			p = (data[5] & 0x01) != 0 ? 1 : 0;
+			dv = (data[5] & 0x02) != 0 ? 1 : 0;
+			left = (data[5] & aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
+			right = (data[5] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
+			middle = (data[5] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
+
+			if (dv != 0) {
+				/* If we've not already sent a tool_button_?? code, do
+				 * so now. Then set FIRED_BIT so it won't be resent unless
+				 * the user forces FIRED_BIT off.
+				 */
+				if (TOOL_BUTTON_FIRED
+				    (aiptek->curSetting.toolMode) == 0) {
+					input_report_key(inputdev,
+							 TOOL_BUTTON(aiptek->curSetting.toolMode),
+							 1);
+					aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
+				}
+
+				if (p != 0) {
+					input_report_abs(inputdev, ABS_X, x);
+					input_report_abs(inputdev, ABS_Y, y);
+
+					input_report_key(inputdev, BTN_LEFT, left);
+					input_report_key(inputdev, BTN_MIDDLE, middle);
+					input_report_key(inputdev, BTN_RIGHT, right);
+
+					/* Wheel support is in the form of a single-event
+					 * firing.
+					 */
+					if (aiptek->curSetting.wheel != AIPTEK_WHEEL_DISABLE) {
+						input_report_abs(inputdev,
+								 ABS_WHEEL,
+								 aiptek->curSetting.wheel);
+						aiptek->curSetting.wheel = AIPTEK_WHEEL_DISABLE;
+					}
+				}
+				input_report_rel(inputdev, REL_MISC, p | AIPTEK_REPORT_TOOL_MOUSE);
+				input_sync(inputdev);
+			}
+		}
+	}
+	/* Report 4s come from the macro keys when pressed by stylus
+	 */
+	else if (data[0] == 4) {
+		jitterable = data[1] & 0x18;
+
+		p = (data[1] & 0x01) != 0 ? 1 : 0;
+		dv = (data[1] & 0x02) != 0 ? 1 : 0;
+		tip = (data[1] & 0x04) != 0 ? 1 : 0;
+		bs = (data[1] & aiptek->curSetting.stylusButtonLower) != 0 ? 1 : 0;
+		pck = (data[1] & aiptek->curSetting.stylusButtonUpper) != 0 ? 1 : 0;
+
+		macro = data[3];
+		z = le16_to_cpu(get_unaligned((__le16 *) (data + 4)));
+
+		if (dv != 0) {
+			input_regs(inputdev, regs);
+
+			/* If we've not already sent a tool_button_?? code, do
+			 * so now. Then set FIRED_BIT so it won't be resent unless
+			 * the user forces FIRED_BIT off.
+			 */
+			if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) {
+				input_report_key(inputdev,
+						 TOOL_BUTTON(aiptek->curSetting.toolMode),
+						 1);
+				aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
+			}
+
+			if (p != 0) {
+				input_report_key(inputdev, BTN_TOUCH, tip);
+				input_report_key(inputdev, BTN_STYLUS, bs);
+				input_report_key(inputdev, BTN_STYLUS2, pck);
+				input_report_abs(inputdev, ABS_PRESSURE, z);
+			}
+
+			/* For safety, we're sending key 'break' codes for the
+			 * neighboring macro keys.
+			 */
+			if (macro > 0) {
+				input_report_key(inputdev,
+						 macroKeyEvents[macro - 1], 0);
+			}
+			if (macro < 25) {
+				input_report_key(inputdev,
+						 macroKeyEvents[macro + 1], 0);
+			}
+			input_report_key(inputdev, macroKeyEvents[macro], p);
+			input_report_abs(inputdev, ABS_MISC,
+					 p | AIPTEK_REPORT_TOOL_STYLUS);
+			input_sync(inputdev);
+		}
+	}
+	/* Report 5s come from the macro keys when pressed by mouse
+	 */
+	else if (data[0] == 5) {
+		jitterable = data[1] & 0x1c;
+
+		p = (data[1] & 0x01) != 0 ? 1 : 0;
+		dv = (data[1] & 0x02) != 0 ? 1 : 0;
+		left = (data[1]& aiptek->curSetting.mouseButtonLeft) != 0 ? 1 : 0;
+		right = (data[1] & aiptek->curSetting.mouseButtonRight) != 0 ? 1 : 0;
+		middle = (data[1] & aiptek->curSetting.mouseButtonMiddle) != 0 ? 1 : 0;
+		macro = data[3];
+
+		if (dv != 0) {
+			input_regs(inputdev, regs);
+
+			/* If we've not already sent a tool_button_?? code, do
+			 * so now. Then set FIRED_BIT so it won't be resent unless
+			 * the user forces FIRED_BIT off.
+			 */
+			if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) {
+				input_report_key(inputdev,
+						 TOOL_BUTTON(aiptek->curSetting.toolMode),
+						 1);
+				aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
+			}
+
+			if (p != 0) {
+				input_report_key(inputdev, BTN_LEFT, left);
+				input_report_key(inputdev, BTN_MIDDLE, middle);
+				input_report_key(inputdev, BTN_RIGHT, right);
+			}
+
+			/* For safety, we're sending key 'break' codes for the
+			 * neighboring macro keys.
+			 */
+			if (macro > 0) {
+				input_report_key(inputdev,
+						 macroKeyEvents[macro - 1], 0);
+			}
+			if (macro < 25) {
+				input_report_key(inputdev,
+						 macroKeyEvents[macro + 1], 0);
+			}
+
+			input_report_key(inputdev, macroKeyEvents[macro], 1);
+			input_report_rel(inputdev, ABS_MISC,
+					 p | AIPTEK_REPORT_TOOL_MOUSE);
+			input_sync(inputdev);
+		}
+	}
+	/* We have no idea which tool can generate a report 6. Theoretically,
+	 * neither need to, having been given reports 4 & 5 for such use.
+	 * However, report 6 is the 'official-looking' report for macroKeys;
+	 * reports 4 & 5 supposively are used to support unnamed, unknown
+	 * hat switches (which just so happen to be the macroKeys.)
+	 */
+	else if (data[0] == 6) {
+		macro = le16_to_cpu(get_unaligned((__le16 *) (data + 1)));
+		input_regs(inputdev, regs);
+
+		if (macro > 0) {
+			input_report_key(inputdev, macroKeyEvents[macro - 1],
+					 0);
+		}
+		if (macro < 25) {
+			input_report_key(inputdev, macroKeyEvents[macro + 1],
+					 0);
+		}
+
+		/* If we've not already sent a tool_button_?? code, do
+		 * so now. Then set FIRED_BIT so it won't be resent unless
+		 * the user forces FIRED_BIT off.
+		 */
+		if (TOOL_BUTTON_FIRED(aiptek->curSetting.toolMode) == 0) {
+			input_report_key(inputdev,
+					 TOOL_BUTTON(aiptek->curSetting.
+						     toolMode), 1);
+			aiptek->curSetting.toolMode |= TOOL_BUTTON_FIRED_BIT;
+		}
+
+		input_report_key(inputdev, macroKeyEvents[macro], 1);
+		input_report_abs(inputdev, ABS_MISC,
+				 1 | AIPTEK_REPORT_TOOL_UNKNOWN);
+		input_sync(inputdev);
+	} else {
+		dbg("Unknown report %d", data[0]);
+	}
+
+	/* Jitter may occur when the user presses a button on the stlyus
+	 * or the mouse. What we do to prevent that is wait 'x' milliseconds
+	 * following a 'jitterable' event, which should give the hand some time
+	 * stabilize itself.
+	 *
+	 * We just introduced aiptek->previousJitterable to carry forth the
+	 * notion that jitter occurs when the button state changes from on to off:
+	 * a person drawing, holding a button down is not subject to jittering.
+	 * With that in mind, changing from upper button depressed to lower button
+	 * WILL transition through a jitter delay.
+	 */
+
+	if (aiptek->previousJitterable != jitterable &&
+	    aiptek->curSetting.jitterDelay != 0 && aiptek->inDelay != 1) {
+		aiptek->endDelay = jiffies +
+		    ((aiptek->curSetting.jitterDelay * HZ) / 1000);
+		aiptek->inDelay = 1;
+	}
+	aiptek->previousJitterable = jitterable;
+
+exit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval != 0) {
+		err("%s - usb_submit_urb failed with result %d",
+		    __FUNCTION__, retval);
+	}
+}
+
+/***********************************************************************
+ * These are the USB id's known so far. We do not identify them to
+ * specific Aiptek model numbers, because there has been overlaps,
+ * use, and reuse of id's in existing models. Certain models have
+ * been known to use more than one ID, indicative perhaps of
+ * manufacturing revisions. In any event, we consider these 
+ * IDs to not be model-specific nor unique.
+ */
+static const struct usb_device_id aiptek_ids[] = {
+	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x01)},
+	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x10)},
+	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x20)},
+	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x21)},
+	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x22)},
+	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x23)},
+	{USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x24)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, aiptek_ids);
+
+/***********************************************************************
+ * Open an instance of the tablet driver.
+ */
+static int aiptek_open(struct input_dev *inputdev)
+{
+	struct aiptek *aiptek = inputdev->private;
+
+	if (aiptek->openCount++ > 0) {
+		return 0;
+	}
+
+	aiptek->urb->dev = aiptek->usbdev;
+	if (usb_submit_urb(aiptek->urb, GFP_KERNEL) != 0) {
+		aiptek->openCount--;
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/***********************************************************************
+ * Close an instance of the tablet driver.
+ */
+static void aiptek_close(struct input_dev *inputdev)
+{
+	struct aiptek *aiptek = inputdev->private;
+
+	if (--aiptek->openCount == 0) {
+		usb_kill_urb(aiptek->urb);
+	}
+}
+
+/***********************************************************************
+ * aiptek_set_report and aiptek_get_report() are borrowed from Linux 2.4.x, 
+ * where they were known as usb_set_report and usb_get_report.
+ */
+static int
+aiptek_set_report(struct aiptek *aiptek,
+		  unsigned char report_type,
+		  unsigned char report_id, void *buffer, int size)
+{
+	return usb_control_msg(aiptek->usbdev,
+			       usb_sndctrlpipe(aiptek->usbdev, 0),
+			       USB_REQ_SET_REPORT,
+			       USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+			       USB_DIR_OUT, (report_type << 8) + report_id,
+			       aiptek->ifnum, buffer, size, 5000);
+}
+
+static int
+aiptek_get_report(struct aiptek *aiptek,
+		  unsigned char report_type,
+		  unsigned char report_id, void *buffer, int size)
+{
+	return usb_control_msg(aiptek->usbdev,
+			       usb_rcvctrlpipe(aiptek->usbdev, 0),
+			       USB_REQ_GET_REPORT,
+			       USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+			       USB_DIR_IN, (report_type << 8) + report_id,
+			       aiptek->ifnum, buffer, size, 5000);
+}
+
+/***********************************************************************
+ * Send a command to the tablet.
+ */
+static int
+aiptek_command(struct aiptek *aiptek, unsigned char command, unsigned char data)
+{
+	const int sizeof_buf = 3 * sizeof(u8);
+	int ret;
+	u8 *buf;
+
+	buf = kmalloc(sizeof_buf, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = 2;
+	buf[1] = command;
+	buf[2] = data;
+
+	if ((ret =
+	     aiptek_set_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) {
+		dbg("aiptek_program: failed, tried to send: 0x%02x 0x%02x",
+		    command, data);
+	}
+	kfree(buf);
+	return ret < 0 ? ret : 0;
+}
+
+/***********************************************************************
+ * Retrieve information from the tablet. Querying info is defined as first
+ * sending the {command,data} sequence as a command, followed by a wait
+ * (aka, "programmaticDelay") and then a "read" request.
+ */
+static int
+aiptek_query(struct aiptek *aiptek, unsigned char command, unsigned char data)
+{
+	const int sizeof_buf = 3 * sizeof(u8);
+	int ret;
+	u8 *buf;
+
+	buf = kmalloc(sizeof_buf, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = 2;
+	buf[1] = command;
+	buf[2] = data;
+
+	if (aiptek_command(aiptek, command, data) != 0) {
+		kfree(buf);
+		return -EIO;
+	}
+	msleep(aiptek->curSetting.programmableDelay);
+
+	if ((ret =
+	     aiptek_get_report(aiptek, 3, 2, buf, sizeof_buf)) != sizeof_buf) {
+		dbg("aiptek_query failed: returned 0x%02x 0x%02x 0x%02x",
+		    buf[0], buf[1], buf[2]);
+		ret = -EIO;
+	} else {
+		ret = le16_to_cpu(get_unaligned((__le16 *) (buf + 1)));
+	}
+	kfree(buf);
+	return ret;
+}
+
+/***********************************************************************
+ * Program the tablet into either absolute or relative mode.
+ * We also get information about the tablet's size.
+ */
+static int aiptek_program_tablet(struct aiptek *aiptek)
+{
+	int ret;
+	/* Execute Resolution500LPI */
+	if ((ret = aiptek_command(aiptek, 0x18, 0x04)) < 0)
+		return ret;
+
+	/* Query getModelCode */
+	if ((ret = aiptek_query(aiptek, 0x02, 0x00)) < 0)
+		return ret;
+	aiptek->features.modelCode = ret & 0xff;
+
+	/* Query getODMCode */
+	if ((ret = aiptek_query(aiptek, 0x03, 0x00)) < 0)
+		return ret;
+	aiptek->features.odmCode = ret;
+
+	/* Query getFirmwareCode */
+	if ((ret = aiptek_query(aiptek, 0x04, 0x00)) < 0)
+		return ret;
+	aiptek->features.firmwareCode = ret;
+
+	/* Query getXextension */
+	if ((ret = aiptek_query(aiptek, 0x01, 0x00)) < 0)
+		return ret;
+	aiptek->inputdev.absmin[ABS_X] = 0;
+	aiptek->inputdev.absmax[ABS_X] = ret - 1;
+
+	/* Query getYextension */
+	if ((ret = aiptek_query(aiptek, 0x01, 0x01)) < 0)
+		return ret;
+	aiptek->inputdev.absmin[ABS_Y] = 0;
+	aiptek->inputdev.absmax[ABS_Y] = ret - 1;
+
+	/* Query getPressureLevels */
+	if ((ret = aiptek_query(aiptek, 0x08, 0x00)) < 0)
+		return ret;
+	aiptek->inputdev.absmin[ABS_PRESSURE] = 0;
+	aiptek->inputdev.absmax[ABS_PRESSURE] = ret - 1;
+
+	/* Depending on whether we are in absolute or relative mode, we will
+	 * do a switchToTablet(absolute) or switchToMouse(relative) command.
+	 */
+	if (aiptek->curSetting.coordinateMode ==
+	    AIPTEK_COORDINATE_ABSOLUTE_MODE) {
+		/* Execute switchToTablet */
+		if ((ret = aiptek_command(aiptek, 0x10, 0x01)) < 0) {
+			return ret;
+		}
+	} else {
+		/* Execute switchToMouse */
+		if ((ret = aiptek_command(aiptek, 0x10, 0x00)) < 0) {
+			return ret;
+		}
+	}
+
+	/* Enable the macro keys */
+	if ((ret = aiptek_command(aiptek, 0x11, 0x02)) < 0)
+		return ret;
+#if 0
+	/* Execute FilterOn */
+	if ((ret = aiptek_command(aiptek, 0x17, 0x00)) < 0)
+		return ret;
+#endif
+
+	/* Execute AutoGainOn */
+	if ((ret = aiptek_command(aiptek, 0x12, 0xff)) < 0)
+		return ret;
+
+	/* Reset the eventCount, so we track events from last (re)programming
+	 */
+	aiptek->diagnostic = AIPTEK_DIAGNOSTIC_NA;
+	aiptek->eventCount = 0;
+
+	return 0;
+}
+
+/***********************************************************************
+ * Sysfs functions. Sysfs prefers that individually-tunable parameters
+ * exist in their separate pseudo-files. Summary data that is immutable
+ * may exist in a singular file so long as you don't define a writeable
+ * interface.
+ */
+
+/***********************************************************************
+ * support the 'size' file -- display support
+ */
+static ssize_t show_tabletSize(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	return snprintf(buf, PAGE_SIZE, "%dx%d\n",
+			aiptek->inputdev.absmax[ABS_X] + 1,
+			aiptek->inputdev.absmax[ABS_Y] + 1);
+}
+
+/* These structs define the sysfs files, param #1 is the name of the
+ * file, param 2 is the file permissions, param 3 & 4 are to the
+ * output generator and input parser routines. Absence of a routine is
+ * permitted -- it only means can't either 'cat' the file, or send data
+ * to it.
+ */
+static DEVICE_ATTR(size, S_IRUGO, show_tabletSize, NULL);
+
+/***********************************************************************
+ * support routines for the 'product_id' file
+ */
+static ssize_t show_tabletProductId(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	return snprintf(buf, PAGE_SIZE, "0x%04x\n",
+			aiptek->inputdev.id.product);
+}
+
+static DEVICE_ATTR(product_id, S_IRUGO, show_tabletProductId, NULL);
+
+/***********************************************************************
+ * support routines for the 'vendor_id' file
+ */
+static ssize_t show_tabletVendorId(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->inputdev.id.vendor);
+}
+
+static DEVICE_ATTR(vendor_id, S_IRUGO, show_tabletVendorId, NULL);
+
+/***********************************************************************
+ * support routines for the 'vendor' file
+ */
+static ssize_t show_tabletManufacturer(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int retval;
+
+	if (aiptek == NULL)
+		return 0;
+
+	retval = snprintf(buf, PAGE_SIZE, "%s\n", aiptek->usbdev->manufacturer);
+	return retval;
+}
+
+static DEVICE_ATTR(vendor, S_IRUGO, show_tabletManufacturer, NULL);
+
+/***********************************************************************
+ * support routines for the 'product' file
+ */
+static ssize_t show_tabletProduct(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int retval;
+
+	if (aiptek == NULL)
+		return 0;
+
+	retval = snprintf(buf, PAGE_SIZE, "%s\n", aiptek->usbdev->product);
+	return retval;
+}
+
+static DEVICE_ATTR(product, S_IRUGO, show_tabletProduct, NULL);
+
+/***********************************************************************
+ * support routines for the 'pointer_mode' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletPointerMode(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	char *s;
+
+	if (aiptek == NULL)
+		return 0;
+
+	switch (aiptek->curSetting.pointerMode) {
+	case AIPTEK_POINTER_ONLY_STYLUS_MODE:
+		s = "stylus";
+		break;
+
+	case AIPTEK_POINTER_ONLY_MOUSE_MODE:
+		s = "mouse";
+		break;
+
+	case AIPTEK_POINTER_EITHER_MODE:
+		s = "either";
+		break;
+
+	default:
+		s = "unknown";
+		break;
+	}
+	return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletPointerMode(struct device *dev, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	if (aiptek == NULL)
+		return 0;
+
+	if (strcmp(buf, "stylus") == 0) {
+		aiptek->newSetting.pointerMode =
+		    AIPTEK_POINTER_ONLY_STYLUS_MODE;
+	} else if (strcmp(buf, "mouse") == 0) {
+		aiptek->newSetting.pointerMode = AIPTEK_POINTER_ONLY_MOUSE_MODE;
+	} else if (strcmp(buf, "either") == 0) {
+		aiptek->newSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE;
+	}
+	return count;
+}
+
+static DEVICE_ATTR(pointer_mode,
+		   S_IRUGO | S_IWUGO,
+		   show_tabletPointerMode, store_tabletPointerMode);
+
+/***********************************************************************
+ * support routines for the 'coordinate_mode' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletCoordinateMode(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	char *s;
+
+	if (aiptek == NULL)
+		return 0;
+
+	switch (aiptek->curSetting.coordinateMode) {
+	case AIPTEK_COORDINATE_ABSOLUTE_MODE:
+		s = "absolute";
+		break;
+
+	case AIPTEK_COORDINATE_RELATIVE_MODE:
+		s = "relative";
+		break;
+
+	default:
+		s = "unknown";
+		break;
+	}
+	return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletCoordinateMode(struct device *dev, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	if (aiptek == NULL)
+		return 0;
+
+	if (strcmp(buf, "absolute") == 0) {
+		aiptek->newSetting.pointerMode =
+		    AIPTEK_COORDINATE_ABSOLUTE_MODE;
+	} else if (strcmp(buf, "relative") == 0) {
+		aiptek->newSetting.pointerMode =
+		    AIPTEK_COORDINATE_RELATIVE_MODE;
+	}
+	return count;
+}
+
+static DEVICE_ATTR(coordinate_mode,
+		   S_IRUGO | S_IWUGO,
+		   show_tabletCoordinateMode, store_tabletCoordinateMode);
+
+/***********************************************************************
+ * support routines for the 'tool_mode' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletToolMode(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	char *s;
+
+	if (aiptek == NULL)
+		return 0;
+
+	switch (TOOL_BUTTON(aiptek->curSetting.toolMode)) {
+	case AIPTEK_TOOL_BUTTON_MOUSE_MODE:
+		s = "mouse";
+		break;
+
+	case AIPTEK_TOOL_BUTTON_ERASER_MODE:
+		s = "eraser";
+		break;
+
+	case AIPTEK_TOOL_BUTTON_PENCIL_MODE:
+		s = "pencil";
+		break;
+
+	case AIPTEK_TOOL_BUTTON_PEN_MODE:
+		s = "pen";
+		break;
+
+	case AIPTEK_TOOL_BUTTON_BRUSH_MODE:
+		s = "brush";
+		break;
+
+	case AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE:
+		s = "airbrush";
+		break;
+
+	case AIPTEK_TOOL_BUTTON_LENS_MODE:
+		s = "lens";
+		break;
+
+	default:
+		s = "unknown";
+		break;
+	}
+	return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletToolMode(struct device *dev, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	if (aiptek == NULL)
+		return 0;
+
+	if (strcmp(buf, "mouse") == 0) {
+		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_MOUSE_MODE;
+	} else if (strcmp(buf, "eraser") == 0) {
+		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_ERASER_MODE;
+	} else if (strcmp(buf, "pencil") == 0) {
+		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_PENCIL_MODE;
+	} else if (strcmp(buf, "pen") == 0) {
+		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE;
+	} else if (strcmp(buf, "brush") == 0) {
+		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_BRUSH_MODE;
+	} else if (strcmp(buf, "airbrush") == 0) {
+		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_AIRBRUSH_MODE;
+	} else if (strcmp(buf, "lens") == 0) {
+		aiptek->newSetting.toolMode = AIPTEK_TOOL_BUTTON_LENS_MODE;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(tool_mode,
+		   S_IRUGO | S_IWUGO,
+		   show_tabletToolMode, store_tabletToolMode);
+
+/***********************************************************************
+ * support routines for the 'xtilt' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletXtilt(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	if (aiptek->curSetting.xTilt == AIPTEK_TILT_DISABLE) {
+		return snprintf(buf, PAGE_SIZE, "disable\n");
+	} else {
+		return snprintf(buf, PAGE_SIZE, "%d\n",
+				aiptek->curSetting.xTilt);
+	}
+}
+
+static ssize_t
+store_tabletXtilt(struct device *dev, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int x;
+
+	if (aiptek == NULL)
+		return 0;
+
+	if (strcmp(buf, "disable") == 0) {
+		aiptek->newSetting.xTilt = AIPTEK_TILT_DISABLE;
+	} else {
+		x = (int)simple_strtol(buf, NULL, 10);
+		if (x >= AIPTEK_TILT_MIN && x <= AIPTEK_TILT_MAX) {
+			aiptek->newSetting.xTilt = x;
+		}
+	}
+	return count;
+}
+
+static DEVICE_ATTR(xtilt,
+		   S_IRUGO | S_IWUGO, show_tabletXtilt, store_tabletXtilt);
+
+/***********************************************************************
+ * support routines for the 'ytilt' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletYtilt(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	if (aiptek->curSetting.yTilt == AIPTEK_TILT_DISABLE) {
+		return snprintf(buf, PAGE_SIZE, "disable\n");
+	} else {
+		return snprintf(buf, PAGE_SIZE, "%d\n",
+				aiptek->curSetting.yTilt);
+	}
+}
+
+static ssize_t
+store_tabletYtilt(struct device *dev, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	int y;
+
+	if (aiptek == NULL)
+		return 0;
+
+	if (strcmp(buf, "disable") == 0) {
+		aiptek->newSetting.yTilt = AIPTEK_TILT_DISABLE;
+	} else {
+		y = (int)simple_strtol(buf, NULL, 10);
+		if (y >= AIPTEK_TILT_MIN && y <= AIPTEK_TILT_MAX) {
+			aiptek->newSetting.yTilt = y;
+		}
+	}
+	return count;
+}
+
+static DEVICE_ATTR(ytilt,
+		   S_IRUGO | S_IWUGO, show_tabletYtilt, store_tabletYtilt);
+
+/***********************************************************************
+ * support routines for the 'jitter' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletJitterDelay(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", aiptek->curSetting.jitterDelay);
+}
+
+static ssize_t
+store_tabletJitterDelay(struct device *dev, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	aiptek->newSetting.jitterDelay = (int)simple_strtol(buf, NULL, 10);
+	return count;
+}
+
+static DEVICE_ATTR(jitter,
+		   S_IRUGO | S_IWUGO,
+		   show_tabletJitterDelay, store_tabletJitterDelay);
+
+/***********************************************************************
+ * support routines for the 'delay' file. Note that this file
+ * both displays current setting and allows reprogramming.
+ */
+static ssize_t show_tabletProgrammableDelay(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			aiptek->curSetting.programmableDelay);
+}
+
+static ssize_t
+store_tabletProgrammableDelay(struct device *dev, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	aiptek->newSetting.programmableDelay = (int)simple_strtol(buf, NULL, 10);
+	return count;
+}
+
+static DEVICE_ATTR(delay,
+		   S_IRUGO | S_IWUGO,
+		   show_tabletProgrammableDelay, store_tabletProgrammableDelay);
+
+/***********************************************************************
+ * support routines for the 'input_path' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_tabletInputDevice(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	return snprintf(buf, PAGE_SIZE, "/dev/input/%s\n",
+			aiptek->features.inputPath);
+}
+
+static DEVICE_ATTR(input_path, S_IRUGO, show_tabletInputDevice, NULL);
+
+/***********************************************************************
+ * support routines for the 'event_count' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_tabletEventsReceived(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	return snprintf(buf, PAGE_SIZE, "%ld\n", aiptek->eventCount);
+}
+
+static DEVICE_ATTR(event_count, S_IRUGO, show_tabletEventsReceived, NULL);
+
+/***********************************************************************
+ * support routines for the 'diagnostic' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_tabletDiagnosticMessage(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	char *retMsg;
+
+	if (aiptek == NULL)
+		return 0;
+
+	switch (aiptek->diagnostic) {
+	case AIPTEK_DIAGNOSTIC_NA:
+		retMsg = "no errors\n";
+		break;
+
+	case AIPTEK_DIAGNOSTIC_SENDING_RELATIVE_IN_ABSOLUTE:
+		retMsg = "Error: receiving relative reports\n";
+		break;
+
+	case AIPTEK_DIAGNOSTIC_SENDING_ABSOLUTE_IN_RELATIVE:
+		retMsg = "Error: receiving absolute reports\n";
+		break;
+
+	case AIPTEK_DIAGNOSTIC_TOOL_DISALLOWED:
+		if (aiptek->curSetting.pointerMode ==
+		    AIPTEK_POINTER_ONLY_MOUSE_MODE) {
+			retMsg = "Error: receiving stylus reports\n";
+		} else {
+			retMsg = "Error: receiving mouse reports\n";
+		}
+		break;
+
+	default:
+		return 0;
+	}
+	return snprintf(buf, PAGE_SIZE, retMsg);
+}
+
+static DEVICE_ATTR(diagnostic, S_IRUGO, show_tabletDiagnosticMessage, NULL);
+
+/***********************************************************************
+ * support routines for the 'stylus_upper' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletStylusUpper(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	char *s;
+
+	if (aiptek == NULL)
+		return 0;
+
+	switch (aiptek->curSetting.stylusButtonUpper) {
+	case AIPTEK_STYLUS_UPPER_BUTTON:
+		s = "upper";
+		break;
+
+	case AIPTEK_STYLUS_LOWER_BUTTON:
+		s = "lower";
+		break;
+
+	default:
+		s = "unknown";
+		break;
+	}
+	return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletStylusUpper(struct device *dev, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	if (strcmp(buf, "upper") == 0) {
+		aiptek->newSetting.stylusButtonUpper =
+		    AIPTEK_STYLUS_UPPER_BUTTON;
+	} else if (strcmp(buf, "lower") == 0) {
+		aiptek->newSetting.stylusButtonUpper =
+		    AIPTEK_STYLUS_LOWER_BUTTON;
+	}
+	return count;
+}
+
+static DEVICE_ATTR(stylus_upper,
+		   S_IRUGO | S_IWUGO,
+		   show_tabletStylusUpper, store_tabletStylusUpper);
+
+/***********************************************************************
+ * support routines for the 'stylus_lower' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletStylusLower(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	char *s;
+
+	if (aiptek == NULL)
+		return 0;
+
+	switch (aiptek->curSetting.stylusButtonLower) {
+	case AIPTEK_STYLUS_UPPER_BUTTON:
+		s = "upper";
+		break;
+
+	case AIPTEK_STYLUS_LOWER_BUTTON:
+		s = "lower";
+		break;
+
+	default:
+		s = "unknown";
+		break;
+	}
+	return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletStylusLower(struct device *dev, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	if (strcmp(buf, "upper") == 0) {
+		aiptek->newSetting.stylusButtonLower =
+		    AIPTEK_STYLUS_UPPER_BUTTON;
+	} else if (strcmp(buf, "lower") == 0) {
+		aiptek->newSetting.stylusButtonLower =
+		    AIPTEK_STYLUS_LOWER_BUTTON;
+	}
+	return count;
+}
+
+static DEVICE_ATTR(stylus_lower,
+		   S_IRUGO | S_IWUGO,
+		   show_tabletStylusLower, store_tabletStylusLower);
+
+/***********************************************************************
+ * support routines for the 'mouse_left' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletMouseLeft(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	char *s;
+
+	if (aiptek == NULL)
+		return 0;
+
+	switch (aiptek->curSetting.mouseButtonLeft) {
+	case AIPTEK_MOUSE_LEFT_BUTTON:
+		s = "left";
+		break;
+
+	case AIPTEK_MOUSE_MIDDLE_BUTTON:
+		s = "middle";
+		break;
+
+	case AIPTEK_MOUSE_RIGHT_BUTTON:
+		s = "right";
+		break;
+
+	default:
+		s = "unknown";
+		break;
+	}
+	return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletMouseLeft(struct device *dev, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	if (strcmp(buf, "left") == 0) {
+		aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON;
+	} else if (strcmp(buf, "middle") == 0) {
+		aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_MIDDLE_BUTTON;
+	} else if (strcmp(buf, "right") == 0) {
+		aiptek->newSetting.mouseButtonLeft = AIPTEK_MOUSE_RIGHT_BUTTON;
+	}
+	return count;
+}
+
+static DEVICE_ATTR(mouse_left,
+		   S_IRUGO | S_IWUGO,
+		   show_tabletMouseLeft, store_tabletMouseLeft);
+
+/***********************************************************************
+ * support routines for the 'mouse_middle' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletMouseMiddle(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	char *s;
+
+	if (aiptek == NULL)
+		return 0;
+
+	switch (aiptek->curSetting.mouseButtonMiddle) {
+	case AIPTEK_MOUSE_LEFT_BUTTON:
+		s = "left";
+		break;
+
+	case AIPTEK_MOUSE_MIDDLE_BUTTON:
+		s = "middle";
+		break;
+
+	case AIPTEK_MOUSE_RIGHT_BUTTON:
+		s = "right";
+		break;
+
+	default:
+		s = "unknown";
+		break;
+	}
+	return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletMouseMiddle(struct device *dev, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	if (strcmp(buf, "left") == 0) {
+		aiptek->newSetting.mouseButtonMiddle = AIPTEK_MOUSE_LEFT_BUTTON;
+	} else if (strcmp(buf, "middle") == 0) {
+		aiptek->newSetting.mouseButtonMiddle =
+		    AIPTEK_MOUSE_MIDDLE_BUTTON;
+	} else if (strcmp(buf, "right") == 0) {
+		aiptek->newSetting.mouseButtonMiddle =
+		    AIPTEK_MOUSE_RIGHT_BUTTON;
+	}
+	return count;
+}
+
+static DEVICE_ATTR(mouse_middle,
+		   S_IRUGO | S_IWUGO,
+		   show_tabletMouseMiddle, store_tabletMouseMiddle);
+
+/***********************************************************************
+ * support routines for the 'mouse_right' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletMouseRight(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+	char *s;
+
+	if (aiptek == NULL)
+		return 0;
+
+	switch (aiptek->curSetting.mouseButtonRight) {
+	case AIPTEK_MOUSE_LEFT_BUTTON:
+		s = "left";
+		break;
+
+	case AIPTEK_MOUSE_MIDDLE_BUTTON:
+		s = "middle";
+		break;
+
+	case AIPTEK_MOUSE_RIGHT_BUTTON:
+		s = "right";
+		break;
+
+	default:
+		s = "unknown";
+		break;
+	}
+	return snprintf(buf, PAGE_SIZE, "%s\n", s);
+}
+
+static ssize_t
+store_tabletMouseRight(struct device *dev, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	if (strcmp(buf, "left") == 0) {
+		aiptek->newSetting.mouseButtonRight = AIPTEK_MOUSE_LEFT_BUTTON;
+	} else if (strcmp(buf, "middle") == 0) {
+		aiptek->newSetting.mouseButtonRight =
+		    AIPTEK_MOUSE_MIDDLE_BUTTON;
+	} else if (strcmp(buf, "right") == 0) {
+		aiptek->newSetting.mouseButtonRight = AIPTEK_MOUSE_RIGHT_BUTTON;
+	}
+	return count;
+}
+
+static DEVICE_ATTR(mouse_right,
+		   S_IRUGO | S_IWUGO,
+		   show_tabletMouseRight, store_tabletMouseRight);
+
+/***********************************************************************
+ * support routines for the 'wheel' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletWheel(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	if (aiptek->curSetting.wheel == AIPTEK_WHEEL_DISABLE) {
+		return snprintf(buf, PAGE_SIZE, "disable\n");
+	} else {
+		return snprintf(buf, PAGE_SIZE, "%d\n",
+				aiptek->curSetting.wheel);
+	}
+}
+
+static ssize_t
+store_tabletWheel(struct device *dev, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	aiptek->newSetting.wheel = (int)simple_strtol(buf, NULL, 10);
+	return count;
+}
+
+static DEVICE_ATTR(wheel,
+		   S_IRUGO | S_IWUGO, show_tabletWheel, store_tabletWheel);
+
+/***********************************************************************
+ * support routines for the 'execute' file. Note that this file
+ * both displays current setting and allows for setting changing.
+ */
+static ssize_t show_tabletExecute(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	/* There is nothing useful to display, so a one-line manual
+	 * is in order...
+	 */
+	return snprintf(buf, PAGE_SIZE,
+			"Write anything to this file to program your tablet.\n");
+}
+
+static ssize_t
+store_tabletExecute(struct device *dev, const char *buf, size_t count)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	/* We do not care what you write to this file. Merely the action
+	 * of writing to this file triggers a tablet reprogramming.
+	 */
+	memcpy(&aiptek->curSetting, &aiptek->newSetting,
+	       sizeof(struct aiptek_settings));
+
+	if (aiptek_program_tablet(aiptek) < 0)
+		return -EIO;
+
+	return count;
+}
+
+static DEVICE_ATTR(execute,
+		   S_IRUGO | S_IWUGO, show_tabletExecute, store_tabletExecute);
+
+/***********************************************************************
+ * support routines for the 'odm_code' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_tabletODMCode(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.odmCode);
+}
+
+static DEVICE_ATTR(odm_code, S_IRUGO, show_tabletODMCode, NULL);
+
+/***********************************************************************
+ * support routines for the 'model_code' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_tabletModelCode(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	return snprintf(buf, PAGE_SIZE, "0x%04x\n", aiptek->features.modelCode);
+}
+
+static DEVICE_ATTR(model_code, S_IRUGO, show_tabletModelCode, NULL);
+
+/***********************************************************************
+ * support routines for the 'firmware_code' file. Note that this file
+ * only displays current setting.
+ */
+static ssize_t show_firmwareCode(struct device *dev, char *buf)
+{
+	struct aiptek *aiptek = dev_get_drvdata(dev);
+
+	if (aiptek == NULL)
+		return 0;
+
+	return snprintf(buf, PAGE_SIZE, "%04x\n",
+			aiptek->features.firmwareCode);
+}
+
+static DEVICE_ATTR(firmware_code, S_IRUGO, show_firmwareCode, NULL);
+
+/***********************************************************************
+ * This routine removes all existing sysfs files managed by this device
+ * driver.
+ */
+static void aiptek_delete_files(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_size);
+	device_remove_file(dev, &dev_attr_product_id);
+	device_remove_file(dev, &dev_attr_vendor_id);
+	device_remove_file(dev, &dev_attr_vendor);
+	device_remove_file(dev, &dev_attr_product);
+	device_remove_file(dev, &dev_attr_pointer_mode);
+	device_remove_file(dev, &dev_attr_coordinate_mode);
+	device_remove_file(dev, &dev_attr_tool_mode);
+	device_remove_file(dev, &dev_attr_xtilt);
+	device_remove_file(dev, &dev_attr_ytilt);
+	device_remove_file(dev, &dev_attr_jitter);
+	device_remove_file(dev, &dev_attr_delay);
+	device_remove_file(dev, &dev_attr_input_path);
+	device_remove_file(dev, &dev_attr_event_count);
+	device_remove_file(dev, &dev_attr_diagnostic);
+	device_remove_file(dev, &dev_attr_odm_code);
+	device_remove_file(dev, &dev_attr_model_code);
+	device_remove_file(dev, &dev_attr_firmware_code);
+	device_remove_file(dev, &dev_attr_stylus_lower);
+	device_remove_file(dev, &dev_attr_stylus_upper);
+	device_remove_file(dev, &dev_attr_mouse_left);
+	device_remove_file(dev, &dev_attr_mouse_middle);
+	device_remove_file(dev, &dev_attr_mouse_right);
+	device_remove_file(dev, &dev_attr_wheel);
+	device_remove_file(dev, &dev_attr_execute);
+}
+
+/***********************************************************************
+ * This routine creates the sysfs files managed by this device
+ * driver.
+ */
+static int aiptek_add_files(struct device *dev)
+{
+	int ret;
+
+	if ((ret = device_create_file(dev, &dev_attr_size)) ||
+	    (ret = device_create_file(dev, &dev_attr_product_id)) ||
+	    (ret = device_create_file(dev, &dev_attr_vendor_id)) ||
+	    (ret = device_create_file(dev, &dev_attr_vendor)) ||
+	    (ret = device_create_file(dev, &dev_attr_product)) ||
+	    (ret = device_create_file(dev, &dev_attr_pointer_mode)) ||
+	    (ret = device_create_file(dev, &dev_attr_coordinate_mode)) ||
+	    (ret = device_create_file(dev, &dev_attr_tool_mode)) ||
+	    (ret = device_create_file(dev, &dev_attr_xtilt)) ||
+	    (ret = device_create_file(dev, &dev_attr_ytilt)) ||
+	    (ret = device_create_file(dev, &dev_attr_jitter)) ||
+	    (ret = device_create_file(dev, &dev_attr_delay)) ||
+	    (ret = device_create_file(dev, &dev_attr_input_path)) ||
+	    (ret = device_create_file(dev, &dev_attr_event_count)) ||
+	    (ret = device_create_file(dev, &dev_attr_diagnostic)) ||
+	    (ret = device_create_file(dev, &dev_attr_odm_code)) ||
+	    (ret = device_create_file(dev, &dev_attr_model_code)) ||
+	    (ret = device_create_file(dev, &dev_attr_firmware_code)) ||
+	    (ret = device_create_file(dev, &dev_attr_stylus_lower)) ||
+	    (ret = device_create_file(dev, &dev_attr_stylus_upper)) ||
+	    (ret = device_create_file(dev, &dev_attr_mouse_left)) ||
+	    (ret = device_create_file(dev, &dev_attr_mouse_middle)) ||
+	    (ret = device_create_file(dev, &dev_attr_mouse_right)) ||
+	    (ret = device_create_file(dev, &dev_attr_wheel)) ||
+	    (ret = device_create_file(dev, &dev_attr_execute))) {
+		err("aiptek: killing own sysfs device files\n");
+		aiptek_delete_files(dev);
+	}
+	return ret;
+}
+
+/***********************************************************************
+ * This routine is called when a tablet has been identified. It basically
+ * sets up the tablet and the driver's internal structures.
+ */
+static int
+aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *usbdev = interface_to_usbdev(intf);
+	struct usb_endpoint_descriptor *endpoint;
+	struct aiptek *aiptek;
+	struct input_dev *inputdev;
+	struct input_handle *inputhandle;
+	struct list_head *node, *next;
+	char path[64 + 1];
+	int i;
+	int speeds[] = { 0,
+		AIPTEK_PROGRAMMABLE_DELAY_50,
+		AIPTEK_PROGRAMMABLE_DELAY_400,
+		AIPTEK_PROGRAMMABLE_DELAY_25,
+		AIPTEK_PROGRAMMABLE_DELAY_100,
+		AIPTEK_PROGRAMMABLE_DELAY_200,
+		AIPTEK_PROGRAMMABLE_DELAY_300
+	};
+
+	/* programmableDelay is where the command-line specified
+	 * delay is kept. We make it the first element of speeds[],
+	 * so therefore, your override speed is tried first, then the
+	 * remainder. Note that the default value of 400ms will be tried
+	 * if you do not specify any command line parameter.
+	 */
+	speeds[0] = programmableDelay;
+
+	if ((aiptek = kmalloc(sizeof(struct aiptek), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	memset(aiptek, 0, sizeof(struct aiptek));
+
+	aiptek->data = usb_buffer_alloc(usbdev, AIPTEK_PACKET_LENGTH,
+					SLAB_ATOMIC, &aiptek->data_dma);
+	if (aiptek->data == NULL) {
+		kfree(aiptek);
+		return -ENOMEM;
+	}
+
+	aiptek->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (aiptek->urb == NULL) {
+		usb_buffer_free(usbdev, AIPTEK_PACKET_LENGTH, aiptek->data,
+				aiptek->data_dma);
+		kfree(aiptek);
+		return -ENOMEM;
+	}
+
+	/* Set up the curSettings struct. Said struct contains the current
+	 * programmable parameters. The newSetting struct contains changes
+	 * the user makes to the settings via the sysfs interface. Those
+	 * changes are not "committed" to curSettings until the user
+	 * writes to the sysfs/.../execute file.
+	 */
+	aiptek->curSetting.pointerMode = AIPTEK_POINTER_EITHER_MODE;
+	aiptek->curSetting.coordinateMode = AIPTEK_COORDINATE_ABSOLUTE_MODE;
+	aiptek->curSetting.toolMode = AIPTEK_TOOL_BUTTON_PEN_MODE;
+	aiptek->curSetting.xTilt = AIPTEK_TILT_DISABLE;
+	aiptek->curSetting.yTilt = AIPTEK_TILT_DISABLE;
+	aiptek->curSetting.mouseButtonLeft = AIPTEK_MOUSE_LEFT_BUTTON;
+	aiptek->curSetting.mouseButtonMiddle = AIPTEK_MOUSE_MIDDLE_BUTTON;
+	aiptek->curSetting.mouseButtonRight = AIPTEK_MOUSE_RIGHT_BUTTON;
+	aiptek->curSetting.stylusButtonUpper = AIPTEK_STYLUS_UPPER_BUTTON;
+	aiptek->curSetting.stylusButtonLower = AIPTEK_STYLUS_LOWER_BUTTON;
+	aiptek->curSetting.jitterDelay = jitterDelay;
+	aiptek->curSetting.programmableDelay = programmableDelay;
+
+	/* Both structs should have equivalent settings
+	 */
+	memcpy(&aiptek->newSetting, &aiptek->curSetting,
+	       sizeof(struct aiptek_settings));
+
+	/* Now program the capacities of the tablet, in terms of being
+	 * an input device.
+	 */
+	aiptek->inputdev.evbit[0] |= BIT(EV_KEY)
+	    | BIT(EV_ABS)
+	    | BIT(EV_REL)
+	    | BIT(EV_MSC);
+
+	aiptek->inputdev.absbit[0] |=
+	    (BIT(ABS_X) |
+	     BIT(ABS_Y) |
+	     BIT(ABS_PRESSURE) |
+	     BIT(ABS_TILT_X) |
+	     BIT(ABS_TILT_Y) | BIT(ABS_WHEEL) | BIT(ABS_MISC));
+
+	aiptek->inputdev.relbit[0] |=
+	    (BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL) | BIT(REL_MISC));
+
+	aiptek->inputdev.keybit[LONG(BTN_LEFT)] |=
+	    (BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE));
+
+	aiptek->inputdev.keybit[LONG(BTN_DIGI)] |=
+	    (BIT(BTN_TOOL_PEN) |
+	     BIT(BTN_TOOL_RUBBER) |
+	     BIT(BTN_TOOL_PENCIL) |
+	     BIT(BTN_TOOL_AIRBRUSH) |
+	     BIT(BTN_TOOL_BRUSH) |
+	     BIT(BTN_TOOL_MOUSE) |
+	     BIT(BTN_TOOL_LENS) |
+	     BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2));
+
+	aiptek->inputdev.mscbit[0] = BIT(MSC_SERIAL);
+
+	/* Programming the tablet macro keys needs to be done with a for loop
+	 * as the keycodes are discontiguous.
+	 */
+	for (i = 0; i < sizeof(macroKeyEvents) / sizeof(macroKeyEvents[0]); ++i)
+		set_bit(macroKeyEvents[i], aiptek->inputdev.keybit);
+
+	/* Set up client data, pointers to open and close routines
+	 * for the input device.
+	 */
+	aiptek->inputdev.private = aiptek;
+	aiptek->inputdev.open = aiptek_open;
+	aiptek->inputdev.close = aiptek_close;
+
+	/* Determine the usb devices' physical path.
+	 * Asketh not why we always pretend we're using "../input0",
+	 * but I suspect this will have to be refactored one
+	 * day if a single USB device can be a keyboard & a mouse
+	 * & a tablet, and the inputX number actually will tell
+	 * us something...
+	 */
+	if (usb_make_path(usbdev, path, 64) > 0)
+		sprintf(aiptek->features.usbPath, "%s/input0", path);
+
+	/* Program the input device coordinate capacities. We do not yet
+	 * know what maximum X, Y, and Z values are, so we're putting fake
+	 * values in. Later, we'll ask the tablet to put in the correct
+	 * values.
+	 */
+	aiptek->inputdev.absmin[ABS_X] = 0;
+	aiptek->inputdev.absmax[ABS_X] = 2999;
+	aiptek->inputdev.absmin[ABS_Y] = 0;
+	aiptek->inputdev.absmax[ABS_Y] = 2249;
+	aiptek->inputdev.absmin[ABS_PRESSURE] = 0;
+	aiptek->inputdev.absmax[ABS_PRESSURE] = 511;
+	aiptek->inputdev.absmin[ABS_TILT_X] = AIPTEK_TILT_MIN;
+	aiptek->inputdev.absmax[ABS_TILT_X] = AIPTEK_TILT_MAX;
+	aiptek->inputdev.absmin[ABS_TILT_Y] = AIPTEK_TILT_MIN;
+	aiptek->inputdev.absmax[ABS_TILT_Y] = AIPTEK_TILT_MAX;
+	aiptek->inputdev.absmin[ABS_WHEEL] = AIPTEK_WHEEL_MIN;
+	aiptek->inputdev.absmax[ABS_WHEEL] = AIPTEK_WHEEL_MAX - 1;
+	aiptek->inputdev.absfuzz[ABS_X] = 0;
+	aiptek->inputdev.absfuzz[ABS_Y] = 0;
+	aiptek->inputdev.absfuzz[ABS_PRESSURE] = 0;
+	aiptek->inputdev.absfuzz[ABS_TILT_X] = 0;
+	aiptek->inputdev.absfuzz[ABS_TILT_Y] = 0;
+	aiptek->inputdev.absfuzz[ABS_WHEEL] = 0;
+	aiptek->inputdev.absflat[ABS_X] = 0;
+	aiptek->inputdev.absflat[ABS_Y] = 0;
+	aiptek->inputdev.absflat[ABS_PRESSURE] = 0;
+	aiptek->inputdev.absflat[ABS_TILT_X] = 0;
+	aiptek->inputdev.absflat[ABS_TILT_Y] = 0;
+	aiptek->inputdev.absflat[ABS_WHEEL] = 0;
+	aiptek->inputdev.name = "Aiptek";
+	aiptek->inputdev.phys = aiptek->features.usbPath;
+	aiptek->inputdev.id.bustype = BUS_USB;
+	aiptek->inputdev.id.vendor = le16_to_cpu(usbdev->descriptor.idVendor);
+	aiptek->inputdev.id.product = le16_to_cpu(usbdev->descriptor.idProduct);
+	aiptek->inputdev.id.version = le16_to_cpu(usbdev->descriptor.bcdDevice);
+
+	aiptek->usbdev = usbdev;
+	aiptek->ifnum = intf->altsetting[0].desc.bInterfaceNumber;
+	aiptek->inDelay = 0;
+	aiptek->endDelay = 0;
+	aiptek->previousJitterable = 0;
+
+	endpoint = &intf->altsetting[0].endpoint[0].desc;
+
+	/* Go set up our URB, which is called when the tablet receives
+	 * input.
+	 */
+	usb_fill_int_urb(aiptek->urb,
+			 aiptek->usbdev,
+			 usb_rcvintpipe(aiptek->usbdev,
+					endpoint->bEndpointAddress),
+			 aiptek->data, 8, aiptek_irq, aiptek,
+			 endpoint->bInterval);
+
+	aiptek->urb->transfer_dma = aiptek->data_dma;
+	aiptek->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* Register the tablet as an Input Device
+	 */
+	input_register_device(&aiptek->inputdev);
+
+	/* We now will look for the evdev device which is mapped to
+	 * the tablet. The partial name is kept in the link list of
+	 * input_handles associated with this input device.
+	 * What identifies an evdev input_handler is that it begins
+	 * with 'event', continues with a digit, and that in turn
+	 * is mapped to /{devfs}/input/eventN.
+	 */
+	inputdev = &aiptek->inputdev;
+	list_for_each_safe(node, next, &inputdev->h_list) {
+		inputhandle = to_handle(node);
+		if (strncmp(inputhandle->name, "event", 5) == 0) {
+			strcpy(aiptek->features.inputPath, inputhandle->name);
+			break;
+		}
+	}
+
+	info("input: Aiptek on %s (%s)\n", path, aiptek->features.inputPath);
+
+	/* Program the tablet. This sets the tablet up in the mode
+	 * specified in newSetting, and also queries the tablet's
+	 * physical capacities.
+	 *
+	 * Sanity check: if a tablet doesn't like the slow programmatic
+	 * delay, we often get sizes of 0x0. Let's use that as an indicator
+	 * to try faster delays, up to 25 ms. If that logic fails, well, you'll
+	 * have to explain to us how your tablet thinks it's 0x0, and yet that's
+	 * not an error :-)
+	 */
+
+	for (i = 0; i < sizeof(speeds) / sizeof(speeds[0]); ++i) {
+		aiptek->curSetting.programmableDelay = speeds[i];
+		(void)aiptek_program_tablet(aiptek);
+		if (aiptek->inputdev.absmax[ABS_X] > 0) {
+			info("input: Aiptek using %d ms programming speed\n",
+			     aiptek->curSetting.programmableDelay);
+			break;
+		}
+	}
+
+	/* Associate this driver's struct with the usb interface.
+	 */
+	usb_set_intfdata(intf, aiptek);
+
+	/* Set up the sysfs files
+	 */
+	aiptek_add_files(&intf->dev);
+
+	/* Make sure the evdev module is loaded. Assuming evdev IS a module :-)
+	 */
+	if (request_module("evdev") != 0)
+		info("aiptek: error loading 'evdev' module");
+
+	return 0;
+}
+
+/* Forward declaration */
+static void aiptek_disconnect(struct usb_interface *intf);
+
+static struct usb_driver aiptek_driver = {
+	.owner = THIS_MODULE,
+	.name = "aiptek",
+	.probe = aiptek_probe,
+	.disconnect = aiptek_disconnect,
+	.id_table = aiptek_ids,
+};
+
+/***********************************************************************
+ * Deal with tablet disconnecting from the system.
+ */
+static void aiptek_disconnect(struct usb_interface *intf)
+{
+	struct aiptek *aiptek = usb_get_intfdata(intf);
+
+	/* Disassociate driver's struct with usb interface
+	 */
+	usb_set_intfdata(intf, NULL);
+	if (aiptek != NULL) {
+		/* Free & unhook everything from the system.
+		 */
+		usb_kill_urb(aiptek->urb);
+		input_unregister_device(&aiptek->inputdev);
+		aiptek_delete_files(&intf->dev);
+		usb_free_urb(aiptek->urb);
+		usb_buffer_free(interface_to_usbdev(intf),
+				AIPTEK_PACKET_LENGTH,
+				aiptek->data, aiptek->data_dma);
+		kfree(aiptek);
+		aiptek = NULL;
+	}
+}
+
+static int __init aiptek_init(void)
+{
+	int result = usb_register(&aiptek_driver);
+	if (result == 0) {
+		info(DRIVER_VERSION ": " DRIVER_AUTHOR);
+		info(DRIVER_DESC);
+	}
+	return result;
+}
+
+static void __exit aiptek_exit(void)
+{
+	usb_deregister(&aiptek_driver);
+}
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(programmableDelay, int, 0);
+MODULE_PARM_DESC(programmableDelay, "delay used during tablet programming");
+module_param(jitterDelay, int, 0);
+MODULE_PARM_DESC(jitterDelay, "stylus/mouse settlement delay");
+
+module_init(aiptek_init);
+module_exit(aiptek_exit);
diff --git a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c
new file mode 100644
index 0000000..355add5
--- /dev/null
+++ b/drivers/usb/input/ati_remote.c
@@ -0,0 +1,850 @@
+/* 
+ *  USB ATI Remote support
+ *
+ *  Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
+ *  Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev
+ *
+ *  This 2.2.0 version is a rewrite / cleanup of the 2.1.1 driver, including
+ *  porting to the 2.6 kernel interfaces, along with other modification 
+ *  to better match the style of the existing usb/input drivers.  However, the
+ *  protocol and hardware handling is essentially unchanged from 2.1.1.
+ *  
+ *  The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by 
+ *  Vojtech Pavlik.
+ *
+ *  Changes:
+ *
+ *  Feb 2004: Torrey Hoffman <thoffman@arnor.net>
+ *            Version 2.2.0
+ *  Jun 2004: Torrey Hoffman <thoffman@arnor.net>
+ *            Version 2.2.1
+ *            Added key repeat support contributed by:
+ *                Vincent Vanackere <vanackere@lif.univ-mrs.fr>
+ *            Added support for the "Lola" remote contributed by:
+ *                Seth Cohn <sethcohn@yahoo.com>
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or 
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
+ *
+ * Hardware & software notes
+ *
+ * These remote controls are distributed by ATI as part of their 
+ * "All-In-Wonder" video card packages.  The receiver self-identifies as a 
+ * "USB Receiver" with manufacturer "X10 Wireless Technology Inc".
+ *
+ * The "Lola" remote is available from X10.  See: 
+ *    http://www.x10.com/products/lola_sg1.htm
+ * The Lola is similar to the ATI remote but has no mouse support, and slightly
+ * different keys.
+ *
+ * It is possible to use multiple receivers and remotes on multiple computers 
+ * simultaneously by configuring them to use specific channels.
+ * 
+ * The RF protocol used by the remote supports 16 distinct channels, 1 to 16.  
+ * Actually, it may even support more, at least in some revisions of the 
+ * hardware.
+ *
+ * Each remote can be configured to transmit on one channel as follows:
+ *   - Press and hold the "hand icon" button.  
+ *   - When the red LED starts to blink, let go of the "hand icon" button. 
+ *   - When it stops blinking, input the channel code as two digits, from 01 
+ *     to 16, and press the hand icon again.
+ * 
+ * The timing can be a little tricky.  Try loading the module with debug=1
+ * to have the kernel print out messages about the remote control number
+ * and mask.  Note: debugging prints remote numbers as zero-based hexadecimal.
+ *
+ * The driver has a "channel_mask" parameter. This bitmask specifies which
+ * channels will be ignored by the module.  To mask out channels, just add 
+ * all the 2^channel_number values together.
+ *
+ * For instance, set channel_mask = 2^4 = 16 (binary 10000) to make ati_remote
+ * ignore signals coming from remote controls transmitting on channel 4, but 
+ * accept all other channels.
+ *
+ * Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be 
+ * ignored.
+ *
+ * The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this 
+ * parameter are unused.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/wait.h>
+
+/*
+ * Module and Version Information, Module Parameters
+ */
+ 
+#define ATI_REMOTE_VENDOR_ID 	0x0bc7
+#define ATI_REMOTE_PRODUCT_ID 	0x004
+#define LOLA_REMOTE_PRODUCT_ID 	0x002
+#define MEDION_REMOTE_PRODUCT_ID 0x006
+
+#define DRIVER_VERSION 	        "2.2.1"
+#define DRIVER_AUTHOR           "Torrey Hoffman <thoffman@arnor.net>"
+#define DRIVER_DESC             "ATI/X10 RF USB Remote Control"
+
+#define NAME_BUFSIZE      80    /* size of product name, path buffers */
+#define DATA_BUFSIZE      63    /* size of URB data buffers */
+#define ATI_INPUTNUM      1     /* Which input device to register as */
+
+static unsigned long channel_mask = 0;
+module_param(channel_mask, ulong, 0444);
+MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore");
+
+static int debug = 0;
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
+#undef err
+#define err(format, arg...) printk(KERN_ERR format , ## arg)
+ 
+static struct usb_device_id ati_remote_table[] = {
+	{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) },
+	{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID) },
+	{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID) },
+	{}	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ati_remote_table);
+
+/* Get hi and low bytes of a 16-bits int */
+#define HI(a)	((unsigned char)((a) >> 8))
+#define LO(a)	((unsigned char)((a) & 0xff))
+
+#define SEND_FLAG_IN_PROGRESS	1
+#define SEND_FLAG_COMPLETE	2
+
+/* Device initialization strings */
+static char init1[] = { 0x01, 0x00, 0x20, 0x14 };
+static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 };
+
+/* Acceleration curve for directional control pad */
+static char accel[] = { 1, 2, 4, 6, 9, 13, 20 };
+
+/* Duplicate event filtering time. 
+ * Sequential, identical KIND_FILTERED inputs with less than
+ * FILTER_TIME jiffies between them are considered as repeat
+ * events. The hardware generates 5 events for the first keypress
+ * and we have to take this into account for an accurate repeat
+ * behaviour.
+ * (HZ / 20) == 50 ms and works well for me.
+ */
+#define FILTER_TIME (HZ / 20)
+
+static DECLARE_MUTEX(disconnect_sem);
+
+struct ati_remote {
+	struct input_dev idev;		
+	struct usb_device *udev;
+	struct usb_interface *interface;
+		
+	struct urb *irq_urb;
+	struct urb *out_urb;
+	struct usb_endpoint_descriptor *endpoint_in;
+	struct usb_endpoint_descriptor *endpoint_out;
+	unsigned char *inbuf;
+	unsigned char *outbuf;
+	dma_addr_t inbuf_dma;
+	dma_addr_t outbuf_dma;
+
+	int open;                   /* open counter */
+	
+	unsigned char old_data[2];  /* Detect duplicate events */
+	unsigned long old_jiffies;
+	unsigned long acc_jiffies;  /* handle acceleration */
+	unsigned int repeat_count;
+	
+	char name[NAME_BUFSIZE];
+	char phys[NAME_BUFSIZE];
+
+	wait_queue_head_t wait;
+	int send_flags;
+};
+
+/* "Kinds" of messages sent from the hardware to the driver. */
+#define KIND_END        0
+#define KIND_LITERAL    1   /* Simply pass to input system */
+#define KIND_FILTERED   2   /* Add artificial key-up events, drop keyrepeats */
+#define KIND_LU         3   /* Directional keypad diagonals - left up, */
+#define KIND_RU         4   /*   right up,  */
+#define KIND_LD         5   /*   left down, */
+#define KIND_RD         6   /*   right down */
+#define KIND_ACCEL      7   /* Directional keypad - left, right, up, down.*/
+
+/* Translation table from hardware messages to input events. */
+static struct
+{
+	short kind;
+	unsigned char data1, data2;
+	int type;
+	unsigned int code;
+	int value;
+}  ati_remote_tbl[] = 
+{
+	/* Directional control pad axes */
+	{KIND_ACCEL,   0x35, 0x70, EV_REL, REL_X, -1},	 /* left */
+	{KIND_ACCEL,   0x36, 0x71, EV_REL, REL_X, 1},    /* right */
+	{KIND_ACCEL,   0x37, 0x72, EV_REL, REL_Y, -1},	 /* up */
+	{KIND_ACCEL,   0x38, 0x73, EV_REL, REL_Y, 1},    /* down */
+	/* Directional control pad diagonals */	
+	{KIND_LU,      0x39, 0x74, EV_REL, 0, 0},        /* left up */
+	{KIND_RU,      0x3a, 0x75, EV_REL, 0, 0},        /* right up */
+	{KIND_LD,      0x3c, 0x77, EV_REL, 0, 0},        /* left down */
+	{KIND_RD,      0x3b, 0x76, EV_REL, 0, 0},        /* right down */
+
+	/* "Mouse button" buttons */
+	{KIND_LITERAL, 0x3d, 0x78, EV_KEY, BTN_LEFT, 1}, /* left btn down */
+	{KIND_LITERAL, 0x3e, 0x79, EV_KEY, BTN_LEFT, 0}, /* left btn up */
+	{KIND_LITERAL, 0x41, 0x7c, EV_KEY, BTN_RIGHT, 1},/* right btn down */
+	{KIND_LITERAL, 0x42, 0x7d, EV_KEY, BTN_RIGHT, 0},/* right btn up */
+
+	/* Artificial "doubleclick" events are generated by the hardware. 
+	 * They are mapped to the "side" and "extra" mouse buttons here. */
+	{KIND_FILTERED, 0x3f, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */
+	{KIND_FILTERED, 0x43, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */
+
+	/* keyboard. */
+	{KIND_FILTERED, 0xd2, 0x0d, EV_KEY, KEY_1, 1},
+	{KIND_FILTERED, 0xd3, 0x0e, EV_KEY, KEY_2, 1},
+	{KIND_FILTERED, 0xd4, 0x0f, EV_KEY, KEY_3, 1},
+	{KIND_FILTERED, 0xd5, 0x10, EV_KEY, KEY_4, 1},
+	{KIND_FILTERED, 0xd6, 0x11, EV_KEY, KEY_5, 1},
+	{KIND_FILTERED, 0xd7, 0x12, EV_KEY, KEY_6, 1},
+	{KIND_FILTERED, 0xd8, 0x13, EV_KEY, KEY_7, 1},
+	{KIND_FILTERED, 0xd9, 0x14, EV_KEY, KEY_8, 1},
+	{KIND_FILTERED, 0xda, 0x15, EV_KEY, KEY_9, 1},
+	{KIND_FILTERED, 0xdc, 0x17, EV_KEY, KEY_0, 1},
+	{KIND_FILTERED, 0xc5, 0x00, EV_KEY, KEY_A, 1},
+	{KIND_FILTERED, 0xc6, 0x01, EV_KEY, KEY_B, 1},
+	{KIND_FILTERED, 0xde, 0x19, EV_KEY, KEY_C, 1},
+	{KIND_FILTERED, 0xe0, 0x1b, EV_KEY, KEY_D, 1},
+	{KIND_FILTERED, 0xe6, 0x21, EV_KEY, KEY_E, 1},
+	{KIND_FILTERED, 0xe8, 0x23, EV_KEY, KEY_F, 1},
+
+	/* "special" keys */
+	{KIND_FILTERED, 0xdd, 0x18, EV_KEY, KEY_KPENTER, 1},    /* "check" */
+	{KIND_FILTERED, 0xdb, 0x16, EV_KEY, KEY_MENU, 1},       /* "menu" */
+	{KIND_FILTERED, 0xc7, 0x02, EV_KEY, KEY_POWER, 1},      /* Power */
+	{KIND_FILTERED, 0xc8, 0x03, EV_KEY, KEY_TV, 1},         /* TV */
+	{KIND_FILTERED, 0xc9, 0x04, EV_KEY, KEY_DVD, 1},        /* DVD */
+	{KIND_FILTERED, 0xca, 0x05, EV_KEY, KEY_WWW, 1},        /* WEB */
+	{KIND_FILTERED, 0xcb, 0x06, EV_KEY, KEY_BOOKMARKS, 1},  /* "book" */
+	{KIND_FILTERED, 0xcc, 0x07, EV_KEY, KEY_EDIT, 1},       /* "hand" */
+	{KIND_FILTERED, 0xe1, 0x1c, EV_KEY, KEY_COFFEE, 1},     /* "timer" */
+	{KIND_FILTERED, 0xe5, 0x20, EV_KEY, KEY_FRONT, 1},      /* "max" */
+	{KIND_FILTERED, 0xe2, 0x1d, EV_KEY, KEY_LEFT, 1},       /* left */
+	{KIND_FILTERED, 0xe4, 0x1f, EV_KEY, KEY_RIGHT, 1},      /* right */
+	{KIND_FILTERED, 0xe7, 0x22, EV_KEY, KEY_DOWN, 1},       /* down */
+	{KIND_FILTERED, 0xdf, 0x1a, EV_KEY, KEY_UP, 1},         /* up */
+	{KIND_FILTERED, 0xe3, 0x1e, EV_KEY, KEY_OK, 1},         /* "OK" */
+	{KIND_FILTERED, 0xce, 0x09, EV_KEY, KEY_VOLUMEDOWN, 1}, /* VOL + */
+	{KIND_FILTERED, 0xcd, 0x08, EV_KEY, KEY_VOLUMEUP, 1},   /* VOL - */
+	{KIND_FILTERED, 0xcf, 0x0a, EV_KEY, KEY_MUTE, 1},       /* MUTE  */
+	{KIND_FILTERED, 0xd0, 0x0b, EV_KEY, KEY_CHANNELUP, 1},  /* CH + */
+	{KIND_FILTERED, 0xd1, 0x0c, EV_KEY, KEY_CHANNELDOWN, 1},/* CH - */
+	{KIND_FILTERED, 0xec, 0x27, EV_KEY, KEY_RECORD, 1},     /* ( o) red */
+	{KIND_FILTERED, 0xea, 0x25, EV_KEY, KEY_PLAY, 1},       /* ( >) */
+	{KIND_FILTERED, 0xe9, 0x24, EV_KEY, KEY_REWIND, 1},     /* (<<) */
+	{KIND_FILTERED, 0xeb, 0x26, EV_KEY, KEY_FORWARD, 1},    /* (>>) */
+	{KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1},       /* ([]) */ 
+	{KIND_FILTERED, 0xee, 0x29, EV_KEY, KEY_PAUSE, 1},      /* ('') */
+	{KIND_FILTERED, 0xf0, 0x2b, EV_KEY, KEY_PREVIOUS, 1},   /* (<-) */
+	{KIND_FILTERED, 0xef, 0x2a, EV_KEY, KEY_NEXT, 1},       /* (>+) */
+	{KIND_FILTERED, 0xf2, 0x2D, EV_KEY, KEY_INFO, 1},       /* PLAYING */
+	{KIND_FILTERED, 0xf3, 0x2E, EV_KEY, KEY_HOME, 1},       /* TOP */
+	{KIND_FILTERED, 0xf4, 0x2F, EV_KEY, KEY_END, 1},        /* END */
+	{KIND_FILTERED, 0xf5, 0x30, EV_KEY, KEY_SELECT, 1},     /* SELECT */	
+	
+	{KIND_END, 0x00, 0x00, EV_MAX + 1, 0, 0}
+};
+
+/* Local function prototypes */
+static void ati_remote_dump		(unsigned char *data, unsigned int actual_length);
+static void ati_remote_delete		(struct ati_remote *dev);
+static int ati_remote_open		(struct input_dev *inputdev);
+static void ati_remote_close		(struct input_dev *inputdev);
+static int ati_remote_sendpacket	(struct ati_remote *ati_remote, u16 cmd, unsigned char *data);
+static void ati_remote_irq_out		(struct urb *urb, struct pt_regs *regs);
+static void ati_remote_irq_in		(struct urb *urb, struct pt_regs *regs);
+static void ati_remote_input_report	(struct urb *urb, struct pt_regs *regs);
+static int ati_remote_initialize	(struct ati_remote *ati_remote);
+static int ati_remote_probe		(struct usb_interface *interface, const struct usb_device_id *id);
+static void ati_remote_disconnect	(struct usb_interface *interface);
+
+/* usb specific object to register with the usb subsystem */
+static struct usb_driver ati_remote_driver = {
+	.owner        = THIS_MODULE,
+	.name         = "ati_remote",
+	.probe        = ati_remote_probe,
+	.disconnect   = ati_remote_disconnect,
+	.id_table     = ati_remote_table,
+};
+
+/*
+ *	ati_remote_dump_input
+ */
+static void ati_remote_dump(unsigned char *data, unsigned int len)
+{
+	if ((len == 1) && (data[0] != (unsigned char)0xff) && (data[0] != 0x00))
+		warn("Weird byte 0x%02x", data[0]);
+	else if (len == 4)
+		warn("Weird key %02x %02x %02x %02x", 
+		     data[0], data[1], data[2], data[3]);
+	else
+		warn("Weird data, len=%d %02x %02x %02x %02x %02x %02x ...",
+		     len, data[0], data[1], data[2], data[3], data[4], data[5]);
+}
+
+/*
+ *	ati_remote_open
+ */
+static int ati_remote_open(struct input_dev *inputdev)
+{
+	struct ati_remote *ati_remote = inputdev->private;
+	int retval = 0;
+
+	down(&disconnect_sem);
+
+	if (ati_remote->open++)
+		goto exit;
+
+	/* On first open, submit the read urb which was set up previously. */
+	ati_remote->irq_urb->dev = ati_remote->udev;
+	if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) {
+		dev_err(&ati_remote->interface->dev, 
+			"%s: usb_submit_urb failed!\n", __FUNCTION__);
+		ati_remote->open--;
+		retval = -EIO;
+	}
+
+exit:
+	up(&disconnect_sem);
+	return retval;
+}
+
+/*
+ *	ati_remote_close
+ */
+static void ati_remote_close(struct input_dev *inputdev)
+{
+	struct ati_remote *ati_remote = inputdev->private;
+	
+	if (!--ati_remote->open)
+		usb_kill_urb(ati_remote->irq_urb);
+}
+
+/*
+ *		ati_remote_irq_out
+ */
+static void ati_remote_irq_out(struct urb *urb, struct pt_regs *regs)
+{
+	struct ati_remote *ati_remote = urb->context;
+	
+	if (urb->status) {
+		dev_dbg(&ati_remote->interface->dev, "%s: status %d\n",
+			__FUNCTION__, urb->status);
+		return;
+	}
+	
+	ati_remote->send_flags |= SEND_FLAG_COMPLETE;
+	wmb();
+	wake_up(&ati_remote->wait);
+}
+
+/*
+ *	ati_remote_sendpacket
+ *		
+ *	Used to send device initialization strings
+ */
+static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data)
+{
+	int retval = 0;
+	
+	/* Set up out_urb */
+	memcpy(ati_remote->out_urb->transfer_buffer + 1, data, LO(cmd));
+	((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd);	
+
+	ati_remote->out_urb->transfer_buffer_length = LO(cmd) + 1;
+	ati_remote->out_urb->dev = ati_remote->udev;
+	ati_remote->send_flags = SEND_FLAG_IN_PROGRESS;
+
+	retval = usb_submit_urb(ati_remote->out_urb, GFP_ATOMIC);
+	if (retval) {
+		dev_dbg(&ati_remote->interface->dev, 
+			 "sendpacket: usb_submit_urb failed: %d\n", retval);
+		return retval;
+	}
+
+	wait_event_timeout(ati_remote->wait,
+		((ati_remote->out_urb->status != -EINPROGRESS) ||
+		 	(ati_remote->send_flags & SEND_FLAG_COMPLETE)),
+		HZ);
+	usb_kill_urb(ati_remote->out_urb);
+	
+	return retval;
+}
+
+/*
+ *	ati_remote_event_lookup
+ */
+static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2)
+{
+	int i;
+
+	for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
+		/* 
+		 * Decide if the table entry matches the remote input. 
+		 */
+		if ((((ati_remote_tbl[i].data1 & 0x0f) == (d1 & 0x0f))) &&
+		    ((((ati_remote_tbl[i].data1 >> 4) - 
+		       (d1 >> 4) + rem) & 0x0f) == 0x0f) && 
+		    (ati_remote_tbl[i].data2 == d2))
+			return i;
+		
+	}
+	return -1;
+}
+
+/*
+ *	ati_remote_report_input
+ */
+static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs)	
+{
+	struct ati_remote *ati_remote = urb->context;
+	unsigned char *data= ati_remote->inbuf;
+	struct input_dev *dev = &ati_remote->idev; 
+	int index, acc;
+	int remote_num;
+	
+	/* Deal with strange looking inputs */
+	if ( (urb->actual_length != 4) || (data[0] != 0x14) || 
+		((data[3] & 0x0f) != 0x00) ) {
+		ati_remote_dump(data, urb->actual_length);
+		return;
+	}
+
+	/* Mask unwanted remote channels.  */
+	/* note: remote_num is 0-based, channel 1 on remote == 0 here */
+	remote_num = (data[3] >> 4) & 0x0f;
+        if (channel_mask & (1 << (remote_num + 1))) { 
+		dbginfo(&ati_remote->interface->dev,
+			"Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n",
+			remote_num, data[1], data[2], channel_mask);
+		return;
+	}
+
+	/* Look up event code index in translation table */
+	index = ati_remote_event_lookup(remote_num, data[1], data[2]);
+	if (index < 0) {
+		dev_warn(&ati_remote->interface->dev, 
+			 "Unknown input from channel 0x%02x: data %02x,%02x\n", 
+			 remote_num, data[1], data[2]);
+		return;
+	} 
+	dbginfo(&ati_remote->interface->dev, 
+		"channel 0x%02x; data %02x,%02x; index %d; keycode %d\n",
+		remote_num, data[1], data[2], index, ati_remote_tbl[index].code);
+	
+	if (ati_remote_tbl[index].kind == KIND_LITERAL) {
+		input_regs(dev, regs);
+		input_event(dev, ati_remote_tbl[index].type,
+			ati_remote_tbl[index].code,
+			ati_remote_tbl[index].value);
+		input_sync(dev);
+		
+		ati_remote->old_jiffies = jiffies;
+		return;
+	}
+	
+	if (ati_remote_tbl[index].kind == KIND_FILTERED) {
+		/* Filter duplicate events which happen "too close" together. */
+		if ((ati_remote->old_data[0] == data[1]) && 
+	 		(ati_remote->old_data[1] == data[2]) && 
+	 		((ati_remote->old_jiffies + FILTER_TIME) > jiffies)) {
+			ati_remote->repeat_count++;
+		} 
+		else {
+			ati_remote->repeat_count = 0;
+		}
+		
+		ati_remote->old_data[0] = data[1];
+		ati_remote->old_data[1] = data[2];
+		ati_remote->old_jiffies = jiffies;
+
+		if ((ati_remote->repeat_count > 0)
+		    && (ati_remote->repeat_count < 5))
+			return;
+		
+
+		input_regs(dev, regs);
+		input_event(dev, ati_remote_tbl[index].type,
+			ati_remote_tbl[index].code, 1);
+		input_event(dev, ati_remote_tbl[index].type,
+			ati_remote_tbl[index].code, 0);
+		input_sync(dev);
+
+		return;
+	}			
+	
+	/* 
+	 * Other event kinds are from the directional control pad, and have an
+	 * acceleration factor applied to them.  Without this acceleration, the
+	 * control pad is mostly unusable.
+	 * 
+	 * If elapsed time since last event is > 1/4 second, user "stopped",
+	 * so reset acceleration. Otherwise, user is probably holding the control
+	 * pad down, so we increase acceleration, ramping up over two seconds to
+	 * a maximum speed.  The acceleration curve is #defined above.
+	 */
+	if ((jiffies - ati_remote->old_jiffies) > (HZ >> 2)) {
+		acc = 1;
+		ati_remote->acc_jiffies = jiffies;
+	}
+	else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 3))  acc = accel[0];
+	else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 2))  acc = accel[1];
+	else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 1))  acc = accel[2];
+	else if ((jiffies - ati_remote->acc_jiffies) < HZ )        acc = accel[3];
+	else if ((jiffies - ati_remote->acc_jiffies) < HZ+(HZ>>1)) acc = accel[4];
+	else if ((jiffies - ati_remote->acc_jiffies) < (HZ << 1))  acc = accel[5];
+	else acc = accel[6];
+
+	input_regs(dev, regs);
+	switch (ati_remote_tbl[index].kind) {
+	case KIND_ACCEL:
+		input_event(dev, ati_remote_tbl[index].type,
+			ati_remote_tbl[index].code,
+			ati_remote_tbl[index].value * acc);
+		break;
+	case KIND_LU:
+		input_report_rel(dev, REL_X, -acc);
+		input_report_rel(dev, REL_Y, -acc);
+		break;
+	case KIND_RU:
+		input_report_rel(dev, REL_X, acc);
+		input_report_rel(dev, REL_Y, -acc);
+		break;
+	case KIND_LD:
+		input_report_rel(dev, REL_X, -acc);
+		input_report_rel(dev, REL_Y, acc);
+		break;
+	case KIND_RD:
+		input_report_rel(dev, REL_X, acc);
+		input_report_rel(dev, REL_Y, acc);
+		break;
+	default:
+		dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n", 
+			ati_remote_tbl[index].kind);
+	}
+	input_sync(dev);
+
+	ati_remote->old_jiffies = jiffies;
+	ati_remote->old_data[0] = data[1];
+	ati_remote->old_data[1] = data[2];
+}
+
+/*
+ *	ati_remote_irq_in
+ */
+static void ati_remote_irq_in(struct urb *urb, struct pt_regs *regs)
+{
+	struct ati_remote *ati_remote = urb->context;
+	int retval;
+
+	switch (urb->status) {
+	case 0:			/* success */
+		ati_remote_input_report(urb, regs);
+		break;
+	case -ECONNRESET:	/* unlink */
+	case -ENOENT:
+	case -ESHUTDOWN:
+		dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n",
+			__FUNCTION__);
+		return;	
+	default:		/* error */
+		dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n", 
+			__FUNCTION__, urb->status);
+	}
+	
+	retval = usb_submit_urb(urb, SLAB_ATOMIC);
+	if (retval)
+		dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n",
+			__FUNCTION__, retval);
+}
+
+/*
+ *	ati_remote_delete
+ */
+static void ati_remote_delete(struct ati_remote *ati_remote)
+{
+	if (!ati_remote) return;
+
+	if (ati_remote->irq_urb)
+		usb_kill_urb(ati_remote->irq_urb);
+
+	if (ati_remote->out_urb)
+		usb_kill_urb(ati_remote->out_urb);
+
+	input_unregister_device(&ati_remote->idev);
+
+	if (ati_remote->inbuf)
+		usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, 
+				ati_remote->inbuf, ati_remote->inbuf_dma);
+		
+	if (ati_remote->outbuf)
+		usb_buffer_free(ati_remote->udev, DATA_BUFSIZE, 
+				ati_remote->inbuf, ati_remote->outbuf_dma);
+	
+	if (ati_remote->irq_urb)
+		usb_free_urb(ati_remote->irq_urb);
+	
+	if (ati_remote->out_urb)
+		usb_free_urb(ati_remote->out_urb);
+
+	kfree(ati_remote);
+}
+
+static void ati_remote_input_init(struct ati_remote *ati_remote)
+{
+	struct input_dev *idev = &(ati_remote->idev);
+	int i;
+
+	idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+	idev->keybit[LONG(BTN_MOUSE)] = ( BIT(BTN_LEFT) | BIT(BTN_RIGHT) | 
+					  BIT(BTN_SIDE) | BIT(BTN_EXTRA) );
+	idev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
+	for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++)
+		if (ati_remote_tbl[i].type == EV_KEY)
+			set_bit(ati_remote_tbl[i].code, idev->keybit);
+	
+	idev->private = ati_remote;
+	idev->open = ati_remote_open;
+	idev->close = ati_remote_close;
+	
+	idev->name = ati_remote->name;
+	idev->phys = ati_remote->phys;
+	
+	idev->id.bustype = BUS_USB;		
+	idev->id.vendor = le16_to_cpu(ati_remote->udev->descriptor.idVendor);
+	idev->id.product = le16_to_cpu(ati_remote->udev->descriptor.idProduct);
+	idev->id.version = le16_to_cpu(ati_remote->udev->descriptor.bcdDevice);
+}
+
+static int ati_remote_initialize(struct ati_remote *ati_remote)
+{
+	struct usb_device *udev = ati_remote->udev;
+	int pipe, maxp;
+		
+	init_waitqueue_head(&ati_remote->wait);
+
+	/* Set up irq_urb */
+	pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress);
+	maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+	maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
+	
+	usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf, 
+			 maxp, ati_remote_irq_in, ati_remote, 
+			 ati_remote->endpoint_in->bInterval);
+	ati_remote->irq_urb->transfer_dma = ati_remote->inbuf_dma;
+	ati_remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	
+	/* Set up out_urb */
+	pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress);
+	maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+	maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
+
+	usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf, 
+			 maxp, ati_remote_irq_out, ati_remote, 
+			 ati_remote->endpoint_out->bInterval);
+	ati_remote->out_urb->transfer_dma = ati_remote->outbuf_dma;
+	ati_remote->out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* send initialization strings */
+	if ((ati_remote_sendpacket(ati_remote, 0x8004, init1)) ||
+	    (ati_remote_sendpacket(ati_remote, 0x8007, init2))) {
+		dev_err(&ati_remote->interface->dev, 
+			 "Initializing ati_remote hardware failed.\n");
+		return 1;
+	}
+	
+	return 0;
+}
+
+/*
+ *	ati_remote_probe
+ */
+static int ati_remote_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct ati_remote *ati_remote = NULL;
+	struct usb_host_interface *iface_host;
+	int retval = -ENOMEM;
+	char path[64];
+
+	/* Allocate and clear an ati_remote struct */
+	if (!(ati_remote = kmalloc(sizeof (struct ati_remote), GFP_KERNEL)))
+		return -ENOMEM;
+	memset(ati_remote, 0x00, sizeof (struct ati_remote));
+
+	iface_host = interface->cur_altsetting;
+	if (iface_host->desc.bNumEndpoints != 2) {
+		err("%s: Unexpected desc.bNumEndpoints\n", __FUNCTION__);
+		retval = -ENODEV;
+		goto error;
+	}
+
+	ati_remote->endpoint_in = &(iface_host->endpoint[0].desc);
+	ati_remote->endpoint_out = &(iface_host->endpoint[1].desc);
+	ati_remote->udev = udev;
+	ati_remote->interface = interface;
+
+	if (!(ati_remote->endpoint_in->bEndpointAddress & 0x80)) {
+		err("%s: Unexpected endpoint_in->bEndpointAddress\n", __FUNCTION__);
+		retval = -ENODEV;
+		goto error;
+	}
+	if ((ati_remote->endpoint_in->bmAttributes & 3) != 3) {
+		err("%s: Unexpected endpoint_in->bmAttributes\n", __FUNCTION__);
+		retval = -ENODEV;
+		goto error;
+	}
+	if (le16_to_cpu(ati_remote->endpoint_in->wMaxPacketSize) == 0) {
+		err("%s: endpoint_in message size==0? \n", __FUNCTION__);
+		retval = -ENODEV;
+		goto error;
+	}
+
+	/* Allocate URB buffers, URBs */
+	ati_remote->inbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, SLAB_ATOMIC,
+					     &ati_remote->inbuf_dma);
+	if (!ati_remote->inbuf)
+		goto error;
+
+	ati_remote->outbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, SLAB_ATOMIC,
+					      &ati_remote->outbuf_dma);
+	if (!ati_remote->outbuf)
+		goto error;
+
+	ati_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!ati_remote->irq_urb)
+		goto error;
+
+	ati_remote->out_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!ati_remote->out_urb)
+		goto error;
+
+	usb_make_path(udev, path, NAME_BUFSIZE);
+	sprintf(ati_remote->phys, "%s/input%d", path, ATI_INPUTNUM);
+	if (udev->manufacturer)
+		strcat(ati_remote->name, udev->manufacturer);
+
+	if (udev->product)
+		sprintf(ati_remote->name, "%s %s", ati_remote->name, udev->product);
+
+	if (!strlen(ati_remote->name))
+		sprintf(ati_remote->name, DRIVER_DESC "(%04x,%04x)",
+			le16_to_cpu(ati_remote->udev->descriptor.idVendor), 
+			le16_to_cpu(ati_remote->udev->descriptor.idProduct));
+
+	/* Device Hardware Initialization - fills in ati_remote->idev from udev. */
+	retval = ati_remote_initialize(ati_remote);
+	if (retval)
+		goto error;
+
+	/* Set up and register input device */
+	ati_remote_input_init(ati_remote);
+	input_register_device(&ati_remote->idev);
+
+	dev_info(&ati_remote->interface->dev, "Input registered: %s on %s\n", 
+		 ati_remote->name, path);
+
+	usb_set_intfdata(interface, ati_remote);
+	
+error:
+	if (retval)
+		ati_remote_delete(ati_remote);
+
+	return retval;
+}
+
+/*
+ *	ati_remote_disconnect
+ */
+static void ati_remote_disconnect(struct usb_interface *interface)
+{
+	struct ati_remote *ati_remote;
+
+	down(&disconnect_sem);
+
+	ati_remote = usb_get_intfdata(interface);
+	usb_set_intfdata(interface, NULL);
+	if (!ati_remote) {
+		warn("%s - null device?\n", __FUNCTION__);
+		return;
+	}
+	
+	ati_remote_delete(ati_remote);
+
+	up(&disconnect_sem);
+}
+
+/*
+ *	ati_remote_init
+ */
+static int __init ati_remote_init(void)
+{
+	int result;
+	
+	result = usb_register(&ati_remote_driver);
+	if (result)
+		err("usb_register error #%d\n", result);
+	else
+		info("Registered USB driver " DRIVER_DESC " v. " DRIVER_VERSION);
+
+	return result;
+}
+
+/*
+ *	ati_remote_exit
+ */
+static void __exit ati_remote_exit(void)
+{
+	usb_deregister(&ati_remote_driver);
+}
+
+/* 
+ *	module specification 
+ */
+
+module_init(ati_remote_init);
+module_exit(ati_remote_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/input/fixp-arith.h b/drivers/usb/input/fixp-arith.h
new file mode 100644
index 0000000..26ca5b8
--- /dev/null
+++ b/drivers/usb/input/fixp-arith.h
@@ -0,0 +1,90 @@
+#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}
+static 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 */
+static inline fixp_t fixp_new(s16 a)
+{
+	return a<<FRAC_N;
+}
+
+/* a: 0xFFFF -> -1.0
+      0x8000 -> 1.0
+      0x0000 -> 0.0
+*/
+static inline fixp_t fixp_new16(s16 a)
+{
+	return ((s32)a)>>(16-FRAC_N);
+}
+
+static inline fixp_t fixp_cos(unsigned int degrees)
+{
+	int quadrant = (degrees / 90) & 3;
+	unsigned int i = degrees % 90;
+
+	if (quadrant == 1 || quadrant == 3) {
+		i = 89 - i;
+	}
+
+	i >>= 1;
+
+	return (quadrant == 1 || quadrant == 2)? -cos_table[i] : cos_table[i];
+}
+
+static inline fixp_t fixp_sin(unsigned int degrees)
+{
+	return -fixp_cos(degrees + 90);
+}
+
+static inline fixp_t fixp_mult(fixp_t a, fixp_t b)
+{
+	return ((s32)(a*b))>>FRAC_N;
+}
+
+#endif
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
new file mode 100644
index 0000000..7662cf4
--- /dev/null
+++ b/drivers/usb/input/hid-core.c
@@ -0,0 +1,1864 @@
+/*
+ *  USB HID support for Linux
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2001 Vojtech Pavlik <vojtech@suse.cz>
+ */
+
+/*
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+#include <linux/input.h>
+#include <linux/wait.h>
+
+#undef DEBUG
+#undef DEBUG_DATA
+
+#include <linux/usb.h>
+
+#include "hid.h"
+#include <linux/hiddev.h>
+
+/*
+ * Version Information
+ */
+
+#define DRIVER_VERSION "v2.01"
+#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik"
+#define DRIVER_DESC "USB HID core driver"
+#define DRIVER_LICENSE "GPL"
+
+static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick",
+				"Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"};
+/*
+ * Module parameters.
+ */
+
+static unsigned int hid_mousepoll_interval;
+module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644);
+MODULE_PARM_DESC(mousepoll, "Polling interval of mice");
+
+/*
+ * Register a new report for a device.
+ */
+
+static struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id)
+{
+	struct hid_report_enum *report_enum = device->report_enum + type;
+	struct hid_report *report;
+
+	if (report_enum->report_id_hash[id])
+		return report_enum->report_id_hash[id];
+
+	if (!(report = kmalloc(sizeof(struct hid_report), GFP_KERNEL)))
+		return NULL;
+	memset(report, 0, sizeof(struct hid_report));
+
+	if (id != 0)
+		report_enum->numbered = 1;
+
+	report->id = id;
+	report->type = type;
+	report->size = 0;
+	report->device = device;
+	report_enum->report_id_hash[id] = report;
+
+	list_add_tail(&report->list, &report_enum->report_list);
+
+	return report;
+}
+
+/*
+ * Register a new field for this report.
+ */
+
+static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values)
+{
+	struct hid_field *field;
+
+	if (report->maxfield == HID_MAX_FIELDS) {
+		dbg("too many fields in report");
+		return NULL;
+	}
+
+	if (!(field = kmalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
+		+ values * sizeof(unsigned), GFP_KERNEL))) return NULL;
+
+	memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage)
+		+ values * sizeof(unsigned));
+
+	field->index = report->maxfield++;
+	report->field[field->index] = field;
+	field->usage = (struct hid_usage *)(field + 1);
+	field->value = (unsigned *)(field->usage + usages);
+	field->report = report;
+
+	return field;
+}
+
+/*
+ * Open a collection. The type/usage is pushed on the stack.
+ */
+
+static int open_collection(struct hid_parser *parser, unsigned type)
+{
+	struct hid_collection *collection;
+	unsigned usage;
+
+	usage = parser->local.usage[0];
+
+	if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
+		dbg("collection stack overflow");
+		return -1;
+	}
+
+	if (parser->device->maxcollection == parser->device->collection_size) {
+		collection = kmalloc(sizeof(struct hid_collection) *
+				parser->device->collection_size * 2, GFP_KERNEL);
+		if (collection == NULL) {
+			dbg("failed to reallocate collection array");
+			return -1;
+		}
+		memcpy(collection, parser->device->collection,
+			sizeof(struct hid_collection) *
+			parser->device->collection_size);
+		memset(collection + parser->device->collection_size, 0,
+			sizeof(struct hid_collection) *
+			parser->device->collection_size);
+		kfree(parser->device->collection);
+		parser->device->collection = collection;
+		parser->device->collection_size *= 2;
+	}
+
+	parser->collection_stack[parser->collection_stack_ptr++] =
+		parser->device->maxcollection;
+
+	collection = parser->device->collection +
+		parser->device->maxcollection++;
+	collection->type = type;
+	collection->usage = usage;
+	collection->level = parser->collection_stack_ptr - 1;
+
+	if (type == HID_COLLECTION_APPLICATION)
+		parser->device->maxapplication++;
+
+	return 0;
+}
+
+/*
+ * Close a collection.
+ */
+
+static int close_collection(struct hid_parser *parser)
+{
+	if (!parser->collection_stack_ptr) {
+		dbg("collection stack underflow");
+		return -1;
+	}
+	parser->collection_stack_ptr--;
+	return 0;
+}
+
+/*
+ * Climb up the stack, search for the specified collection type
+ * and return the usage.
+ */
+
+static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
+{
+	int n;
+	for (n = parser->collection_stack_ptr - 1; n >= 0; n--)
+		if (parser->device->collection[parser->collection_stack[n]].type == type)
+			return parser->device->collection[parser->collection_stack[n]].usage;
+	return 0; /* we know nothing about this usage type */
+}
+
+/*
+ * Add a usage to the temporary parser table.
+ */
+
+static int hid_add_usage(struct hid_parser *parser, unsigned usage)
+{
+	if (parser->local.usage_index >= HID_MAX_USAGES) {
+		dbg("usage index exceeded");
+		return -1;
+	}
+	parser->local.usage[parser->local.usage_index] = usage;
+	parser->local.collection_index[parser->local.usage_index] =
+		parser->collection_stack_ptr ?
+		parser->collection_stack[parser->collection_stack_ptr - 1] : 0;
+	parser->local.usage_index++;
+	return 0;
+}
+
+/*
+ * Register a new field for this report.
+ */
+
+static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsigned flags)
+{
+	struct hid_report *report;
+	struct hid_field *field;
+	int usages;
+	unsigned offset;
+	int i;
+
+	if (!(report = hid_register_report(parser->device, report_type, parser->global.report_id))) {
+		dbg("hid_register_report failed");
+		return -1;
+	}
+
+	if (parser->global.logical_maximum < parser->global.logical_minimum) {
+		dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum);
+		return -1;
+	}
+
+	offset = report->size;
+	report->size += parser->global.report_size * parser->global.report_count;
+
+	if (!parser->local.usage_index) /* Ignore padding fields */
+		return 0; 
+
+	usages = max_t(int, parser->local.usage_index, parser->global.report_count);
+
+	if ((field = hid_register_field(report, usages, parser->global.report_count)) == NULL)
+		return 0;
+
+	field->physical = hid_lookup_collection(parser, HID_COLLECTION_PHYSICAL);
+	field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL);
+	field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION);
+
+	for (i = 0; i < usages; i++) {
+		int j = i;
+		/* Duplicate the last usage we parsed if we have excess values */
+		if (i >= parser->local.usage_index)
+			j = parser->local.usage_index - 1;
+		field->usage[i].hid = parser->local.usage[j];
+		field->usage[i].collection_index =
+			parser->local.collection_index[j];
+	}
+
+	field->maxusage = usages;
+	field->flags = flags;
+	field->report_offset = offset;
+	field->report_type = report_type;
+	field->report_size = parser->global.report_size;
+	field->report_count = parser->global.report_count;
+	field->logical_minimum = parser->global.logical_minimum;
+	field->logical_maximum = parser->global.logical_maximum;
+	field->physical_minimum = parser->global.physical_minimum;
+	field->physical_maximum = parser->global.physical_maximum;
+	field->unit_exponent = parser->global.unit_exponent;
+	field->unit = parser->global.unit;
+
+	return 0;
+}
+
+/*
+ * Read data value from item.
+ */
+
+static __inline__ __u32 item_udata(struct hid_item *item)
+{
+	switch (item->size) {
+		case 1: return item->data.u8;
+		case 2: return item->data.u16;
+		case 4: return item->data.u32;
+	}
+	return 0;
+}
+
+static __inline__ __s32 item_sdata(struct hid_item *item)
+{
+	switch (item->size) {
+		case 1: return item->data.s8;
+		case 2: return item->data.s16;
+		case 4: return item->data.s32;
+	}
+	return 0;
+}
+
+/*
+ * Process a global item.
+ */
+
+static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
+{
+	switch (item->tag) {
+
+		case HID_GLOBAL_ITEM_TAG_PUSH:
+
+			if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) {
+				dbg("global enviroment stack overflow");
+				return -1;
+			}
+
+			memcpy(parser->global_stack + parser->global_stack_ptr++,
+				&parser->global, sizeof(struct hid_global));
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_POP:
+
+			if (!parser->global_stack_ptr) {
+				dbg("global enviroment stack underflow");
+				return -1;
+			}
+
+			memcpy(&parser->global, parser->global_stack + --parser->global_stack_ptr,
+				sizeof(struct hid_global));
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
+			parser->global.usage_page = item_udata(item);
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
+			parser->global.logical_minimum = item_sdata(item);
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
+			if (parser->global.logical_minimum < 0)
+				parser->global.logical_maximum = item_sdata(item);
+			else
+				parser->global.logical_maximum = item_udata(item);
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM:
+			parser->global.physical_minimum = item_sdata(item);
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM:
+			if (parser->global.physical_minimum < 0)
+				parser->global.physical_maximum = item_sdata(item);
+			else
+				parser->global.physical_maximum = item_udata(item);
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT:
+			parser->global.unit_exponent = item_sdata(item);
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_UNIT:
+			parser->global.unit = item_udata(item);
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
+			if ((parser->global.report_size = item_udata(item)) > 32) {
+				dbg("invalid report_size %d", parser->global.report_size);
+				return -1;
+			}
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
+			if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) {
+				dbg("invalid report_count %d", parser->global.report_count);
+				return -1;
+			}
+			return 0;
+
+		case HID_GLOBAL_ITEM_TAG_REPORT_ID:
+			if ((parser->global.report_id = item_udata(item)) == 0) {
+				dbg("report_id 0 is invalid");
+				return -1;
+			}
+			return 0;
+
+		default:
+			dbg("unknown global tag 0x%x", item->tag);
+			return -1;
+	}
+}
+
+/*
+ * Process a local item.
+ */
+
+static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
+{
+	__u32 data;
+	unsigned n;
+
+	if (item->size == 0) {
+		dbg("item data expected for local item");
+		return -1;
+	}
+
+	data = item_udata(item);
+
+	switch (item->tag) {
+
+		case HID_LOCAL_ITEM_TAG_DELIMITER:
+
+			if (data) {
+				/*
+				 * We treat items before the first delimiter
+				 * as global to all usage sets (branch 0).
+				 * In the moment we process only these global
+				 * items and the first delimiter set.
+				 */
+				if (parser->local.delimiter_depth != 0) {
+					dbg("nested delimiters");
+					return -1;
+				}
+				parser->local.delimiter_depth++;
+				parser->local.delimiter_branch++;
+			} else {
+				if (parser->local.delimiter_depth < 1) {
+					dbg("bogus close delimiter");
+					return -1;
+				}
+				parser->local.delimiter_depth--;
+			}
+			return 1;
+
+		case HID_LOCAL_ITEM_TAG_USAGE:
+
+			if (parser->local.delimiter_branch > 1) {
+				dbg("alternative usage ignored");
+				return 0;
+			}
+
+			if (item->size <= 2)
+				data = (parser->global.usage_page << 16) + data;
+
+			return hid_add_usage(parser, data);
+
+		case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
+
+			if (parser->local.delimiter_branch > 1) {
+				dbg("alternative usage ignored");
+				return 0;
+			}
+
+			if (item->size <= 2)
+				data = (parser->global.usage_page << 16) + data;
+
+			parser->local.usage_minimum = data;
+			return 0;
+
+		case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
+
+			if (parser->local.delimiter_branch > 1) {
+				dbg("alternative usage ignored");
+				return 0;
+			}
+
+			if (item->size <= 2)
+				data = (parser->global.usage_page << 16) + data;
+
+			for (n = parser->local.usage_minimum; n <= data; n++)
+				if (hid_add_usage(parser, n)) {
+					dbg("hid_add_usage failed\n");
+					return -1;
+				}
+			return 0;
+
+		default:
+
+			dbg("unknown local item tag 0x%x", item->tag);
+			return 0;
+	}
+	return 0;
+}
+
+/*
+ * Process a main item.
+ */
+
+static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
+{
+	__u32 data;
+	int ret;
+
+	data = item_udata(item);
+
+	switch (item->tag) {
+		case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
+			ret = open_collection(parser, data & 0xff);
+			break;
+		case HID_MAIN_ITEM_TAG_END_COLLECTION:
+			ret = close_collection(parser);
+			break;
+		case HID_MAIN_ITEM_TAG_INPUT:
+			ret = hid_add_field(parser, HID_INPUT_REPORT, data);
+			break;
+		case HID_MAIN_ITEM_TAG_OUTPUT:
+			ret = hid_add_field(parser, HID_OUTPUT_REPORT, data);
+			break;
+		case HID_MAIN_ITEM_TAG_FEATURE:
+			ret = hid_add_field(parser, HID_FEATURE_REPORT, data);
+			break;
+		default:
+			dbg("unknown main item tag 0x%x", item->tag);
+			ret = 0;
+	}
+
+	memset(&parser->local, 0, sizeof(parser->local));	/* Reset the local parser environment */
+
+	return ret;
+}
+
+/*
+ * Process a reserved item.
+ */
+
+static int hid_parser_reserved(struct hid_parser *parser, struct hid_item *item)
+{
+	dbg("reserved item type, tag 0x%x", item->tag);
+	return 0;
+}
+
+/*
+ * Free a report and all registered fields. The field->usage and
+ * field->value table's are allocated behind the field, so we need
+ * only to free(field) itself.
+ */
+
+static void hid_free_report(struct hid_report *report)
+{
+	unsigned n;
+
+	for (n = 0; n < report->maxfield; n++)
+		kfree(report->field[n]);
+	kfree(report);
+}
+
+/*
+ * Free a device structure, all reports, and all fields.
+ */
+
+static void hid_free_device(struct hid_device *device)
+{
+	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;
+
+		for (j = 0; j < 256; j++) {
+			struct hid_report *report = report_enum->report_id_hash[j];
+			if (report)
+				hid_free_report(report);
+		}
+	}
+
+	if (device->rdesc)
+		kfree(device->rdesc);
+	kfree(device);
+}
+
+/*
+ * Fetch a report description item from the data stream. We support long
+ * items, though they are not used yet.
+ */
+
+static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
+{
+	u8 b;
+
+	if ((end - start) <= 0)
+		return NULL;
+
+	b = *start++;
+
+	item->type = (b >> 2) & 3;
+	item->tag  = (b >> 4) & 15;
+
+	if (item->tag == HID_ITEM_TAG_LONG) {
+
+		item->format = HID_ITEM_FORMAT_LONG;
+
+		if ((end - start) < 2)
+			return NULL;
+
+		item->size = *start++;
+		item->tag  = *start++;
+
+		if ((end - start) < item->size)
+			return NULL;
+
+		item->data.longdata = start;
+		start += item->size;
+		return start;
+	}
+
+	item->format = HID_ITEM_FORMAT_SHORT;
+	item->size = b & 3;
+
+	switch (item->size) {
+
+		case 0:
+			return start;
+
+		case 1:
+			if ((end - start) < 1)
+				return NULL;
+			item->data.u8 = *start++;
+			return start;
+
+		case 2:
+			if ((end - start) < 2)
+				return NULL;
+			item->data.u16 = le16_to_cpu(get_unaligned((__le16*)start));
+			start = (__u8 *)((__le16 *)start + 1);
+			return start;
+
+		case 3:
+			item->size++;
+			if ((end - start) < 4)
+				return NULL;
+			item->data.u32 = le32_to_cpu(get_unaligned((__le32*)start));
+			start = (__u8 *)((__le32 *)start + 1);
+			return start;
+	}
+
+	return NULL;
+}
+
+/*
+ * Parse a report description into a hid_device structure. Reports are
+ * enumerated, fields are attached to these reports.
+ */
+
+static struct hid_device *hid_parse_report(__u8 *start, unsigned size)
+{
+	struct hid_device *device;
+	struct hid_parser *parser;
+	struct hid_item item;
+	__u8 *end;
+	unsigned i;
+	static int (*dispatch_type[])(struct hid_parser *parser,
+				      struct hid_item *item) = {
+		hid_parser_main,
+		hid_parser_global,
+		hid_parser_local,
+		hid_parser_reserved
+	};
+
+	if (!(device = kmalloc(sizeof(struct hid_device), GFP_KERNEL)))
+		return NULL;
+	memset(device, 0, sizeof(struct hid_device));
+
+	if (!(device->collection = kmalloc(sizeof(struct hid_collection) *
+				   HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) {
+		kfree(device);
+		return NULL;
+	}
+	memset(device->collection, 0, sizeof(struct hid_collection) *
+		HID_DEFAULT_NUM_COLLECTIONS);
+	device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
+
+	for (i = 0; i < HID_REPORT_TYPES; i++)
+		INIT_LIST_HEAD(&device->report_enum[i].report_list);
+
+	if (!(device->rdesc = (__u8 *)kmalloc(size, GFP_KERNEL))) {
+		kfree(device->collection);
+		kfree(device);
+		return NULL;
+	}
+	memcpy(device->rdesc, start, size);
+	device->rsize = size;
+
+	if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) {
+		kfree(device->rdesc);
+		kfree(device->collection);
+		kfree(device);
+		return NULL;
+	}
+	memset(parser, 0, sizeof(struct hid_parser));
+	parser->device = device;
+
+	end = start + size;
+	while ((start = fetch_item(start, end, &item)) != NULL) {
+
+		if (item.format != HID_ITEM_FORMAT_SHORT) {
+			dbg("unexpected long global item");
+			kfree(device->collection);
+			hid_free_device(device);
+			kfree(parser);
+			return NULL;
+		}
+
+		if (dispatch_type[item.type](parser, &item)) {
+			dbg("item %u %u %u %u parsing failed\n",
+				item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag);
+			kfree(device->collection);
+			hid_free_device(device);
+			kfree(parser);
+			return NULL;
+		}
+
+		if (start == end) {
+			if (parser->collection_stack_ptr) {
+				dbg("unbalanced collection at end of report description");
+				kfree(device->collection);
+				hid_free_device(device);
+				kfree(parser);
+				return NULL;
+			}
+			if (parser->local.delimiter_depth) {
+				dbg("unbalanced delimiter at end of report description");
+				kfree(device->collection);
+				hid_free_device(device);
+				kfree(parser);
+				return NULL;
+			}
+			kfree(parser);
+			return device;
+		}
+	}
+
+	dbg("item fetching failed at offset %d\n", (int)(end - start));
+	kfree(device->collection);
+	hid_free_device(device);
+	kfree(parser);
+	return NULL;
+}
+
+/*
+ * Convert a signed n-bit integer to signed 32-bit integer. Common
+ * cases are done through the compiler, the screwed things has to be
+ * done by hand.
+ */
+
+static __inline__ __s32 snto32(__u32 value, unsigned n)
+{
+	switch (n) {
+		case 8:  return ((__s8)value);
+		case 16: return ((__s16)value);
+		case 32: return ((__s32)value);
+	}
+	return value & (1 << (n - 1)) ? value | (-1 << n) : value;
+}
+
+/*
+ * Convert a signed 32-bit integer to a signed n-bit integer.
+ */
+
+static __inline__ __u32 s32ton(__s32 value, unsigned n)
+{
+	__s32 a = value >> (n - 1);
+	if (a && a != -1)
+		return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1;
+	return value & ((1 << n) - 1);
+}
+
+/*
+ * Extract/implement a data field from/to a report.
+ */
+
+static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n)
+{
+	report += (offset >> 5) << 2; offset &= 31;
+	return (le64_to_cpu(get_unaligned((__le64*)report)) >> offset) & ((1 << n) - 1);
+}
+
+static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value)
+{
+	report += (offset >> 5) << 2; offset &= 31;
+	put_unaligned((get_unaligned((__le64*)report)
+		& cpu_to_le64(~((((__u64) 1 << n) - 1) << offset)))
+		| cpu_to_le64((__u64)value << offset), (__le64*)report);
+}
+
+/*
+ * Search an array for a value.
+ */
+
+static __inline__ int search(__s32 *array, __s32 value, unsigned n)
+{
+	while (n--) {
+		if (*array++ == value)
+			return 0;
+	}
+	return -1;
+}
+
+static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, struct pt_regs *regs)
+{
+	hid_dump_input(usage, value);
+	if (hid->claimed & HID_CLAIMED_INPUT)
+		hidinput_hid_event(hid, field, usage, value, regs);
+	if (hid->claimed & HID_CLAIMED_HIDDEV)
+		hiddev_hid_event(hid, field, usage, value, regs);
+}
+
+/*
+ * Analyse a received field, and fetch the data from it. The field
+ * content is stored for next report processing (we do differential
+ * reporting to the layer).
+ */
+
+static void hid_input_field(struct hid_device *hid, struct hid_field *field, __u8 *data, struct pt_regs *regs)
+{
+	unsigned n;
+	unsigned count = field->report_count;
+	unsigned offset = field->report_offset;
+	unsigned size = field->report_size;
+	__s32 min = field->logical_minimum;
+	__s32 max = field->logical_maximum;
+	__s32 *value;
+
+	if (!(value = kmalloc(sizeof(__s32) * count, GFP_ATOMIC)))
+		return;
+
+	for (n = 0; n < count; n++) {
+
+			value[n] = min < 0 ? snto32(extract(data, offset + n * size, size), size) :
+						    extract(data, offset + n * size, size);
+
+			if (!(field->flags & HID_MAIN_ITEM_VARIABLE) /* Ignore report if ErrorRollOver */
+			    && value[n] >= min && value[n] <= max
+			    && field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1)
+				goto exit;
+	}
+
+	for (n = 0; n < count; n++) {
+
+		if (HID_MAIN_ITEM_VARIABLE & field->flags) {
+			hid_process_event(hid, field, &field->usage[n], value[n], regs);
+			continue;
+		}
+
+		if (field->value[n] >= min && field->value[n] <= max
+			&& field->usage[field->value[n] - min].hid
+			&& search(value, field->value[n], count))
+				hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, regs);
+
+		if (value[n] >= min && value[n] <= max
+			&& field->usage[value[n] - min].hid
+			&& search(field->value, value[n], count))
+				hid_process_event(hid, field, &field->usage[value[n] - min], 1, regs);
+	}
+
+	memcpy(field->value, value, count * sizeof(__s32));
+exit:
+	kfree(value);
+}
+
+static int hid_input_report(int type, struct urb *urb, struct pt_regs *regs)
+{
+	struct hid_device *hid = urb->context;
+	struct hid_report_enum *report_enum = hid->report_enum + type;
+	u8 *data = urb->transfer_buffer;
+	int len = urb->actual_length;
+	struct hid_report *report;
+	int n, size;
+
+	if (!len) {
+		dbg("empty report");
+		return -1;
+	}
+
+#ifdef DEBUG_DATA
+	printk(KERN_DEBUG __FILE__ ": report (size %u) (%snumbered)\n", len, report_enum->numbered ? "" : "un");
+#endif
+
+	n = 0;				/* Normally report number is 0 */
+	if (report_enum->numbered) {	/* Device uses numbered reports, data[0] is report number */
+		n = *data++;
+		len--;
+	}
+
+#ifdef DEBUG_DATA
+	{
+		int i;
+		printk(KERN_DEBUG __FILE__ ": report %d (size %u) = ", n, len);
+		for (i = 0; i < len; i++)
+			printk(" %02x", data[i]);
+		printk("\n");
+	}
+#endif
+
+	if (!(report = report_enum->report_id_hash[n])) {
+		dbg("undefined report_id %d received", n);
+		return -1;
+	}
+
+	size = ((report->size - 1) >> 3) + 1;
+
+	if (len < size)
+		dbg("report %d is too short, (%d < %d)", report->id, len, size);
+
+	if (hid->claimed & HID_CLAIMED_HIDDEV)
+		hiddev_report_event(hid, report);
+
+	for (n = 0; n < report->maxfield; n++)
+		hid_input_field(hid, report->field[n], data, regs);
+
+	if (hid->claimed & HID_CLAIMED_INPUT)
+		hidinput_report_event(hid, report);
+
+	return 0;
+}
+
+/*
+ * Input interrupt completion handler.
+ */
+
+static void hid_irq_in(struct urb *urb, struct pt_regs *regs)
+{
+	struct hid_device	*hid = urb->context;
+	int			status;
+
+	switch (urb->status) {
+		case 0:			/* success */
+			hid_input_report(HID_INPUT_REPORT, urb, regs);
+			break;
+		case -ECONNRESET:	/* unlink */
+		case -ENOENT:
+		case -EPERM:
+		case -ESHUTDOWN:	/* unplug */
+		case -EILSEQ:		/* unplug timeout on uhci */
+			return;
+		case -ETIMEDOUT:	/* NAK */
+			break;
+		default:		/* error */
+			warn("input irq status %d received", urb->status);
+	}
+
+	status = usb_submit_urb(urb, SLAB_ATOMIC);
+	if (status)
+		err("can't resubmit intr, %s-%s/input%d, status %d",
+				hid->dev->bus->bus_name, hid->dev->devpath,
+				hid->ifnum, status);
+}
+
+/*
+ * Output the field into the report.
+ */
+
+static void hid_output_field(struct hid_field *field, __u8 *data)
+{
+	unsigned count = field->report_count;
+	unsigned offset = field->report_offset;
+	unsigned size = field->report_size;
+	unsigned n;
+
+	for (n = 0; n < count; n++) {
+		if (field->logical_minimum < 0)	/* signed values */
+			implement(data, offset + n * size, size, s32ton(field->value[n], size));
+		else				/* unsigned values */
+			implement(data, offset + n * size, size, field->value[n]);
+	}
+}
+
+/*
+ * Create a report.
+ */
+
+static void hid_output_report(struct hid_report *report, __u8 *data)
+{
+	unsigned n;
+
+	if (report->id > 0)
+		*data++ = report->id;
+
+	for (n = 0; n < report->maxfield; n++)
+		hid_output_field(report->field[n], data);
+}
+
+/*
+ * Set a field value. The report this field belongs to has to be
+ * created and transferred to the device, to set this value in the
+ * device.
+ */
+
+int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
+{
+	unsigned size = field->report_size;
+
+	hid_dump_input(field->usage + offset, value);
+
+	if (offset >= field->report_count) {
+		dbg("offset (%d) exceeds report_count (%d)", offset, field->report_count);
+		hid_dump_field(field, 8);
+		return -1;
+	}
+	if (field->logical_minimum < 0) {
+		if (value != snto32(s32ton(value, size), size)) {
+			dbg("value %d is out of range", value);
+			return -1;
+		}
+	}
+	field->value[offset] = value;
+	return 0;
+}
+
+/*
+ * Find a report field with a specified HID usage.
+ */
+
+struct hid_field *hid_find_field_by_usage(struct hid_device *hid, __u32 wanted_usage, int type)
+{
+	struct hid_report *report;
+	int i;
+
+	list_for_each_entry(report, &hid->report_enum[type].report_list, list)
+		for (i = 0; i < report->maxfield; i++)
+			if (report->field[i]->logical == wanted_usage)
+				return report->field[i];
+	return NULL;
+}
+
+static int hid_submit_out(struct hid_device *hid)
+{
+	struct hid_report *report;
+
+	report = hid->out[hid->outtail];
+
+	hid_output_report(report, hid->outbuf);
+	hid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
+	hid->urbout->dev = hid->dev;
+
+	dbg("submitting out urb");
+
+	if (usb_submit_urb(hid->urbout, GFP_ATOMIC)) {
+		err("usb_submit_urb(out) failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int hid_submit_ctrl(struct hid_device *hid)
+{
+	struct hid_report *report;
+	unsigned char dir;
+	int len;
+
+	report = hid->ctrl[hid->ctrltail].report;
+	dir = hid->ctrl[hid->ctrltail].dir;
+
+	len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
+	if (dir == USB_DIR_OUT) {
+		hid_output_report(report, hid->ctrlbuf);
+		hid->urbctrl->pipe = usb_sndctrlpipe(hid->dev, 0);
+		hid->urbctrl->transfer_buffer_length = len;
+	} else {
+		int maxpacket, padlen;
+
+		hid->urbctrl->pipe = usb_rcvctrlpipe(hid->dev, 0);
+		maxpacket = usb_maxpacket(hid->dev, hid->urbctrl->pipe, 0);
+		if (maxpacket > 0) {
+			padlen = (len + maxpacket - 1) / maxpacket;
+			padlen *= maxpacket;
+			if (padlen > HID_BUFFER_SIZE)
+				padlen = HID_BUFFER_SIZE;
+		} else
+			padlen = 0;
+		hid->urbctrl->transfer_buffer_length = padlen;
+	}
+	hid->urbctrl->dev = hid->dev;
+
+	hid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
+	hid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT;
+	hid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id);
+	hid->cr->wIndex = cpu_to_le16(hid->ifnum);
+	hid->cr->wLength = cpu_to_le16(len);
+
+	dbg("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u",
+		hid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
+		hid->cr->wValue, hid->cr->wIndex, hid->cr->wLength);
+
+	if (usb_submit_urb(hid->urbctrl, GFP_ATOMIC)) {
+		err("usb_submit_urb(ctrl) failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Output interrupt completion handler.
+ */
+
+static void hid_irq_out(struct urb *urb, struct pt_regs *regs)
+{
+	struct hid_device *hid = urb->context;
+	unsigned long flags;
+	int unplug = 0;
+
+	switch (urb->status) {
+		case 0:			/* success */
+		case -ESHUTDOWN:	/* unplug */
+		case -EILSEQ:		/* unplug timeout on uhci */
+			unplug = 1;
+		case -ECONNRESET:	/* unlink */
+		case -ENOENT:
+			break;
+		default:		/* error */
+			warn("output irq status %d received", urb->status);
+	}
+
+	spin_lock_irqsave(&hid->outlock, flags);
+
+	if (unplug)
+		hid->outtail = hid->outhead;
+	else
+		hid->outtail = (hid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
+
+	if (hid->outhead != hid->outtail) {
+		if (hid_submit_out(hid)) {
+			clear_bit(HID_OUT_RUNNING, &hid->iofl);;
+			wake_up(&hid->wait);
+		}
+		spin_unlock_irqrestore(&hid->outlock, flags);
+		return;
+	}
+
+	clear_bit(HID_OUT_RUNNING, &hid->iofl);
+	spin_unlock_irqrestore(&hid->outlock, flags);
+	wake_up(&hid->wait);
+}
+
+/*
+ * Control pipe completion handler.
+ */
+
+static void hid_ctrl(struct urb *urb, struct pt_regs *regs)
+{
+	struct hid_device *hid = urb->context;
+	unsigned long flags;
+	int unplug = 0;
+
+	spin_lock_irqsave(&hid->ctrllock, flags);
+
+	switch (urb->status) {
+		case 0:			/* success */
+			if (hid->ctrl[hid->ctrltail].dir == USB_DIR_IN)
+				hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb, regs);
+		case -ESHUTDOWN:	/* unplug */
+		case -EILSEQ:		/* unplug timectrl on uhci */
+			unplug = 1;
+		case -ECONNRESET:	/* unlink */
+		case -ENOENT:
+		case -EPIPE:		/* report not available */
+			break;
+		default:		/* error */
+			warn("ctrl urb status %d received", urb->status);
+	}
+
+	if (unplug)
+		hid->ctrltail = hid->ctrlhead;
+	else
+		hid->ctrltail = (hid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
+
+	if (hid->ctrlhead != hid->ctrltail) {
+		if (hid_submit_ctrl(hid)) {
+			clear_bit(HID_CTRL_RUNNING, &hid->iofl);
+			wake_up(&hid->wait);
+		}
+		spin_unlock_irqrestore(&hid->ctrllock, flags);
+		return;
+	}
+
+	clear_bit(HID_CTRL_RUNNING, &hid->iofl);
+	spin_unlock_irqrestore(&hid->ctrllock, flags);
+	wake_up(&hid->wait);
+}
+
+void hid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
+{
+	int head;
+	unsigned long flags;
+
+	if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN)
+		return;
+
+	if (hid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) {
+
+		spin_lock_irqsave(&hid->outlock, flags);
+
+		if ((head = (hid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == hid->outtail) {
+			spin_unlock_irqrestore(&hid->outlock, flags);
+			warn("output queue full");
+			return;
+		}
+
+		hid->out[hid->outhead] = report;
+		hid->outhead = head;
+
+		if (!test_and_set_bit(HID_OUT_RUNNING, &hid->iofl))
+			if (hid_submit_out(hid))
+				clear_bit(HID_OUT_RUNNING, &hid->iofl);
+
+		spin_unlock_irqrestore(&hid->outlock, flags);
+		return;
+	}
+
+	spin_lock_irqsave(&hid->ctrllock, flags);
+
+	if ((head = (hid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == hid->ctrltail) {
+		spin_unlock_irqrestore(&hid->ctrllock, flags);
+		warn("control queue full");
+		return;
+	}
+
+	hid->ctrl[hid->ctrlhead].report = report;
+	hid->ctrl[hid->ctrlhead].dir = dir;
+	hid->ctrlhead = head;
+
+	if (!test_and_set_bit(HID_CTRL_RUNNING, &hid->iofl))
+		if (hid_submit_ctrl(hid))
+			clear_bit(HID_CTRL_RUNNING, &hid->iofl);
+
+	spin_unlock_irqrestore(&hid->ctrllock, flags);
+}
+
+int hid_wait_io(struct hid_device *hid)
+{
+	if (!wait_event_timeout(hid->wait, (!test_bit(HID_CTRL_RUNNING, &hid->iofl) &&
+					!test_bit(HID_OUT_RUNNING, &hid->iofl)),
+					10*HZ)) {
+		dbg("timeout waiting for ctrl or out queue to clear");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
+		unsigned char type, void *buf, int size)
+{
+	int result, retries = 4;
+
+	memset(buf,0,size);	// Make sure we parse really received data
+
+	do {
+		result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+				USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN,
+				(type << 8), ifnum, buf, size, USB_CTRL_GET_TIMEOUT);
+		retries--;
+	} while (result < size && retries);
+	return result;
+}
+
+int hid_open(struct hid_device *hid)
+{
+	if (hid->open++)
+		return 0;
+
+	hid->urbin->dev = hid->dev;
+
+	if (usb_submit_urb(hid->urbin, GFP_KERNEL))
+		return -EIO;
+
+	return 0;
+}
+
+void hid_close(struct hid_device *hid)
+{
+	if (!--hid->open)
+		usb_kill_urb(hid->urbin);
+}
+
+/*
+ * Initialize all reports
+ */
+
+void hid_init_reports(struct hid_device *hid)
+{
+	struct hid_report *report;
+	int err, ret;
+
+	list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) {
+		int size = ((report->size - 1) >> 3) + 1 + hid->report_enum[HID_INPUT_REPORT].numbered;
+		if (size > HID_BUFFER_SIZE) size = HID_BUFFER_SIZE;
+		if (size > hid->urbin->transfer_buffer_length)
+			hid->urbin->transfer_buffer_length = size;
+		hid_submit_report(hid, report, USB_DIR_IN);
+	}
+
+	list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
+		hid_submit_report(hid, report, USB_DIR_IN);
+
+	err = 0;
+	ret = hid_wait_io(hid);
+	while (ret) {
+		err |= ret;
+		if (test_bit(HID_CTRL_RUNNING, &hid->iofl))
+			usb_kill_urb(hid->urbctrl);
+		if (test_bit(HID_OUT_RUNNING, &hid->iofl))
+			usb_kill_urb(hid->urbout);
+		ret = hid_wait_io(hid);
+	}
+
+	if (err)
+		warn("timeout initializing reports\n");
+
+	usb_control_msg(hid->dev, usb_sndctrlpipe(hid->dev, 0),
+		HID_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+		hid->ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT);
+}
+
+#define USB_VENDOR_ID_WACOM		0x056a
+#define USB_DEVICE_ID_WACOM_PENPARTNER	0x0000
+#define USB_DEVICE_ID_WACOM_GRAPHIRE	0x0010
+#define USB_DEVICE_ID_WACOM_INTUOS	0x0020
+#define USB_DEVICE_ID_WACOM_PL		0x0030
+#define USB_DEVICE_ID_WACOM_INTUOS2	0x0040
+#define USB_DEVICE_ID_WACOM_VOLITO	0x0060
+#define USB_DEVICE_ID_WACOM_PTU		0x0003
+
+#define USB_VENDOR_ID_KBGEAR		0x084e
+#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO	0x1001
+
+#define USB_VENDOR_ID_AIPTEK		0x08ca
+#define USB_DEVICE_ID_AIPTEK_01		0x0001
+#define USB_DEVICE_ID_AIPTEK_10		0x0010
+#define USB_DEVICE_ID_AIPTEK_20		0x0020
+#define USB_DEVICE_ID_AIPTEK_21		0x0021
+#define USB_DEVICE_ID_AIPTEK_22		0x0022
+#define USB_DEVICE_ID_AIPTEK_23		0x0023
+#define USB_DEVICE_ID_AIPTEK_24		0x0024
+
+#define USB_VENDOR_ID_GRIFFIN		0x077d
+#define USB_DEVICE_ID_POWERMATE		0x0410
+#define USB_DEVICE_ID_SOUNDKNOB		0x04AA
+
+#define USB_VENDOR_ID_ATEN		0x0557
+#define USB_DEVICE_ID_ATEN_UC100KM	0x2004
+#define USB_DEVICE_ID_ATEN_CS124U	0x2202
+#define USB_DEVICE_ID_ATEN_2PORTKVM	0x2204
+#define USB_DEVICE_ID_ATEN_4PORTKVM	0x2205
+#define USB_DEVICE_ID_ATEN_4PORTKVMC	0x2208
+
+#define USB_VENDOR_ID_TOPMAX		0x0663
+#define USB_DEVICE_ID_TOPMAX_COBRAPAD	0x0103
+
+#define USB_VENDOR_ID_HAPP		0x078b
+#define USB_DEVICE_ID_UGCI_DRIVING	0x0010
+#define USB_DEVICE_ID_UGCI_FLYING	0x0020
+#define USB_DEVICE_ID_UGCI_FIGHTING	0x0030
+
+#define USB_VENDOR_ID_MGE		0x0463
+#define USB_DEVICE_ID_MGE_UPS		0xffff
+#define USB_DEVICE_ID_MGE_UPS1		0x0001
+
+#define USB_VENDOR_ID_ONTRAK		0x0a07
+#define USB_DEVICE_ID_ONTRAK_ADU100	0x0064
+
+#define USB_VENDOR_ID_TANGTOP		0x0d3d
+#define USB_DEVICE_ID_TANGTOP_USBPS2	0x0001
+
+#define USB_VENDOR_ID_ESSENTIAL_REALITY	0x0d7f
+#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
+
+#define USB_VENDOR_ID_A4TECH		0x09da
+#define USB_DEVICE_ID_A4TECH_WCP32PU	0x0006
+
+#define USB_VENDOR_ID_CYPRESS		0x04b4
+#define USB_DEVICE_ID_CYPRESS_MOUSE	0x0001
+#define USB_DEVICE_ID_CYPRESS_HIDCOM	0x5500
+
+#define USB_VENDOR_ID_BERKSHIRE		0x0c98
+#define USB_DEVICE_ID_BERKSHIRE_PCWD	0x1140
+
+#define USB_VENDOR_ID_ALPS		0x0433
+#define USB_DEVICE_ID_IBM_GAMEPAD	0x1101
+
+#define USB_VENDOR_ID_SAITEK		0x06a3
+#define USB_DEVICE_ID_SAITEK_RUMBLEPAD	0xff17
+
+#define USB_VENDOR_ID_NEC		0x073e
+#define USB_DEVICE_ID_NEC_USB_GAME_PAD	0x0301
+
+#define USB_VENDOR_ID_CHIC		0x05fe
+#define USB_DEVICE_ID_CHIC_GAMEPAD	0x0014
+
+#define USB_VENDOR_ID_GLAB		0x06c2
+#define USB_DEVICE_ID_4_PHIDGETSERVO_30	0x0038
+#define USB_DEVICE_ID_1_PHIDGETSERVO_30	0x0039
+#define USB_DEVICE_ID_8_8_8_IF_KIT	0x0045
+#define USB_DEVICE_ID_0_0_4_IF_KIT	0x0040
+#define USB_DEVICE_ID_0_8_8_IF_KIT	0x0053
+
+#define USB_VENDOR_ID_WISEGROUP		0x0925
+#define USB_DEVICE_ID_1_PHIDGETSERVO_20	0x8101
+#define USB_DEVICE_ID_4_PHIDGETSERVO_20	0x8104
+
+#define USB_VENDOR_ID_CODEMERCS		0x07c0
+#define USB_DEVICE_ID_CODEMERCS_IOW40	0x1500
+#define USB_DEVICE_ID_CODEMERCS_IOW24	0x1501
+#define USB_DEVICE_ID_CODEMERCS_IOW48	0x1502
+#define USB_DEVICE_ID_CODEMERCS_IOW28	0x1503
+
+#define USB_VENDOR_ID_DELORME		0x1163
+#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
+
+#define USB_VENDOR_ID_MCC		0x09db
+#define USB_DEVICE_ID_MCC_PMD1024LS	0x0076
+#define USB_DEVICE_ID_MCC_PMD1208LS	0x007a
+
+#define USB_VENDOR_ID_CHICONY		0x04f2
+#define USB_DEVICE_ID_CHICONY_USBHUB_KB	0x0100
+
+#define USB_VENDOR_ID_BTC		0x046e
+#define USB_DEVICE_ID_BTC_KEYBOARD	0x5303
+
+
+/*
+ * Alphabetically sorted blacklist by quirk type.
+ */
+
+static struct hid_blacklist {
+	__u16 idVendor;
+	__u16 idProduct;
+	unsigned quirks;
+} hid_blacklist[] = {
+
+	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW48, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW28, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PENPARTNER, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE + 1, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE + 2, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE + 3, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE + 4, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 1, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 2, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 3, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 4, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 1, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 2, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 3, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 4, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PL + 5, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 1, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 2, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 3, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 4, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 5, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS2 + 7, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_VOLITO, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PTU, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
+	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
+
+	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
+	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
+	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
+	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
+	{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
+	{ USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_KEYBOARD, HID_QUIRK_NOGET},
+	{ USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_USBHUB_KB, HID_QUIRK_NOGET},
+	{ USB_VENDOR_ID_TANGTOP, USB_DEVICE_ID_TANGTOP_USBPS2, HID_QUIRK_NOGET },
+
+	{ USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 },
+	{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 },
+
+	{ USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
+	{ USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },
+	{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
+	{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
+	{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
+	{ USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD },
+	{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
+	{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
+
+	{ 0, 0 }
+};
+
+static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid)
+{
+	if (!(hid->inbuf = usb_buffer_alloc(dev, HID_BUFFER_SIZE, SLAB_ATOMIC, &hid->inbuf_dma)))
+		return -1;
+	if (!(hid->outbuf = usb_buffer_alloc(dev, HID_BUFFER_SIZE, SLAB_ATOMIC, &hid->outbuf_dma)))
+		return -1;
+	if (!(hid->cr = usb_buffer_alloc(dev, sizeof(*(hid->cr)), SLAB_ATOMIC, &hid->cr_dma)))
+		return -1;
+	if (!(hid->ctrlbuf = usb_buffer_alloc(dev, HID_BUFFER_SIZE, SLAB_ATOMIC, &hid->ctrlbuf_dma)))
+		return -1;
+
+	return 0;
+}
+
+static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
+{
+	if (hid->inbuf)
+		usb_buffer_free(dev, HID_BUFFER_SIZE, hid->inbuf, hid->inbuf_dma);
+	if (hid->outbuf)
+		usb_buffer_free(dev, HID_BUFFER_SIZE, hid->outbuf, hid->outbuf_dma);
+	if (hid->cr)
+		usb_buffer_free(dev, sizeof(*(hid->cr)), hid->cr, hid->cr_dma);
+	if (hid->ctrlbuf)
+		usb_buffer_free(dev, HID_BUFFER_SIZE, hid->ctrlbuf, hid->ctrlbuf_dma);
+}
+
+static struct hid_device *usb_hid_configure(struct usb_interface *intf)
+{
+	struct usb_host_interface *interface = intf->cur_altsetting;
+	struct usb_device *dev = interface_to_usbdev (intf);
+	struct hid_descriptor *hdesc;
+	struct hid_device *hid;
+	unsigned quirks = 0, rsize = 0;
+	char *buf, *rdesc;
+	int n;
+
+	for (n = 0; hid_blacklist[n].idVendor; n++)
+		if ((hid_blacklist[n].idVendor == le16_to_cpu(dev->descriptor.idVendor)) &&
+			(hid_blacklist[n].idProduct == le16_to_cpu(dev->descriptor.idProduct)))
+				quirks = hid_blacklist[n].quirks;
+
+	if (quirks & HID_QUIRK_IGNORE)
+		return NULL;
+
+	if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) && ((!interface->desc.bNumEndpoints) ||
+		usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
+			dbg("class descriptor not present\n");
+			return NULL;
+	}
+
+	for (n = 0; n < hdesc->bNumDescriptors; n++)
+		if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
+			rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
+
+	if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
+		dbg("weird size of report descriptor (%u)", rsize);
+		return NULL;
+	}
+
+	if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
+		dbg("couldn't allocate rdesc memory");
+		return NULL;
+	}
+
+	if ((n = hid_get_class_descriptor(dev, interface->desc.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 char) rdesc[n]);
+	printk("\n");
+#endif
+
+	if (!(hid = hid_parse_report(rdesc, n))) {
+		dbg("parsing report descriptor failed");
+		kfree(rdesc);
+		return NULL;
+	}
+
+	kfree(rdesc);
+	hid->quirks = quirks;
+
+	if (hid_alloc_buffers(dev, hid)) {
+		hid_free_buffers(dev, hid);
+		goto fail;
+	}
+
+	for (n = 0; n < interface->desc.bNumEndpoints; n++) {
+
+		struct usb_endpoint_descriptor *endpoint;
+		int pipe;
+		int interval;
+
+		endpoint = &interface->endpoint[n].desc;
+		if ((endpoint->bmAttributes & 3) != 3)		/* Not an interrupt endpoint */
+			continue;
+
+		/* handle potential highspeed HID correctly */
+		interval = endpoint->bInterval;
+		if (dev->speed == USB_SPEED_HIGH)
+			interval = 1 << (interval - 1);
+
+		/* Change the polling interval of mice. */
+		if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
+			interval = hid_mousepoll_interval;
+		
+		if (endpoint->bEndpointAddress & USB_DIR_IN) {
+			if (hid->urbin)
+				continue;
+			if (!(hid->urbin = usb_alloc_urb(0, GFP_KERNEL)))
+				goto fail;
+			pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+			usb_fill_int_urb(hid->urbin, dev, pipe, hid->inbuf, 0,
+					 hid_irq_in, hid, interval);
+			hid->urbin->transfer_dma = hid->inbuf_dma;
+			hid->urbin->transfer_flags |=(URB_NO_TRANSFER_DMA_MAP | URB_ASYNC_UNLINK);
+		} else {
+			if (hid->urbout)
+				continue;
+			if (!(hid->urbout = usb_alloc_urb(0, GFP_KERNEL)))
+				goto fail;
+			pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress);
+			usb_fill_int_urb(hid->urbout, dev, pipe, hid->outbuf, 0,
+					 hid_irq_out, hid, interval);
+			hid->urbout->transfer_dma = hid->outbuf_dma;
+			hid->urbout->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_ASYNC_UNLINK);
+		}
+	}
+
+	if (!hid->urbin) {
+		err("couldn't find an input interrupt endpoint");
+		goto fail;
+	}
+
+	init_waitqueue_head(&hid->wait);
+
+	spin_lock_init(&hid->outlock);
+	spin_lock_init(&hid->ctrllock);
+
+	hid->version = le16_to_cpu(hdesc->bcdHID);
+	hid->country = hdesc->bCountryCode;
+	hid->dev = dev;
+	hid->intf = intf;
+	hid->ifnum = interface->desc.bInterfaceNumber;
+
+	hid->name[0] = 0;
+
+	if (!(buf = kmalloc(64, GFP_KERNEL)))
+		goto fail;
+
+	if (dev->manufacturer) {
+		strcat(hid->name, dev->manufacturer);
+		if (dev->product)
+			snprintf(hid->name, 64, "%s %s", hid->name, dev->product);
+	} else if (dev->product) {
+			snprintf(hid->name, 128, "%s", dev->product);
+	} else
+		snprintf(hid->name, 128, "%04x:%04x",
+			le16_to_cpu(dev->descriptor.idVendor),
+			le16_to_cpu(dev->descriptor.idProduct));
+
+	usb_make_path(dev, buf, 64);
+	snprintf(hid->phys, 64, "%s/input%d", buf,
+			intf->altsetting[0].desc.bInterfaceNumber);
+
+	if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
+		hid->uniq[0] = 0;
+
+	kfree(buf);
+
+	hid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
+	if (!hid->urbctrl)
+		goto fail;
+	usb_fill_control_urb(hid->urbctrl, dev, 0, (void *) hid->cr,
+			     hid->ctrlbuf, 1, hid_ctrl, hid);
+	hid->urbctrl->setup_dma = hid->cr_dma;
+	hid->urbctrl->transfer_dma = hid->ctrlbuf_dma;
+	hid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP | URB_ASYNC_UNLINK);
+
+	return hid;
+
+fail:
+
+	if (hid->urbin)
+		usb_free_urb(hid->urbin);
+	if (hid->urbout)
+		usb_free_urb(hid->urbout);
+	if (hid->urbctrl)
+		usb_free_urb(hid->urbctrl);
+	hid_free_buffers(dev, hid);
+	hid_free_device(hid);
+
+	return NULL;
+}
+
+static void hid_disconnect(struct usb_interface *intf)
+{
+	struct hid_device *hid = usb_get_intfdata (intf);
+
+	if (!hid)
+		return;
+
+	usb_set_intfdata(intf, NULL);
+	usb_kill_urb(hid->urbin);
+	usb_kill_urb(hid->urbout);
+	usb_kill_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);
+
+	hid_free_buffers(hid->dev, hid);
+	hid_free_device(hid);
+}
+
+static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct hid_device *hid;
+	char path[64];
+	int i;
+	char *c;
+
+	dbg("HID probe called for ifnum %d",
+			intf->altsetting->desc.bInterfaceNumber);
+
+	if (!(hid = usb_hid_configure(intf)))
+		return -EIO;
+
+	hid_init_reports(hid);
+	hid_dump_device(hid);
+
+	if (!hidinput_connect(hid))
+		hid->claimed |= HID_CLAIMED_INPUT;
+	if (!hiddev_connect(hid))
+		hid->claimed |= HID_CLAIMED_HIDDEV;
+
+	usb_set_intfdata(intf, hid);
+
+	if (!hid->claimed) {
+		printk ("HID device not claimed by input or hiddev\n");
+		hid_disconnect(intf);
+		return -EIO;
+	}
+
+	printk(KERN_INFO);
+
+	if (hid->claimed & HID_CLAIMED_INPUT)
+		printk("input");
+	if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV))
+		printk(",");
+	if (hid->claimed & HID_CLAIMED_HIDDEV)
+		printk("hiddev%d", hid->minor);
+
+	c = "Device";
+	for (i = 0; i < hid->maxcollection; i++) {
+		if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
+		    (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
+		    (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {
+			c = hid_types[hid->collection[i].usage & 0xffff];
+			break;
+		}
+	}
+
+	usb_make_path(interface_to_usbdev(intf), path, 63);
+
+	printk(": USB HID v%x.%02x %s [%s] on %s\n",
+		hid->version >> 8, hid->version & 0xff, c, hid->name, path);
+
+	return 0;
+}
+
+static int hid_suspend(struct usb_interface *intf, u32 state)
+{
+	struct hid_device *hid = usb_get_intfdata (intf);
+
+	usb_kill_urb(hid->urbin);
+	intf->dev.power.power_state = state;
+	dev_dbg(&intf->dev, "suspend\n");
+	return 0;
+}
+
+static int hid_resume(struct usb_interface *intf)
+{
+	struct hid_device *hid = usb_get_intfdata (intf);
+	int status;
+
+	intf->dev.power.power_state = PM_SUSPEND_ON;
+	if (hid->open)
+		status = usb_submit_urb(hid->urbin, GFP_NOIO);
+	else
+		status = 0;
+	dev_dbg(&intf->dev, "resume status %d\n", status);
+	return status;
+}
+
+static struct usb_device_id hid_usb_ids [] = {
+	{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
+		.bInterfaceClass = USB_INTERFACE_CLASS_HID },
+	{ }						/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, hid_usb_ids);
+
+static struct usb_driver hid_driver = {
+	.owner =	THIS_MODULE,
+	.name =		"usbhid",
+	.probe =	hid_probe,
+	.disconnect =	hid_disconnect,
+	.suspend =	hid_suspend,
+	.resume =	hid_resume,
+	.id_table =	hid_usb_ids,
+};
+
+static int __init hid_init(void)
+{
+	int retval;
+	retval = hiddev_init();
+	if (retval)
+		goto hiddev_init_fail;
+	retval = usb_register(&hid_driver);
+	if (retval)
+		goto usb_register_fail;
+	info(DRIVER_VERSION ":" DRIVER_DESC);
+
+	return 0;
+usb_register_fail:
+	hiddev_exit();
+hiddev_init_fail:
+	return retval;
+}
+
+static void __exit hid_exit(void)
+{
+	usb_deregister(&hid_driver);
+	hiddev_exit();
+}
+
+module_init(hid_init);
+module_exit(hid_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
diff --git a/drivers/usb/input/hid-debug.h b/drivers/usb/input/hid-debug.h
new file mode 100644
index 0000000..2b91705
--- /dev/null
+++ b/drivers/usb/input/hid-debug.h
@@ -0,0 +1,720 @@
+/*
+ * $Id: hid-debug.h,v 1.8 2001/09/25 09:37:57 vojtech Exp $
+ *
+ *  (c) 1999 Andreas Gal		<gal@cs.uni-magdeburg.de>
+ *  (c) 2000-2001 Vojtech Pavlik	<vojtech@ucw.cz>
+ *
+ *  Some debug stuff for the HID parser.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/input.h>
+
+struct hid_usage_entry {
+	unsigned  page;
+	unsigned  usage;
+	char     *description;
+};
+
+static const struct hid_usage_entry hid_usage_table[] = {
+  {  0,      0, "Undefined" },
+  {  1,      0, "GenericDesktop" },
+    {0, 0x01, "Pointer"},
+    {0, 0x02, "Mouse"},
+    {0, 0x04, "Joystick"},
+    {0, 0x05, "GamePad"},
+    {0, 0x06, "Keyboard"},
+    {0, 0x07, "Keypad"},
+    {0, 0x08, "MultiAxis"},
+      {0, 0x30, "X"},
+      {0, 0x31, "Y"},
+      {0, 0x32, "Z"},
+      {0, 0x33, "Rx"},
+      {0, 0x34, "Ry"},
+      {0, 0x35, "Rz"},
+      {0, 0x36, "Slider"},
+      {0, 0x37, "Dial"},
+      {0, 0x38, "Wheel"},
+      {0, 0x39, "HatSwitch"},
+    {0, 0x3a, "CountedBuffer"},
+      {0, 0x3b, "ByteCount"},
+      {0, 0x3c, "MotionWakeup"},
+      {0, 0x3d, "Start"},
+      {0, 0x3e, "Select"},
+      {0, 0x40, "Vx"},
+      {0, 0x41, "Vy"},
+      {0, 0x42, "Vz"},
+      {0, 0x43, "Vbrx"},
+      {0, 0x44, "Vbry"},
+      {0, 0x45, "Vbrz"},
+      {0, 0x46, "Vno"},
+    {0, 0x80, "SystemControl"}, 
+      {0, 0x81, "SystemPowerDown"},
+      {0, 0x82, "SystemSleep"},
+      {0, 0x83, "SystemWakeUp"},
+      {0, 0x84, "SystemContextMenu"},
+      {0, 0x85, "SystemMainMenu"},
+      {0, 0x86, "SystemAppMenu"},
+      {0, 0x87, "SystemMenuHelp"},
+      {0, 0x88, "SystemMenuExit"},
+      {0, 0x89, "SystemMenuSelect"},
+      {0, 0x8a, "SystemMenuRight"},
+      {0, 0x8b, "SystemMenuLeft"},
+      {0, 0x8c, "SystemMenuUp"},
+      {0, 0x8d, "SystemMenuDown"},
+      {0, 0x90, "D-PadUp"},
+      {0, 0x91, "D-PadDown"},
+      {0, 0x92, "D-PadRight"},
+      {0, 0x93, "D-PadLeft"},
+  {  7, 0, "Keyboard" },
+  {  8, 0, "LED" },
+      {0, 0x01, "NumLock"},
+      {0, 0x02, "CapsLock"},
+      {0, 0x03, "ScrollLock"},
+      {0, 0x04, "Compose"},
+      {0, 0x05, "Kana"},
+  {  9, 0, "Button" },
+  { 10, 0, "Ordinal" },
+  { 12, 0, "Consumer" },
+      {0, 0x238, "HorizontalWheel"},
+  { 13, 0, "Digitizers" },
+    {0, 0x01, "Digitizer"},
+    {0, 0x02, "Pen"},
+    {0, 0x03, "LightPen"},
+    {0, 0x04, "TouchScreen"},
+    {0, 0x05, "TouchPad"},
+    {0, 0x20, "Stylus"},
+    {0, 0x21, "Puck"},
+    {0, 0x22, "Finger"},
+    {0, 0x30, "TipPressure"},
+    {0, 0x31, "BarrelPressure"},
+    {0, 0x32, "InRange"},
+    {0, 0x33, "Touch"},
+    {0, 0x34, "UnTouch"},
+    {0, 0x35, "Tap"},
+    {0, 0x39, "TabletFunctionKey"},
+    {0, 0x3a, "ProgramChangeKey"},
+    {0, 0x3c, "Invert"},
+    {0, 0x42, "TipSwitch"},
+    {0, 0x43, "SecondaryTipSwitch"},
+    {0, 0x44, "BarrelSwitch"},
+    {0, 0x45, "Eraser"},
+    {0, 0x46, "TabletPick"},
+  { 15, 0, "PhysicalInterfaceDevice" },
+    {0, 0x00, "Undefined"},
+    {0, 0x01, "Physical_Interface_Device"},
+      {0, 0x20, "Normal"},
+    {0, 0x21, "Set_Effect_Report"},
+      {0, 0x22, "Effect_Block_Index"},
+      {0, 0x23, "Parameter_Block_Offset"},
+      {0, 0x24, "ROM_Flag"},
+      {0, 0x25, "Effect_Type"},
+        {0, 0x26, "ET_Constant_Force"},
+        {0, 0x27, "ET_Ramp"},
+        {0, 0x28, "ET_Custom_Force_Data"},
+        {0, 0x30, "ET_Square"},
+        {0, 0x31, "ET_Sine"},
+        {0, 0x32, "ET_Triangle"},
+        {0, 0x33, "ET_Sawtooth_Up"},
+        {0, 0x34, "ET_Sawtooth_Down"},
+        {0, 0x40, "ET_Spring"},
+        {0, 0x41, "ET_Damper"},
+        {0, 0x42, "ET_Inertia"},
+        {0, 0x43, "ET_Friction"},
+      {0, 0x50, "Duration"},
+      {0, 0x51, "Sample_Period"},
+      {0, 0x52, "Gain"},
+      {0, 0x53, "Trigger_Button"},
+      {0, 0x54, "Trigger_Repeat_Interval"},
+      {0, 0x55, "Axes_Enable"},
+        {0, 0x56, "Direction_Enable"},
+      {0, 0x57, "Direction"},
+      {0, 0x58, "Type_Specific_Block_Offset"},
+        {0, 0x59, "Block_Type"},
+        {0, 0x5A, "Set_Envelope_Report"},
+          {0, 0x5B, "Attack_Level"},
+          {0, 0x5C, "Attack_Time"},
+          {0, 0x5D, "Fade_Level"},
+          {0, 0x5E, "Fade_Time"},
+        {0, 0x5F, "Set_Condition_Report"},
+        {0, 0x60, "CP_Offset"},
+        {0, 0x61, "Positive_Coefficient"},
+        {0, 0x62, "Negative_Coefficient"},
+        {0, 0x63, "Positive_Saturation"},
+        {0, 0x64, "Negative_Saturation"},
+        {0, 0x65, "Dead_Band"},
+      {0, 0x66, "Download_Force_Sample"},
+      {0, 0x67, "Isoch_Custom_Force_Enable"},
+      {0, 0x68, "Custom_Force_Data_Report"},
+        {0, 0x69, "Custom_Force_Data"},
+        {0, 0x6A, "Custom_Force_Vendor_Defined_Data"},
+      {0, 0x6B, "Set_Custom_Force_Report"},
+        {0, 0x6C, "Custom_Force_Data_Offset"},
+        {0, 0x6D, "Sample_Count"},
+      {0, 0x6E, "Set_Periodic_Report"},
+        {0, 0x6F, "Offset"},
+        {0, 0x70, "Magnitude"},
+        {0, 0x71, "Phase"},
+        {0, 0x72, "Period"},
+      {0, 0x73, "Set_Constant_Force_Report"},
+        {0, 0x74, "Set_Ramp_Force_Report"},
+        {0, 0x75, "Ramp_Start"},
+        {0, 0x76, "Ramp_End"},
+      {0, 0x77, "Effect_Operation_Report"},
+        {0, 0x78, "Effect_Operation"},
+          {0, 0x79, "Op_Effect_Start"},
+          {0, 0x7A, "Op_Effect_Start_Solo"},
+          {0, 0x7B, "Op_Effect_Stop"},
+          {0, 0x7C, "Loop_Count"},
+      {0, 0x7D, "Device_Gain_Report"},
+        {0, 0x7E, "Device_Gain"},
+    {0, 0x7F, "PID_Pool_Report"},
+      {0, 0x80, "RAM_Pool_Size"},
+      {0, 0x81, "ROM_Pool_Size"},
+      {0, 0x82, "ROM_Effect_Block_Count"},
+      {0, 0x83, "Simultaneous_Effects_Max"},
+      {0, 0x84, "Pool_Alignment"},
+    {0, 0x85, "PID_Pool_Move_Report"},
+      {0, 0x86, "Move_Source"},
+      {0, 0x87, "Move_Destination"},
+      {0, 0x88, "Move_Length"},
+    {0, 0x89, "PID_Block_Load_Report"},
+      {0, 0x8B, "Block_Load_Status"},
+      {0, 0x8C, "Block_Load_Success"},
+      {0, 0x8D, "Block_Load_Full"},
+      {0, 0x8E, "Block_Load_Error"},
+      {0, 0x8F, "Block_Handle"},
+      {0, 0x90, "PID_Block_Free_Report"},
+      {0, 0x91, "Type_Specific_Block_Handle"},
+    {0, 0x92, "PID_State_Report"},
+      {0, 0x94, "Effect_Playing"},
+      {0, 0x95, "PID_Device_Control_Report"},
+        {0, 0x96, "PID_Device_Control"},
+        {0, 0x97, "DC_Enable_Actuators"},
+        {0, 0x98, "DC_Disable_Actuators"},
+        {0, 0x99, "DC_Stop_All_Effects"},
+        {0, 0x9A, "DC_Device_Reset"},
+        {0, 0x9B, "DC_Device_Pause"},
+        {0, 0x9C, "DC_Device_Continue"},
+      {0, 0x9F, "Device_Paused"},
+      {0, 0xA0, "Actuators_Enabled"},
+      {0, 0xA4, "Safety_Switch"},
+      {0, 0xA5, "Actuator_Override_Switch"},
+      {0, 0xA6, "Actuator_Power"},
+    {0, 0xA7, "Start_Delay"},
+    {0, 0xA8, "Parameter_Block_Size"},
+    {0, 0xA9, "Device_Managed_Pool"},
+    {0, 0xAA, "Shared_Parameter_Blocks"},
+    {0, 0xAB, "Create_New_Effect_Report"},
+    {0, 0xAC, "RAM_Pool_Available"},
+  { 0x84, 0, "Power Device" },
+    { 0x84, 0x02, "PresentStatus" },
+    { 0x84, 0x03, "ChangeStatus" },
+    { 0x84, 0x04, "UPS" },
+    { 0x84, 0x05, "PowerSupply" },
+    { 0x84, 0x10, "BatterySystem" },
+    { 0x84, 0x11, "BatterySystemID" },
+    { 0x84, 0x12, "Battery" },
+    { 0x84, 0x13, "BatteryID" },
+    { 0x84, 0x14, "Charger" },
+    { 0x84, 0x15, "ChargerID" },
+    { 0x84, 0x16, "PowerConverter" },
+    { 0x84, 0x17, "PowerConverterID" },
+    { 0x84, 0x18, "OutletSystem" },
+    { 0x84, 0x19, "OutletSystemID" },
+    { 0x84, 0x1a, "Input" },
+    { 0x84, 0x1b, "InputID" },
+    { 0x84, 0x1c, "Output" },
+    { 0x84, 0x1d, "OutputID" },
+    { 0x84, 0x1e, "Flow" },
+    { 0x84, 0x1f, "FlowID" },
+    { 0x84, 0x20, "Outlet" },
+    { 0x84, 0x21, "OutletID" },
+    { 0x84, 0x22, "Gang" },
+    { 0x84, 0x24, "PowerSummary" },
+    { 0x84, 0x25, "PowerSummaryID" },
+    { 0x84, 0x30, "Voltage" },
+    { 0x84, 0x31, "Current" },
+    { 0x84, 0x32, "Frequency" },
+    { 0x84, 0x33, "ApparentPower" },
+    { 0x84, 0x35, "PercentLoad" },
+    { 0x84, 0x40, "ConfigVoltage" },
+    { 0x84, 0x41, "ConfigCurrent" },
+    { 0x84, 0x43, "ConfigApparentPower" },
+    { 0x84, 0x53, "LowVoltageTransfer" },
+    { 0x84, 0x54, "HighVoltageTransfer" },
+    { 0x84, 0x56, "DelayBeforeStartup" },
+    { 0x84, 0x57, "DelayBeforeShutdown" },
+    { 0x84, 0x58, "Test" },
+    { 0x84, 0x5a, "AudibleAlarmControl" },
+    { 0x84, 0x60, "Present" },
+    { 0x84, 0x61, "Good" },
+    { 0x84, 0x62, "InternalFailure" },
+    { 0x84, 0x65, "Overload" },
+    { 0x84, 0x66, "OverCharged" },
+    { 0x84, 0x67, "OverTemperature" },
+    { 0x84, 0x68, "ShutdownRequested" },
+    { 0x84, 0x69, "ShutdownImminent" },
+    { 0x84, 0x6b, "SwitchOn/Off" },
+    { 0x84, 0x6c, "Switchable" },
+    { 0x84, 0x6d, "Used" },
+    { 0x84, 0x6e, "Boost" },
+    { 0x84, 0x73, "CommunicationLost" },
+    { 0x84, 0xfd, "iManufacturer" },
+    { 0x84, 0xfe, "iProduct" },
+    { 0x84, 0xff, "iSerialNumber" },
+  { 0x85, 0, "Battery System" },
+    { 0x85, 0x01, "SMBBatteryMode" },
+    { 0x85, 0x02, "SMBBatteryStatus" },
+    { 0x85, 0x03, "SMBAlarmWarning" },
+    { 0x85, 0x04, "SMBChargerMode" },
+    { 0x85, 0x05, "SMBChargerStatus" },
+    { 0x85, 0x06, "SMBChargerSpecInfo" },
+    { 0x85, 0x07, "SMBSelectorState" },
+    { 0x85, 0x08, "SMBSelectorPresets" },
+    { 0x85, 0x09, "SMBSelectorInfo" },
+    { 0x85, 0x29, "RemainingCapacityLimit" },
+    { 0x85, 0x2c, "CapacityMode" },
+    { 0x85, 0x42, "BelowRemainingCapacityLimit" },
+    { 0x85, 0x44, "Charging" },
+    { 0x85, 0x45, "Discharging" },
+    { 0x85, 0x4b, "NeedReplacement" },
+    { 0x85, 0x66, "RemainingCapacity" },
+    { 0x85, 0x68, "RunTimeToEmpty" },
+    { 0x85, 0x6a, "AverageTimeToFull" },
+    { 0x85, 0x83, "DesignCapacity" },
+    { 0x85, 0x85, "ManufacturerDate" },
+    { 0x85, 0x89, "iDeviceChemistry" },
+    { 0x85, 0x8b, "Rechargable" },
+    { 0x85, 0x8f, "iOEMInformation" },
+    { 0x85, 0x8d, "CapacityGranularity1" },
+    { 0x85, 0xd0, "ACPresent" },
+  /* pages 0xff00 to 0xffff are vendor-specific */
+  { 0xffff, 0, "Vendor-specific-FF" },
+  { 0, 0, NULL }
+};
+
+static void resolv_usage_page(unsigned page) {
+	const struct hid_usage_entry *p;
+
+	for (p = hid_usage_table; p->description; p++)
+		if (p->page == page) {
+			printk("%s", p->description);
+			return;
+		}
+	printk("%04x", page);
+}
+
+static void resolv_usage(unsigned usage) {
+	const struct hid_usage_entry *p;
+
+	resolv_usage_page(usage >> 16);
+	printk(".");
+	for (p = hid_usage_table; p->description; p++)
+		if (p->page == (usage >> 16)) {
+			for(++p; p->description && p->usage != 0; p++)
+				if (p->usage == (usage & 0xffff)) {
+					printk("%s", p->description);
+					return;
+				}
+			break;
+		}
+	printk("%04x", usage & 0xffff);
+}
+
+__inline__ static void tab(int n) {
+	while (n--) printk(" ");
+}
+
+static void hid_dump_field(struct hid_field *field, int n) {
+	int j;
+	
+	if (field->physical) {
+		tab(n);
+		printk("Physical(");
+		resolv_usage(field->physical); printk(")\n");
+	}
+	if (field->logical) {
+		tab(n);
+		printk("Logical(");
+		resolv_usage(field->logical); printk(")\n");
+	}
+	tab(n); printk("Usage(%d)\n", field->maxusage);
+	for (j = 0; j < field->maxusage; j++) {
+		tab(n+2);resolv_usage(field->usage[j].hid); printk("\n");
+	}
+	if (field->logical_minimum != field->logical_maximum) {
+		tab(n); printk("Logical Minimum(%d)\n", field->logical_minimum);
+		tab(n); printk("Logical Maximum(%d)\n", field->logical_maximum);
+	}
+	if (field->physical_minimum != field->physical_maximum) {
+		tab(n); printk("Physical Minimum(%d)\n", field->physical_minimum);
+		tab(n); printk("Physical Maximum(%d)\n", field->physical_maximum);
+	}
+	if (field->unit_exponent) {
+		tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent);
+	}
+	if (field->unit) {
+		char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" };
+		char *units[5][8] = {
+			{ "None", "None", "None", "None", "None", "None", "None", "None" },
+			{ "None", "Centimeter", "Gram", "Seconds", "Kelvin",     "Ampere", "Candela", "None" },
+			{ "None", "Radians",    "Gram", "Seconds", "Kelvin",     "Ampere", "Candela", "None" },
+			{ "None", "Inch",       "Slug", "Seconds", "Fahrenheit", "Ampere", "Candela", "None" },
+			{ "None", "Degrees",    "Slug", "Seconds", "Fahrenheit", "Ampere", "Candela", "None" }
+		};
+
+		int i;
+		int sys;
+                __u32 data = field->unit;
+
+		/* First nibble tells us which system we're in. */
+		sys = data & 0xf;
+		data >>= 4;
+
+		if(sys > 4) {
+			tab(n); printk("Unit(Invalid)\n");
+		}
+		else {
+			int earlier_unit = 0;
+
+			tab(n); printk("Unit(%s : ", systems[sys]);
+
+			for (i=1 ; i<sizeof(__u32)*2 ; i++) {
+				char nibble = data & 0xf;
+				data >>= 4;
+				if (nibble != 0) {
+					if(earlier_unit++ > 0)
+						printk("*");
+					printk("%s", units[sys][i]);
+					if(nibble != 1) {
+						/* This is a _signed_ nibble(!) */
+	
+						int val = nibble & 0x7;
+						if(nibble & 0x08)
+							val = -((0x7 & ~val) +1);
+						printk("^%d", val);
+					}
+				}
+			}
+			printk(")\n");
+		}
+	}
+	tab(n); printk("Report Size(%u)\n", field->report_size);
+	tab(n); printk("Report Count(%u)\n", field->report_count);
+	tab(n); printk("Report Offset(%u)\n", field->report_offset);
+
+	tab(n); printk("Flags( ");
+	j = field->flags;
+	printk("%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : "");
+	printk("%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array ");
+	printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute ");
+	printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : "");
+	printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : "");
+	printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPrefferedState " : "");
+	printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : "");
+	printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : "");
+	printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : "");
+	printk(")\n");
+}
+
+static void __attribute__((unused)) hid_dump_device(struct hid_device *device) {
+	struct hid_report_enum *report_enum;
+	struct hid_report *report;
+	struct list_head *list;
+	unsigned i,k;
+	static char *table[] = {"INPUT", "OUTPUT", "FEATURE"};
+	
+	for (i = 0; i < HID_REPORT_TYPES; i++) {
+		report_enum = device->report_enum + i;
+		list = report_enum->report_list.next;
+		while (list != &report_enum->report_list) {
+			report = (struct hid_report *) list;
+			tab(2);
+			printk("%s", table[i]);
+			if (report->id)
+				printk("(%d)", report->id);
+			printk("[%s]", table[report->type]);
+			printk("\n");
+			for (k = 0; k < report->maxfield; k++) {
+				tab(4);
+				printk("Field(%d)\n", k);
+				hid_dump_field(report->field[k], 6);
+			}
+			list = list->next;
+		}
+	}
+}
+
+static void __attribute__((unused)) hid_dump_input(struct hid_usage *usage, __s32 value) {
+	printk("hid-debug: input ");
+	resolv_usage(usage->hid);
+	printk(" = %d\n", value);
+}
+
+
+static char *events[EV_MAX + 1] = {
+	[EV_SYN] = "Sync",			[EV_KEY] = "Key",
+	[EV_REL] = "Relative",			[EV_ABS] = "Absolute",
+	[EV_MSC] = "Misc",			[EV_LED] = "LED",
+	[EV_SND] = "Sound",			[EV_REP] = "Repeat",
+	[EV_FF] = "ForceFeedback",		[EV_PWR] = "Power",
+	[EV_FF_STATUS] = "ForceFeedbackStatus",
+};
+
+static char *syncs[2] = {
+	[SYN_REPORT] = "Report",		[SYN_CONFIG] = "Config",
+};
+static char *keys[KEY_MAX + 1] = {
+	[KEY_RESERVED] = "Reserved",		[KEY_ESC] = "Esc",
+	[KEY_1] = "1",				[KEY_2] = "2",
+	[KEY_3] = "3",				[KEY_4] = "4",
+	[KEY_5] = "5",				[KEY_6] = "6",
+	[KEY_7] = "7",				[KEY_8] = "8",
+	[KEY_9] = "9",				[KEY_0] = "0",
+	[KEY_MINUS] = "Minus",			[KEY_EQUAL] = "Equal",
+	[KEY_BACKSPACE] = "Backspace",		[KEY_TAB] = "Tab",
+	[KEY_Q] = "Q",				[KEY_W] = "W",
+	[KEY_E] = "E",				[KEY_R] = "R",
+	[KEY_T] = "T",				[KEY_Y] = "Y",
+	[KEY_U] = "U",				[KEY_I] = "I",
+	[KEY_O] = "O",				[KEY_P] = "P",
+	[KEY_LEFTBRACE] = "LeftBrace",		[KEY_RIGHTBRACE] = "RightBrace",
+	[KEY_ENTER] = "Enter",			[KEY_LEFTCTRL] = "LeftControl",
+	[KEY_A] = "A",				[KEY_S] = "S",
+	[KEY_D] = "D",				[KEY_F] = "F",
+	[KEY_G] = "G",				[KEY_H] = "H",
+	[KEY_J] = "J",				[KEY_K] = "K",
+	[KEY_L] = "L",				[KEY_SEMICOLON] = "Semicolon",
+	[KEY_APOSTROPHE] = "Apostrophe",	[KEY_GRAVE] = "Grave",
+	[KEY_LEFTSHIFT] = "LeftShift",		[KEY_BACKSLASH] = "BackSlash",
+	[KEY_Z] = "Z",				[KEY_X] = "X",
+	[KEY_C] = "C",				[KEY_V] = "V",
+	[KEY_B] = "B",				[KEY_N] = "N",
+	[KEY_M] = "M",				[KEY_COMMA] = "Comma",
+	[KEY_DOT] = "Dot",			[KEY_SLASH] = "Slash",
+	[KEY_RIGHTSHIFT] = "RightShift",	[KEY_KPASTERISK] = "KPAsterisk",
+	[KEY_LEFTALT] = "LeftAlt",		[KEY_SPACE] = "Space",
+	[KEY_CAPSLOCK] = "CapsLock",		[KEY_F1] = "F1",
+	[KEY_F2] = "F2",			[KEY_F3] = "F3",
+	[KEY_F4] = "F4",			[KEY_F5] = "F5",
+	[KEY_F6] = "F6",			[KEY_F7] = "F7",
+	[KEY_F8] = "F8",			[KEY_F9] = "F9",
+	[KEY_F10] = "F10",			[KEY_NUMLOCK] = "NumLock",
+	[KEY_SCROLLLOCK] = "ScrollLock",	[KEY_KP7] = "KP7",
+	[KEY_KP8] = "KP8",			[KEY_KP9] = "KP9",
+	[KEY_KPMINUS] = "KPMinus",		[KEY_KP4] = "KP4",
+	[KEY_KP5] = "KP5",			[KEY_KP6] = "KP6",
+	[KEY_KPPLUS] = "KPPlus",		[KEY_KP1] = "KP1",
+	[KEY_KP2] = "KP2",			[KEY_KP3] = "KP3",
+	[KEY_KP0] = "KP0",			[KEY_KPDOT] = "KPDot",
+	[KEY_ZENKAKUHANKAKU] = "Zenkaku/Hankaku", [KEY_102ND] = "102nd",
+	[KEY_F11] = "F11",			[KEY_F12] = "F12",
+	[KEY_RO] = "RO",			[KEY_KATAKANA] = "Katakana",
+	[KEY_HIRAGANA] = "HIRAGANA",		[KEY_HENKAN] = "Henkan",
+	[KEY_KATAKANAHIRAGANA] = "Katakana/Hiragana", [KEY_MUHENKAN] = "Muhenkan",
+	[KEY_KPJPCOMMA] = "KPJpComma",		[KEY_KPENTER] = "KPEnter",
+	[KEY_RIGHTCTRL] = "RightCtrl",		[KEY_KPSLASH] = "KPSlash",
+	[KEY_SYSRQ] = "SysRq",			[KEY_RIGHTALT] = "RightAlt",
+	[KEY_LINEFEED] = "LineFeed",		[KEY_HOME] = "Home",
+	[KEY_UP] = "Up",			[KEY_PAGEUP] = "PageUp",
+	[KEY_LEFT] = "Left",			[KEY_RIGHT] = "Right",
+	[KEY_END] = "End",			[KEY_DOWN] = "Down",
+	[KEY_PAGEDOWN] = "PageDown",		[KEY_INSERT] = "Insert",
+	[KEY_DELETE] = "Delete",		[KEY_MACRO] = "Macro",
+	[KEY_MUTE] = "Mute",			[KEY_VOLUMEDOWN] = "VolumeDown",
+	[KEY_VOLUMEUP] = "VolumeUp",		[KEY_POWER] = "Power",
+	[KEY_KPEQUAL] = "KPEqual",		[KEY_KPPLUSMINUS] = "KPPlusMinus",
+	[KEY_PAUSE] = "Pause",			[KEY_KPCOMMA] = "KPComma",
+	[KEY_HANGUEL] = "Hanguel",		[KEY_HANJA] = "Hanja",
+	[KEY_YEN] = "Yen",			[KEY_LEFTMETA] = "LeftMeta",
+	[KEY_RIGHTMETA] = "RightMeta",		[KEY_COMPOSE] = "Compose",
+	[KEY_STOP] = "Stop",			[KEY_AGAIN] = "Again",
+	[KEY_PROPS] = "Props",			[KEY_UNDO] = "Undo",
+	[KEY_FRONT] = "Front",			[KEY_COPY] = "Copy",
+	[KEY_OPEN] = "Open",			[KEY_PASTE] = "Paste",
+	[KEY_FIND] = "Find",			[KEY_CUT] = "Cut",
+	[KEY_HELP] = "Help",			[KEY_MENU] = "Menu",
+	[KEY_CALC] = "Calc",			[KEY_SETUP] = "Setup",
+	[KEY_SLEEP] = "Sleep",			[KEY_WAKEUP] = "WakeUp",
+	[KEY_FILE] = "File",			[KEY_SENDFILE] = "SendFile",
+	[KEY_DELETEFILE] = "DeleteFile",	[KEY_XFER] = "X-fer",
+	[KEY_PROG1] = "Prog1",			[KEY_PROG2] = "Prog2",
+	[KEY_WWW] = "WWW",			[KEY_MSDOS] = "MSDOS",
+	[KEY_COFFEE] = "Coffee",		[KEY_DIRECTION] = "Direction",
+	[KEY_CYCLEWINDOWS] = "CycleWindows",	[KEY_MAIL] = "Mail",
+	[KEY_BOOKMARKS] = "Bookmarks",		[KEY_COMPUTER] = "Computer",
+	[KEY_BACK] = "Back",			[KEY_FORWARD] = "Forward",
+	[KEY_CLOSECD] = "CloseCD",		[KEY_EJECTCD] = "EjectCD",
+	[KEY_EJECTCLOSECD] = "EjectCloseCD",	[KEY_NEXTSONG] = "NextSong",
+	[KEY_PLAYPAUSE] = "PlayPause",		[KEY_PREVIOUSSONG] = "PreviousSong",
+	[KEY_STOPCD] = "StopCD",		[KEY_RECORD] = "Record",
+	[KEY_REWIND] = "Rewind",		[KEY_PHONE] = "Phone",
+	[KEY_ISO] = "ISOKey",			[KEY_CONFIG] = "Config",
+	[KEY_HOMEPAGE] = "HomePage",		[KEY_REFRESH] = "Refresh",
+	[KEY_EXIT] = "Exit",			[KEY_MOVE] = "Move",
+	[KEY_EDIT] = "Edit",			[KEY_SCROLLUP] = "ScrollUp",
+	[KEY_SCROLLDOWN] = "ScrollDown",	[KEY_KPLEFTPAREN] = "KPLeftParenthesis",
+	[KEY_KPRIGHTPAREN] = "KPRightParenthesis", [KEY_F13] = "F13",
+	[KEY_F14] = "F14",			[KEY_F15] = "F15",
+	[KEY_F16] = "F16",			[KEY_F17] = "F17",
+	[KEY_F18] = "F18",			[KEY_F19] = "F19",
+	[KEY_F20] = "F20",			[KEY_F21] = "F21",
+	[KEY_F22] = "F22",			[KEY_F23] = "F23",
+	[KEY_F24] = "F24",			[KEY_PLAYCD] = "PlayCD",
+	[KEY_PAUSECD] = "PauseCD",		[KEY_PROG3] = "Prog3",
+	[KEY_PROG4] = "Prog4",			[KEY_SUSPEND] = "Suspend",
+	[KEY_CLOSE] = "Close",			[KEY_PLAY] = "Play",
+	[KEY_FASTFORWARD] = "Fast Forward",	[KEY_BASSBOOST] = "Bass Boost",
+	[KEY_PRINT] = "Print",			[KEY_HP] = "HP",
+	[KEY_CAMERA] = "Camera",		[KEY_SOUND] = "Sound",
+	[KEY_QUESTION] = "Question",		[KEY_EMAIL] = "Email",
+	[KEY_CHAT] = "Chat",			[KEY_SEARCH] = "Search",
+	[KEY_CONNECT] = "Connect",		[KEY_FINANCE] = "Finance",
+	[KEY_SPORT] = "Sport",			[KEY_SHOP] = "Shop",
+	[KEY_ALTERASE] = "Alternate Erase",	[KEY_CANCEL] = "Cancel",
+	[KEY_BRIGHTNESSDOWN] = "Brightness down", [KEY_BRIGHTNESSUP] = "Brightness up",
+	[KEY_MEDIA] = "Media",			[KEY_UNKNOWN] = "Unknown",
+	[BTN_0] = "Btn0",			[BTN_1] = "Btn1",
+	[BTN_2] = "Btn2",			[BTN_3] = "Btn3",
+	[BTN_4] = "Btn4",			[BTN_5] = "Btn5",
+	[BTN_6] = "Btn6",			[BTN_7] = "Btn7",
+	[BTN_8] = "Btn8",			[BTN_9] = "Btn9",
+	[BTN_LEFT] = "LeftBtn",			[BTN_RIGHT] = "RightBtn",
+	[BTN_MIDDLE] = "MiddleBtn",		[BTN_SIDE] = "SideBtn",
+	[BTN_EXTRA] = "ExtraBtn",		[BTN_FORWARD] = "ForwardBtn",
+	[BTN_BACK] = "BackBtn",			[BTN_TASK] = "TaskBtn",
+	[BTN_TRIGGER] = "Trigger",		[BTN_THUMB] = "ThumbBtn",
+	[BTN_THUMB2] = "ThumbBtn2",		[BTN_TOP] = "TopBtn",
+	[BTN_TOP2] = "TopBtn2",			[BTN_PINKIE] = "PinkieBtn",
+	[BTN_BASE] = "BaseBtn",			[BTN_BASE2] = "BaseBtn2",
+	[BTN_BASE3] = "BaseBtn3",		[BTN_BASE4] = "BaseBtn4",
+	[BTN_BASE5] = "BaseBtn5",		[BTN_BASE6] = "BaseBtn6",
+	[BTN_DEAD] = "BtnDead",			[BTN_A] = "BtnA",
+	[BTN_B] = "BtnB",			[BTN_C] = "BtnC",
+	[BTN_X] = "BtnX",			[BTN_Y] = "BtnY",
+	[BTN_Z] = "BtnZ",			[BTN_TL] = "BtnTL",
+	[BTN_TR] = "BtnTR",			[BTN_TL2] = "BtnTL2",
+	[BTN_TR2] = "BtnTR2",			[BTN_SELECT] = "BtnSelect",
+	[BTN_START] = "BtnStart",		[BTN_MODE] = "BtnMode",
+	[BTN_THUMBL] = "BtnThumbL",		[BTN_THUMBR] = "BtnThumbR",
+	[BTN_TOOL_PEN] = "ToolPen",		[BTN_TOOL_RUBBER] = "ToolRubber",
+	[BTN_TOOL_BRUSH] = "ToolBrush",		[BTN_TOOL_PENCIL] = "ToolPencil",
+	[BTN_TOOL_AIRBRUSH] = "ToolAirbrush",	[BTN_TOOL_FINGER] = "ToolFinger",
+	[BTN_TOOL_MOUSE] = "ToolMouse",		[BTN_TOOL_LENS] = "ToolLens",
+	[BTN_TOUCH] = "Touch",			[BTN_STYLUS] = "Stylus",
+	[BTN_STYLUS2] = "Stylus2",		[BTN_TOOL_DOUBLETAP] = "Tool Doubletap",
+	[BTN_TOOL_TRIPLETAP] = "Tool Tripletap", [BTN_GEAR_DOWN] = "WheelBtn",
+	[BTN_GEAR_UP] = "Gear up",		[KEY_OK] = "Ok",
+	[KEY_SELECT] = "Select",		[KEY_GOTO] = "Goto",
+	[KEY_CLEAR] = "Clear",			[KEY_POWER2] = "Power2",
+	[KEY_OPTION] = "Option",		[KEY_INFO] = "Info",
+	[KEY_TIME] = "Time",			[KEY_VENDOR] = "Vendor",
+	[KEY_ARCHIVE] = "Archive",		[KEY_PROGRAM] = "Program",
+	[KEY_CHANNEL] = "Channel",		[KEY_FAVORITES] = "Favorites",
+	[KEY_EPG] = "EPG",			[KEY_PVR] = "PVR",
+	[KEY_MHP] = "MHP",			[KEY_LANGUAGE] = "Language",
+	[KEY_TITLE] = "Title",			[KEY_SUBTITLE] = "Subtitle",
+	[KEY_ANGLE] = "Angle",			[KEY_ZOOM] = "Zoom",
+	[KEY_MODE] = "Mode",			[KEY_KEYBOARD] = "Keyboard",
+	[KEY_SCREEN] = "Screen",		[KEY_PC] = "PC",
+	[KEY_TV] = "TV",			[KEY_TV2] = "TV2",
+	[KEY_VCR] = "VCR",			[KEY_VCR2] = "VCR2",
+	[KEY_SAT] = "Sat",			[KEY_SAT2] = "Sat2",
+	[KEY_CD] = "CD",			[KEY_TAPE] = "Tape",
+	[KEY_RADIO] = "Radio",			[KEY_TUNER] = "Tuner",
+	[KEY_PLAYER] = "Player",		[KEY_TEXT] = "Text",
+	[KEY_DVD] = "DVD",			[KEY_AUX] = "Aux",
+	[KEY_MP3] = "MP3",			[KEY_AUDIO] = "Audio",
+	[KEY_VIDEO] = "Video",			[KEY_DIRECTORY] = "Directory",
+	[KEY_LIST] = "List",			[KEY_MEMO] = "Memo",
+	[KEY_CALENDAR] = "Calendar",		[KEY_RED] = "Red",
+	[KEY_GREEN] = "Green",			[KEY_YELLOW] = "Yellow",
+	[KEY_BLUE] = "Blue",			[KEY_CHANNELUP] = "ChannelUp",
+	[KEY_CHANNELDOWN] = "ChannelDown",	[KEY_FIRST] = "First",
+	[KEY_LAST] = "Last",			[KEY_AB] = "AB",
+	[KEY_NEXT] = "Next",			[KEY_RESTART] = "Restart",
+	[KEY_SLOW] = "Slow",			[KEY_SHUFFLE] = "Shuffle",
+	[KEY_BREAK] = "Break",			[KEY_PREVIOUS] = "Previous",
+	[KEY_DIGITS] = "Digits",		[KEY_TEEN] = "TEEN",
+	[KEY_TWEN] = "TWEN",			[KEY_DEL_EOL] = "DeleteEOL",
+	[KEY_DEL_EOS] = "DeleteEOS",		[KEY_INS_LINE] = "InsertLine",
+	[KEY_DEL_LINE] = "DeleteLine",
+};
+
+static char *relatives[REL_MAX + 1] = {
+	[REL_X] = "X",			[REL_Y] = "Y",
+	[REL_Z] = "Z",			[REL_HWHEEL] = "HWheel",
+	[REL_DIAL] = "Dial",		[REL_WHEEL] = "Wheel", 
+	[REL_MISC] = "Misc",	
+};
+
+static char *absolutes[ABS_MAX + 1] = {
+	[ABS_X] = "X",			[ABS_Y] = "Y",
+	[ABS_Z] = "Z",			[ABS_RX] = "Rx",
+	[ABS_RY] = "Ry",		[ABS_RZ] = "Rz",
+	[ABS_THROTTLE] = "Throttle",	[ABS_RUDDER] = "Rudder",
+	[ABS_WHEEL] = "Wheel",		[ABS_GAS] = "Gas",
+	[ABS_BRAKE] = "Brake",		[ABS_HAT0X] = "Hat0X",
+	[ABS_HAT0Y] = "Hat0Y",		[ABS_HAT1X] = "Hat1X",
+	[ABS_HAT1Y] = "Hat1Y",		[ABS_HAT2X] = "Hat2X",
+	[ABS_HAT2Y] = "Hat2Y",		[ABS_HAT3X] = "Hat3X",
+	[ABS_HAT3Y] = "Hat 3Y",		[ABS_PRESSURE] = "Pressure",
+	[ABS_DISTANCE] = "Distance",	[ABS_TILT_X] = "XTilt",
+	[ABS_TILT_Y] = "YTilt",		[ABS_TOOL_WIDTH] = "Tool Width",
+	[ABS_VOLUME] = "Volume",	[ABS_MISC] = "Misc",
+};
+
+static char *misc[MSC_MAX + 1] = {
+	[MSC_SERIAL] = "Serial",	[MSC_PULSELED] = "Pulseled",
+	[MSC_GESTURE] = "Gesture",	[MSC_RAW] = "RawData"
+};
+
+static char *leds[LED_MAX + 1] = {
+	[LED_NUML] = "NumLock",		[LED_CAPSL] = "CapsLock", 
+	[LED_SCROLLL] = "ScrollLock",	[LED_COMPOSE] = "Compose",
+	[LED_KANA] = "Kana",		[LED_SLEEP] = "Sleep", 
+	[LED_SUSPEND] = "Suspend",	[LED_MUTE] = "Mute",
+	[LED_MISC] = "Misc",
+};
+
+static char *repeats[REP_MAX + 1] = {
+	[REP_DELAY] = "Delay",		[REP_PERIOD] = "Period"
+};
+
+static char *sounds[SND_MAX + 1] = {
+	[SND_CLICK] = "Click",		[SND_BELL] = "Bell",
+	[SND_TONE] = "Tone"
+};
+
+static char **names[EV_MAX + 1] = {
+	[EV_SYN] = syncs,			[EV_KEY] = keys,
+	[EV_REL] = relatives,			[EV_ABS] = absolutes,
+	[EV_MSC] = misc,			[EV_LED] = leds,
+	[EV_SND] = sounds,			[EV_REP] = repeats,
+};
+
+static void __attribute__((unused)) resolv_event(__u8 type, __u16 code) {
+
+	printk("%s.%s", events[type] ? events[type] : "?",
+		names[type] ? (names[type][code] ? names[type][code] : "?") : "?");
+}
diff --git a/drivers/usb/input/hid-ff.c b/drivers/usb/input/hid-ff.c
new file mode 100644
index 0000000..72e6986
--- /dev/null
+++ b/drivers/usb/input/hid-ff.c
@@ -0,0 +1,94 @@
+/*
+ * $Id: hid-ff.c,v 1.2 2002/04/18 22:02:47 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-2004 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 <johann.deneux@it.uu.se>
+ */
+
+#include <linux/input.h>
+
+#undef 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);
+extern int hid_tmff_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_FF
+	{0x46d, 0xc211, hid_lgff_init}, // Logitech Cordless rumble pad
+	{0x46d, 0xc283, hid_lgff_init}, // Logitech Wingman Force 3d
+	{0x46d, 0xc295, hid_lgff_init},	// Logitech MOMO force wheel
+	{0x46d, 0xc219, hid_lgff_init}, // Logitech Cordless rumble pad 2
+#endif
+#ifdef CONFIG_HID_PID
+	{0x45e, 0x001b, hid_pid_init},
+#endif
+#ifdef CONFIG_THRUSTMASTER_FF
+	{0x44f, 0xb304, hid_tmff_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(le16_to_cpu(hid->dev->descriptor.idVendor),
+			       le16_to_cpu(hid->dev->descriptor.idProduct));
+
+	if (!init) {
+		dbg("hid_ff_init could not find initializer");
+		return -ENOSYS;
+	}
+	return init->init(hid);
+}
diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c
new file mode 100644
index 0000000..5553c35
--- /dev/null
+++ b/drivers/usb/input/hid-input.c
@@ -0,0 +1,630 @@
+/*
+ * $Id: hid-input.c,v 1.2 2002/04/23 00:59:25 rdamazio Exp $
+ *
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ *  USB HID to Linux Input mapping
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+
+#undef DEBUG
+
+#include "hid.h"
+
+#define unk	KEY_UNKNOWN
+
+static unsigned char hid_keyboard[256] = {
+	  0,  0,  0,  0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
+	 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44,  2,  3,
+	  4,  5,  6,  7,  8,  9, 10, 11, 28,  1, 14, 15, 57, 12, 13, 26,
+	 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
+	 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
+	105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
+	 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
+	191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
+	115,114,unk,unk,unk,121,unk, 89, 93,124, 92, 94, 95,unk,unk,unk,
+	122,123, 90, 91, 85,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
+	unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
+	unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
+	unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
+	unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
+	 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
+	150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk
+};
+
+static struct {
+	__s32 x;
+	__s32 y;
+}  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
+
+#define map_abs(c)	do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
+#define map_rel(c)	do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
+#define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
+#define map_led(c)	do { usage->code = c; usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; } while (0)
+#define map_ff(c)	do { usage->code = c; usage->type = EV_FF;  bit = input->ffbit;  max =  FF_MAX; } while (0)
+
+#define map_abs_clear(c)	do { map_abs(c); clear_bit(c, bit); } while (0)
+#define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
+#define map_ff_effect(c)	do { set_bit(c, input->ffbit); } while (0)
+
+static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
+				     struct hid_usage *usage)
+{
+	struct input_dev *input = &hidinput->input;
+	struct hid_device *device = hidinput->input.private;
+	int max, code;
+	unsigned long *bit;
+
+	field->hidinput = hidinput;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "Mapping: ");
+	resolv_usage(usage->hid);
+	printk(" ---> ");
+#endif
+
+	if (field->flags & HID_MAIN_ITEM_CONSTANT)
+		goto ignore;
+
+	switch (usage->hid & HID_USAGE_PAGE) {
+
+		case HID_UP_UNDEFINED:
+			goto ignore;
+
+		case HID_UP_KEYBOARD:
+
+			set_bit(EV_REP, input->evbit);
+
+			if ((usage->hid & HID_USAGE) < 256) {
+				if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore;
+				map_key_clear(hid_keyboard[usage->hid & HID_USAGE]);
+			} else
+				map_key(KEY_UNKNOWN);
+
+			break;
+
+		case HID_UP_BUTTON:
+
+			code = ((usage->hid - 1) & 0xf);
+
+			switch (field->application) {
+				case HID_GD_MOUSE:
+				case HID_GD_POINTER:  code += 0x110; break;
+				case HID_GD_JOYSTICK: code += 0x120; break;
+				case HID_GD_GAMEPAD:  code += 0x130; break;
+				default:
+					switch (field->physical) {
+						case HID_GD_MOUSE:
+						case HID_GD_POINTER:  code += 0x110; break;
+						case HID_GD_JOYSTICK: code += 0x120; break;
+						case HID_GD_GAMEPAD:  code += 0x130; break;
+						default:              code += 0x100;
+					}
+			}
+
+			map_key(code);
+			break;
+
+		case HID_UP_GENDESK:
+
+			if ((usage->hid & 0xf0) == 0x80) {	/* SystemControl */
+				switch (usage->hid & 0xf) {
+					case 0x1: map_key_clear(KEY_POWER);  break;
+					case 0x2: map_key_clear(KEY_SLEEP);  break;
+					case 0x3: map_key_clear(KEY_WAKEUP); break;
+					default: goto unknown;
+				}
+				break;
+			}
+
+			if ((usage->hid & 0xf0) == 0x90) {	/* D-pad */
+				switch (usage->hid) {
+					case HID_GD_UP:	   usage->hat_dir = 1; break;
+					case HID_GD_DOWN:  usage->hat_dir = 5; break;
+					case HID_GD_RIGHT: usage->hat_dir = 3; break;
+					case HID_GD_LEFT:  usage->hat_dir = 7; break;
+					default: goto unknown;
+				}
+				if (field->dpad) {
+					map_abs(field->dpad);
+					goto ignore;
+				}
+				map_abs(ABS_HAT0X);
+				break;
+			}
+
+			switch (usage->hid) {
+
+				/* These usage IDs map directly to the usage codes. */
+				case HID_GD_X: case HID_GD_Y: case HID_GD_Z:
+				case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ:
+				case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL:
+					if (field->flags & HID_MAIN_ITEM_RELATIVE) 
+						map_rel(usage->hid & 0xf);
+					else
+						map_abs(usage->hid & 0xf);
+					break;
+
+				case HID_GD_HATSWITCH:
+					usage->hat_min = field->logical_minimum;
+					usage->hat_max = field->logical_maximum;
+					map_abs(ABS_HAT0X);
+					break;
+
+				case HID_GD_START:	map_key_clear(BTN_START);	break;
+				case HID_GD_SELECT:	map_key_clear(BTN_SELECT);	break;
+
+				default: goto unknown;
+			}
+
+			break;
+
+		case HID_UP_LED:
+			if (((usage->hid - 1) & 0xffff) >= LED_MAX)
+				goto ignore;
+			map_led((usage->hid - 1) & 0xffff);
+			break;
+
+		case HID_UP_DIGITIZER:
+
+			switch (usage->hid & 0xff) {
+
+				case 0x30: /* TipPressure */
+					if (!test_bit(BTN_TOUCH, input->keybit)) {
+						device->quirks |= HID_QUIRK_NOTOUCH;
+						set_bit(EV_KEY, input->evbit);
+						set_bit(BTN_TOUCH, input->keybit);
+					}
+
+					map_abs_clear(ABS_PRESSURE);
+					break;
+
+				case 0x32: /* InRange */
+					switch (field->physical & 0xff) {
+						case 0x21: map_key(BTN_TOOL_MOUSE); break;
+						case 0x22: map_key(BTN_TOOL_FINGER); break;
+						default: map_key(BTN_TOOL_PEN); break;
+					}
+					break;
+
+				case 0x3c: /* Invert */
+					map_key_clear(BTN_TOOL_RUBBER);
+					break;
+
+				case 0x33: /* Touch */
+				case 0x42: /* TipSwitch */
+				case 0x43: /* TipSwitch2 */
+					device->quirks &= ~HID_QUIRK_NOTOUCH;
+					map_key_clear(BTN_TOUCH);
+					break;
+
+				case 0x44: /* BarrelSwitch */
+					map_key_clear(BTN_STYLUS);
+					break;
+
+				default:  goto unknown;
+			}
+			break;
+
+		case HID_UP_CONSUMER:	/* USB HUT v1.1, pages 56-62 */
+
+			switch (usage->hid & HID_USAGE) {
+				case 0x000: goto ignore;
+				case 0x034: map_key_clear(KEY_SLEEP);		break;
+				case 0x036: map_key_clear(BTN_MISC);		break;
+				case 0x08a: map_key_clear(KEY_WWW);		break;
+				case 0x095: map_key_clear(KEY_HELP);		break;
+				case 0x0b0: map_key_clear(KEY_PLAY);		break;
+				case 0x0b1: map_key_clear(KEY_PAUSE);		break;
+				case 0x0b2: map_key_clear(KEY_RECORD);		break;
+				case 0x0b3: map_key_clear(KEY_FASTFORWARD);	break;
+				case 0x0b4: map_key_clear(KEY_REWIND);		break;
+				case 0x0b5: map_key_clear(KEY_NEXTSONG);	break;
+				case 0x0b6: map_key_clear(KEY_PREVIOUSSONG);	break;
+				case 0x0b7: map_key_clear(KEY_STOPCD);		break;
+				case 0x0b8: map_key_clear(KEY_EJECTCD);		break;
+				case 0x0cd: map_key_clear(KEY_PLAYPAUSE);	break;
+			        case 0x0e0: map_abs_clear(ABS_VOLUME);		break;
+				case 0x0e2: map_key_clear(KEY_MUTE);		break;
+				case 0x0e5: map_key_clear(KEY_BASSBOOST);	break;
+				case 0x0e9: map_key_clear(KEY_VOLUMEUP);	break;
+				case 0x0ea: map_key_clear(KEY_VOLUMEDOWN);	break;
+				case 0x183: map_key_clear(KEY_CONFIG);		break;
+				case 0x18a: map_key_clear(KEY_MAIL);		break;
+				case 0x192: map_key_clear(KEY_CALC);		break;
+				case 0x194: map_key_clear(KEY_FILE);		break;
+				case 0x21a: map_key_clear(KEY_UNDO);		break;
+				case 0x21b: map_key_clear(KEY_COPY);		break;
+				case 0x21c: map_key_clear(KEY_CUT);		break;
+				case 0x21d: map_key_clear(KEY_PASTE);		break;
+				case 0x221: map_key_clear(KEY_FIND);		break;
+				case 0x223: map_key_clear(KEY_HOMEPAGE);	break;
+				case 0x224: map_key_clear(KEY_BACK);		break;
+				case 0x225: map_key_clear(KEY_FORWARD);		break;
+				case 0x226: map_key_clear(KEY_STOP);		break;
+				case 0x227: map_key_clear(KEY_REFRESH);		break;
+				case 0x22a: map_key_clear(KEY_BOOKMARKS);	break;
+				case 0x238: map_rel(REL_HWHEEL);		break;
+				default:    goto unknown;
+			}
+			break;
+
+		case HID_UP_HPVENDOR:	/* Reported on a Dutch layout HP5308 */
+
+			set_bit(EV_REP, input->evbit);
+			switch (usage->hid & HID_USAGE) {
+			        case 0x021: map_key_clear(KEY_PRINT);           break;
+				case 0x070: map_key_clear(KEY_HP);		break;
+				case 0x071: map_key_clear(KEY_CAMERA);		break;
+				case 0x072: map_key_clear(KEY_SOUND);		break;
+				case 0x073: map_key_clear(KEY_QUESTION);	break;
+				case 0x080: map_key_clear(KEY_EMAIL);		break;
+				case 0x081: map_key_clear(KEY_CHAT);		break;
+				case 0x082: map_key_clear(KEY_SEARCH);		break;
+				case 0x083: map_key_clear(KEY_CONNECT);	        break;
+				case 0x084: map_key_clear(KEY_FINANCE);		break;
+				case 0x085: map_key_clear(KEY_SPORT);		break;
+				case 0x086: map_key_clear(KEY_SHOP);	        break;
+				default:    goto ignore;
+			}
+			break;
+
+		case HID_UP_MSVENDOR:
+
+			goto ignore;
+			
+		case HID_UP_PID:
+
+			set_bit(EV_FF, input->evbit);
+			switch(usage->hid & HID_USAGE) {
+				case 0x26: map_ff_effect(FF_CONSTANT);	goto ignore;
+				case 0x27: map_ff_effect(FF_RAMP);	goto ignore;
+				case 0x28: map_ff_effect(FF_CUSTOM);	goto ignore;
+				case 0x30: map_ff_effect(FF_SQUARE);	map_ff_effect(FF_PERIODIC); goto ignore;
+				case 0x31: map_ff_effect(FF_SINE);	map_ff_effect(FF_PERIODIC); goto ignore;
+				case 0x32: map_ff_effect(FF_TRIANGLE);	map_ff_effect(FF_PERIODIC); goto ignore;
+				case 0x33: map_ff_effect(FF_SAW_UP);	map_ff_effect(FF_PERIODIC); goto ignore;
+				case 0x34: map_ff_effect(FF_SAW_DOWN);	map_ff_effect(FF_PERIODIC); goto ignore;
+				case 0x40: map_ff_effect(FF_SPRING);	goto ignore;
+				case 0x41: map_ff_effect(FF_DAMPER);	goto ignore;
+				case 0x42: map_ff_effect(FF_INERTIA);	goto ignore;
+				case 0x43: map_ff_effect(FF_FRICTION);	goto ignore;
+				case 0x7e: map_ff(FF_GAIN);		break;
+				case 0x83: input->ff_effects_max = field->value[0]; goto ignore;
+				case 0x98: map_ff(FF_AUTOCENTER);	break;
+				case 0xa4: map_key_clear(BTN_DEAD);	break;
+				default: goto ignore;
+			}
+			break;
+
+		default:
+		unknown:
+			if (field->report_size == 1) {
+				if (field->report->type == HID_OUTPUT_REPORT) {
+					map_led(LED_MISC);
+					break;
+				}
+				map_key(BTN_MISC);
+				break;
+			}
+			if (field->flags & HID_MAIN_ITEM_RELATIVE) {
+				map_rel(REL_MISC);
+				break;
+			}
+			map_abs(ABS_MISC);
+			break;
+	}
+
+	set_bit(usage->type, input->evbit);
+
+	while (usage->code <= max && test_and_set_bit(usage->code, bit))
+		usage->code = find_next_zero_bit(bit, max + 1, usage->code);
+
+	if (usage->code > max)
+		goto ignore;
+
+	if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | HID_QUIRK_2WHEEL_MOUSE_HACK_5)) &&
+		 (usage->type == EV_REL) && (usage->code == REL_WHEEL)) 
+			set_bit(REL_HWHEEL, bit);
+
+	if (((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005))
+		|| ((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007)))
+		goto ignore;
+
+	if (usage->type == EV_ABS) {
+
+		int a = field->logical_minimum;
+		int b = field->logical_maximum;
+
+		if ((device->quirks & HID_QUIRK_BADPAD) && (usage->code == ABS_X || usage->code == ABS_Y)) {
+			a = field->logical_minimum = 0;
+			b = field->logical_maximum = 255;
+		}
+		
+		if (field->application == HID_GD_GAMEPAD || field->application == HID_GD_JOYSTICK)
+			input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4);
+		else	input_set_abs_params(input, usage->code, a, b, 0, 0);
+		
+	}
+
+	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
+		int i;
+		for (i = usage->code; i < usage->code + 2 && i <= max; i++) {
+			input_set_abs_params(input, i, -1, 1, 0, 0);
+			set_bit(i, input->absbit);
+		}
+		if (usage->hat_dir && !field->dpad)
+			field->dpad = usage->code;
+	}
+
+#ifdef DEBUG
+	resolv_event(usage->type, usage->code);
+	printk("\n");
+#endif
+	return;
+
+ignore:
+#ifdef DEBUG
+	printk("IGNORED\n");
+#endif
+	return;
+}
+
+void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, struct pt_regs *regs)
+{
+	struct input_dev *input = &field->hidinput->input;
+	int *quirks = &hid->quirks;
+
+	if (!input)
+		return;
+
+	input_regs(input, regs);
+
+	if (!usage->type)
+		return;
+
+	if (((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005))
+		|| ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007))) {
+		if (value) hid->quirks |=  HID_QUIRK_2WHEEL_MOUSE_HACK_ON;
+		else       hid->quirks &= ~HID_QUIRK_2WHEEL_MOUSE_HACK_ON;
+		return;
+	}
+
+	if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_ON) && (usage->code == REL_WHEEL)) {
+		input_event(input, usage->type, REL_HWHEEL, value);
+		return;
+	}
+
+	if (usage->hat_min < usage->hat_max || usage->hat_dir) { 
+		int hat_dir = usage->hat_dir;
+		if (!hat_dir)
+			hat_dir = (value - usage->hat_min) * 8 / (usage->hat_max - usage->hat_min + 1) + 1;
+		if (hat_dir < 0 || hat_dir > 8) hat_dir = 0;
+		input_event(input, usage->type, usage->code    , hid_hat_to_axis[hat_dir].x);
+                input_event(input, usage->type, usage->code + 1, hid_hat_to_axis[hat_dir].y);
+                return;
+        }
+
+	if (usage->hid == (HID_UP_DIGITIZER | 0x003c)) { /* Invert */
+		*quirks = value ? (*quirks | HID_QUIRK_INVERT) : (*quirks & ~HID_QUIRK_INVERT);
+		return;
+	}
+
+	if (usage->hid == (HID_UP_DIGITIZER | 0x0032)) { /* InRange */
+		if (value) {
+			input_event(input, usage->type, (*quirks & HID_QUIRK_INVERT) ? BTN_TOOL_RUBBER : usage->code, 1);
+			return;
+		}
+		input_event(input, usage->type, usage->code, 0);
+		input_event(input, usage->type, BTN_TOOL_RUBBER, 0);
+		return;
+	}
+
+	if (usage->hid == (HID_UP_DIGITIZER | 0x0030) && (*quirks & HID_QUIRK_NOTOUCH)) { /* Pressure */
+		int a = field->logical_minimum;
+		int b = field->logical_maximum;
+		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_UNKNOWN */
+		return;
+
+	input_event(input, usage->type, usage->code, value);
+
+	if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY))
+		input_event(input, usage->type, usage->code, 0);
+}
+
+void hidinput_report_event(struct hid_device *hid, struct hid_report *report)
+{
+	struct list_head *lh;
+	struct hid_input *hidinput;
+
+	list_for_each (lh, &hid->inputs) {
+		hidinput = list_entry(lh, struct hid_input, list);
+		input_sync(&hidinput->input);
+	}
+}
+
+static int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field)
+{
+	struct hid_report *report;
+	int i, j;
+
+	list_for_each_entry(report, &hid->report_enum[HID_OUTPUT_REPORT].report_list, list) {
+		for (i = 0; i < report->maxfield; i++) {
+			*field = report->field[i];
+			for (j = 0; j < (*field)->maxusage; j++)
+				if ((*field)->usage[j].type == type && (*field)->usage[j].code == code)
+					return j;
+		}
+	}
+	return -1;
+}
+
+static int hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	struct hid_device *hid = dev->private;
+	struct hid_field *field;
+	int offset;
+
+	if (type == EV_FF)
+		return hid_ff_event(hid, dev, type, code, value);
+
+	if (type != EV_LED)
+		return -1;
+
+	if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) {
+		warn("event field not found");
+		return -1;
+	}
+
+	hid_set_field(field, offset, value);
+	hid_submit_report(hid, field->report, USB_DIR_OUT);
+
+	return 0;
+}
+
+static int hidinput_open(struct input_dev *dev)
+{
+	struct hid_device *hid = dev->private;
+	return hid_open(hid);
+}
+
+static void hidinput_close(struct input_dev *dev)
+{
+	struct hid_device *hid = dev->private;
+	hid_close(hid);
+}
+
+/*
+ * Register the input device; print a message.
+ * Configure the input layer interface
+ * Read all reports and initialize the absolute field values.
+ */
+
+int hidinput_connect(struct hid_device *hid)
+{
+	struct usb_device *dev = hid->dev;
+	struct hid_report *report;
+	struct hid_input *hidinput = NULL;
+	int i, j, k;
+
+	INIT_LIST_HEAD(&hid->inputs);
+
+	for (i = 0; i < hid->maxcollection; i++)
+		if (hid->collection[i].type == HID_COLLECTION_APPLICATION ||
+		    hid->collection[i].type == HID_COLLECTION_PHYSICAL)
+		    	if (IS_INPUT_APPLICATION(hid->collection[i].usage))
+				break;
+
+	if (i == hid->maxcollection)
+		return -1;
+
+	for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++)
+		list_for_each_entry(report, &hid->report_enum[k].report_list, list) {
+
+			if (!report->maxfield)
+				continue;
+
+			if (!hidinput) {
+				hidinput = kmalloc(sizeof(*hidinput), GFP_KERNEL);
+				if (!hidinput) {
+					err("Out of memory during hid input probe");
+					return -1;
+				}
+				memset(hidinput, 0, sizeof(*hidinput));
+
+				list_add_tail(&hidinput->list, &hid->inputs);
+
+				hidinput->input.private = hid;
+				hidinput->input.event = hidinput_input_event;
+				hidinput->input.open = hidinput_open;
+				hidinput->input.close = hidinput_close;
+
+				hidinput->input.name = hid->name;
+				hidinput->input.phys = hid->phys;
+				hidinput->input.uniq = hid->uniq;
+				hidinput->input.id.bustype = BUS_USB;
+				hidinput->input.id.vendor = le16_to_cpu(dev->descriptor.idVendor);
+				hidinput->input.id.product = le16_to_cpu(dev->descriptor.idProduct);
+				hidinput->input.id.version = le16_to_cpu(dev->descriptor.bcdDevice);
+				hidinput->input.dev = &hid->intf->dev;
+			}
+
+			for (i = 0; i < report->maxfield; i++)
+				for (j = 0; j < report->field[i]->maxusage; j++)
+					hidinput_configure_usage(hidinput, report->field[i],
+								 report->field[i]->usage + j);
+			
+			if (hid->quirks & HID_QUIRK_MULTI_INPUT) {
+				/* This will leave hidinput NULL, so that it
+				 * allocates another one if we have more inputs on
+				 * the same interface. Some devices (e.g. Happ's
+				 * UGCI) cram a lot of unrelated inputs into the
+				 * same interface. */
+				hidinput->report = report;
+				input_register_device(&hidinput->input);
+				hidinput = NULL;
+			}
+		}
+
+	/* This only gets called when we are a single-input (most of the
+	 * time). IOW, not a HID_QUIRK_MULTI_INPUT. The hid_ff_init() is
+	 * only useful in this case, and not for multi-input quirks. */
+	if (hidinput) {
+		hid_ff_init(hid);
+		input_register_device(&hidinput->input);
+	}
+
+	return 0;
+}
+
+void hidinput_disconnect(struct hid_device *hid)
+{
+	struct list_head *lh, *next;
+	struct hid_input *hidinput;
+
+	list_for_each_safe(lh, next, &hid->inputs) {
+		hidinput = list_entry(lh, struct hid_input, list);
+		input_unregister_device(&hidinput->input);
+		list_del(&hidinput->list);
+		kfree(hidinput);
+	}
+}
diff --git a/drivers/usb/input/hid-lgff.c b/drivers/usb/input/hid-lgff.c
new file mode 100644
index 0000000..0d7404b
--- /dev/null
+++ b/drivers/usb/input/hid-lgff.c
@@ -0,0 +1,528 @@
+/*
+ * $$
+ *
+ * Force feedback support for hid-compliant for some of the devices from
+ * Logitech, namely:
+ * - WingMan Cordless RumblePad
+ * - WingMan Force 3D
+ *
+ *  Copyright (c) 2002-2004 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 <johann.deneux@it.uu.se>
+ */
+
+#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"
+
+
+/* Periodicity of the update */
+#define PERIOD (HZ/10)
+
+#define RUN_AT(t) (jiffies + (t))
+
+/* 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
+
+// For lgff_device::flags
+#define DEVICE_CLOSING 0     /* The driver is being unitialised */
+
+/* Check that the current process can access an effect */
+#define CHECK_OWNERSHIP(effect) (current->pid == 0 \
+        || effect.owner == current->pid)
+
+#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_EFFECTS 8
+
+struct device_type {
+	u16 idVendor;
+	u16 idProduct;
+	signed short *ff;
+};
+
+struct lgff_effect {
+	pid_t owner;
+
+	struct ff_effect effect;
+
+	unsigned long flags[1];
+	unsigned int count;          /* Number of times left to play */
+	unsigned long started_at;    /* When the effect started to play */
+};
+
+struct lgff_device {
+	struct hid_device* hid;
+
+	struct hid_report* constant;
+	struct hid_report* rumble;
+	struct hid_report* condition;
+
+	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 */
+
+	unsigned long flags[1];      /* Contains various information about the
+				        state of the driver for this device */	
+
+	struct timer_list timer;
+};
+
+/* Callbacks */
+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 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);
+
+/* Local functions */
+static void hid_lgff_input_init(struct hid_device* hid);
+static void hid_lgff_timer(unsigned long timer_data);
+static struct hid_report* hid_lgff_duplicate_report(struct hid_report*);
+static void hid_lgff_delete_report(struct hid_report*);
+
+static signed short ff_rumble[] = {
+	FF_RUMBLE,
+	-1
+};
+
+static signed short ff_joystick[] = {
+	FF_CONSTANT,
+	-1
+};
+
+static struct device_type devices[] = {
+	{0x046d, 0xc211, ff_rumble},
+	{0x046d, 0xc219, ff_rumble},
+	{0x046d, 0xc283, ff_joystick},
+	{0x0000, 0x0000, ff_joystick}
+};
+
+int hid_lgff_init(struct hid_device* hid)
+{
+	struct lgff_device *private;
+	struct hid_report* report;
+	struct hid_field* field;
+
+	/* Find the report to use */
+	if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) {
+		err("No output report found");
+		return -1;
+	}
+	/* Check that the report looks ok */
+	report = (struct hid_report*)hid->report_enum[HID_OUTPUT_REPORT].report_list.next;
+	if (!report) {
+		err("NULL output report");
+		return -1;
+	}
+	field = report->field[0];
+	if (!field) {
+		err("NULL field");
+		return -1;
+	}
+
+	private = kmalloc(sizeof(struct lgff_device), GFP_KERNEL);
+	if (!private)
+		return -1;
+	memset(private, 0, sizeof(struct lgff_device));
+	hid->ff_private = private;
+
+	/* Input init */
+	hid_lgff_input_init(hid);
+
+
+	private->constant = hid_lgff_duplicate_report(report);
+	if (!private->constant) {
+		kfree(private);
+		return -1;
+	}
+	private->constant->field[0]->value[0] = 0x51;
+	private->constant->field[0]->value[1] = 0x08;
+	private->constant->field[0]->value[2] = 0x7f;
+	private->constant->field[0]->value[3] = 0x7f;
+
+	private->rumble = hid_lgff_duplicate_report(report);
+	if (!private->rumble) {
+		hid_lgff_delete_report(private->constant);
+		kfree(private);
+		return -1;
+	}
+	private->rumble->field[0]->value[0] = 0x42;
+
+
+	private->condition = hid_lgff_duplicate_report(report);
+	if (!private->condition) {
+		hid_lgff_delete_report(private->rumble);
+		hid_lgff_delete_report(private->constant);
+		kfree(private);
+		return -1;
+	}
+
+	private->hid = hid;
+
+	spin_lock_init(&private->lock);
+	init_timer(&private->timer);
+	private->timer.data = (unsigned long)private;
+	private->timer.function = hid_lgff_timer;
+
+	/* Event and exit callbacks */
+	hid->ff_exit = hid_lgff_exit;
+	hid->ff_event = hid_lgff_event;
+
+	/* 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 */
+
+	printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
+
+	return 0;
+}
+
+static struct hid_report* hid_lgff_duplicate_report(struct hid_report* report)
+{
+	struct hid_report* ret;
+
+	ret = kmalloc(sizeof(struct lgff_device), GFP_KERNEL);
+	if (!ret)
+		return NULL;
+	*ret = *report;
+
+	ret->field[0] = kmalloc(sizeof(struct hid_field), GFP_KERNEL);
+	if (!ret->field[0]) {
+		kfree(ret);
+		return NULL;
+	}
+	*ret->field[0] = *report->field[0];
+
+	ret->field[0]->value = kmalloc(sizeof(s32[8]), GFP_KERNEL);
+	if (!ret->field[0]->value) {
+		kfree(ret->field[0]);
+		kfree(ret);
+		return NULL;
+	}
+	memset(ret->field[0]->value, 0, sizeof(s32[8]));	
+
+	return ret;
+}
+
+static void hid_lgff_delete_report(struct hid_report* report)
+{
+	if (report) {
+		kfree(report->field[0]->value);
+		kfree(report->field[0]);
+		kfree(report);
+	}
+}
+
+static void hid_lgff_input_init(struct hid_device* hid)
+{
+	struct device_type* dev = devices;
+	signed short* ff;
+	u16 idVendor = le16_to_cpu(hid->dev->descriptor.idVendor);
+	u16 idProduct = le16_to_cpu(hid->dev->descriptor.idProduct);
+	struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+
+	while (dev->idVendor && (idVendor != dev->idVendor || idProduct != dev->idProduct))
+		dev++;
+
+	ff = dev->ff;
+
+	while (*ff >= 0) {
+		set_bit(*ff, hidinput->input.ffbit);
+		++ff;
+	}
+
+	hidinput->input.upload_effect = hid_lgff_upload_effect;
+	hidinput->input.flush = hid_lgff_flush;
+
+	set_bit(EV_FF, hidinput->input.evbit);
+	hidinput->input.ff_effects_max = LGFF_EFFECTS;
+}
+
+static void hid_lgff_exit(struct hid_device* hid)
+{
+	struct lgff_device *lgff = hid->ff_private;
+
+	set_bit(DEVICE_CLOSING, lgff->flags);
+	del_timer_sync(&lgff->timer);
+
+	hid_lgff_delete_report(lgff->condition);
+	hid_lgff_delete_report(lgff->rumble);
+	hid_lgff_delete_report(lgff->constant);
+
+	kfree(lgff);
+}
+
+static int hid_lgff_event(struct hid_device *hid, struct input_dev* input,
+			  unsigned int type, unsigned int code, int value)
+{
+	struct lgff_device *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->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(&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 lgff_device *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 lgff_device *lgff = hid->ff_private;
+	unsigned long flags;
+
+	if (!LGFF_CHECK_OWNERSHIP(id, lgff)) return -EACCES;
+
+	spin_lock_irqsave(&lgff->lock, flags);
+	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 lgff_device *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;
+
+	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.effect = *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.effect.replay.delay != lgff->effects[id].effect.replay.delay
+		    || new.effect.replay.length != lgff->effects[id].effect.replay.length) {
+			spin_unlock_irqrestore(&lgff->lock, flags);
+			return -ENOSYS;
+		}
+
+		lgff->effects[id] = new;
+
+	} else {
+		lgff->effects[id] = new;
+	}
+
+	spin_unlock_irqrestore(&lgff->lock, flags);
+	return 0;
+}
+
+static void hid_lgff_timer(unsigned long timer_data)
+{
+	struct lgff_device *lgff = (struct lgff_device*)timer_data;
+	struct hid_device *hid = lgff->hid;
+	unsigned long flags;
+	int x = 0x7f, y = 0x7f;   // Coordinates of constant effects
+	unsigned int left = 0, right = 0;   // Rumbling
+	int i;
+
+	spin_lock_irqsave(&lgff->lock, flags);
+
+ 	for (i=0; i<LGFF_EFFECTS; ++i) {
+		struct lgff_effect* effect = lgff->effects +i;
+
+		if (test_bit(EFFECT_PLAYING, effect->flags)) {
+
+			switch (effect->effect.type) {
+			case 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));
+			}       break;
+			case FF_RUMBLE:
+				right += effect->effect.u.rumble.strong_magnitude;
+				left += effect->effect.u.rumble.weak_magnitude;
+				break;
+			};
+
+			/* 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 %d", i);
+				if (--effect->count <= 0) {
+					dbg("Stopped %d", i);
+					clear_bit(EFFECT_PLAYING, effect->flags);
+				}
+				else {
+					dbg("Start again %d", i);
+					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, lgff->effects[i].flags)) {
+			/* Check if we should start playing the effect */
+			if (time_after(jiffies,
+					lgff->effects[i].started_at
+					+ lgff->effects[i].effect.replay.delay*HZ/1000)) {
+				dbg("Now playing %d", i);
+				clear_bit(EFFECT_STARTED, lgff->effects[i].flags);
+				set_bit(EFFECT_PLAYING, lgff->effects[i].flags);
+			}
+		}
+ 	}
+
+#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
+
+	// Clamp values
+	CLAMP(x);
+	CLAMP(y);
+	CLAMP(left);
+	CLAMP(right);
+
+#undef CLAMP
+
+	if (x != lgff->constant->field[0]->value[2]
+	    || y != lgff->constant->field[0]->value[3]) {
+		lgff->constant->field[0]->value[2] = x;
+		lgff->constant->field[0]->value[3] = y;
+		dbg("(x,y)=(%04x, %04x)", x, y);
+		hid_submit_report(hid, lgff->constant, USB_DIR_OUT);
+	}
+
+	if (left != lgff->rumble->field[0]->value[2]
+	    || right != lgff->rumble->field[0]->value[3]) {
+		lgff->rumble->field[0]->value[2] = left;
+		lgff->rumble->field[0]->value[3] = right;
+		dbg("(left,right)=(%04x, %04x)", left, right);
+		hid_submit_report(hid, lgff->rumble, USB_DIR_OUT);
+	}
+
+	if (!test_bit(DEVICE_CLOSING, lgff->flags)) {
+		lgff->timer.expires = RUN_AT(PERIOD);
+		add_timer(&lgff->timer);
+	}
+
+ 	spin_unlock_irqrestore(&lgff->lock, flags);
+}
diff --git a/drivers/usb/input/hid-tmff.c b/drivers/usb/input/hid-tmff.c
new file mode 100644
index 0000000..8f6a0a6
--- /dev/null
+++ b/drivers/usb/input/hid-tmff.c
@@ -0,0 +1,463 @@
+/*
+ * Force feedback support for various HID compliant devices by ThrustMaster:
+ *    ThrustMaster FireStorm Dual Power 2
+ * and possibly others whose device ids haven't been added.
+ *
+ *  Modified to support ThrustMaster devices by Zinx Verituse
+ *  on 2003-01-25 from the Logitech force feedback driver,
+ *  which is by Johann Deneux.
+ *
+ *  Copyright (c) 2003 Zinx Verituse <zinx@epicsol.org>
+ *  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
+ */
+
+#include <linux/input.h>
+#include <linux/sched.h>
+
+#undef DEBUG
+#include <linux/usb.h>
+
+#include <linux/circ_buf.h>
+
+#include "hid.h"
+#include "fixp-arith.h"
+
+/* Usages for thrustmaster devices I know about */
+#define THRUSTMASTER_USAGE_RUMBLE_LR	(HID_UP_GENDESK | 0xbb)
+#define DELAY_CALC(t,delay)		((t) + (delay)*HZ/1000)
+
+/* Effect status */
+#define EFFECT_STARTED 0	/* Effect is going to play after some time */
+#define EFFECT_PLAYING 1	/* Effect is playing */
+#define EFFECT_USED    2
+
+/* For tmff_device::flags */
+#define DEVICE_CLOSING 0	/* The driver is being unitialised */
+
+/* Check that the current process can access an effect */
+#define CHECK_OWNERSHIP(effect) (current->pid == 0 \
+        || effect.owner == current->pid)
+
+#define TMFF_CHECK_ID(id)	((id) >= 0 && (id) < TMFF_EFFECTS)
+
+#define TMFF_CHECK_OWNERSHIP(i, l) \
+        (test_bit(EFFECT_USED, l->effects[i].flags) \
+        && CHECK_OWNERSHIP(l->effects[i]))
+
+#define TMFF_EFFECTS 8
+
+struct tmff_effect {
+	pid_t owner;
+
+	struct ff_effect effect;
+
+	unsigned long flags[1];
+	unsigned int count;             /* Number of times left to play */
+
+	unsigned long play_at;          /* When the effect starts to play */
+	unsigned long stop_at;		/* When the effect ends */
+};
+
+struct tmff_device {
+	struct hid_device *hid;
+
+	struct hid_report *report;
+
+	struct hid_field *rumble;
+
+	unsigned int effects_playing;
+	struct tmff_effect effects[TMFF_EFFECTS];
+	spinlock_t lock;             /* device-level lock. Having locks on
+					a per-effect basis could be nice, but
+					isn't really necessary */
+
+	unsigned long flags[1];      /* Contains various information about the
+					state of the driver for this device */
+
+	struct timer_list timer;
+};
+
+/* Callbacks */
+static void hid_tmff_exit(struct hid_device *hid);
+static int hid_tmff_event(struct hid_device *hid, struct input_dev *input,
+			  unsigned int type, unsigned int code, int value);
+static int hid_tmff_flush(struct input_dev *input, struct file *file);
+static int hid_tmff_upload_effect(struct input_dev *input,
+				  struct ff_effect *effect);
+static int hid_tmff_erase(struct input_dev *input, int id);
+
+/* Local functions */
+static void hid_tmff_recalculate_timer(struct tmff_device *tmff);
+static void hid_tmff_timer(unsigned long timer_data);
+
+int hid_tmff_init(struct hid_device *hid)
+{
+	struct tmff_device *private;
+	struct list_head *pos;
+	struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+
+	private = kmalloc(sizeof(struct tmff_device), GFP_KERNEL);
+	if (!private)
+		return -ENOMEM;
+
+	memset(private, 0, sizeof(struct tmff_device));
+	hid->ff_private = private;
+
+	/* Find the report to use */
+	__list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) {
+		struct hid_report *report = (struct hid_report *)pos;
+		int fieldnum;
+
+		for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) {
+			struct hid_field *field = report->field[fieldnum];
+
+			if (field->maxusage <= 0)
+				continue;
+
+			switch (field->usage[0].hid) {
+				case THRUSTMASTER_USAGE_RUMBLE_LR:
+					if (field->report_count < 2) {
+						warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with report_count < 2");
+						continue;
+					}
+
+					if (field->logical_maximum == field->logical_minimum) {
+						warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with logical_maximum == logical_minimum");
+						continue;
+					}
+
+					if (private->report && private->report != report) {
+						warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report");
+						continue;
+					}
+
+					if (private->rumble && private->rumble != field) {
+						warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR");
+						continue;
+					}
+
+					private->report = report;
+					private->rumble = field;
+
+					set_bit(FF_RUMBLE, hidinput->input.ffbit);
+					break;
+
+				default:
+					warn("ignoring unknown output usage %08x", field->usage[0].hid);
+					continue;
+			}
+
+			/* Fallthrough to here only when a valid usage is found */
+			hidinput->input.upload_effect = hid_tmff_upload_effect;
+			hidinput->input.flush = hid_tmff_flush;
+
+			set_bit(EV_FF, hidinput->input.evbit);
+			hidinput->input.ff_effects_max = TMFF_EFFECTS;
+		}
+	}
+
+	private->hid = hid;
+
+	spin_lock_init(&private->lock);
+	init_timer(&private->timer);
+	private->timer.data = (unsigned long)private;
+	private->timer.function = hid_tmff_timer;
+
+	/* Event and exit callbacks */
+	hid->ff_exit = hid_tmff_exit;
+	hid->ff_event = hid_tmff_event;
+
+	info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <zinx@epicsol.org>");
+
+	return 0;
+}
+
+static void hid_tmff_exit(struct hid_device *hid)
+{
+	struct tmff_device *tmff = hid->ff_private;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tmff->lock, flags);
+
+	set_bit(DEVICE_CLOSING, tmff->flags);
+	del_timer_sync(&tmff->timer);
+
+	spin_unlock_irqrestore(&tmff->lock, flags);
+
+	kfree(tmff);
+}
+
+static int hid_tmff_event(struct hid_device *hid, struct input_dev *input,
+			  unsigned int type, unsigned int code, int value)
+{
+	struct tmff_device *tmff = hid->ff_private;
+	struct tmff_effect *effect = &tmff->effects[code];
+	unsigned long flags;
+
+	if (type != EV_FF)
+		return -EINVAL;
+	if (!TMFF_CHECK_ID(code))
+		return -EINVAL;
+	if (!TMFF_CHECK_OWNERSHIP(code, tmff))
+		return -EACCES;
+	if (value < 0)
+		return -EINVAL;
+
+	spin_lock_irqsave(&tmff->lock, flags);
+
+	if (value > 0) {
+		set_bit(EFFECT_STARTED, effect->flags);
+		clear_bit(EFFECT_PLAYING, effect->flags);
+		effect->count = value;
+		effect->play_at = DELAY_CALC(jiffies, effect->effect.replay.delay);
+	} else {
+		clear_bit(EFFECT_STARTED, effect->flags);
+		clear_bit(EFFECT_PLAYING, effect->flags);
+	}
+
+	hid_tmff_recalculate_timer(tmff);
+
+	spin_unlock_irqrestore(&tmff->lock, flags);
+
+	return 0;
+
+}
+
+/* Erase all effects this process owns */
+
+static int hid_tmff_flush(struct input_dev *dev, struct file *file)
+{
+	struct hid_device *hid = dev->private;
+	struct tmff_device *tmff = 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 == tmff->effects[i].owner
+		     && test_bit(EFFECT_USED, tmff->effects[i].flags))
+			if (hid_tmff_erase(dev, i))
+				warn("erase effect %d failed", i);
+
+
+	return 0;
+}
+
+static int hid_tmff_erase(struct input_dev *dev, int id)
+{
+	struct hid_device *hid = dev->private;
+	struct tmff_device *tmff = hid->ff_private;
+	unsigned long flags;
+
+	if (!TMFF_CHECK_ID(id))
+		return -EINVAL;
+	if (!TMFF_CHECK_OWNERSHIP(id, tmff))
+		return -EACCES;
+
+	spin_lock_irqsave(&tmff->lock, flags);
+
+	tmff->effects[id].flags[0] = 0;
+	hid_tmff_recalculate_timer(tmff);
+
+	spin_unlock_irqrestore(&tmff->lock, flags);
+
+	return 0;
+}
+
+static int hid_tmff_upload_effect(struct input_dev *input,
+				  struct ff_effect *effect)
+{
+	struct hid_device *hid = input->private;
+	struct tmff_device *tmff = hid->ff_private;
+	int id;
+	unsigned long flags;
+
+	if (!test_bit(effect->type, input->ffbit))
+		return -EINVAL;
+	if (effect->id != -1 && !TMFF_CHECK_ID(effect->id))
+		return -EINVAL;
+
+	spin_lock_irqsave(&tmff->lock, flags);
+
+	if (effect->id == -1) {
+		/* Find a free effect */
+		for (id = 0; id < TMFF_EFFECTS && test_bit(EFFECT_USED, tmff->effects[id].flags); ++id);
+
+		if (id >= TMFF_EFFECTS) {
+			spin_unlock_irqrestore(&tmff->lock, flags);
+			return -ENOSPC;
+		}
+
+		effect->id = id;
+		tmff->effects[id].owner = current->pid;
+		tmff->effects[id].flags[0] = 0;
+		set_bit(EFFECT_USED, tmff->effects[id].flags);
+
+	} else {
+		/* Re-uploading an owned effect, to change parameters */
+		id = effect->id;
+		clear_bit(EFFECT_PLAYING, tmff->effects[id].flags);
+	}
+
+	tmff->effects[id].effect = *effect;
+
+	hid_tmff_recalculate_timer(tmff);
+
+	spin_unlock_irqrestore(&tmff->lock, flags);
+	return 0;
+}
+
+/* Start the timer for the next start/stop/delay */
+/* Always call this while tmff->lock is locked */
+
+static void hid_tmff_recalculate_timer(struct tmff_device *tmff)
+{
+	int i;
+	int events = 0;
+	unsigned long next_time;
+
+	next_time = 0;	/* Shut up compiler's incorrect warning */
+
+	/* Find the next change in an effect's status */
+	for (i = 0; i < TMFF_EFFECTS; ++i) {
+		struct tmff_effect *effect = &tmff->effects[i];
+		unsigned long play_time;
+
+		if (!test_bit(EFFECT_STARTED, effect->flags))
+			continue;
+
+		effect->stop_at = DELAY_CALC(effect->play_at, effect->effect.replay.length);
+
+		if (!test_bit(EFFECT_PLAYING, effect->flags))
+			play_time = effect->play_at;
+		else
+			play_time = effect->stop_at;
+
+		events++;
+
+		if (time_after(jiffies, play_time))
+			play_time = jiffies;
+
+		if (events == 1)
+			next_time = play_time;
+		else {
+			if (time_after(next_time, play_time))
+				next_time = play_time;
+		}
+	}
+
+	if (!events && tmff->effects_playing) {
+		/* Treat all effects turning off as an event */
+		events = 1;
+		next_time = jiffies;
+	}
+
+	if (!events) {
+		/* No events, no time, no need for a timer. */
+		del_timer_sync(&tmff->timer);
+		return;
+	}
+
+	mod_timer(&tmff->timer, next_time);
+}
+
+/* Changes values from 0 to 0xffff into values from minimum to maximum */
+static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum)
+{
+	int ret;
+
+	ret = (in * (maximum - minimum) / 0xffff) + minimum;
+	if (ret < minimum)
+		return minimum;
+	if (ret > maximum)
+		return maximum;
+	return ret;
+}
+
+static void hid_tmff_timer(unsigned long timer_data)
+{
+	struct tmff_device *tmff = (struct tmff_device *) timer_data;
+	struct hid_device *hid = tmff->hid;
+	unsigned long flags;
+	int left = 0, right = 0;	/* Rumbling */
+	int i;
+
+	spin_lock_irqsave(&tmff->lock, flags);
+
+	tmff->effects_playing = 0;
+
+	for (i = 0; i < TMFF_EFFECTS; ++i) {
+		struct tmff_effect *effect = &tmff->effects[i];
+
+		if (!test_bit(EFFECT_STARTED, effect->flags))
+			continue;
+
+		if (!time_after(jiffies, effect->play_at))
+			continue;
+
+		if (time_after(jiffies, effect->stop_at)) {
+
+			dbg("Finished playing once %d", i);
+			clear_bit(EFFECT_PLAYING, effect->flags);
+
+			if (--effect->count <= 0) {
+				dbg("Stopped %d", i);
+				clear_bit(EFFECT_STARTED, effect->flags);
+				continue;
+			} else {
+				dbg("Start again %d", i);
+				effect->play_at = DELAY_CALC(jiffies, effect->effect.replay.delay);
+				continue;
+			}
+		}
+
+		++tmff->effects_playing;
+
+		set_bit(EFFECT_PLAYING, effect->flags);
+
+		switch (effect->effect.type) {
+			case FF_RUMBLE:
+				right += effect->effect.u.rumble.strong_magnitude;
+				left += effect->effect.u.rumble.weak_magnitude;
+				break;
+			default:
+				BUG();
+				break;
+		}
+	}
+
+	left = hid_tmff_scale(left, tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
+	right = hid_tmff_scale(right, tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
+
+	if (left != tmff->rumble->value[0] || right != tmff->rumble->value[1]) {
+		tmff->rumble->value[0] = left;
+		tmff->rumble->value[1] = right;
+		dbg("(left,right)=(%08x, %08x)", left, right);
+		hid_submit_report(hid, tmff->report, USB_DIR_OUT);
+	}
+
+	if (!test_bit(DEVICE_CLOSING, tmff->flags))
+		hid_tmff_recalculate_timer(tmff);
+
+	spin_unlock_irqrestore(&tmff->lock, flags);
+}
diff --git a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h
new file mode 100644
index 0000000..6d9329c
--- /dev/null
+++ b/drivers/usb/input/hid.h
@@ -0,0 +1,510 @@
+#ifndef __HID_H
+#define __HID_H
+
+/*
+ * $Id: hid.h,v 1.24 2001/12/27 10:37:41 vojtech Exp $
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+/*
+ * USB HID (Human Interface Device) interface class code
+ */
+
+#define USB_INTERFACE_CLASS_HID		3
+
+/*
+ * HID class requests
+ */
+
+#define HID_REQ_GET_REPORT		0x01
+#define HID_REQ_GET_IDLE		0x02
+#define HID_REQ_GET_PROTOCOL		0x03
+#define HID_REQ_SET_REPORT		0x09
+#define HID_REQ_SET_IDLE		0x0A
+#define HID_REQ_SET_PROTOCOL		0x0B
+
+/*
+ * HID class descriptor types
+ */
+
+#define HID_DT_HID			(USB_TYPE_CLASS | 0x01)
+#define HID_DT_REPORT			(USB_TYPE_CLASS | 0x02)
+#define HID_DT_PHYSICAL			(USB_TYPE_CLASS | 0x03)
+
+/*
+ * We parse each description item into this structure. Short items data
+ * values are expanded to 32-bit signed int, long items contain a pointer
+ * into the data area.
+ */
+
+struct hid_item {
+	unsigned  format;
+	__u8      size;
+	__u8      type;
+	__u8      tag;
+	union {
+	    __u8   u8;
+	    __s8   s8;
+	    __u16  u16;
+	    __s16  s16;
+	    __u32  u32;
+	    __s32  s32;
+	    __u8  *longdata;
+	} data;
+};
+
+/*
+ * HID report item format
+ */
+
+#define HID_ITEM_FORMAT_SHORT	0
+#define HID_ITEM_FORMAT_LONG	1
+
+/*
+ * Special tag indicating long items
+ */
+
+#define HID_ITEM_TAG_LONG	15
+
+/*
+ * HID report descriptor item type (prefix bit 2,3)
+ */
+
+#define HID_ITEM_TYPE_MAIN		0
+#define HID_ITEM_TYPE_GLOBAL		1
+#define HID_ITEM_TYPE_LOCAL		2
+#define HID_ITEM_TYPE_RESERVED		3
+
+/*
+ * HID report descriptor main item tags
+ */
+
+#define HID_MAIN_ITEM_TAG_INPUT			8
+#define HID_MAIN_ITEM_TAG_OUTPUT		9
+#define HID_MAIN_ITEM_TAG_FEATURE		11
+#define HID_MAIN_ITEM_TAG_BEGIN_COLLECTION	10
+#define HID_MAIN_ITEM_TAG_END_COLLECTION	12
+
+/*
+ * HID report descriptor main item contents
+ */
+
+#define HID_MAIN_ITEM_CONSTANT		0x001
+#define HID_MAIN_ITEM_VARIABLE		0x002
+#define HID_MAIN_ITEM_RELATIVE		0x004
+#define HID_MAIN_ITEM_WRAP		0x008	
+#define HID_MAIN_ITEM_NONLINEAR		0x010
+#define HID_MAIN_ITEM_NO_PREFERRED	0x020
+#define HID_MAIN_ITEM_NULL_STATE	0x040
+#define HID_MAIN_ITEM_VOLATILE		0x080
+#define HID_MAIN_ITEM_BUFFERED_BYTE	0x100
+
+/*
+ * HID report descriptor collection item types
+ */
+
+#define HID_COLLECTION_PHYSICAL		0
+#define HID_COLLECTION_APPLICATION	1
+#define HID_COLLECTION_LOGICAL		2
+
+/*
+ * HID report descriptor global item tags
+ */
+
+#define HID_GLOBAL_ITEM_TAG_USAGE_PAGE		0
+#define HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM	1
+#define HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM	2
+#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM	3
+#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM	4
+#define HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT	5
+#define HID_GLOBAL_ITEM_TAG_UNIT		6
+#define HID_GLOBAL_ITEM_TAG_REPORT_SIZE		7
+#define HID_GLOBAL_ITEM_TAG_REPORT_ID		8
+#define HID_GLOBAL_ITEM_TAG_REPORT_COUNT	9
+#define HID_GLOBAL_ITEM_TAG_PUSH		10
+#define HID_GLOBAL_ITEM_TAG_POP			11
+
+/*
+ * HID report descriptor local item tags
+ */
+
+#define HID_LOCAL_ITEM_TAG_USAGE		0
+#define HID_LOCAL_ITEM_TAG_USAGE_MINIMUM	1
+#define HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM	2
+#define HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX	3
+#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM	4
+#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM	5
+#define HID_LOCAL_ITEM_TAG_STRING_INDEX		7
+#define HID_LOCAL_ITEM_TAG_STRING_MINIMUM	8
+#define HID_LOCAL_ITEM_TAG_STRING_MAXIMUM	9
+#define HID_LOCAL_ITEM_TAG_DELIMITER		10
+
+/*
+ * HID usage tables
+ */
+
+#define HID_USAGE_PAGE		0xffff0000
+
+#define HID_UP_UNDEFINED	0x00000000
+#define HID_UP_GENDESK 		0x00010000
+#define HID_UP_KEYBOARD 	0x00070000
+#define HID_UP_LED 		0x00080000
+#define HID_UP_BUTTON 		0x00090000
+#define HID_UP_ORDINAL 		0x000a0000
+#define HID_UP_CONSUMER		0x000c0000
+#define HID_UP_DIGITIZER 	0x000d0000
+#define HID_UP_PID 		0x000f0000
+#define HID_UP_HPVENDOR         0xff7f0000
+#define HID_UP_MSVENDOR		0xff000000
+
+#define HID_USAGE		0x0000ffff
+
+#define HID_GD_POINTER		0x00010001
+#define HID_GD_MOUSE		0x00010002
+#define HID_GD_JOYSTICK		0x00010004
+#define HID_GD_GAMEPAD		0x00010005
+#define HID_GD_KEYBOARD		0x00010006
+#define HID_GD_KEYPAD		0x00010007
+#define HID_GD_MULTIAXIS	0x00010008
+#define HID_GD_X		0x00010030
+#define HID_GD_Y		0x00010031
+#define HID_GD_Z		0x00010032
+#define HID_GD_RX		0x00010033
+#define HID_GD_RY		0x00010034
+#define HID_GD_RZ		0x00010035
+#define HID_GD_SLIDER		0x00010036
+#define HID_GD_DIAL		0x00010037
+#define HID_GD_WHEEL		0x00010038
+#define HID_GD_HATSWITCH	0x00010039
+#define HID_GD_BUFFER		0x0001003a
+#define HID_GD_BYTECOUNT	0x0001003b
+#define HID_GD_MOTION		0x0001003c
+#define HID_GD_START		0x0001003d
+#define HID_GD_SELECT		0x0001003e
+#define HID_GD_VX		0x00010040
+#define HID_GD_VY		0x00010041
+#define HID_GD_VZ		0x00010042
+#define HID_GD_VBRX		0x00010043
+#define HID_GD_VBRY		0x00010044
+#define HID_GD_VBRZ		0x00010045
+#define HID_GD_VNO		0x00010046
+#define HID_GD_FEATURE		0x00010047
+#define HID_GD_UP		0x00010090
+#define HID_GD_DOWN		0x00010091
+#define HID_GD_RIGHT		0x00010092
+#define HID_GD_LEFT		0x00010093
+
+/*
+ * HID report types --- Ouch! HID spec says 1 2 3!
+ */
+
+#define HID_INPUT_REPORT	0
+#define HID_OUTPUT_REPORT	1
+#define HID_FEATURE_REPORT	2
+
+/*
+ * HID device quirks.
+ */
+
+#define HID_QUIRK_INVERT			0x001
+#define HID_QUIRK_NOTOUCH			0x002
+#define HID_QUIRK_IGNORE			0x004
+#define HID_QUIRK_NOGET				0x008
+#define HID_QUIRK_HIDDEV			0x010
+#define HID_QUIRK_BADPAD			0x020
+#define HID_QUIRK_MULTI_INPUT			0x040
+#define HID_QUIRK_2WHEEL_MOUSE_HACK_7		0x080
+#define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x100
+#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x200
+
+/*
+ * This is the global environment of the parser. This information is
+ * persistent for main-items. The global environment can be saved and
+ * restored with PUSH/POP statements.
+ */
+
+struct hid_global {
+	unsigned usage_page;
+	__s32    logical_minimum;
+	__s32    logical_maximum;
+	__s32    physical_minimum;
+	__s32    physical_maximum;
+	__s32    unit_exponent;
+	unsigned unit;
+	unsigned report_id;
+	unsigned report_size;
+	unsigned report_count;
+};
+
+/*
+ * This is the local environment. It is persistent up the next main-item.
+ */
+
+#define HID_MAX_DESCRIPTOR_SIZE		4096
+#define HID_MAX_USAGES			1024
+#define HID_DEFAULT_NUM_COLLECTIONS	16
+
+struct hid_local {
+	unsigned usage[HID_MAX_USAGES]; /* usage array */
+	unsigned collection_index[HID_MAX_USAGES]; /* collection index array */
+	unsigned usage_index;
+	unsigned usage_minimum;
+	unsigned delimiter_depth;
+	unsigned delimiter_branch;
+};
+
+/*
+ * This is the collection stack. We climb up the stack to determine
+ * application and function of each field.
+ */
+
+struct hid_collection {
+	unsigned type;
+	unsigned usage;
+	unsigned level;
+};
+
+struct hid_usage {
+	unsigned  hid;			/* hid usage code */
+	unsigned  collection_index;	/* index into collection array */
+	/* hidinput data */
+	__u16     code;			/* input driver code */
+	__u8      type;			/* input driver type */
+	__s8	  hat_min;		/* hat switch fun */
+	__s8	  hat_max;		/* ditto */
+	__s8	  hat_dir;		/* ditto */
+};
+
+struct hid_input;
+
+struct hid_field {
+	unsigned  physical;		/* physical usage for this field */
+	unsigned  logical;		/* logical usage for this field */
+	unsigned  application;		/* application usage for this field */
+	struct hid_usage *usage;	/* usage table for this function */
+	unsigned  maxusage;		/* maximum usage index */
+	unsigned  flags;		/* main-item flags (i.e. volatile,array,constant) */
+	unsigned  report_offset;	/* bit offset in the report */
+	unsigned  report_size;		/* size of this field in the report */
+	unsigned  report_count;		/* number of this field in the report */
+	unsigned  report_type;		/* (input,output,feature) */
+	__s32    *value;		/* last known value(s) */
+	__s32     logical_minimum;
+	__s32     logical_maximum;
+	__s32     physical_minimum;
+	__s32     physical_maximum;
+	__s32     unit_exponent;
+	unsigned  unit;
+	struct hid_report *report;	/* associated report */
+	unsigned index;			/* index into report->field[] */
+	/* hidinput data */
+	struct hid_input *hidinput;	/* associated input structure */
+	__u16 dpad;			/* dpad input code */
+};
+
+#define HID_MAX_FIELDS 64
+
+struct hid_report {
+	struct list_head list;
+	unsigned id;					/* id of this report */
+	unsigned type;					/* report type */
+	struct hid_field *field[HID_MAX_FIELDS];	/* fields of the report */
+	unsigned maxfield;				/* maximum valid field index */
+	unsigned size;					/* size of the report (bits) */
+	struct hid_device *device;			/* associated device */
+};
+
+struct hid_report_enum {
+	unsigned numbered;
+	struct list_head report_list;
+	struct hid_report *report_id_hash[256];
+};
+
+#define HID_REPORT_TYPES 3
+
+#define HID_BUFFER_SIZE		64		/* use 64 for compatibility with all possible packetlen */
+#define HID_CONTROL_FIFO_SIZE	256		/* to init devices with >100 reports */
+#define HID_OUTPUT_FIFO_SIZE	64
+
+struct hid_control_fifo {
+	unsigned char dir;
+	struct hid_report *report;
+};
+
+#define HID_CLAIMED_INPUT	1
+#define HID_CLAIMED_HIDDEV	2
+
+#define HID_CTRL_RUNNING	1
+#define HID_OUT_RUNNING		2
+
+struct hid_input {
+	struct list_head list;
+	struct hid_report *report;
+	struct input_dev input;
+};
+
+struct hid_device {							/* device report descriptor */
+	 __u8 *rdesc;
+	unsigned rsize;
+	struct hid_collection *collection;				/* List of HID collections */
+	unsigned collection_size;					/* Number of allocated hid_collections */
+	unsigned maxcollection;						/* Number of parsed collections */
+	unsigned maxapplication;					/* Number of applications */
+	unsigned version;						/* HID version */
+	unsigned country;						/* HID country */
+	struct hid_report_enum report_enum[HID_REPORT_TYPES];
+
+	struct usb_device *dev;						/* USB device */
+	struct usb_interface *intf;					/* USB interface */
+	int ifnum;							/* USB interface number */
+
+	unsigned long iofl;						/* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
+
+	struct urb *urbin;						/* Input URB */
+	char *inbuf;							/* Input buffer */
+	dma_addr_t inbuf_dma;						/* Input buffer dma */
+
+	struct urb *urbctrl;						/* Control URB */
+	struct usb_ctrlrequest *cr;					/* Control request struct */
+	dma_addr_t cr_dma;						/* Control request struct dma */
+	struct hid_control_fifo ctrl[HID_CONTROL_FIFO_SIZE];		/* Control fifo */
+	unsigned char ctrlhead, ctrltail;				/* Control fifo head & tail */
+	char *ctrlbuf;							/* Control buffer */
+	dma_addr_t ctrlbuf_dma;						/* Control buffer dma */
+	spinlock_t ctrllock;						/* Control fifo spinlock */
+
+	struct urb *urbout;						/* Output URB */
+	struct hid_report *out[HID_CONTROL_FIFO_SIZE];			/* Output pipe fifo */
+	unsigned char outhead, outtail;					/* Output pipe fifo head & tail */
+	char *outbuf;							/* Output buffer */
+	dma_addr_t outbuf_dma;						/* Output buffer dma */
+	spinlock_t outlock;						/* Output fifo spinlock */
+
+	unsigned claimed;						/* Claimed by hidinput, hiddev? */	
+	unsigned quirks;						/* Various quirks the device can pull on us */
+
+	struct list_head inputs;					/* The list of inputs */
+	void *hiddev;							/* The hiddev structure */
+	int minor;							/* Hiddev minor number */
+
+	wait_queue_head_t wait;						/* For sleeping */
+
+	int open;							/* is the device open by anyone? */
+	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
+#define HID_COLLECTION_STACK_SIZE 4
+
+struct hid_parser {
+	struct hid_global     global;
+	struct hid_global     global_stack[HID_GLOBAL_STACK_SIZE];
+	unsigned              global_stack_ptr;
+	struct hid_local      local;
+	unsigned              collection_stack[HID_COLLECTION_STACK_SIZE];
+	unsigned              collection_stack_ptr;
+	struct hid_device    *device;
+};
+
+struct hid_class_descriptor {
+	__u8  bDescriptorType;
+	__u16 wDescriptorLength;
+} __attribute__ ((packed));
+
+struct hid_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u16 bcdHID;
+	__u8  bCountryCode;
+	__u8  bNumDescriptors;
+
+	struct hid_class_descriptor desc[1];
+} __attribute__ ((packed));
+
+#ifdef DEBUG
+#include "hid-debug.h"
+#else
+#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)
+#define resolv_event(a,b)	do { } while (0)
+#endif
+
+#endif
+
+#ifdef CONFIG_USB_HIDINPUT
+/* Applications from HID Usage Tables 4/8/99 Version 1.1 */
+/* We ignore a few input applications that are not widely used */
+#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001))
+extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, __s32, struct pt_regs *regs);
+extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report);
+extern int hidinput_connect(struct hid_device *);
+extern void hidinput_disconnect(struct hid_device *);
+#else
+#define IS_INPUT_APPLICATION(a) (0)
+static inline void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, struct pt_regs *regs) { }
+static inline void hidinput_report_event(struct hid_device *hid, struct hid_report *report) { }
+static inline int hidinput_connect(struct hid_device *hid) { return -ENODEV; }
+static inline void hidinput_disconnect(struct hid_device *hid) { }
+#endif
+
+int hid_open(struct hid_device *);
+void hid_close(struct hid_device *);
+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);
+struct hid_field *hid_find_field_by_usage(struct hid_device *hid, __u32 wanted_usage, int type);
+int hid_wait_io(struct hid_device* hid);
+
+
+#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 --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c
new file mode 100644
index 0000000..96b7c90
--- /dev/null
+++ b/drivers/usb/input/hiddev.c
@@ -0,0 +1,844 @@
+/*
+ *  Copyright (c) 2001 Paul Stewart
+ *  Copyright (c) 2001 Vojtech Pavlik
+ *
+ *  HID char devices, giving access to raw HID device events.
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to Paul Stewart <stewart@wetlogic.net>
+ */
+
+#include <linux/config.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+#include "hid.h"
+#include <linux/hiddev.h>
+#include <linux/devfs_fs_kernel.h>
+
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define HIDDEV_MINOR_BASE	0
+#define HIDDEV_MINORS		256
+#else
+#define HIDDEV_MINOR_BASE	96
+#define HIDDEV_MINORS		16
+#endif
+#define HIDDEV_BUFFER_SIZE	64
+
+struct hiddev {
+	int exist;
+	int open;
+	wait_queue_head_t wait;
+	struct hid_device *hid;
+	struct hiddev_list *list;
+};
+
+struct hiddev_list {
+	struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE];
+	int head;
+	int tail;
+	unsigned flags;
+	struct fasync_struct *fasync;
+	struct hiddev *hiddev;
+	struct hiddev_list *next;
+};
+
+static struct hiddev *hiddev_table[HIDDEV_MINORS];
+
+/*
+ * Find a report, given the report's type and ID.  The ID can be specified
+ * indirectly by REPORT_ID_FIRST (which returns the first report of the given
+ * type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the
+ * given type which follows old_id.
+ */
+static struct hid_report *
+hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
+{
+	unsigned flags = rinfo->report_id & ~HID_REPORT_ID_MASK;
+	struct hid_report_enum *report_enum;
+	struct list_head *list;
+
+	if (rinfo->report_type < HID_REPORT_TYPE_MIN ||
+	    rinfo->report_type > HID_REPORT_TYPE_MAX) return NULL;
+
+	report_enum = hid->report_enum +
+		(rinfo->report_type - HID_REPORT_TYPE_MIN);
+
+	switch (flags) {
+	case 0: /* Nothing to do -- report_id is already set correctly */
+		break;
+
+	case HID_REPORT_ID_FIRST:
+		list = report_enum->report_list.next;
+		if (list == &report_enum->report_list)
+			return NULL;
+		rinfo->report_id = ((struct hid_report *) list)->id;
+		break;
+		
+	case HID_REPORT_ID_NEXT:
+		list = (struct list_head *)
+			report_enum->report_id_hash[rinfo->report_id & HID_REPORT_ID_MASK];
+		if (list == NULL)
+			return NULL;
+		list = list->next;
+		if (list == &report_enum->report_list)
+			return NULL;
+		rinfo->report_id = ((struct hid_report *) list)->id;
+		break;
+		
+	default:
+		return NULL;
+	}
+
+	return report_enum->report_id_hash[rinfo->report_id];
+}
+
+/*
+ * Perform an exhaustive search of the report table for a usage, given its
+ * type and usage id.
+ */
+static struct hid_field *
+hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref)
+{
+	int i, j;
+	struct hid_report *report;
+	struct hid_report_enum *report_enum;
+	struct hid_field *field;
+
+	if (uref->report_type < HID_REPORT_TYPE_MIN ||
+	    uref->report_type > HID_REPORT_TYPE_MAX) return NULL;
+
+	report_enum = hid->report_enum +
+		(uref->report_type - HID_REPORT_TYPE_MIN);
+
+	list_for_each_entry(report, &report_enum->report_list, list)
+		for (i = 0; i < report->maxfield; i++) {
+			field = report->field[i];
+			for (j = 0; j < field->maxusage; j++) {
+				if (field->usage[j].hid == uref->usage_code) {
+					uref->report_id = report->id;
+					uref->field_index = i;
+					uref->usage_index = j;
+					return field;
+				}
+			}
+		}
+
+	return NULL;
+}
+
+static void hiddev_send_event(struct hid_device *hid,
+			      struct hiddev_usage_ref *uref)
+{
+	struct hiddev *hiddev = hid->hiddev;
+	struct hiddev_list *list = hiddev->list;
+
+	while (list) {
+		if (uref->field_index != HID_FIELD_INDEX_NONE ||
+		    (list->flags & HIDDEV_FLAG_REPORT) != 0) {
+			list->buffer[list->head] = *uref;
+			list->head = (list->head + 1) & 
+				(HIDDEV_BUFFER_SIZE - 1);
+			kill_fasync(&list->fasync, SIGIO, POLL_IN);
+		}
+
+		list = list->next;
+	}
+
+	wake_up_interruptible(&hiddev->wait);
+}
+
+/*
+ * This is where hid.c calls into hiddev to pass an event that occurred over
+ * the interrupt pipe
+ */
+void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
+		      struct hid_usage *usage, __s32 value, struct pt_regs *regs)
+{
+	unsigned type = field->report_type;
+	struct hiddev_usage_ref uref;
+
+	uref.report_type = 
+	  (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
+	  ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : 
+	   ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0));
+	uref.report_id = field->report->id;
+	uref.field_index = field->index;
+	uref.usage_index = (usage - field->usage);
+	uref.usage_code = usage->hid;
+	uref.value = value;
+
+	hiddev_send_event(hid, &uref);
+}
+
+
+void hiddev_report_event(struct hid_device *hid, struct hid_report *report)
+{
+	unsigned type = report->type;
+	struct hiddev_usage_ref uref;
+
+	memset(&uref, 0, sizeof(uref));
+	uref.report_type = 
+	  (type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
+	  ((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT : 
+	   ((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE:0));
+	uref.report_id = report->id;
+	uref.field_index = HID_FIELD_INDEX_NONE;
+
+	hiddev_send_event(hid, &uref);
+}
+/*
+ * fasync file op
+ */
+static int hiddev_fasync(int fd, struct file *file, int on)
+{
+	int retval;
+	struct hiddev_list *list = file->private_data;
+	retval = fasync_helper(fd, file, on, &list->fasync);
+	return retval < 0 ? retval : 0;
+}
+
+
+/*
+ * release file op
+ */
+static int hiddev_release(struct inode * inode, struct file * file)
+{
+	struct hiddev_list *list = file->private_data;
+	struct hiddev_list **listptr;
+
+	listptr = &list->hiddev->list;
+	hiddev_fasync(-1, file, 0);
+
+	while (*listptr && (*listptr != list))
+		listptr = &((*listptr)->next);
+	*listptr = (*listptr)->next;
+
+	if (!--list->hiddev->open) {
+		if (list->hiddev->exist) 
+			hid_close(list->hiddev->hid);
+		else
+			kfree(list->hiddev);
+	}
+
+	kfree(list);
+
+	return 0;
+}
+
+/*
+ * open file op
+ */
+static int hiddev_open(struct inode * inode, struct file * file) {
+	struct hiddev_list *list;
+
+	int i = iminor(inode) - HIDDEV_MINOR_BASE;
+
+	if (i >= HIDDEV_MINORS || !hiddev_table[i])
+		return -ENODEV;
+
+	if (!(list = kmalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
+		return -ENOMEM;
+	memset(list, 0, sizeof(struct hiddev_list));
+
+	list->hiddev = hiddev_table[i];
+	list->next = hiddev_table[i]->list;
+	hiddev_table[i]->list = list;
+
+	file->private_data = list;
+
+	if (!list->hiddev->open++)
+		if (list->hiddev->exist)
+			hid_open(hiddev_table[i]->hid);
+
+	return 0;
+}
+
+/*
+ * "write" file op
+ */
+static ssize_t hiddev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
+{
+	return -EINVAL;
+}
+
+/*
+ * "read" file op
+ */
+static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct hiddev_list *list = file->private_data;
+	int event_size;
+	int retval = 0;
+
+	event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ?
+		sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event);
+
+	if (count < event_size)
+		return 0;
+
+	while (retval == 0) {
+		if (list->head == list->tail) {
+			add_wait_queue(&list->hiddev->wait, &wait);
+			set_current_state(TASK_INTERRUPTIBLE);
+			
+			while (list->head == list->tail) {
+				if (file->f_flags & O_NONBLOCK) {
+					retval = -EAGAIN;
+					break;
+				}
+				if (signal_pending(current)) {
+					retval = -ERESTARTSYS;
+					break;
+				}
+				if (!list->hiddev->exist) {
+					retval = -EIO;
+					break;
+				}
+				
+				schedule();
+			}
+
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&list->hiddev->wait, &wait);
+		}
+
+		if (retval)
+			return retval;
+
+
+		while (list->head != list->tail && 
+		       retval + event_size <= count) {
+			if ((list->flags & HIDDEV_FLAG_UREF) == 0) {
+				if (list->buffer[list->tail].field_index !=
+				    HID_FIELD_INDEX_NONE) {
+					struct hiddev_event event;
+					event.hid = list->buffer[list->tail].usage_code;
+					event.value = list->buffer[list->tail].value;
+					if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event)))
+						return -EFAULT;
+					retval += sizeof(struct hiddev_event);
+				}
+			} else {
+				if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE ||
+				    (list->flags & HIDDEV_FLAG_REPORT) != 0) {
+					if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref)))
+						return -EFAULT;
+					retval += sizeof(struct hiddev_usage_ref);
+				}
+			}
+			list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1);
+		}
+
+	}
+
+	return retval;
+}
+
+/*
+ * "poll" file op
+ * No kernel lock - fine
+ */
+static unsigned int hiddev_poll(struct file *file, poll_table *wait)
+{
+	struct hiddev_list *list = file->private_data;
+	poll_wait(file, &list->hiddev->wait, wait);
+	if (list->head != list->tail)
+		return POLLIN | POLLRDNORM;
+	if (!list->hiddev->exist)
+		return POLLERR | POLLHUP;
+	return 0;
+}
+
+/*
+ * "ioctl" file op
+ */
+static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct hiddev_list *list = file->private_data;
+	struct hiddev *hiddev = list->hiddev;
+	struct hid_device *hid = hiddev->hid;
+	struct usb_device *dev = hid->dev;
+	struct hiddev_collection_info cinfo;
+	struct hiddev_report_info rinfo;
+	struct hiddev_field_info finfo;
+	struct hiddev_usage_ref_multi *uref_multi=NULL;
+	struct hiddev_usage_ref *uref;
+	struct hiddev_devinfo dinfo;
+	struct hid_report *report;
+	struct hid_field *field;
+	void __user *user_arg = (void __user *)arg;
+	int i;
+
+	if (!hiddev->exist)
+		return -EIO;
+
+	switch (cmd) {
+
+	case HIDIOCGVERSION:
+		return put_user(HID_VERSION, (int __user *)arg);
+
+	case HIDIOCAPPLICATION:
+		if (arg < 0 || arg >= hid->maxapplication)
+			return -EINVAL;
+
+		for (i = 0; i < hid->maxcollection; i++)
+			if (hid->collection[i].type == 
+			    HID_COLLECTION_APPLICATION && arg-- == 0)
+				break;
+		
+		if (i == hid->maxcollection)
+			return -EINVAL;
+
+		return hid->collection[i].usage;
+
+	case HIDIOCGDEVINFO:
+		dinfo.bustype = BUS_USB;
+		dinfo.busnum = dev->bus->busnum;
+		dinfo.devnum = dev->devnum;
+		dinfo.ifnum = hid->ifnum;
+		dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor);
+		dinfo.product = le16_to_cpu(dev->descriptor.idProduct);
+		dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice);
+		dinfo.num_applications = hid->maxapplication;
+		if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
+			return -EFAULT;
+
+		return 0;
+
+	case HIDIOCGFLAG:
+		if (put_user(list->flags, (int __user *)arg))
+			return -EFAULT;
+
+		return 0;
+
+	case HIDIOCSFLAG:
+		{
+			int newflags;
+			if (get_user(newflags, (int __user *)arg))
+				return -EFAULT;
+
+			if ((newflags & ~HIDDEV_FLAGS) != 0 ||
+			    ((newflags & HIDDEV_FLAG_REPORT) != 0 &&
+			     (newflags & HIDDEV_FLAG_UREF) == 0))
+				return -EINVAL;
+
+			list->flags = newflags;
+
+			return 0;
+		}
+
+	case HIDIOCGSTRING:
+		{
+			int idx, len;
+			char *buf;
+
+			if (get_user(idx, (int __user *)arg))
+				return -EFAULT;
+
+			if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL)
+				return -ENOMEM;
+
+			if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) {
+				kfree(buf);
+				return -EINVAL;
+			}
+
+			if (copy_to_user(user_arg+sizeof(int), buf, len+1)) {
+				kfree(buf);
+				return -EFAULT;
+			}
+
+			kfree(buf);
+
+			return len;
+		}
+
+	case HIDIOCINITREPORT:
+		hid_init_reports(hid);
+
+		return 0;
+
+	case HIDIOCGREPORT:
+		if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
+			return -EFAULT;
+
+		if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT)
+			return -EINVAL;
+
+		if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+			return -EINVAL;
+
+		hid_submit_report(hid, report, USB_DIR_IN);
+		hid_wait_io(hid);
+
+		return 0;
+
+	case HIDIOCSREPORT:
+		if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
+			return -EFAULT;
+
+		if (rinfo.report_type == HID_REPORT_TYPE_INPUT)
+			return -EINVAL;
+
+		if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+			return -EINVAL;
+
+		hid_submit_report(hid, report, USB_DIR_OUT);
+
+		return 0;
+
+	case HIDIOCGREPORTINFO:
+		if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
+			return -EFAULT;
+
+		if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+			return -EINVAL;
+
+		rinfo.num_fields = report->maxfield;
+
+		if (copy_to_user(user_arg, &rinfo, sizeof(rinfo)))
+			return -EFAULT;
+
+		return 0;
+
+	case HIDIOCGFIELDINFO:
+		if (copy_from_user(&finfo, user_arg, sizeof(finfo)))
+			return -EFAULT;
+		rinfo.report_type = finfo.report_type;
+		rinfo.report_id = finfo.report_id;
+		if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+			return -EINVAL;
+
+		if (finfo.field_index >= report->maxfield)
+			return -EINVAL;
+
+		field = report->field[finfo.field_index];
+		memset(&finfo, 0, sizeof(finfo));
+		finfo.report_type = rinfo.report_type;
+		finfo.report_id = rinfo.report_id;
+		finfo.field_index = field->report_count - 1;
+		finfo.maxusage = field->maxusage;
+		finfo.flags = field->flags;
+		finfo.physical = field->physical;
+		finfo.logical = field->logical;
+		finfo.application = field->application;
+		finfo.logical_minimum = field->logical_minimum;
+		finfo.logical_maximum = field->logical_maximum;
+		finfo.physical_minimum = field->physical_minimum;
+		finfo.physical_maximum = field->physical_maximum;
+		finfo.unit_exponent = field->unit_exponent;
+		finfo.unit = field->unit;
+
+		if (copy_to_user(user_arg, &finfo, sizeof(finfo)))
+			return -EFAULT;
+
+		return 0;
+
+	case HIDIOCGUCODE:
+		uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
+		if (!uref_multi)
+			return -ENOMEM;
+		uref = &uref_multi->uref;
+		if (copy_from_user(uref, user_arg, sizeof(*uref))) 
+			goto fault;
+
+		rinfo.report_type = uref->report_type;
+		rinfo.report_id = uref->report_id;
+		if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+			goto inval;
+
+		if (uref->field_index >= report->maxfield)
+			goto inval;
+
+		field = report->field[uref->field_index];
+		if (uref->usage_index >= field->maxusage)
+			goto inval;
+
+		uref->usage_code = field->usage[uref->usage_index].hid;
+
+		if (copy_to_user(user_arg, uref, sizeof(*uref)))
+			goto fault;
+
+		kfree(uref_multi);
+		return 0;
+
+	case HIDIOCGUSAGE:
+	case HIDIOCSUSAGE:
+	case HIDIOCGUSAGES:
+	case HIDIOCSUSAGES:
+	case HIDIOCGCOLLECTIONINDEX:
+		uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
+		if (!uref_multi)
+			return -ENOMEM;
+		uref = &uref_multi->uref;
+		if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
+			if (copy_from_user(uref_multi, user_arg, 
+					   sizeof(*uref_multi)))
+				goto fault;
+		} else {
+			if (copy_from_user(uref, user_arg, sizeof(*uref)))
+				goto fault;
+		}
+
+		if (cmd != HIDIOCGUSAGE && 
+		    cmd != HIDIOCGUSAGES &&
+		    uref->report_type == HID_REPORT_TYPE_INPUT)
+			goto inval;
+
+		if (uref->report_id == HID_REPORT_ID_UNKNOWN) {
+			field = hiddev_lookup_usage(hid, uref);
+			if (field == NULL)
+				goto inval;
+		} else {
+			rinfo.report_type = uref->report_type;
+			rinfo.report_id = uref->report_id;
+			if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
+				goto inval;
+
+			if (uref->field_index >= report->maxfield)
+				goto inval;
+
+			field = report->field[uref->field_index];
+
+			if (cmd == HIDIOCGCOLLECTIONINDEX) {
+				if (uref->usage_index >= field->maxusage)
+					goto inval;
+			} else if (uref->usage_index >= field->report_count)
+				goto inval;
+
+			else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) &&
+				 (uref_multi->num_values > HID_MAX_MULTI_USAGES ||
+				  uref->usage_index + uref_multi->num_values >= field->report_count))
+				goto inval;
+			}
+
+		switch (cmd) {
+			case HIDIOCGUSAGE:
+				uref->value = field->value[uref->usage_index];
+				if (copy_to_user(user_arg, uref, sizeof(*uref)))
+					goto fault;
+				goto goodreturn;
+
+			case HIDIOCSUSAGE:
+				field->value[uref->usage_index] = uref->value;
+				goto goodreturn;
+
+			case HIDIOCGCOLLECTIONINDEX:
+				kfree(uref_multi);
+				return field->usage[uref->usage_index].collection_index;
+			case HIDIOCGUSAGES:
+				for (i = 0; i < uref_multi->num_values; i++)
+					uref_multi->values[i] = 
+					    field->value[uref->usage_index + i];
+				if (copy_to_user(user_arg, uref_multi, 
+						 sizeof(*uref_multi)))
+					goto fault;
+				goto goodreturn;
+			case HIDIOCSUSAGES:
+				for (i = 0; i < uref_multi->num_values; i++)
+					field->value[uref->usage_index + i] = 
+				  	    uref_multi->values[i];
+				goto goodreturn;
+		}
+
+goodreturn:
+		kfree(uref_multi);
+		return 0;
+fault:
+		kfree(uref_multi);
+		return -EFAULT;
+inval:		
+		kfree(uref_multi);
+		return -EINVAL;
+
+	case HIDIOCGCOLLECTIONINFO:
+		if (copy_from_user(&cinfo, user_arg, sizeof(cinfo)))
+			return -EFAULT;
+
+		if (cinfo.index >= hid->maxcollection)
+			return -EINVAL;
+
+		cinfo.type = hid->collection[cinfo.index].type;
+		cinfo.usage = hid->collection[cinfo.index].usage;
+		cinfo.level = hid->collection[cinfo.index].level;
+
+		if (copy_to_user(user_arg, &cinfo, sizeof(cinfo)))
+			return -EFAULT;
+		return 0;
+
+	default:
+
+		if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ)
+			return -EINVAL;
+
+		if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) {
+			int len;
+			if (!hid->name)
+				return 0;
+			len = strlen(hid->name) + 1;
+			if (len > _IOC_SIZE(cmd))
+				 len = _IOC_SIZE(cmd);
+			return copy_to_user(user_arg, hid->name, len) ?
+				-EFAULT : len;
+		}
+
+		if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) {
+			int len;
+			if (!hid->phys)
+				return 0;
+			len = strlen(hid->phys) + 1;
+			if (len > _IOC_SIZE(cmd))
+				len = _IOC_SIZE(cmd);
+			return copy_to_user(user_arg, hid->phys, len) ?
+				-EFAULT : len;
+		}
+	}
+	return -EINVAL;
+}
+
+static struct file_operations hiddev_fops = {
+	.owner =	THIS_MODULE,
+	.read =		hiddev_read,
+	.write =	hiddev_write,
+	.poll =		hiddev_poll,
+	.open =		hiddev_open,
+	.release =	hiddev_release,
+	.ioctl =	hiddev_ioctl,
+	.fasync =	hiddev_fasync,
+};
+
+static struct usb_class_driver hiddev_class = {
+	.name =		"usb/hid/hiddev%d",
+	.fops =		&hiddev_fops,
+	.mode =		S_IFCHR | S_IRUGO | S_IWUSR,
+       	.minor_base =	HIDDEV_MINOR_BASE,
+};
+
+/*
+ * This is where hid.c calls us to connect a hid device to the hiddev driver
+ */
+int hiddev_connect(struct hid_device *hid)
+{
+	struct hiddev *hiddev;
+	int i;
+	int retval;
+
+	for (i = 0; i < hid->maxcollection; i++)
+		if (hid->collection[i].type == 
+		    HID_COLLECTION_APPLICATION &&
+		    !IS_INPUT_APPLICATION(hid->collection[i].usage))
+			break;
+
+	if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0)
+		return -1;
+
+ 	if (!(hiddev = kmalloc(sizeof(struct hiddev), GFP_KERNEL)))
+		return -1;
+	memset(hiddev, 0, sizeof(struct hiddev));
+
+ 	retval = usb_register_dev(hid->intf, &hiddev_class);
+	if (retval) {
+		err("Not able to get a minor for this device.");
+		kfree(hiddev);
+		return -1;
+	}
+
+	init_waitqueue_head(&hiddev->wait);
+
+ 	hiddev_table[hid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
+
+	hiddev->hid = hid;
+	hiddev->exist = 1;
+
+ 	hid->minor = hid->intf->minor;
+	hid->hiddev = hiddev;
+
+	return 0;
+}
+
+/*
+ * This is where hid.c calls us to disconnect a hiddev device from the
+ * corresponding hid device (usually because the usb device has disconnected)
+ */
+static struct usb_class_driver hiddev_class;
+void hiddev_disconnect(struct hid_device *hid)
+{
+	struct hiddev *hiddev = hid->hiddev;
+
+	hiddev->exist = 0;
+
+	hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL;
+	usb_deregister_dev(hiddev->hid->intf, &hiddev_class);
+
+	if (hiddev->open) {
+		hid_close(hiddev->hid);
+		wake_up_interruptible(&hiddev->wait);
+	} else {
+		kfree(hiddev);
+	}
+}
+
+/* Currently this driver is a USB driver.  It's not a conventional one in
+ * the sense that it doesn't probe at the USB level.  Instead it waits to
+ * be connected by HID through the hiddev_connect / hiddev_disconnect
+ * routines.  The reason to register as a USB device is to gain part of the
+ * minor number space from the USB major.
+ *
+ * In theory, should the HID code be generalized to more than one physical
+ * medium (say, IEEE 1384), this driver will probably need to register its
+ * own major number, and in doing so, no longer need to register with USB.
+ * At that point the probe routine and hiddev_driver struct below will no
+ * longer be useful.
+ */
+
+
+/* We never attach in this manner, and rely on HID to connect us.  This
+ * is why there is no disconnect routine defined in the usb_driver either.
+ */
+static int hiddev_usbd_probe(struct usb_interface *intf, 
+			     const struct usb_device_id *hiddev_info)
+{
+	return -ENODEV;
+}
+
+
+static /* const */ struct usb_driver hiddev_driver = {
+	.owner =	THIS_MODULE,
+	.name =		"hiddev",
+	.probe =	hiddev_usbd_probe,
+};
+
+int __init hiddev_init(void)
+{
+	devfs_mk_dir("usb/hid");
+	return usb_register(&hiddev_driver);
+}
+
+void hiddev_exit(void)
+{
+	usb_deregister(&hiddev_driver);
+	devfs_remove("usb/hid");
+}
diff --git a/drivers/usb/input/kbtab.c b/drivers/usb/input/kbtab.c
new file mode 100644
index 0000000..a68c5b4
--- /dev/null
+++ b/drivers/usb/input/kbtab.c
@@ -0,0 +1,241 @@
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+/*
+ * Version Information
+ * v0.0.1 - Original, extremely basic version, 2.4.xx only
+ * v0.0.2 - Updated, works with 2.5.62 and 2.4.20;
+ *           - added pressure-threshold modules param code from
+ *              Alex Perry <alex.perry@ieee.org>
+ */
+
+#define DRIVER_VERSION "v0.0.2"
+#define DRIVER_AUTHOR "Josh Myer <josh@joshisanerd.com>"
+#define DRIVER_DESC "USB KB Gear JamStudio Tablet driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_KBGEAR	0x084e
+
+static int kb_pressure_click = 0x10;
+module_param(kb_pressure_click, int, 0);
+MODULE_PARM_DESC(kb_pressure_click, "pressure threshold for clicks");
+
+struct kbtab {
+	signed char *data;
+	dma_addr_t data_dma;
+	struct input_dev dev;
+	struct usb_device *usbdev;
+	struct urb *irq;
+	int open;
+	int x, y;
+	int button;
+	int pressure;
+	__u32 serial[2];
+	char phys[32];
+};
+
+static void kbtab_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct kbtab *kbtab = urb->context;
+	unsigned char *data = kbtab->data;
+	struct input_dev *dev = &kbtab->dev;
+	int retval;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+		goto exit;
+	}
+
+	kbtab->x = le16_to_cpu(get_unaligned((__le16 *) &data[1]));
+	kbtab->y = le16_to_cpu(get_unaligned((__le16 *) &data[3]));
+
+	kbtab->pressure = (data[5]);
+
+	input_report_key(dev, BTN_TOOL_PEN, 1);
+
+	input_report_abs(dev, ABS_X, kbtab->x);
+	input_report_abs(dev, ABS_Y, kbtab->y);
+
+	/*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/
+	input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
+
+	if( -1 == kb_pressure_click){ 
+		input_report_abs(dev, ABS_PRESSURE, kbtab->pressure);
+	} else {
+		input_report_key(dev, BTN_LEFT, (kbtab->pressure > kb_pressure_click) ? 1 : 0);
+	};
+	
+	input_sync(dev);
+
+ exit:
+	retval = usb_submit_urb (urb, GFP_ATOMIC);
+	if (retval)
+		err ("%s - usb_submit_urb failed with result %d",
+		     __FUNCTION__, retval);
+}
+
+static struct usb_device_id kbtab_ids[] = {
+	{ USB_DEVICE(USB_VENDOR_ID_KBGEAR, 0x1001), .driver_info = 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, kbtab_ids);
+
+static int kbtab_open(struct input_dev *dev)
+{
+	struct kbtab *kbtab = dev->private;
+
+	if (kbtab->open++)
+		return 0;
+
+	kbtab->irq->dev = kbtab->usbdev;
+	if (usb_submit_urb(kbtab->irq, GFP_KERNEL)) {
+		kbtab->open--;
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void kbtab_close(struct input_dev *dev)
+{
+	struct kbtab *kbtab = dev->private;
+
+	if (!--kbtab->open)
+		usb_kill_urb(kbtab->irq);
+}
+
+static int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_endpoint_descriptor *endpoint;
+	struct kbtab *kbtab;
+	char path[64];
+
+	if (!(kbtab = kmalloc(sizeof(struct kbtab), GFP_KERNEL)))
+		return -ENOMEM;
+	memset(kbtab, 0, sizeof(struct kbtab));
+
+	kbtab->data = usb_buffer_alloc(dev, 8, GFP_KERNEL, &kbtab->data_dma);
+	if (!kbtab->data) {
+		kfree(kbtab);
+		return -ENOMEM;
+	}
+
+	kbtab->irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (!kbtab->irq) {
+		usb_buffer_free(dev, 10, kbtab->data, kbtab->data_dma);
+		kfree(kbtab);
+		return -ENOMEM;
+	}
+
+	kbtab->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC);
+	kbtab->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
+
+	kbtab->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+
+	kbtab->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH);
+
+	kbtab->dev.mscbit[0] |= BIT(MSC_SERIAL);
+
+	kbtab->dev.absmax[ABS_X] = 0x2000;
+	kbtab->dev.absmax[ABS_Y] = 0x1750;
+	kbtab->dev.absmax[ABS_PRESSURE] = 0xff;
+	
+	kbtab->dev.absfuzz[ABS_X] = 4;
+	kbtab->dev.absfuzz[ABS_Y] = 4;
+
+	kbtab->dev.private = kbtab;
+	kbtab->dev.open = kbtab_open;
+	kbtab->dev.close = kbtab_close;
+
+	usb_make_path(dev, path, 64);
+	sprintf(kbtab->phys, "%s/input0", path);
+
+	kbtab->dev.name = "KB Gear Tablet";
+	kbtab->dev.phys = kbtab->phys;
+	kbtab->dev.id.bustype = BUS_USB;
+	kbtab->dev.id.vendor = le16_to_cpu(dev->descriptor.idVendor);
+	kbtab->dev.id.product = le16_to_cpu(dev->descriptor.idProduct);
+	kbtab->dev.id.version = le16_to_cpu(dev->descriptor.bcdDevice);
+	kbtab->dev.dev = &intf->dev;
+	kbtab->usbdev = dev;
+
+	endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+	usb_fill_int_urb(kbtab->irq, dev,
+			 usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+			 kbtab->data, 8,
+			 kbtab_irq, kbtab, endpoint->bInterval);
+	kbtab->irq->transfer_dma = kbtab->data_dma;
+	kbtab->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	input_register_device(&kbtab->dev);
+
+	printk(KERN_INFO "input: KB Gear Tablet on %s\n",  path);
+
+	usb_set_intfdata(intf, kbtab);
+
+	return 0;
+}
+
+static void kbtab_disconnect(struct usb_interface *intf)
+{
+	struct kbtab *kbtab = usb_get_intfdata (intf);
+
+	usb_set_intfdata(intf, NULL);
+	if (kbtab) {
+		usb_kill_urb(kbtab->irq);
+		input_unregister_device(&kbtab->dev);
+		usb_free_urb(kbtab->irq);
+		usb_buffer_free(interface_to_usbdev(intf), 10, kbtab->data, kbtab->data_dma);
+		kfree(kbtab);
+	}
+}
+
+static struct usb_driver kbtab_driver = {
+	.owner =	THIS_MODULE,
+	.name =		"kbtab",
+	.probe =	kbtab_probe,
+	.disconnect =	kbtab_disconnect,
+	.id_table =	kbtab_ids,
+};
+
+static int __init kbtab_init(void)
+{
+	int retval;
+	retval = usb_register(&kbtab_driver);
+	if (retval)
+		goto out;
+	info(DRIVER_VERSION ":" DRIVER_DESC);
+out:
+	return retval;
+}
+
+static void __exit kbtab_exit(void)
+{
+	usb_deregister(&kbtab_driver);
+}
+
+module_init(kbtab_init);
+module_exit(kbtab_exit);
diff --git a/drivers/usb/input/mtouchusb.c b/drivers/usb/input/mtouchusb.c
new file mode 100644
index 0000000..6b45a66
--- /dev/null
+++ b/drivers/usb/input/mtouchusb.c
@@ -0,0 +1,367 @@
+/******************************************************************************
+ * mtouchusb.c  --  Driver for Microtouch (Now 3M) USB Touchscreens
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Based upon original work by Radoslaw Garbacz (usb-support@ite.pl)
+ *  (http://freshmeat.net/projects/3mtouchscreendriver)
+ *
+ * History
+ *
+ *  0.3 & 0.4  2002 (TEJ) tejohnson@yahoo.com
+ *    Updated to 2.4.18, then 2.4.19
+ *    Old version still relied on stealing a minor
+ *
+ *  0.5  02/26/2004 (TEJ) tejohnson@yahoo.com
+ *    Complete rewrite using Linux Input in 2.6.3
+ *    Unfortunately no calibration support at this time
+ *
+ *  1.4 04/25/2004 (TEJ) tejohnson@yahoo.com
+ *    Changed reset from standard USB dev reset to vendor reset
+ *    Changed data sent to host from compensated to raw coordinates
+ *    Eliminated vendor/product module params
+ *    Performed multiple successfull tests with an EXII-5010UC
+ *
+ *  1.5 02/27/2005 ddstreet@ieee.org
+ *    Added module parameter to select raw or hw-calibrated coordinate reporting
+ *
+ *****************************************************************************/
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+        #define DEBUG
+#else
+        #undef DEBUG
+#endif
+
+#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 MTOUCHUSB_MIN_XC                0x0
+#define MTOUCHUSB_MAX_RAW_XC            0x4000
+#define MTOUCHUSB_MAX_CALIB_XC          0xffff
+#define MTOUCHUSB_XC_FUZZ               0x0
+#define MTOUCHUSB_XC_FLAT               0x0
+#define MTOUCHUSB_MIN_YC                0x0
+#define MTOUCHUSB_MAX_RAW_YC            0x4000
+#define MTOUCHUSB_MAX_CALIB_YC          0xffff
+#define MTOUCHUSB_YC_FUZZ               0x0
+#define MTOUCHUSB_YC_FLAT               0x0
+
+#define MTOUCHUSB_ASYNC_REPORT          1
+#define MTOUCHUSB_RESET                 7
+#define MTOUCHUSB_REPORT_DATA_SIZE      11
+#define MTOUCHUSB_REQ_CTRLLR_ID         10
+
+#define MTOUCHUSB_GET_RAW_XC(data)      (data[8]<<8 | data[7])
+#define MTOUCHUSB_GET_CALIB_XC(data)    (data[4]<<8 | data[3])
+#define MTOUCHUSB_GET_RAW_YC(data)      (data[10]<<8 | data[9])
+#define MTOUCHUSB_GET_CALIB_YC(data)    (data[6]<<8 | data[5])
+#define MTOUCHUSB_GET_XC(data)          (raw_coordinates ? \
+                                         MTOUCHUSB_GET_RAW_XC(data) : \
+                                         MTOUCHUSB_GET_CALIB_XC(data))
+#define MTOUCHUSB_GET_YC(data)          (raw_coordinates ? \
+                                         MTOUCHUSB_GET_RAW_YC(data) : \
+                                         MTOUCHUSB_GET_CALIB_YC(data))
+#define MTOUCHUSB_GET_TOUCHED(data)     ((data[2] & 0x40) ? 1:0)
+
+#define DRIVER_VERSION "v1.5"
+#define DRIVER_AUTHOR "Todd E. Johnson, tejohnson@yahoo.com"
+#define DRIVER_DESC "3M USB Touchscreen Driver"
+#define DRIVER_LICENSE "GPL"
+
+static int raw_coordinates = 1;
+
+module_param(raw_coordinates, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(raw_coordinates, "report raw coordinate values (y, default) or hardware-calibrated coordinate values (n)");
+
+struct mtouch_usb {
+        unsigned char *data;
+        dma_addr_t data_dma;
+        struct urb *irq;
+        struct usb_device *udev;
+        struct input_dev input;
+        int open;
+        char name[128];
+        char phys[64];
+};
+
+static struct usb_device_id mtouchusb_devices [] = {
+        { USB_DEVICE(0x0596, 0x0001) },
+        { }
+};
+
+static void mtouchusb_irq(struct urb *urb, struct pt_regs *regs)
+{
+        struct mtouch_usb *mtouch = urb->context;
+        int retval;
+
+        switch (urb->status) {
+                case 0:
+                        /* success */
+                        break;
+                case -ETIMEDOUT:
+                        /* this urb is timing out */
+                        dbg("%s - urb timed out - was the device unplugged?",
+                            __FUNCTION__);
+                        return;
+                case -ECONNRESET:
+                case -ENOENT:
+                case -ESHUTDOWN:
+                        /* this urb is terminated, clean up */
+                        dbg("%s - urb shutting down with status: %d",
+                            __FUNCTION__, urb->status);
+                        return;
+                default:
+                        dbg("%s - nonzero urb status received: %d",
+                            __FUNCTION__, urb->status);
+                        goto exit;
+        }
+
+        input_regs(&mtouch->input, regs);
+        input_report_key(&mtouch->input, BTN_TOUCH,
+                         MTOUCHUSB_GET_TOUCHED(mtouch->data));
+        input_report_abs(&mtouch->input, ABS_X,
+                         MTOUCHUSB_GET_XC(mtouch->data));
+        input_report_abs(&mtouch->input, ABS_Y,
+			 (raw_coordinates ? MTOUCHUSB_MAX_RAW_YC : MTOUCHUSB_MAX_CALIB_YC)
+                         - MTOUCHUSB_GET_YC(mtouch->data));
+        input_sync(&mtouch->input);
+
+exit:
+        retval = usb_submit_urb (urb, GFP_ATOMIC);
+        if (retval)
+                err ("%s - usb_submit_urb failed with result: %d",
+                     __FUNCTION__, retval);
+}
+
+static int mtouchusb_open (struct input_dev *input)
+{
+        struct mtouch_usb *mtouch = input->private;
+
+        if (mtouch->open++)
+                return 0;
+
+        mtouch->irq->dev = mtouch->udev;
+
+        if (usb_submit_urb (mtouch->irq, GFP_ATOMIC)) {
+                mtouch->open--;
+                return -EIO;
+        }
+
+        return 0;
+}
+
+static void mtouchusb_close (struct input_dev *input)
+{
+        struct mtouch_usb *mtouch = input->private;
+
+        if (!--mtouch->open)
+                usb_kill_urb (mtouch->irq);
+}
+
+static int mtouchusb_alloc_buffers(struct usb_device *udev, struct mtouch_usb *mtouch)
+{
+        dbg("%s - called", __FUNCTION__);
+
+        mtouch->data = usb_buffer_alloc(udev, MTOUCHUSB_REPORT_DATA_SIZE,
+                                        SLAB_ATOMIC, &mtouch->data_dma);
+
+        if (!mtouch->data)
+                return -1;
+
+        return 0;
+}
+
+static void mtouchusb_free_buffers(struct usb_device *udev, struct mtouch_usb *mtouch)
+{
+        dbg("%s - called", __FUNCTION__);
+
+        if (mtouch->data)
+                usb_buffer_free(udev, MTOUCHUSB_REPORT_DATA_SIZE,
+                                mtouch->data, mtouch->data_dma);
+}
+
+static int mtouchusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+        struct mtouch_usb *mtouch;
+        struct usb_host_interface *interface;
+        struct usb_endpoint_descriptor *endpoint;
+        struct usb_device *udev = interface_to_usbdev (intf);
+        char path[64];
+        int nRet;
+
+        dbg("%s - called", __FUNCTION__);
+
+        dbg("%s - setting interface", __FUNCTION__);
+        interface = intf->cur_altsetting;
+
+        dbg("%s - setting endpoint", __FUNCTION__);
+        endpoint = &interface->endpoint[0].desc;
+
+        if (!(mtouch = kmalloc (sizeof (struct mtouch_usb), GFP_KERNEL))) {
+                err("%s - Out of memory.", __FUNCTION__);
+                return -ENOMEM;
+        }
+
+        memset(mtouch, 0, sizeof(struct mtouch_usb));
+        mtouch->udev = udev;
+
+        dbg("%s - allocating buffers", __FUNCTION__);
+        if (mtouchusb_alloc_buffers(udev, mtouch)) {
+                mtouchusb_free_buffers(udev, mtouch);
+                kfree(mtouch);
+                return -ENOMEM;
+        }
+
+        mtouch->input.private = mtouch;
+        mtouch->input.open = mtouchusb_open;
+        mtouch->input.close = mtouchusb_close;
+
+        usb_make_path(udev, path, 64);
+        sprintf(mtouch->phys, "%s/input0", path);
+
+        mtouch->input.name = mtouch->name;
+        mtouch->input.phys = mtouch->phys;
+        mtouch->input.id.bustype = BUS_USB;
+        mtouch->input.id.vendor = le16_to_cpu(udev->descriptor.idVendor);
+        mtouch->input.id.product = le16_to_cpu(udev->descriptor.idProduct);
+        mtouch->input.id.version = le16_to_cpu(udev->descriptor.bcdDevice);
+        mtouch->input.dev = &intf->dev;
+
+        mtouch->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+        mtouch->input.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+        mtouch->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+
+        /* Used to Scale Compensated Data and Flip Y */
+        mtouch->input.absmin[ABS_X] =  MTOUCHUSB_MIN_XC;
+        mtouch->input.absmax[ABS_X] =  raw_coordinates ? \
+                                       MTOUCHUSB_MAX_RAW_XC : MTOUCHUSB_MAX_CALIB_XC;
+        mtouch->input.absfuzz[ABS_X] = MTOUCHUSB_XC_FUZZ;
+        mtouch->input.absflat[ABS_X] = MTOUCHUSB_XC_FLAT;
+        mtouch->input.absmin[ABS_Y] =  MTOUCHUSB_MIN_YC;
+        mtouch->input.absmax[ABS_Y] =  raw_coordinates ? \
+                                       MTOUCHUSB_MAX_RAW_YC : MTOUCHUSB_MAX_CALIB_YC;
+        mtouch->input.absfuzz[ABS_Y] = MTOUCHUSB_YC_FUZZ;
+        mtouch->input.absflat[ABS_Y] = MTOUCHUSB_YC_FLAT;
+
+	if (udev->manufacturer)
+		strcat(mtouch->name, udev->manufacturer);
+	if (udev->product)
+		sprintf(mtouch->name, "%s %s", mtouch->name, udev->product);
+
+        if (!strlen(mtouch->name))
+                sprintf(mtouch->name, "USB Touchscreen %04x:%04x",
+                        mtouch->input.id.vendor, mtouch->input.id.product);
+
+        nRet = usb_control_msg(mtouch->udev,
+                               usb_rcvctrlpipe(udev, 0),
+                               MTOUCHUSB_RESET,
+                               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                               1,
+                               0,
+                               NULL,
+                               0,
+                               USB_CTRL_SET_TIMEOUT);
+        dbg("%s - usb_control_msg - MTOUCHUSB_RESET - bytes|err: %d",
+            __FUNCTION__, nRet);
+
+        dbg("%s - usb_alloc_urb: mtouch->irq", __FUNCTION__);
+        mtouch->irq = usb_alloc_urb(0, GFP_KERNEL);
+        if (!mtouch->irq) {
+                dbg("%s - usb_alloc_urb failed: mtouch->irq", __FUNCTION__);
+                mtouchusb_free_buffers(udev, mtouch);
+                kfree(mtouch);
+                return -ENOMEM;
+        }
+
+        dbg("%s - usb_fill_int_urb", __FUNCTION__);
+        usb_fill_int_urb(mtouch->irq,
+                         mtouch->udev,
+                         usb_rcvintpipe(mtouch->udev, 0x81),
+                         mtouch->data,
+                         MTOUCHUSB_REPORT_DATA_SIZE,
+                         mtouchusb_irq,
+                         mtouch,
+                         endpoint->bInterval);
+
+        dbg("%s - input_register_device", __FUNCTION__);
+        input_register_device(&mtouch->input);
+
+        nRet = usb_control_msg(mtouch->udev,
+                               usb_rcvctrlpipe(udev, 0),
+                               MTOUCHUSB_ASYNC_REPORT,
+                               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                               1,
+                               1,
+                               NULL,
+                               0,
+                               USB_CTRL_SET_TIMEOUT);
+        dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d",
+            __FUNCTION__, nRet);
+
+        printk(KERN_INFO "input: %s on %s\n", mtouch->name, path);
+        usb_set_intfdata(intf, mtouch);
+
+        return 0;
+}
+
+static void mtouchusb_disconnect(struct usb_interface *intf)
+{
+        struct mtouch_usb *mtouch = usb_get_intfdata (intf);
+
+        dbg("%s - called", __FUNCTION__);
+        usb_set_intfdata(intf, NULL);
+        if (mtouch) {
+                dbg("%s - mtouch is initialized, cleaning up", __FUNCTION__);
+                usb_kill_urb(mtouch->irq);
+                input_unregister_device(&mtouch->input);
+                usb_free_urb(mtouch->irq);
+                mtouchusb_free_buffers(interface_to_usbdev(intf), mtouch);
+                kfree(mtouch);
+        }
+}
+
+MODULE_DEVICE_TABLE (usb, mtouchusb_devices);
+
+static struct usb_driver mtouchusb_driver = {
+        .owner =      THIS_MODULE,
+        .name =       "mtouchusb",
+        .probe =      mtouchusb_probe,
+        .disconnect = mtouchusb_disconnect,
+        .id_table =   mtouchusb_devices,
+};
+
+static int __init mtouchusb_init(void) {
+        dbg("%s - called", __FUNCTION__);
+        return usb_register(&mtouchusb_driver);
+}
+
+static void __exit mtouchusb_cleanup(void) {
+        dbg("%s - called", __FUNCTION__);
+        usb_deregister(&mtouchusb_driver);
+}
+
+module_init(mtouchusb_init);
+module_exit(mtouchusb_cleanup);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/input/pid.c b/drivers/usb/input/pid.c
new file mode 100644
index 0000000..2569638
--- /dev/null
+++ b/drivers/usb/input/pid.c
@@ -0,0 +1,295 @@
+/*
+ *  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
+
+#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, struct pt_regs *regs)
+{
+	dev_dbg(&u->dev->dev, "hid_pid_ctrl_out - Transfer Completed\n");
+}
+
+static void hid_pid_exit(struct hid_device *hid)
+{
+	struct hid_ff_pid *private = hid->ff_private;
+
+	if (private->urbffout) {
+		usb_kill_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)
+{
+	dev_info(&pid->hid->dev->dev, "requested periodic force upload\n");
+	return 0;
+}
+
+static int pid_upload_constant(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update)
+{
+	dev_info(&pid->hid->dev->dev, "requested constant force upload\n");
+	return 0;
+}
+
+static int pid_upload_condition(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update)
+{
+	dev_info(&pid->hid->dev->dev, "requested Condition force upload\n");
+	return 0;
+}
+
+static int pid_upload_ramp(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update)
+{
+	dev_info(&pid->hid->dev->dev, "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)
+{
+	dev_dbg(&hid->dev->dev, "PID event received: type=%d,code=%d,value=%d.\n", type, code, value);
+
+	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_ff_pid *pid = hid->ff_private;
+	struct hid_field *field;
+	unsigned long flags;
+	int ret;
+
+	if (!CHECK_OWNERSHIP(id, pid))
+		return -EACCES;
+
+	/* Find report */
+	field = hid_find_field_by_usage(hid, HID_UP_PID | FF_PID_USAGE_BLOCK_FREE,
+					HID_OUTPUT_REPORT);
+	if (!field) {
+		dev_err(&hid->dev->dev, "couldn't find report\n");
+		return -EIO;
+	}
+
+	ret = hid_set_field(field, 0, pid->effects[id].device_id);
+	if (ret) {
+		dev_err(&hid->dev->dev, "couldn't set field\n");
+		return ret;
+	}
+
+	hid_submit_report(hid, field->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 0;
+}
+
+/* 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 */
+	/*FIXME: multiple threads, anyone? */
+	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))
+				dev_warn(&hid->dev->dev, "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;
+	unsigned long flags;
+
+	dev_dbg(&pid_private->hid->dev->dev, "upload effect called: effect_type=%x\n", effect->type);
+	/* Check this effect type is supported by this device */
+	if (!test_bit(effect->type, dev->ffbit)) {
+		dev_dbg(&pid_private->hid->dev->dev,
+			"invalid kind of effect requested.\n");
+		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) {
+			spin_unlock_irqrestore(&pid_private->lock, flags);
+// TEMP - We need to get ff_effects_max correctly first:  || id >= dev->ff_effects_max) {
+			dev_dbg(&pid_private->hid->dev->dev, "Not enough device memory\n");
+			return -ENOMEM;
+		}
+
+		effect->id = id;
+		dev_dbg(&pid_private->hid->dev->dev, "effect ID is %d\n.", id);
+		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:
+		dev_dbg(&pid_private->hid->dev->dev,
+			"invalid type of effect requested - %x.\n",
+			effect->type);
+		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;
+	struct hid_input *hidinput = list_entry(&hid->inputs, struct hid_input, list);
+
+	private = hid->ff_private = kcalloc(1, sizeof(struct hid_ff_pid), GFP_KERNEL);
+	if (!private)
+		return -ENOMEM;
+
+	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);
+	hidinput->input.upload_effect = hid_pid_upload_effect;
+	hidinput->input.flush = hid_pid_flush;
+	hidinput->input.ff_effects_max = 8;	// A random default
+	set_bit(EV_FF, hidinput->input.evbit);
+	set_bit(EV_FF_STATUS, hidinput->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;
+}
diff --git a/drivers/usb/input/pid.h b/drivers/usb/input/pid.h
new file mode 100644
index 0000000..a2cb962
--- /dev/null
+++ b/drivers/usb/input/pid.h
@@ -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 gain;
+
+	struct urb *urbffout;
+	struct usb_ctrlrequest ffcr;
+	spinlock_t lock;
+
+	unsigned 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 --git a/drivers/usb/input/powermate.c b/drivers/usb/input/powermate.c
new file mode 100644
index 0000000..7fa2f9b
--- /dev/null
+++ b/drivers/usb/input/powermate.c
@@ -0,0 +1,464 @@
+/*
+ * A driver for the Griffin Technology, Inc. "PowerMate" USB controller dial.
+ *
+ * v1.1, (c)2002 William R Sowerbutts <will@sowerbutts.com>
+ *
+ * This device is a anodised aluminium 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. Griffin say that it loses a
+ * pulse or two on a direction change; the granularity is so fine that I never
+ * noticed this in practice.
+ *
+ * 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.
+ *
+ * Some userspace tools are available on the web: http://sowerbutts.com/powermate/
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+
+#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)
+
+/* at least two versions of the hardware exist, with differing payload
+   sizes. the first three bytes always contain the "interesting" data in
+   the relevant format. */
+#define POWERMATE_PAYLOAD_SIZE_MAX 6
+#define POWERMATE_PAYLOAD_SIZE_MIN 3
+struct powermate_device {
+	signed char *data;
+	dma_addr_t data_dma;
+	struct urb *irq, *config;
+	struct usb_ctrlrequest *configcr;
+	dma_addr_t configcr_dma;
+	struct usb_device *udev;
+	struct input_dev input;
+	spinlock_t 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, struct pt_regs *regs);
+
+/* Callback for data arriving from the PowerMate over the USB interrupt pipe */
+static void powermate_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct powermate_device *pm = urb->context;
+	int retval;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+		goto exit;
+	}
+
+	/* handle updates to device state */
+	input_regs(&pm->input, regs);
+	input_report_key(&pm->input, BTN_0, pm->data[0] & 0x01);
+	input_report_rel(&pm->input, REL_DIAL, pm->data[1]);
+	input_sync(&pm->input);
+
+exit:
+	retval = usb_submit_urb (urb, GFP_ATOMIC);
+	if (retval)
+		err ("%s - usb_submit_urb failed with result %d",
+		     __FUNCTION__, retval);
+}
+
+/* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */
+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_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 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{
+		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->configcr->bRequestType = 0x41; /* vendor request */
+	pm->configcr->bRequest = 0x01;
+	pm->configcr->wLength = 0;
+
+	usb_fill_control_urb(pm->config, pm->udev, usb_sndctrlpipe(pm->udev, 0),
+			     (void *) pm->configcr, NULL, 0,
+			     powermate_config_complete, pm);
+	pm->config->setup_dma = pm->configcr_dma;
+	pm->config->transfer_flags |= URB_NO_SETUP_DMA_MAP;
+
+	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 pt_regs *regs)
+{
+	struct powermate_device *pm = urb->context;
+	unsigned long flags;
+
+	if (urb->status)
+		printk(KERN_ERR "powermate: config urb returned %d\n", urb->status);
+	
+	spin_lock_irqsave(&pm->lock, flags);
+	powermate_sync_state(pm);
+	spin_unlock_irqrestore(&pm->lock, flags);
+}
+
+/* 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)
+{
+	unsigned long flags;
+
+	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;
+
+
+	spin_lock_irqsave(&pm->lock, flags);
+
+	/* 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 | UPDATE_STATIC_BRIGHTNESS);
+	}
+	if (pulse_awake != pm->pulse_awake){
+		pm->pulse_awake = pulse_awake;
+		pm->requires_update |= (UPDATE_PULSE_AWAKE | UPDATE_STATIC_BRIGHTNESS);
+	}
+	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);
+   
+	spin_unlock_irqrestore(&pm->lock, flags);
+}
+
+/* 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;
+}
+
+static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_device *pm)
+{
+	pm->data = usb_buffer_alloc(udev, POWERMATE_PAYLOAD_SIZE_MAX,
+				    SLAB_ATOMIC, &pm->data_dma);
+	if (!pm->data)
+		return -1;
+	pm->configcr = usb_buffer_alloc(udev, sizeof(*(pm->configcr)),
+					SLAB_ATOMIC, &pm->configcr_dma);
+	if (!pm->configcr)
+		return -1;
+
+	return 0;
+}
+
+static void powermate_free_buffers(struct usb_device *udev, struct powermate_device *pm)
+{
+	if (pm->data)
+		usb_buffer_free(udev, POWERMATE_PAYLOAD_SIZE_MAX,
+				pm->data, pm->data_dma);
+	if (pm->configcr)
+		usb_buffer_free(udev, sizeof(*(pm->configcr)),
+				pm->configcr, pm->configcr_dma);
+}
+
+/* Called whenever a USB device matching one in our supported devices table is connected */
+static int powermate_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev (intf);
+	struct usb_host_interface *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct powermate_device *pm;
+	int pipe, maxp;
+	char path[64];
+
+	interface = intf->cur_altsetting;
+	endpoint = &interface->endpoint[0].desc;
+	if (!(endpoint->bEndpointAddress & 0x80))
+		return -EIO;
+	if ((endpoint->bmAttributes & 3) != 3)
+		return -EIO;
+
+	usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+		0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		0, interface->desc.bInterfaceNumber, NULL, 0,
+		USB_CTRL_SET_TIMEOUT);
+
+	if (!(pm = kmalloc(sizeof(struct powermate_device), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(pm, 0, sizeof(struct powermate_device));
+	pm->udev = udev;
+
+	if (powermate_alloc_buffers(udev, pm)) {
+		powermate_free_buffers(udev, pm);
+		kfree(pm);
+		return -ENOMEM;
+	}
+
+	pm->irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (!pm->irq) {
+		powermate_free_buffers(udev, pm);
+		kfree(pm);
+		return -ENOMEM;
+	}
+
+	pm->config = usb_alloc_urb(0, GFP_KERNEL);
+	if (!pm->config) {
+		usb_free_urb(pm->irq);
+		powermate_free_buffers(udev, pm);
+		kfree(pm);
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&pm->lock);
+	init_input_dev(&pm->input);
+
+	/* 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_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX){
+		printk("powermate: Expected payload of %d--%d bytes, found %d bytes!\n",
+			POWERMATE_PAYLOAD_SIZE_MIN, POWERMATE_PAYLOAD_SIZE_MAX, maxp);
+		maxp = POWERMATE_PAYLOAD_SIZE_MAX;
+	}
+
+	usb_fill_int_urb(pm->irq, udev, pipe, pm->data,
+			 maxp, powermate_irq,
+			 pm, endpoint->bInterval);
+	pm->irq->transfer_dma = pm->data_dma;
+	pm->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* register our interrupt URB with the USB system */
+	if (usb_submit_urb(pm->irq, GFP_KERNEL)) {
+		powermate_free_buffers(udev, pm);
+		kfree(pm);
+		return -EIO; /* failure */
+	}
+
+	switch (le16_to_cpu(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",
+		       le16_to_cpu(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.id.bustype = BUS_USB;
+	pm->input.id.vendor = le16_to_cpu(udev->descriptor.idVendor);
+	pm->input.id.product = le16_to_cpu(udev->descriptor.idProduct);
+	pm->input.id.version = le16_to_cpu(udev->descriptor.bcdDevice);
+	pm->input.event = powermate_input_event;
+	pm->input.dev = &intf->dev;
+	pm->input.phys = pm->phys;
+
+	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
+  
+	usb_set_intfdata(intf, pm);
+	return 0;
+}
+
+/* Called when a USB device we've accepted ownership of is removed */
+static void powermate_disconnect(struct usb_interface *intf)
+{
+	struct powermate_device *pm = usb_get_intfdata (intf);
+
+	usb_set_intfdata(intf, NULL);
+	if (pm) {
+		pm->requires_update = 0;
+		usb_kill_urb(pm->irq);
+		input_unregister_device(&pm->input);
+		usb_free_urb(pm->irq);
+		usb_free_urb(pm->config);
+		powermate_free_buffers(interface_to_usbdev(intf), pm);
+
+		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 = {
+	.owner =	THIS_MODULE,
+        .name =         "powermate",
+        .probe =        powermate_probe,
+        .disconnect =   powermate_disconnect,
+        .id_table =     powermate_devices,
+};
+
+static int __init powermate_init(void)
+{
+	return usb_register(&powermate_driver);
+}
+
+static void __exit 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");
diff --git a/drivers/usb/input/touchkitusb.c b/drivers/usb/input/touchkitusb.c
new file mode 100644
index 0000000..a71f1bb
--- /dev/null
+++ b/drivers/usb/input/touchkitusb.c
@@ -0,0 +1,310 @@
+/******************************************************************************
+ * touchkitusb.c  --  Driver for eGalax TouchKit USB Touchscreens
+ *
+ * Copyright (C) 2004 by Daniel Ritz
+ * Copyright (C) by Todd E. Johnson (mtouchusb.c)
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Based upon mtouchusb.c
+ *
+ *****************************************************************************/
+
+//#define DEBUG
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#if !defined(DEBUG) && defined(CONFIG_USB_DEBUG)
+#define DEBUG
+#endif
+#include <linux/usb.h>
+
+
+#define TOUCHKIT_MIN_XC			0x0
+#define TOUCHKIT_MAX_XC			0x07ff
+#define TOUCHKIT_XC_FUZZ		0x0
+#define TOUCHKIT_XC_FLAT		0x0
+#define TOUCHKIT_MIN_YC			0x0
+#define TOUCHKIT_MAX_YC			0x07ff
+#define TOUCHKIT_YC_FUZZ		0x0
+#define TOUCHKIT_YC_FLAT		0x0
+#define TOUCHKIT_REPORT_DATA_SIZE	8
+
+#define TOUCHKIT_DOWN			0x01
+#define TOUCHKIT_POINT_TOUCH		0x81
+#define TOUCHKIT_POINT_NOTOUCH		0x80
+
+#define TOUCHKIT_GET_TOUCHED(dat)	((((dat)[0]) & TOUCHKIT_DOWN) ? 1 : 0)
+#define TOUCHKIT_GET_X(dat)		(((dat)[3] << 7) | (dat)[4])
+#define TOUCHKIT_GET_Y(dat)		(((dat)[1] << 7) | (dat)[2])
+
+#define DRIVER_VERSION			"v0.1"
+#define DRIVER_AUTHOR			"Daniel Ritz <daniel.ritz@gmx.ch>"
+#define DRIVER_DESC			"eGalax TouchKit USB HID Touchscreen Driver"
+
+static int swap_xy;
+module_param(swap_xy, bool, 0644);
+MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped.");
+
+struct touchkit_usb {
+	unsigned char *data;
+	dma_addr_t data_dma;
+	struct urb *irq;
+	struct usb_device *udev;
+	struct input_dev input;
+	int open;
+	char name[128];
+	char phys[64];
+};
+
+static struct usb_device_id touchkit_devices[] = {
+	{USB_DEVICE(0x3823, 0x0001)},
+	{USB_DEVICE(0x0eef, 0x0001)},
+	{}
+};
+
+static void touchkit_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct touchkit_usb *touchkit = urb->context;
+	int retval;
+	int x, y;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ETIMEDOUT:
+		/* this urb is timing out */
+		dbg("%s - urb timed out - was the device unplugged?",
+		    __FUNCTION__);
+		return;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d",
+		    __FUNCTION__, urb->status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d",
+		    __FUNCTION__, urb->status);
+		goto exit;
+	}
+
+	if (swap_xy) {
+		y = TOUCHKIT_GET_X(touchkit->data);
+		x = TOUCHKIT_GET_Y(touchkit->data);
+	} else {
+		x = TOUCHKIT_GET_X(touchkit->data);
+		y = TOUCHKIT_GET_Y(touchkit->data);
+	}
+
+	input_regs(&touchkit->input, regs);
+	input_report_key(&touchkit->input, BTN_TOUCH,
+	                 TOUCHKIT_GET_TOUCHED(touchkit->data));
+	input_report_abs(&touchkit->input, ABS_X, x);
+	input_report_abs(&touchkit->input, ABS_Y, y);
+	input_sync(&touchkit->input);
+
+exit:
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		err("%s - usb_submit_urb failed with result: %d",
+		    __FUNCTION__, retval);
+}
+
+static int touchkit_open(struct input_dev *input)
+{
+	struct touchkit_usb *touchkit = input->private;
+
+	if (touchkit->open++)
+		return 0;
+
+	touchkit->irq->dev = touchkit->udev;
+
+	if (usb_submit_urb(touchkit->irq, GFP_ATOMIC)) {
+		touchkit->open--;
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void touchkit_close(struct input_dev *input)
+{
+	struct touchkit_usb *touchkit = input->private;
+
+	if (!--touchkit->open)
+		usb_kill_urb(touchkit->irq);
+}
+
+static int touchkit_alloc_buffers(struct usb_device *udev,
+				  struct touchkit_usb *touchkit)
+{
+	touchkit->data = usb_buffer_alloc(udev, TOUCHKIT_REPORT_DATA_SIZE,
+	                                  SLAB_ATOMIC, &touchkit->data_dma);
+
+	if (!touchkit->data)
+		return -1;
+
+	return 0;
+}
+
+static void touchkit_free_buffers(struct usb_device *udev,
+				  struct touchkit_usb *touchkit)
+{
+	if (touchkit->data)
+		usb_buffer_free(udev, TOUCHKIT_REPORT_DATA_SIZE,
+		                touchkit->data, touchkit->data_dma);
+}
+
+static int touchkit_probe(struct usb_interface *intf,
+			  const struct usb_device_id *id)
+{
+	int ret;
+	struct touchkit_usb *touchkit;
+	struct usb_host_interface *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	char path[64];
+
+	interface = intf->cur_altsetting;
+	endpoint = &interface->endpoint[0].desc;
+
+	touchkit = kmalloc(sizeof(struct touchkit_usb), GFP_KERNEL);
+	if (!touchkit)
+		return -ENOMEM;
+
+	memset(touchkit, 0, sizeof(struct touchkit_usb));
+	touchkit->udev = udev;
+
+	if (touchkit_alloc_buffers(udev, touchkit)) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	touchkit->input.private = touchkit;
+	touchkit->input.open = touchkit_open;
+	touchkit->input.close = touchkit_close;
+
+	usb_make_path(udev, path, 64);
+	sprintf(touchkit->phys, "%s/input0", path);
+
+	touchkit->input.name = touchkit->name;
+	touchkit->input.phys = touchkit->phys;
+	touchkit->input.id.bustype = BUS_USB;
+	touchkit->input.id.vendor = le16_to_cpu(udev->descriptor.idVendor);
+	touchkit->input.id.product = le16_to_cpu(udev->descriptor.idProduct);
+	touchkit->input.id.version = le16_to_cpu(udev->descriptor.bcdDevice);
+	touchkit->input.dev = &intf->dev;
+
+	touchkit->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+	touchkit->input.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+	touchkit->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+
+	/* Used to Scale Compensated Data */
+	touchkit->input.absmin[ABS_X] = TOUCHKIT_MIN_XC;
+	touchkit->input.absmax[ABS_X] = TOUCHKIT_MAX_XC;
+	touchkit->input.absfuzz[ABS_X] = TOUCHKIT_XC_FUZZ;
+	touchkit->input.absflat[ABS_X] = TOUCHKIT_XC_FLAT;
+	touchkit->input.absmin[ABS_Y] = TOUCHKIT_MIN_YC;
+	touchkit->input.absmax[ABS_Y] = TOUCHKIT_MAX_YC;
+	touchkit->input.absfuzz[ABS_Y] = TOUCHKIT_YC_FUZZ;
+	touchkit->input.absflat[ABS_Y] = TOUCHKIT_YC_FLAT;
+
+	if (udev->manufacturer)
+		strcat(touchkit->name, udev->manufacturer);
+	if (udev->product)
+		sprintf(touchkit->name, "%s %s", touchkit->name, udev->product);
+
+	if (!strlen(touchkit->name))
+		sprintf(touchkit->name, "USB Touchscreen %04x:%04x",
+		        touchkit->input.id.vendor, touchkit->input.id.product);
+
+	touchkit->irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (!touchkit->irq) {
+		dbg("%s - usb_alloc_urb failed: touchkit->irq", __FUNCTION__);
+		ret = -ENOMEM;
+		goto out_free_buffers;
+	}
+
+	usb_fill_int_urb(touchkit->irq, touchkit->udev,
+	                 usb_rcvintpipe(touchkit->udev, 0x81),
+	                 touchkit->data, TOUCHKIT_REPORT_DATA_SIZE,
+	                 touchkit_irq, touchkit, endpoint->bInterval);
+
+	input_register_device(&touchkit->input);
+
+	printk(KERN_INFO "input: %s on %s\n", touchkit->name, path);
+	usb_set_intfdata(intf, touchkit);
+
+	return 0;
+
+out_free_buffers:
+	touchkit_free_buffers(udev, touchkit);
+out_free:
+	kfree(touchkit);
+	return ret;
+}
+
+static void touchkit_disconnect(struct usb_interface *intf)
+{
+	struct touchkit_usb *touchkit = usb_get_intfdata(intf);
+
+	dbg("%s - called", __FUNCTION__);
+
+	if (!touchkit)
+		return;
+
+	dbg("%s - touchkit is initialized, cleaning up", __FUNCTION__);
+	usb_set_intfdata(intf, NULL);
+	input_unregister_device(&touchkit->input);
+	usb_kill_urb(touchkit->irq);
+	usb_free_urb(touchkit->irq);
+	touchkit_free_buffers(interface_to_usbdev(intf), touchkit);
+	kfree(touchkit);
+}
+
+MODULE_DEVICE_TABLE(usb, touchkit_devices);
+
+static struct usb_driver touchkit_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "touchkitusb",
+	.probe		= touchkit_probe,
+	.disconnect	= touchkit_disconnect,
+	.id_table	= touchkit_devices,
+};
+
+static int __init touchkit_init(void)
+{
+	return usb_register(&touchkit_driver);
+}
+
+static void __exit touchkit_cleanup(void)
+{
+	usb_deregister(&touchkit_driver);
+}
+
+module_init(touchkit_init);
+module_exit(touchkit_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/input/usbkbd.c b/drivers/usb/input/usbkbd.c
new file mode 100644
index 0000000..01514b0
--- /dev/null
+++ b/drivers/usb/input/usbkbd.c
@@ -0,0 +1,370 @@
+/*
+ * $Id: usbkbd.c,v 1.27 2001/12/27 10:37:41 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ *  USB HIDBP Keyboard support
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or 
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION ""
+#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
+#define DRIVER_DESC "USB HID Boot Protocol keyboard driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+static unsigned char usb_kbd_keycode[256] = {
+	  0,  0,  0,  0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
+	 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44,  2,  3,
+	  4,  5,  6,  7,  8,  9, 10, 11, 28,  1, 14, 15, 57, 12, 13, 26,
+	 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
+	 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
+	105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
+	 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
+	191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
+	115,114,  0,  0,  0,121,  0, 89, 93,124, 92, 94, 95,  0,  0,  0,
+	122,123, 90, 91, 85,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+	 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
+	150,158,159,128,136,177,178,176,142,152,173,140
+};
+
+struct usb_kbd {
+	struct input_dev dev;
+	struct usb_device *usbdev;
+	unsigned char old[8];
+	struct urb *irq, *led;
+	unsigned char newleds;
+	char name[128];
+	char phys[64];
+	int open;
+
+	unsigned char *new;
+	struct usb_ctrlrequest *cr;
+	unsigned char *leds;
+	dma_addr_t cr_dma;
+	dma_addr_t new_dma;
+	dma_addr_t leds_dma;
+};
+
+static void usb_kbd_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct usb_kbd *kbd = urb->context;
+	int i;
+
+	switch (urb->status) {
+	case 0:			/* success */
+		break;
+	case -ECONNRESET:	/* unlink */
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+	/* -EPIPE:  should clear the halt */
+	default:		/* error */
+		goto resubmit;
+	}
+
+	input_regs(&kbd->dev, regs);
+
+	for (i = 0; i < 8; i++)
+		input_report_key(&kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
+
+	for (i = 2; i < 8; i++) {
+
+		if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
+			if (usb_kbd_keycode[kbd->old[i]])
+				input_report_key(&kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
+			else
+				info("Unknown key (scancode %#x) released.", kbd->old[i]);
+		}
+
+		if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
+			if (usb_kbd_keycode[kbd->new[i]])
+				input_report_key(&kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
+			else
+				info("Unknown key (scancode %#x) pressed.", kbd->new[i]);
+		}
+	}
+
+	input_sync(&kbd->dev);
+
+	memcpy(kbd->old, kbd->new, 8);
+
+resubmit:
+	i = usb_submit_urb (urb, SLAB_ATOMIC);
+	if (i)
+		err ("can't resubmit intr, %s-%s/input0, status %d",
+				kbd->usbdev->bus->bus_name,
+				kbd->usbdev->devpath, i);
+}
+
+int usb_kbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+	struct usb_kbd *kbd = dev->private;
+
+	if (type != EV_LED)
+		return -1;
+
+
+	kbd->newleds = (!!test_bit(LED_KANA,    dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
+		       (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL,   dev->led) << 1) |
+		       (!!test_bit(LED_NUML,    dev->led));
+
+	if (kbd->led->status == -EINPROGRESS)
+		return 0;
+
+	if (*(kbd->leds) == kbd->newleds)
+		return 0;
+
+	*(kbd->leds) = kbd->newleds;
+	kbd->led->dev = kbd->usbdev;
+	if (usb_submit_urb(kbd->led, GFP_ATOMIC))
+		err("usb_submit_urb(leds) failed");
+
+	return 0;
+}
+
+static void usb_kbd_led(struct urb *urb, struct pt_regs *regs)
+{
+	struct usb_kbd *kbd = urb->context;
+
+	if (urb->status)
+		warn("led urb status %d received", urb->status);
+	
+	if (*(kbd->leds) == kbd->newleds)
+		return;
+
+	*(kbd->leds) = kbd->newleds;
+	kbd->led->dev = kbd->usbdev;
+	if (usb_submit_urb(kbd->led, GFP_ATOMIC))
+		err("usb_submit_urb(leds) failed");
+}
+
+static int usb_kbd_open(struct input_dev *dev)
+{
+	struct usb_kbd *kbd = dev->private;
+
+	if (kbd->open++)
+		return 0;
+
+	kbd->irq->dev = kbd->usbdev;
+	if (usb_submit_urb(kbd->irq, GFP_KERNEL)) {
+		kbd->open--;
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void usb_kbd_close(struct input_dev *dev)
+{
+	struct usb_kbd *kbd = dev->private;
+
+	if (!--kbd->open)
+		usb_kill_urb(kbd->irq);
+}
+
+static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
+{
+	if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))
+		return -1;
+	if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))
+		return -1;
+	if (!(kbd->new = usb_buffer_alloc(dev, 8, SLAB_ATOMIC, &kbd->new_dma)))
+		return -1;
+	if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), SLAB_ATOMIC, &kbd->cr_dma)))
+		return -1;
+	if (!(kbd->leds = usb_buffer_alloc(dev, 1, SLAB_ATOMIC, &kbd->leds_dma)))
+		return -1;
+
+	return 0;
+}
+
+static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
+{
+	if (kbd->irq)
+		usb_free_urb(kbd->irq);
+	if (kbd->led)
+		usb_free_urb(kbd->led);
+	if (kbd->new)
+		usb_buffer_free(dev, 8, kbd->new, kbd->new_dma);
+	if (kbd->cr)
+		usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);
+	if (kbd->leds)
+		usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);
+}
+
+static int usb_kbd_probe(struct usb_interface *iface, 
+			 const struct usb_device_id *id)
+{
+	struct usb_device * dev = interface_to_usbdev(iface);
+	struct usb_host_interface *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct usb_kbd *kbd;
+	int i, pipe, maxp;
+	char path[64];
+
+	interface = iface->cur_altsetting;
+
+	if (interface->desc.bNumEndpoints != 1)
+		return -ENODEV;
+
+	endpoint = &interface->endpoint[0].desc;
+	if (!(endpoint->bEndpointAddress & 0x80))
+		return -ENODEV;
+	if ((endpoint->bmAttributes & 3) != 3)
+		return -ENODEV;
+
+	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+	if (!(kbd = kmalloc(sizeof(struct usb_kbd), GFP_KERNEL)))
+		return -ENOMEM;
+	memset(kbd, 0, sizeof(struct usb_kbd));
+
+	if (usb_kbd_alloc_mem(dev, kbd)) {
+		usb_kbd_free_mem(dev, kbd);
+		kfree(kbd);
+		return -ENOMEM;
+	}
+
+	kbd->usbdev = dev;
+
+	kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
+	kbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);
+
+	for (i = 0; i < 255; i++)
+		set_bit(usb_kbd_keycode[i], kbd->dev.keybit);
+	clear_bit(0, kbd->dev.keybit);
+	
+	kbd->dev.private = kbd;
+	kbd->dev.event = usb_kbd_event;
+	kbd->dev.open = usb_kbd_open;
+	kbd->dev.close = usb_kbd_close;
+
+	usb_fill_int_urb(kbd->irq, dev, pipe,
+			 kbd->new, (maxp > 8 ? 8 : maxp),
+			 usb_kbd_irq, kbd, endpoint->bInterval);
+	kbd->irq->transfer_dma = kbd->new_dma;
+	kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+	kbd->cr->bRequest = 0x09;
+	kbd->cr->wValue = cpu_to_le16(0x200);
+	kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
+	kbd->cr->wLength = cpu_to_le16(1);
+
+	usb_make_path(dev, path, 64);
+	sprintf(kbd->phys, "%s/input0", path);
+
+	kbd->dev.name = kbd->name;
+	kbd->dev.phys = kbd->phys;	
+	kbd->dev.id.bustype = BUS_USB;
+	kbd->dev.id.vendor = le16_to_cpu(dev->descriptor.idVendor);
+	kbd->dev.id.product = le16_to_cpu(dev->descriptor.idProduct);
+	kbd->dev.id.version = le16_to_cpu(dev->descriptor.bcdDevice);
+	kbd->dev.dev = &iface->dev;
+
+	if (dev->manufacturer)
+		strcat(kbd->name, dev->manufacturer);
+	if (dev->product)
+		sprintf(kbd->name, "%s %s", kbd->name, dev->product);
+
+	if (!strlen(kbd->name))
+		sprintf(kbd->name, "USB HIDBP Keyboard %04x:%04x",
+			kbd->dev.id.vendor, kbd->dev.id.product);
+
+	usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
+			     (void *) kbd->cr, kbd->leds, 1,
+			     usb_kbd_led, kbd);
+	kbd->led->setup_dma = kbd->cr_dma;
+	kbd->led->transfer_dma = kbd->leds_dma;
+	kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP
+				| URB_NO_SETUP_DMA_MAP);
+
+	input_register_device(&kbd->dev);
+
+	printk(KERN_INFO "input: %s on %s\n", kbd->name, path);
+
+	usb_set_intfdata(iface, kbd);
+	return 0;
+}
+
+static void usb_kbd_disconnect(struct usb_interface *intf)
+{
+	struct usb_kbd *kbd = usb_get_intfdata (intf);
+	
+	usb_set_intfdata(intf, NULL);
+	if (kbd) {
+		usb_kill_urb(kbd->irq);
+		input_unregister_device(&kbd->dev);
+		usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
+		kfree(kbd);
+	}
+}
+
+static struct usb_device_id usb_kbd_id_table [] = {
+	{ USB_INTERFACE_INFO(3, 1, 1) },
+	{ }						/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);
+
+static struct usb_driver usb_kbd_driver = {
+	.owner =	THIS_MODULE,
+	.name =		"usbkbd",
+	.probe =	usb_kbd_probe,
+	.disconnect =	usb_kbd_disconnect,
+	.id_table =	usb_kbd_id_table,
+};
+
+static int __init usb_kbd_init(void)
+{
+	int result = usb_register(&usb_kbd_driver);
+	if (result == 0)
+		info(DRIVER_VERSION ":" DRIVER_DESC);
+	return result;
+}
+
+static void __exit usb_kbd_exit(void)
+{
+	usb_deregister(&usb_kbd_driver);
+}
+
+module_init(usb_kbd_init);
+module_exit(usb_kbd_exit);
diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c
new file mode 100644
index 0000000..01155bb
--- /dev/null
+++ b/drivers/usb/input/usbmouse.c
@@ -0,0 +1,252 @@
+/*
+ * $Id: usbmouse.c,v 1.15 2001/12/27 10:37:41 vojtech Exp $
+ *
+ *  Copyright (c) 1999-2001 Vojtech Pavlik
+ *
+ *  USB HIDBP Mouse support
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or 
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.6"
+#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
+#define DRIVER_DESC "USB HID Boot Protocol mouse driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+struct usb_mouse {
+	char name[128];
+	char phys[64];
+	struct usb_device *usbdev;
+	struct input_dev dev;
+	struct urb *irq;
+	int open;
+
+	signed char *data;
+	dma_addr_t data_dma;
+};
+
+static void usb_mouse_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct usb_mouse *mouse = urb->context;
+	signed char *data = mouse->data;
+	struct input_dev *dev = &mouse->dev;
+	int status;
+
+	switch (urb->status) {
+	case 0:			/* success */
+		break;
+	case -ECONNRESET:	/* unlink */
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+	/* -EPIPE:  should clear the halt */
+	default:		/* error */
+		goto resubmit;
+	}
+
+	input_regs(dev, regs);
+
+	input_report_key(dev, BTN_LEFT,   data[0] & 0x01);
+	input_report_key(dev, BTN_RIGHT,  data[0] & 0x02);
+	input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
+	input_report_key(dev, BTN_SIDE,   data[0] & 0x08);
+	input_report_key(dev, BTN_EXTRA,  data[0] & 0x10);
+
+	input_report_rel(dev, REL_X,     data[1]);
+	input_report_rel(dev, REL_Y,     data[2]);
+	input_report_rel(dev, REL_WHEEL, data[3]);
+
+	input_sync(dev);
+resubmit:
+	status = usb_submit_urb (urb, SLAB_ATOMIC);
+	if (status)
+		err ("can't resubmit intr, %s-%s/input0, status %d",
+				mouse->usbdev->bus->bus_name,
+				mouse->usbdev->devpath, status);
+}
+
+static int usb_mouse_open(struct input_dev *dev)
+{
+	struct usb_mouse *mouse = dev->private;
+
+	if (mouse->open++)
+		return 0;
+
+	mouse->irq->dev = mouse->usbdev;
+	if (usb_submit_urb(mouse->irq, GFP_KERNEL)) {
+		mouse->open--;
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void usb_mouse_close(struct input_dev *dev)
+{
+	struct usb_mouse *mouse = dev->private;
+
+	if (!--mouse->open)
+		usb_kill_urb(mouse->irq);
+}
+
+static int usb_mouse_probe(struct usb_interface * intf, const struct usb_device_id * id)
+{
+	struct usb_device * dev = interface_to_usbdev(intf);
+	struct usb_host_interface *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct usb_mouse *mouse;
+	int pipe, maxp;
+	char path[64];
+
+	interface = intf->cur_altsetting;
+
+	if (interface->desc.bNumEndpoints != 1) 
+		return -ENODEV;
+
+	endpoint = &interface->endpoint[0].desc;
+	if (!(endpoint->bEndpointAddress & 0x80)) 
+		return -ENODEV;
+	if ((endpoint->bmAttributes & 3) != 3) 
+		return -ENODEV;
+
+	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+	if (!(mouse = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL))) 
+		return -ENOMEM;
+	memset(mouse, 0, sizeof(struct usb_mouse));
+
+	mouse->data = usb_buffer_alloc(dev, 8, SLAB_ATOMIC, &mouse->data_dma);
+	if (!mouse->data) {
+		kfree(mouse);
+		return -ENOMEM;
+	}
+
+	mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (!mouse->irq) {
+		usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);
+		kfree(mouse);
+		return -ENODEV;
+	}
+
+	mouse->usbdev = dev;
+
+	mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+	mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+	mouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
+	mouse->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
+	mouse->dev.relbit[0] |= BIT(REL_WHEEL);
+
+	mouse->dev.private = mouse;
+	mouse->dev.open = usb_mouse_open;
+	mouse->dev.close = usb_mouse_close;
+
+	usb_make_path(dev, path, 64);
+	sprintf(mouse->phys, "%s/input0", path);
+
+	mouse->dev.name = mouse->name;
+	mouse->dev.phys = mouse->phys;
+	mouse->dev.id.bustype = BUS_USB;
+	mouse->dev.id.vendor = le16_to_cpu(dev->descriptor.idVendor);
+	mouse->dev.id.product = le16_to_cpu(dev->descriptor.idProduct);
+	mouse->dev.id.version = le16_to_cpu(dev->descriptor.bcdDevice);
+	mouse->dev.dev = &intf->dev;
+
+	if (dev->manufacturer)
+		strcat(mouse->name, dev->manufacturer);
+	if (dev->product)
+		sprintf(mouse->name, "%s %s", mouse->name, dev->product);
+
+	if (!strlen(mouse->name))
+		sprintf(mouse->name, "USB HIDBP Mouse %04x:%04x",
+			mouse->dev.id.vendor, mouse->dev.id.product);
+
+	usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
+			 (maxp > 8 ? 8 : maxp),
+			 usb_mouse_irq, mouse, endpoint->bInterval);
+	mouse->irq->transfer_dma = mouse->data_dma;
+	mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	input_register_device(&mouse->dev);
+	printk(KERN_INFO "input: %s on %s\n", mouse->name, path);
+
+	usb_set_intfdata(intf, mouse);
+	return 0;
+}
+
+static void usb_mouse_disconnect(struct usb_interface *intf)
+{
+	struct usb_mouse *mouse = usb_get_intfdata (intf);
+	
+	usb_set_intfdata(intf, NULL);
+	if (mouse) {
+		usb_kill_urb(mouse->irq);
+		input_unregister_device(&mouse->dev);
+		usb_free_urb(mouse->irq);
+		usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);
+		kfree(mouse);
+	}
+}
+
+static struct usb_device_id usb_mouse_id_table [] = {
+	{ USB_INTERFACE_INFO(3, 1, 2) },
+    { }						/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
+
+static struct usb_driver usb_mouse_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "usbmouse",
+	.probe		= usb_mouse_probe,
+	.disconnect	= usb_mouse_disconnect,
+	.id_table	= usb_mouse_id_table,
+};
+
+static int __init usb_mouse_init(void)
+{
+	int retval = usb_register(&usb_mouse_driver);
+	if (retval == 0) 
+		info(DRIVER_VERSION ":" DRIVER_DESC);
+	return retval;
+}
+
+static void __exit usb_mouse_exit(void)
+{
+	usb_deregister(&usb_mouse_driver);
+}
+
+module_init(usb_mouse_init);
+module_exit(usb_mouse_exit);
diff --git a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c
new file mode 100644
index 0000000..fec04dd
--- /dev/null
+++ b/drivers/usb/input/wacom.c
@@ -0,0 +1,951 @@
+/*
+ *  USB Wacom Graphire and Wacom Intuos tablet support
+ *
+ *  Copyright (c) 2000-2004 Vojtech Pavlik	<vojtech@ucw.cz>
+ *  Copyright (c) 2000 Andreas Bach Aaen	<abach@stofanet.dk>
+ *  Copyright (c) 2000 Clifford Wolf		<clifford@clifford.at>
+ *  Copyright (c) 2000 Sam Mosel		<sam.mosel@computer.org>
+ *  Copyright (c) 2000 James E. Blair		<corvus@gnu.org>
+ *  Copyright (c) 2000 Daniel Egger		<egger@suse.de>
+ *  Copyright (c) 2001 Frederic Lepied		<flepied@mandrakesoft.com>
+ *  Copyright (c) 2004 Panagiotis Issaris	<panagiotis.issaris@mech.kuleuven.ac.be>
+ *  Copyright (c) 2002-2004 Ping Cheng		<pingc@wacom.com>
+ *
+ *  ChangeLog:
+ *      v0.1 (vp)  - Initial release
+ *      v0.2 (aba) - Support for all buttons / combinations
+ *      v0.3 (vp)  - Support for Intuos added
+ *	v0.4 (sm)  - Support for more Intuos models, menustrip
+ *			relative mode, proximity.
+ *	v0.5 (vp)  - Big cleanup, nifty features removed,
+ * 			they belong in userspace
+ *	v1.8 (vp)  - Submit URB only when operating, moved to CVS,
+ *			use input_report_key instead of report_btn and
+ *			other cleanups
+ *	v1.11 (vp) - Add URB ->dev setting for new kernels
+ *	v1.11 (jb) - Add support for the 4D Mouse & Lens
+ *	v1.12 (de) - Add support for two more inking pen IDs
+ *	v1.14 (vp) - Use new USB device id probing scheme.
+ *		     Fix Wacom Graphire mouse wheel
+ *	v1.18 (vp) - Fix mouse wheel direction
+ *		     Make mouse relative
+ *      v1.20 (fl) - Report tool id for Intuos devices
+ *                 - Multi tools support
+ *                 - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...)
+ *                 - Add PL models support
+ *		   - Fix Wacom Graphire mouse wheel again
+ *	v1.21 (vp) - Removed protocol descriptions
+ *		   - Added MISC_SERIAL for tool serial numbers
+ *	      (gb) - Identify version on module load.
+ *    v1.21.1 (fl) - added Graphire2 support
+ *    v1.21.2 (fl) - added Intuos2 support
+ *                 - added all the PL ids
+ *    v1.21.3 (fl) - added another eraser id from Neil Okamoto
+ *                 - added smooth filter for Graphire from Peri Hankey
+ *                 - added PenPartner support from Olaf van Es
+ *                 - new tool ids from Ole Martin Bjoerndalen
+ *	v1.29 (pc) - Add support for more tablets
+ *		   - Fix pressure reporting
+ *	v1.30 (vp) - Merge 2.4 and 2.5 drivers
+ *		   - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse
+ *		   - Cleanups here and there
+ *    v1.30.1 (pi) - Added Graphire3 support
+ *	v1.40 (pc) - Add support for several new devices, fix eraser reporting, ...
+ */
+
+/*
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.40"
+#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
+#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_WACOM	0x056a
+
+struct wacom_features {
+	char *name;
+	int pktlen;
+	int x_max;
+	int y_max;
+	int pressure_max;
+	int distance_max;
+	int type;
+	usb_complete_t irq;
+};
+
+struct wacom {
+	signed char *data;
+	dma_addr_t data_dma;
+	struct input_dev dev;
+	struct usb_device *usbdev;
+	struct urb *irq;
+	struct wacom_features *features;
+	int tool[2];
+	int open;
+	__u32 serial[2];
+	char phys[32];
+};
+
+#define USB_REQ_SET_REPORT	0x09
+static int usb_set_report(struct usb_interface *intf, unsigned char type,
+				unsigned char id, void *buf, int size)
+{
+	return usb_control_msg(interface_to_usbdev(intf),
+		usb_sndctrlpipe(interface_to_usbdev(intf), 0),
+                USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
+		buf, size, 1000);
+}
+
+static void wacom_pl_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct wacom *wacom = urb->context;
+	unsigned char *data = wacom->data;
+	struct input_dev *dev = &wacom->dev;
+	int prox, pressure;
+	int retval;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+		goto exit;
+	}
+
+	if (data[0] != 2) {
+		dbg("wacom_pl_irq: received unknown report #%d", data[0]);
+		goto exit;
+	}
+
+	prox = data[1] & 0x40;
+
+	input_regs(dev, regs);
+	
+	if (prox) {
+
+		pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
+		if (wacom->features->pressure_max > 255)
+			pressure = (pressure << 1) | ((data[4] >> 6) & 1);
+		pressure += (wacom->features->pressure_max + 1) / 2;
+
+		/*
+		 * if going from out of proximity into proximity select between the eraser
+		 * and the pen based on the state of the stylus2 button, choose eraser if
+		 * pressed else choose pen. if not a proximity change from out to in, send
+		 * an out of proximity for previous tool then a in for new tool.
+		 */
+		if (!wacom->tool[0]) {
+			/* Going into proximity select tool */
+			wacom->tool[1] = (data[4] & 0x20)? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+		}
+		else {
+			/* was entered with stylus2 pressed */
+			if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20) ) {
+				/* report out proximity for previous tool */
+				input_report_key(dev, wacom->tool[1], 0);
+				input_sync(dev);
+				wacom->tool[1] = BTN_TOOL_PEN;
+				goto exit;
+			}
+		}
+		if (wacom->tool[1] != BTN_TOOL_RUBBER) {
+			/* Unknown tool selected default to pen tool */
+			wacom->tool[1] = BTN_TOOL_PEN;
+		}
+		input_report_key(dev, wacom->tool[1], prox); /* report in proximity for tool */
+		input_report_abs(dev, ABS_X, data[3] | ((__u32)data[2] << 7) | ((__u32)(data[1] & 0x03) << 14));
+		input_report_abs(dev, ABS_Y, data[6] | ((__u32)data[5] << 7) | ((__u32)(data[4] & 0x03) << 14));
+		input_report_abs(dev, ABS_PRESSURE, pressure);
+
+		input_report_key(dev, BTN_TOUCH, data[4] & 0x08);
+		input_report_key(dev, BTN_STYLUS, data[4] & 0x10);
+		/* Only allow the stylus2 button to be reported for the pen tool. */
+		input_report_key(dev, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
+	}
+	else {
+		/* report proximity-out of a (valid) tool */
+		if (wacom->tool[1] != BTN_TOOL_RUBBER) {
+			/* Unknown tool selected default to pen tool */
+			wacom->tool[1] = BTN_TOOL_PEN;
+		}
+		input_report_key(dev, wacom->tool[1], prox);
+	}
+
+	wacom->tool[0] = prox; /* Save proximity state */
+	input_sync(dev);
+
+exit:
+	retval = usb_submit_urb (urb, GFP_ATOMIC);
+	if (retval)
+		err ("%s - usb_submit_urb failed with result %d",
+		     __FUNCTION__, retval);
+}
+
+static void wacom_ptu_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct wacom *wacom = urb->context;
+	unsigned char *data = wacom->data;
+	struct input_dev *dev = &wacom->dev;
+	int retval;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+		goto exit;
+	}
+
+	if (data[0] != 2)
+	{
+		printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]);
+		goto exit;
+	}
+
+	input_regs(dev, regs);
+	if (data[1] & 0x04)
+	{
+		input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x20);
+		input_report_key(dev, BTN_TOUCH, data[1] & 0x08);
+	}
+	else
+	{
+		input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x20);
+		input_report_key(dev, BTN_TOUCH, data[1] & 0x01);
+	}
+	input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[2]));
+	input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[4]));
+	input_report_abs(dev, ABS_PRESSURE, le16_to_cpu(*(__le16 *) &data[6]));
+	input_report_key(dev, BTN_STYLUS, data[1] & 0x02);
+	input_report_key(dev, BTN_STYLUS2, data[1] & 0x10);
+
+	input_sync(dev);
+
+exit:
+	retval = usb_submit_urb (urb, GFP_ATOMIC);
+	if (retval)
+		err ("%s - usb_submit_urb failed with result %d",
+		     __FUNCTION__, retval);
+}
+
+static void wacom_penpartner_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct wacom *wacom = urb->context;
+	unsigned char *data = wacom->data;
+	struct input_dev *dev = &wacom->dev;
+	int retval;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+		goto exit;
+	}
+
+	if (data[0] != 2) {
+		printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]);
+		goto exit;
+	}
+
+	input_regs(dev, regs);
+	input_report_key(dev, BTN_TOOL_PEN, 1);
+	input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[1]));
+	input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[3]));
+	input_report_abs(dev, ABS_PRESSURE, (signed char)data[6] + 127);
+	input_report_key(dev, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20));
+	input_report_key(dev, BTN_STYLUS, (data[5] & 0x40));
+	input_sync(dev);
+
+exit:
+	retval = usb_submit_urb (urb, GFP_ATOMIC);
+	if (retval)
+		err ("%s - usb_submit_urb failed with result %d",
+		     __FUNCTION__, retval);
+}
+
+static void wacom_graphire_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct wacom *wacom = urb->context;
+	unsigned char *data = wacom->data;
+	struct input_dev *dev = &wacom->dev;
+	int x, y;
+	int retval;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+		goto exit;
+	}
+
+	if (data[0] != 2) {
+		dbg("wacom_graphire_irq: received unknown report #%d", data[0]);
+		goto exit;
+	}
+
+	x = le16_to_cpu(*(__le16 *) &data[2]);
+	y = le16_to_cpu(*(__le16 *) &data[4]);
+
+	input_regs(dev, regs);
+
+	switch ((data[1] >> 5) & 3) {
+
+		case 0:	/* Pen */
+			input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x80);
+			break;
+
+		case 1: /* Rubber */
+			input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x80);
+			break;
+
+		case 2: /* Mouse with wheel */
+			input_report_key(dev, BTN_MIDDLE, data[1] & 0x04);
+			input_report_rel(dev, REL_WHEEL, (signed char) data[6]);
+			/* fall through */
+
+                case 3: /* Mouse without wheel */
+			input_report_key(dev, BTN_TOOL_MOUSE, data[7] > 24);
+			input_report_key(dev, BTN_LEFT, data[1] & 0x01);
+			input_report_key(dev, BTN_RIGHT, data[1] & 0x02);
+			input_report_abs(dev, ABS_DISTANCE, data[7]);
+
+			input_report_abs(dev, ABS_X, x);
+			input_report_abs(dev, ABS_Y, y);
+
+			input_sync(dev);
+			goto exit;
+	}
+
+	if (data[1] & 0x80) {
+		input_report_abs(dev, ABS_X, x);
+		input_report_abs(dev, ABS_Y, y);
+	}
+
+	input_report_abs(dev, ABS_PRESSURE, le16_to_cpu(*(__le16 *) &data[6]));
+	input_report_key(dev, BTN_TOUCH, data[1] & 0x01);
+	input_report_key(dev, BTN_STYLUS, data[1] & 0x02);
+	input_report_key(dev, BTN_STYLUS2, data[1] & 0x04);
+
+	input_sync(dev);
+
+exit:
+	retval = usb_submit_urb (urb, GFP_ATOMIC);
+	if (retval)
+		err ("%s - usb_submit_urb failed with result %d",
+		     __FUNCTION__, retval);
+}
+
+static int wacom_intuos_inout(struct urb *urb)
+{
+	struct wacom *wacom = urb->context;
+	unsigned char *data = wacom->data;
+	struct input_dev *dev = &wacom->dev;
+	int idx;
+
+	/* tool number */
+	idx = data[1] & 0x01;
+
+	/* Enter report */
+	if ((data[1] & 0xfc) == 0xc0)
+	{
+		/* serial number of the tool */
+		wacom->serial[idx] = ((__u32)(data[3] & 0x0f) << 28) +
+			((__u32)data[4] << 20) + ((__u32)data[5] << 12) +
+			((__u32)data[6] << 4) + (data[7] >> 4);
+
+		switch (((__u32)data[2] << 4) | (data[3] >> 4)) {
+			case 0x812: /* Inking pen */
+			case 0x801: /* Intuos3 Inking pen */
+			case 0x012:
+				wacom->tool[idx] = BTN_TOOL_PENCIL;
+				break;
+			case 0x822: /* Pen */
+			case 0x842:
+			case 0x852:
+			case 0x823: /* Intuos3 Grip Pen */
+			case 0x813: /* Intuos3 Classic Pen */
+			case 0x885: /* Intuos3 Marker Pen */
+			case 0x022:
+				wacom->tool[idx] = BTN_TOOL_PEN;
+				break;
+			case 0x832: /* Stroke pen */
+			case 0x032:
+				wacom->tool[idx] = BTN_TOOL_BRUSH;
+				break;
+			case 0x007: /* Mouse 4D and 2D */
+		        case 0x09c:
+			case 0x094:
+			case 0x017: /* Intuos3 2D Mouse */
+				wacom->tool[idx] = BTN_TOOL_MOUSE;
+				break;
+			case 0x096: /* Lens cursor */
+			case 0x097: /* Intuos3 Lens cursor */
+				wacom->tool[idx] = BTN_TOOL_LENS;
+				break;
+			case 0x82a: /* Eraser */
+			case 0x85a:
+		        case 0x91a:
+			case 0xd1a:
+			case 0x0fa:
+			case 0x82b: /* Intuos3 Grip Pen Eraser */
+			case 0x81b: /* Intuos3 Classic Pen Eraser */
+			case 0x91b: /* Intuos3 Airbrush Eraser */
+				wacom->tool[idx] = BTN_TOOL_RUBBER;
+				break;
+			case 0xd12:
+			case 0x912:
+			case 0x112:
+			case 0x913: /* Intuos3 Airbrush */
+				wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
+				break;	/* Airbrush */
+			default: /* Unknown tool */
+				wacom->tool[idx] = BTN_TOOL_PEN;
+		}
+		input_report_key(dev, wacom->tool[idx], 1);
+		input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+		input_sync(dev);
+		return 1;
+	}
+
+	/* Exit report */
+	if ((data[1] & 0xfe) == 0x80) {
+		input_report_key(dev, wacom->tool[idx], 0);
+		input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+		input_sync(dev);
+		return 1;
+	}
+
+	return 0;
+}
+
+static void wacom_intuos_general(struct urb *urb)
+{
+	struct wacom *wacom = urb->context;
+	unsigned char *data = wacom->data;
+	struct input_dev *dev = &wacom->dev;
+	unsigned int t;
+
+	/* general pen packet */
+	if ((data[1] & 0xb8) == 0xa0)
+	{
+		t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3);
+		input_report_abs(dev, ABS_PRESSURE, t);
+		input_report_abs(dev, ABS_TILT_X,
+				((data[7] << 1) & 0x7e) | (data[8] >> 7));
+		input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
+		input_report_key(dev, BTN_STYLUS, data[1] & 2);
+		input_report_key(dev, BTN_STYLUS2, data[1] & 4);
+		input_report_key(dev, BTN_TOUCH, t > 10);
+	}
+
+	/* airbrush second packet */
+	if ((data[1] & 0xbc) == 0xb4)
+	{
+		input_report_abs(dev, ABS_WHEEL,
+				((__u32)data[6] << 2) | ((data[7] >> 6) & 3));
+		input_report_abs(dev, ABS_TILT_X,
+				((data[7] << 1) & 0x7e) | (data[8] >> 7));
+		input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
+	}
+	return;
+}
+
+static void wacom_intuos_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct wacom *wacom = urb->context;
+	unsigned char *data = wacom->data;
+	struct input_dev *dev = &wacom->dev;
+	unsigned int t;
+	int idx;
+	int retval;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+		goto exit;
+	}
+
+	if (data[0] != 2 && data[0] != 5 && data[0] != 6) {
+		dbg("wacom_intuos_irq: received unknown report #%d", data[0]);
+		goto exit;
+	}
+
+	input_regs(dev, regs);
+
+	/* tool number */
+	idx = data[1] & 0x01;
+
+	/* process in/out prox events */
+	if (wacom_intuos_inout(urb)) goto exit;
+
+	input_report_abs(dev, ABS_X, be16_to_cpu(*(__be16 *) &data[2]));
+	input_report_abs(dev, ABS_Y, be16_to_cpu(*(__be16 *) &data[4]));
+	input_report_abs(dev, ABS_DISTANCE, data[9]);
+
+	/* process general packets */
+	wacom_intuos_general(urb);
+	
+	if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) {		/* 4D mouse or Lens cursor packets */
+
+		if (data[1] & 0x02) {						/* Rotation packet */
+
+			t = ((__u32)data[6] << 3) | ((data[7] >> 5) & 7);
+			input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ? ((t - 1) / 2) : -t / 2);
+
+		} else {
+
+	 		if ((data[1] & 0x10) == 0) {				/* 4D mouse packets */
+
+				input_report_key(dev, BTN_LEFT,   data[8] & 0x01);
+				input_report_key(dev, BTN_MIDDLE, data[8] & 0x02);
+				input_report_key(dev, BTN_RIGHT,  data[8] & 0x04);
+
+				input_report_key(dev, BTN_SIDE,   data[8] & 0x20);
+				input_report_key(dev, BTN_EXTRA,  data[8] & 0x10);
+				t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3);
+				input_report_abs(dev, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
+
+			} else {
+				if (wacom->tool[idx] == BTN_TOOL_MOUSE) {	/* 2D mouse packets */	
+					input_report_key(dev, BTN_LEFT,   data[8] & 0x04);
+					input_report_key(dev, BTN_MIDDLE, data[8] & 0x08);
+					input_report_key(dev, BTN_RIGHT,  data[8] & 0x10);
+					input_report_rel(dev, REL_WHEEL, 
+					    (-(__u32)(data[8] & 0x01) + (__u32)((data[8] & 0x02) >> 1)));
+				}
+				else {     /* Lens cursor packets */
+					input_report_key(dev, BTN_LEFT,   data[8] & 0x01);
+					input_report_key(dev, BTN_MIDDLE, data[8] & 0x02);
+					input_report_key(dev, BTN_RIGHT,  data[8] & 0x04);
+					input_report_key(dev, BTN_SIDE,   data[8] & 0x10);
+					input_report_key(dev, BTN_EXTRA,  data[8] & 0x08);
+				}
+			}
+		}
+	}
+	
+	input_report_key(dev, wacom->tool[idx], 1);
+	input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+	input_sync(dev);
+
+exit:
+	retval = usb_submit_urb (urb, GFP_ATOMIC);
+	if (retval)
+		err ("%s - usb_submit_urb failed with result %d",
+		     __FUNCTION__, retval);
+}
+
+static void wacom_intuos3_irq(struct urb *urb, struct pt_regs *regs)
+{
+	struct wacom *wacom = urb->context;
+	unsigned char *data = wacom->data;
+	struct input_dev *dev = &wacom->dev;
+	unsigned int t;
+	int idx, retval;
+
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+		goto exit;
+	}
+
+	/* check for valid report */
+	if (data[0] != 2 && data[0] != 5 && data[0] != 12)
+	{
+		printk(KERN_INFO "wacom_intuos3_irq: received unknown report #%d\n", data[0]);
+		goto exit;
+	}
+
+	input_regs(dev, regs);
+
+	/* tool index is always 0 here since there is no dual input tool */
+	idx = data[1] & 0x01;
+
+	/* pad packets. Works as a second tool and is always in prox */
+	if (data[0] == 12)
+	{
+		/* initiate the pad as a device */
+		if (wacom->tool[1] != BTN_TOOL_FINGER)
+		{
+			wacom->tool[1] = BTN_TOOL_FINGER;
+			input_report_key(dev, wacom->tool[1], 1);
+		}
+		input_report_key(dev, BTN_0, (data[5] & 0x01));
+		input_report_key(dev, BTN_1, (data[5] & 0x02));
+		input_report_key(dev, BTN_2, (data[5] & 0x04));
+		input_report_key(dev, BTN_3, (data[5] & 0x08));
+		input_report_key(dev, BTN_4, (data[6] & 0x01));
+		input_report_key(dev, BTN_5, (data[6] & 0x02));
+		input_report_key(dev, BTN_6, (data[6] & 0x04));
+		input_report_key(dev, BTN_7, (data[6] & 0x08));
+		input_report_abs(dev, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
+		input_report_abs(dev, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
+		input_event(dev, EV_MSC, MSC_SERIAL, 0xffffffff);
+		input_sync(dev);
+		goto exit;
+	}
+
+	/* process in/out prox events */
+	if (wacom_intuos_inout(urb)) goto exit;
+
+	input_report_abs(dev, ABS_X, ((__u32)data[2] << 9) | ((__u32)data[3] << 1) | ((data[9] >> 1) & 1));
+	input_report_abs(dev, ABS_Y, ((__u32)data[4] << 9) | ((__u32)data[5] << 1) | (data[9] & 1));
+	input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
+
+	/* process general packets */
+	wacom_intuos_general(urb);
+
+	if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0)
+	{
+		/* Marker pen rotation packet. Reported as wheel due to valuator limitation */
+		if (data[1] & 0x02)
+		{
+			t = ((__u32)data[6] << 3) | ((data[7] >> 5) & 7);
+			t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
+				((t-1) / 2 + 450)) : (450 - t / 2) ;
+			input_report_abs(dev, ABS_WHEEL, t);
+		}
+
+		/* 2D mouse packets */
+		if (wacom->tool[idx] == BTN_TOOL_MOUSE)
+		{
+			input_report_key(dev, BTN_LEFT,   data[8] & 0x04);
+			input_report_key(dev, BTN_MIDDLE, data[8] & 0x08);
+			input_report_key(dev, BTN_RIGHT,  data[8] & 0x10);
+			input_report_key(dev, BTN_SIDE,   data[8] & 0x40);
+			input_report_key(dev, BTN_EXTRA,  data[8] & 0x20);
+			/* mouse wheel is positive when rolled backwards */
+			input_report_rel(dev, REL_WHEEL,  ((__u32)((data[8] & 0x02) >> 1)
+					 - (__u32)(data[8] & 0x01)));
+		}
+	}
+
+	input_report_key(dev, wacom->tool[idx], 1);
+	input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+	input_sync(dev);
+
+exit:
+	retval = usb_submit_urb (urb, GFP_ATOMIC);
+	if (retval)
+		err ("%s - usb_submit_urb failed with result %d",
+		    __FUNCTION__, retval);
+}
+
+static struct wacom_features wacom_features[] = {
+	{ "Wacom Penpartner",    7,   5040,  3780,  255, 32, 0, wacom_penpartner_irq },
+        { "Wacom Graphire",      8,  10206,  7422,  511, 32, 1, wacom_graphire_irq },
+	{ "Wacom Graphire2 4x5", 8,  10206,  7422,  511, 32, 1, wacom_graphire_irq },
+ 	{ "Wacom Graphire2 5x7", 8,  13918, 10206,  511, 32, 1, wacom_graphire_irq },
+	{ "Wacom Graphire3",     8,  10208,  7424,  511, 32, 1, wacom_graphire_irq },
+	{ "Wacom Graphire3 6x8", 8,  16704, 12064,  511, 32, 1, wacom_graphire_irq },
+  	{ "Wacom Intuos 4x5",   10,  12700, 10600, 1023, 15, 2, wacom_intuos_irq },
+ 	{ "Wacom Intuos 6x8",   10,  20320, 16240, 1023, 15, 2, wacom_intuos_irq },
+ 	{ "Wacom Intuos 9x12",  10,  30480, 24060, 1023, 15, 2, wacom_intuos_irq },
+ 	{ "Wacom Intuos 12x12", 10,  30480, 31680, 1023, 15, 2, wacom_intuos_irq },
+ 	{ "Wacom Intuos 12x18", 10,  45720, 31680, 1023, 15, 2, wacom_intuos_irq },
+ 	{ "Wacom PL400",         8,   5408,  4056,  255, 32, 3, wacom_pl_irq },
+ 	{ "Wacom PL500",         8,   6144,  4608,  255, 32, 3, wacom_pl_irq },
+ 	{ "Wacom PL600",         8,   6126,  4604,  255, 32, 3, wacom_pl_irq },
+ 	{ "Wacom PL600SX",       8,   6260,  5016,  255, 32, 3, wacom_pl_irq },
+ 	{ "Wacom PL550",         8,   6144,  4608,  511, 32, 3, wacom_pl_irq },
+ 	{ "Wacom PL800",         8,   7220,  5780,  511, 32, 3, wacom_pl_irq },
+	{ "Wacom Intuos2 4x5",   10, 12700, 10600, 1023, 15, 2, wacom_intuos_irq },
+	{ "Wacom Intuos2 6x8",   10, 20320, 16240, 1023, 15, 2, wacom_intuos_irq },
+	{ "Wacom Intuos2 9x12",  10, 30480, 24060, 1023, 15, 2, wacom_intuos_irq },
+	{ "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 15, 2, wacom_intuos_irq },
+	{ "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, 2, wacom_intuos_irq },
+	{ "Wacom Volito",        8,   5104,  3712,  511, 32, 1, wacom_graphire_irq },
+	{ "Wacom Cintiq Partner",8,  20480, 15360,  511, 32, 3, wacom_ptu_irq },
+	{ "Wacom Intuos3 4x5",   10, 25400, 20320, 1023, 15, 4, wacom_intuos3_irq },
+	{ "Wacom Intuos3 6x8",   10, 40640, 30480, 1023, 15, 4, wacom_intuos3_irq },
+	{ "Wacom Intuos3 9x12",  10, 60960, 45720, 1023, 15, 4, wacom_intuos3_irq },
+	{ "Wacom Intuos2 6x8",   10, 20320, 16240, 1023, 15, 2, wacom_intuos_irq },
+ 	{ }
+};
+
+static struct usb_device_id wacom_ids[] = {
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x00) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x10) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x11) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x12) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x13) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x14) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x20) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x21) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x22) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x23) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x24) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x30) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x31) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x32) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x33) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x34) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x35) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x41) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x42) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x43) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x44) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x45) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) },
+	{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, wacom_ids);
+
+static int wacom_open(struct input_dev *dev)
+{
+	struct wacom *wacom = dev->private;
+
+	if (wacom->open++)
+		return 0;
+
+	wacom->irq->dev = wacom->usbdev;
+	if (usb_submit_urb(wacom->irq, GFP_KERNEL)) {
+		wacom->open--;
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static void wacom_close(struct input_dev *dev)
+{
+	struct wacom *wacom = dev->private;
+
+	if (!--wacom->open)
+		usb_kill_urb(wacom->irq);
+}
+
+static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_endpoint_descriptor *endpoint;
+	char rep_data[2] = {0x02, 0x02};
+	struct wacom *wacom;
+	char path[64];
+
+	if (!(wacom = kmalloc(sizeof(struct wacom), GFP_KERNEL)))
+		return -ENOMEM;
+	memset(wacom, 0, sizeof(struct wacom));
+
+	wacom->data = usb_buffer_alloc(dev, 10, GFP_KERNEL, &wacom->data_dma);
+	if (!wacom->data) {
+		kfree(wacom);
+		return -ENOMEM;
+	}
+
+	wacom->irq = usb_alloc_urb(0, GFP_KERNEL);
+	if (!wacom->irq) {
+		usb_buffer_free(dev, 10, wacom->data, wacom->data_dma);
+		kfree(wacom);
+		return -ENOMEM;
+	}
+
+	wacom->features = wacom_features + (id - wacom_ids);
+
+	wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS);
+	wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
+	wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS);
+
+	switch (wacom->features->type) {
+		case 1:
+			wacom->dev.evbit[0] |= BIT(EV_REL);
+			wacom->dev.relbit[0] |= BIT(REL_WHEEL);
+			wacom->dev.absbit[0] |= BIT(ABS_DISTANCE);
+			wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+ 			wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2);
+			break;
+
+		case 4: /* new functions for Intuos3 */
+			wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
+			wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
+			wacom->dev.absbit[0] |= BIT(ABS_RX) | BIT(ABS_RY);
+			/* fall through */
+
+		case 2:
+			wacom->dev.evbit[0] |= BIT(EV_MSC) | BIT(EV_REL);
+			wacom->dev.mscbit[0] |= BIT(MSC_SERIAL);
+			wacom->dev.relbit[0] |= BIT(REL_WHEEL);
+			wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE) | BIT(BTN_SIDE) | BIT(BTN_EXTRA);
+ 			wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE)	| BIT(BTN_TOOL_BRUSH)
+							  | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS) | BIT(BTN_STYLUS2);
+			wacom->dev.absbit[0] |= BIT(ABS_DISTANCE) | BIT(ABS_WHEEL) | BIT(ABS_TILT_X) | BIT(ABS_TILT_Y) | BIT(ABS_RZ) | BIT(ABS_THROTTLE);
+			break;
+
+		case 3:
+ 			wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER);
+			break;
+	}
+
+	wacom->dev.absmax[ABS_X] = wacom->features->x_max;
+	wacom->dev.absmax[ABS_Y] = wacom->features->y_max;
+	wacom->dev.absmax[ABS_PRESSURE] = wacom->features->pressure_max;
+	wacom->dev.absmax[ABS_DISTANCE] = wacom->features->distance_max;
+	wacom->dev.absmax[ABS_TILT_X] = 127;
+	wacom->dev.absmax[ABS_TILT_Y] = 127;
+	wacom->dev.absmax[ABS_WHEEL] = 1023;
+
+	wacom->dev.absmax[ABS_RX] = 4097;
+	wacom->dev.absmax[ABS_RY] = 4097;
+	wacom->dev.absmin[ABS_RZ] = -900;
+	wacom->dev.absmax[ABS_RZ] = 899;
+	wacom->dev.absmin[ABS_THROTTLE] = -1023;
+	wacom->dev.absmax[ABS_THROTTLE] = 1023;
+
+	wacom->dev.absfuzz[ABS_X] = 4;
+	wacom->dev.absfuzz[ABS_Y] = 4;
+
+	wacom->dev.private = wacom;
+	wacom->dev.open = wacom_open;
+	wacom->dev.close = wacom_close;
+
+	usb_make_path(dev, path, 64);
+	sprintf(wacom->phys, "%s/input0", path);
+
+	wacom->dev.name = wacom->features->name;
+	wacom->dev.phys = wacom->phys;
+	wacom->dev.id.bustype = BUS_USB;
+	wacom->dev.id.vendor = le16_to_cpu(dev->descriptor.idVendor);
+	wacom->dev.id.product = le16_to_cpu(dev->descriptor.idProduct);
+	wacom->dev.id.version = le16_to_cpu(dev->descriptor.bcdDevice);
+	wacom->dev.dev = &intf->dev;
+	wacom->usbdev = dev;
+
+	endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+	if (wacom->features->pktlen > 10)
+		BUG();
+
+	usb_fill_int_urb(wacom->irq, dev,
+			 usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+			 wacom->data, wacom->features->pktlen,
+			 wacom->features->irq, wacom, endpoint->bInterval);
+	wacom->irq->transfer_dma = wacom->data_dma;
+	wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	input_register_device(&wacom->dev);
+
+	/* ask the tablet to report tablet data */
+	usb_set_report(intf, 3, 2, rep_data, 2);
+	/* repeat once (not sure why the first call often fails) */
+	usb_set_report(intf, 3, 2, rep_data, 2);
+
+	printk(KERN_INFO "input: %s on %s\n", wacom->features->name, path);
+
+	usb_set_intfdata(intf, wacom);
+
+	return 0;
+}
+
+static void wacom_disconnect(struct usb_interface *intf)
+{
+	struct wacom *wacom = usb_get_intfdata (intf);
+
+	usb_set_intfdata(intf, NULL);
+	if (wacom) {
+		usb_kill_urb(wacom->irq);
+		input_unregister_device(&wacom->dev);
+		usb_free_urb(wacom->irq);
+		usb_buffer_free(interface_to_usbdev(intf), 10, wacom->data, wacom->data_dma);
+		kfree(wacom);
+	}
+}
+
+static struct usb_driver wacom_driver = {
+	.owner =	THIS_MODULE,
+	.name =		"wacom",
+	.probe =	wacom_probe,
+	.disconnect =	wacom_disconnect,
+	.id_table =	wacom_ids,
+};
+
+static int __init wacom_init(void)
+{
+	int result = usb_register(&wacom_driver);
+	if (result == 0)
+		info(DRIVER_VERSION ":" DRIVER_DESC);
+	return result;
+}
+
+static void __exit wacom_exit(void)
+{
+	usb_deregister(&wacom_driver);
+}
+
+module_init(wacom_init);
+module_exit(wacom_exit);
diff --git a/drivers/usb/input/xpad.c b/drivers/usb/input/xpad.c
new file mode 100644
index 0000000..d65edb2
--- /dev/null
+++ b/drivers/usb/input/xpad.c
@@ -0,0 +1,362 @@
+/*
+ * X-Box gamepad - v0.0.5
+ *
+ * Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * This driver is based on:
+ *  - information from     http://euc.jp/periphs/xbox-controller.ja.html
+ *  - the iForce driver    drivers/char/joystick/iforce.c
+ *  - the skeleton-driver  drivers/usb/usb-skeleton.c
+ *
+ * Thanks to:
+ *  - ITO Takayuki for providing essential xpad information on his website
+ *  - Vojtech Pavlik     - iforce driver / input subsystem
+ *  - Greg Kroah-Hartman - usb-skeleton driver
+ *
+ * TODO:
+ *  - fine tune axes
+ *  - fix "analog" buttons (reported as digital now)
+ *  - get rumble working
+ *
+ * History:
+ *
+ * 2002-06-27 - 0.0.1 : first version, just said "XBOX HID controller"
+ *
+ * 2002-07-02 - 0.0.2 : basic working version
+ *  - all axes and 9 of the 10 buttons work (german InterAct device)
+ *  - the black button does not work
+ *
+ * 2002-07-14 - 0.0.3 : rework by Vojtech Pavlik
+ *  - indentation fixes
+ *  - usb + input init sequence fixes
+ *
+ * 2002-07-16 - 0.0.4 : minor changes, merge with Vojtech's v0.0.3
+ *  - verified the lack of HID and report descriptors
+ *  - verified that ALL buttons WORK
+ *  - fixed d-pad to axes mapping
+ *
+ * 2002-07-17 - 0.0.5 : simplified d-pad handling
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/usb.h>
+
+#define DRIVER_VERSION "v0.0.5"
+#define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
+#define DRIVER_DESC "X-Box pad driver"
+
+#define XPAD_PKT_LEN 32
+
+static struct xpad_device {
+	u16 idVendor;
+	u16 idProduct;
+	char *name;
+} xpad_device[] = {
+	{ 0x045e, 0x0202, "Microsoft X-Box pad (US)" },
+	{ 0x045e, 0x0285, "Microsoft X-Box pad (Japan)" },
+	{ 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)" },
+	{ 0x0000, 0x0000, "X-Box pad" }
+};
+
+static signed short xpad_btn[] = {
+	BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z,	/* "analog" buttons */
+	BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR,	/* start/back/sticks */
+	-1						/* terminating entry */
+};
+
+static signed short xpad_abs[] = {
+	ABS_X, ABS_Y,		/* left stick */
+	ABS_RX, ABS_RY,		/* right stick */
+	ABS_Z, ABS_RZ,		/* triggers left/right */
+	ABS_HAT0X, ABS_HAT0Y,	/* digital pad */
+	-1			/* terminating entry */
+};
+
+static struct usb_device_id xpad_table [] = {
+	{ USB_INTERFACE_INFO('X', 'B', 0) },	/* X-Box USB-IF not approved class */
+	{ }
+};
+
+MODULE_DEVICE_TABLE (usb, xpad_table);
+
+struct usb_xpad {
+	struct input_dev dev;			/* input device interface */
+	struct usb_device *udev;		/* usb device */
+	
+	struct urb *irq_in;			/* urb for interrupt in report */
+	unsigned char *idata;			/* input data */
+	dma_addr_t idata_dma;
+	
+	char phys[65];				/* physical device path */
+	int open_count;				/* reference count */
+};
+
+/*
+ *	xpad_process_packet
+ *
+ *	Completes a request by converting the data into events for the
+ *	input subsystem.
+ *
+ *	The used report descriptor was taken from ITO Takayukis website:
+ *	 http://euc.jp/periphs/xbox-controller.ja.html
+ */
+
+static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data, struct pt_regs *regs)
+{
+	struct input_dev *dev = &xpad->dev;
+
+	input_regs(dev, regs);
+	
+	/* left stick */
+	input_report_abs(dev, ABS_X, (__s16) (((__s16)data[13] << 8) | data[12]));
+	input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[15] << 8) | data[14]));
+	
+	/* right stick */
+	input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[17] << 8) | data[16]));
+	input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[19] << 8) | data[18]));
+	
+	/* triggers left/right */
+	input_report_abs(dev, ABS_Z, data[10]);
+	input_report_abs(dev, ABS_RZ, data[11]);
+	
+	/* digital pad */
+	input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04));
+	input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01));
+	
+	/* start/back buttons and stick press left/right */
+	input_report_key(dev, BTN_START, (data[2] & 0x10) >> 4);
+	input_report_key(dev, BTN_BACK, (data[2] & 0x20) >> 5);
+	input_report_key(dev, BTN_THUMBL, (data[2] & 0x40) >> 6);
+	input_report_key(dev, BTN_THUMBR, data[2] >> 7);
+	
+	/* "analog" buttons A, B, X, Y */
+	input_report_key(dev, BTN_A, data[4]);
+	input_report_key(dev, BTN_B, data[5]);
+	input_report_key(dev, BTN_X, data[6]);
+	input_report_key(dev, BTN_Y, data[7]);
+	
+	/* "analog" buttons black, white */
+	input_report_key(dev, BTN_C, data[8]);
+	input_report_key(dev, BTN_Z, data[9]);
+
+	input_sync(dev);
+}
+
+static void xpad_irq_in(struct urb *urb, struct pt_regs *regs)
+{
+	struct usb_xpad *xpad = urb->context;
+	int retval;
+	
+	switch (urb->status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+		goto exit;
+	}
+	
+	xpad_process_packet(xpad, 0, xpad->idata, regs);
+
+exit:
+	retval = usb_submit_urb (urb, GFP_ATOMIC);
+	if (retval)
+		err ("%s - usb_submit_urb failed with result %d",
+		     __FUNCTION__, retval);
+}
+
+static int xpad_open (struct input_dev *dev)
+{
+	struct usb_xpad *xpad = dev->private;
+	
+	if (xpad->open_count++)
+		return 0;
+	
+	xpad->irq_in->dev = xpad->udev;
+	if (usb_submit_urb(xpad->irq_in, GFP_KERNEL)) {
+		xpad->open_count--;
+		return -EIO;
+	}
+	
+	return 0;
+}
+
+static void xpad_close (struct input_dev *dev)
+{
+	struct usb_xpad *xpad = dev->private;
+	
+	if (!--xpad->open_count)
+		usb_kill_urb(xpad->irq_in);
+}
+
+static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev (intf);
+	struct usb_xpad *xpad = NULL;
+	struct usb_endpoint_descriptor *ep_irq_in;
+	char path[64];
+	int i;
+	
+	for (i = 0; xpad_device[i].idVendor; i++) {
+		if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
+		    (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct))
+			break;
+	}
+	
+	if ((xpad = kmalloc (sizeof(struct usb_xpad), GFP_KERNEL)) == NULL) {
+		err("cannot allocate memory for new pad");
+		return -ENOMEM;
+	}
+	memset(xpad, 0, sizeof(struct usb_xpad));
+	
+	xpad->idata = usb_buffer_alloc(udev, XPAD_PKT_LEN,
+				       SLAB_ATOMIC, &xpad->idata_dma);
+	if (!xpad->idata) {
+		kfree(xpad);
+		return -ENOMEM;
+	}
+
+	xpad->irq_in = usb_alloc_urb(0, GFP_KERNEL);
+        if (!xpad->irq_in) {
+		err("cannot allocate memory for new pad irq urb");
+		usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
+                kfree(xpad);
+		return -ENOMEM;
+        }
+	
+	ep_irq_in = &intf->cur_altsetting->endpoint[0].desc;
+	
+	usb_fill_int_urb(xpad->irq_in, udev,
+			 usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress),
+			 xpad->idata, XPAD_PKT_LEN, xpad_irq_in,
+			 xpad, ep_irq_in->bInterval);
+	xpad->irq_in->transfer_dma = xpad->idata_dma;
+	xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	
+	xpad->udev = udev;
+	
+	xpad->dev.id.bustype = BUS_USB;
+	xpad->dev.id.vendor = le16_to_cpu(udev->descriptor.idVendor);
+	xpad->dev.id.product = le16_to_cpu(udev->descriptor.idProduct);
+	xpad->dev.id.version = le16_to_cpu(udev->descriptor.bcdDevice);
+	xpad->dev.dev = &intf->dev;
+	xpad->dev.private = xpad;
+	xpad->dev.name = xpad_device[i].name;
+	xpad->dev.phys = xpad->phys;
+	xpad->dev.open = xpad_open;
+	xpad->dev.close = xpad_close;
+	
+	usb_make_path(udev, path, 64);
+	snprintf(xpad->phys, 64,  "%s/input0", path);
+	
+	xpad->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+	
+	for (i = 0; xpad_btn[i] >= 0; i++)
+		set_bit(xpad_btn[i], xpad->dev.keybit);
+	
+	for (i = 0; xpad_abs[i] >= 0; i++) {
+		
+		signed short t = xpad_abs[i];
+		
+		set_bit(t, xpad->dev.absbit);
+		
+		switch (t) {
+			case ABS_X:
+			case ABS_Y:
+			case ABS_RX:
+			case ABS_RY:	/* the two sticks */
+				xpad->dev.absmax[t] =  32767;
+				xpad->dev.absmin[t] = -32768;
+				xpad->dev.absflat[t] = 128;
+				xpad->dev.absfuzz[t] = 16;
+				break;
+			case ABS_Z:
+			case ABS_RZ:	/* the triggers */
+				xpad->dev.absmax[t] = 255;
+				xpad->dev.absmin[t] = 0;
+				break;
+			case ABS_HAT0X:
+			case ABS_HAT0Y:	/* the d-pad */
+				xpad->dev.absmax[t] =  1;
+				xpad->dev.absmin[t] = -1;
+				break;
+		}
+	}
+	
+	input_register_device(&xpad->dev);
+	
+	printk(KERN_INFO "input: %s on %s", xpad->dev.name, path);
+	
+	usb_set_intfdata(intf, xpad);
+	return 0;
+}
+
+static void xpad_disconnect(struct usb_interface *intf)
+{
+	struct usb_xpad *xpad = usb_get_intfdata (intf);
+	
+	usb_set_intfdata(intf, NULL);
+	if (xpad) {
+		usb_kill_urb(xpad->irq_in);
+		input_unregister_device(&xpad->dev);
+		usb_free_urb(xpad->irq_in);
+		usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
+		kfree(xpad);
+	}
+}
+
+static struct usb_driver xpad_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "xpad",
+	.probe		= xpad_probe,
+	.disconnect	= xpad_disconnect,
+	.id_table	= xpad_table,
+};
+
+static int __init usb_xpad_init(void)
+{
+	int result = usb_register(&xpad_driver);
+	if (result == 0)
+		info(DRIVER_DESC ":" DRIVER_VERSION);
+	return result;
+}
+
+static void __exit usb_xpad_exit(void)
+{
+	usb_deregister(&xpad_driver);
+}
+
+module_init(usb_xpad_init);
+module_exit(usb_xpad_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");