aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/input
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/usb/input
downloadlinux-dev-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz
linux-dev-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip
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!
Diffstat (limited to 'drivers/usb/input')
-rw-r--r--drivers/usb/input/Kconfig237
-rw-r--r--drivers/usb/input/Makefile39
-rw-r--r--drivers/usb/input/aiptek.c2283
-rw-r--r--drivers/usb/input/ati_remote.c850
-rw-r--r--drivers/usb/input/fixp-arith.h90
-rw-r--r--drivers/usb/input/hid-core.c1864
-rw-r--r--drivers/usb/input/hid-debug.h720
-rw-r--r--drivers/usb/input/hid-ff.c94
-rw-r--r--drivers/usb/input/hid-input.c630
-rw-r--r--drivers/usb/input/hid-lgff.c528
-rw-r--r--drivers/usb/input/hid-tmff.c463
-rw-r--r--drivers/usb/input/hid.h510
-rw-r--r--drivers/usb/input/hiddev.c844
-rw-r--r--drivers/usb/input/kbtab.c241
-rw-r--r--drivers/usb/input/mtouchusb.c367
-rw-r--r--drivers/usb/input/pid.c295
-rw-r--r--drivers/usb/input/pid.h62
-rw-r--r--drivers/usb/input/powermate.c464
-rw-r--r--drivers/usb/input/touchkitusb.c310
-rw-r--r--drivers/usb/input/usbkbd.c370
-rw-r--r--drivers/usb/input/usbmouse.c252
-rw-r--r--drivers/usb/input/wacom.c951
-rw-r--r--drivers/usb/input/xpad.c362
23 files changed, 12826 insertions, 0 deletions
diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig
new file mode 100644
index 000000000000..d28e7eab6f98
--- /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 000000000000..6bcedd16b0a1
--- /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 000000000000..d7fea9ea301b
--- /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 000000000000..355add5c29f5
--- /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 000000000000..26ca5b890a61
--- /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 000000000000..7662cf4e2621
--- /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 000000000000..2b91705740a7
--- /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 000000000000..72e698658b53
--- /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 000000000000..5553c3553e9d
--- /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 000000000000..0d7404bab92f
--- /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 000000000000..8f6a0a6f94a9
--- /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 000000000000..6d9329c698d9
--- /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 000000000000..96b7c9067951
--- /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 000000000000..a68c5b4e7b37
--- /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 000000000000..6b45a66d58c1
--- /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 000000000000..256963863478
--- /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 000000000000..a2cb9627ed0e
--- /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 000000000000..7fa2f9b9fb69
--- /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 000000000000..a71f1bbd0a17
--- /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 000000000000..01514b0551b8
--- /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 000000000000..01155bbddd43
--- /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 000000000000..fec04dda088e
--- /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 000000000000..d65edb22e545
--- /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");