]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 24 Oct 2010 19:44:59 +0000 (12:44 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 24 Oct 2010 19:44:59 +0000 (12:44 -0700)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (47 commits)
  HID: fix mismerge in hid-lg
  HID: hidraw: fix window in hidraw_release
  HID: hid-sony: override usbhid_output_raw_report for Sixaxis
  HID: add absolute axis resolution calculation
  HID: force feedback support for Logitech RumblePad gamepad
  HID: support STmicroelectronics and Sitronix with hid-stantuml driver
  HID: magicmouse: Adjust major / minor axes to scale
  HID: Fix for problems with eGalax/DWAV multi-touch-screen
  HID: waltop: add support for Waltop Slim Tablet 12.1 inch
  HID: add NOGET quirk for AXIS 295 Video Surveillance Joystick
  HID: usbhid: remove unused hiddev_driver
  HID: magicmouse: Use hid-input parsing rather than bypassing it
  HID: trivial formatting fix
  HID: Add support for Logitech Speed Force Wireless gaming wheel
  HID: don't Send Feature Reports on Interrupt Endpoint
  HID: 3m: Adjust major / minor axes to scale
  HID: 3m: Correct touchscreen emulation
  HID: 3m: Convert to MT slots
  HID: 3m: Output proper orientation range
  HID: 3m: Adjust to sequential MT HID protocol
  ...

42 files changed:
Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra [new file with mode: 0644]
Documentation/input/ntrig.txt [new file with mode: 0644]
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-3m-pct.c
drivers/hid/hid-a4tech.c
drivers/hid/hid-apple.c
drivers/hid/hid-cherry.c
drivers/hid/hid-core.c
drivers/hid/hid-cypress.c
drivers/hid/hid-debug.c
drivers/hid/hid-egalax.c
drivers/hid/hid-elecom.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-kye.c
drivers/hid/hid-lg.c
drivers/hid/hid-lg.h
drivers/hid/hid-lg2ff.c
drivers/hid/hid-lg4ff.c [new file with mode: 0644]
drivers/hid/hid-magicmouse.c
drivers/hid/hid-microsoft.c
drivers/hid/hid-monterey.c
drivers/hid/hid-ntrig.c
drivers/hid/hid-ortek.c
drivers/hid/hid-petalynx.c
drivers/hid/hid-prodikeys.c
drivers/hid/hid-roccat-pyra.c [new file with mode: 0644]
drivers/hid/hid-roccat-pyra.h [new file with mode: 0644]
drivers/hid/hid-samsung.c
drivers/hid/hid-sony.c
drivers/hid/hid-stantum.c
drivers/hid/hid-sunplus.c
drivers/hid/hid-uclogic.c [new file with mode: 0644]
drivers/hid/hid-waltop.c [new file with mode: 0644]
drivers/hid/hid-zydacron.c
drivers/hid/hidraw.c
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/hid-quirks.c
drivers/hid/usbhid/hiddev.c
include/linux/hid.h
include/linux/hiddev.h

diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra b/Documentation/ABI/testing/sysfs-driver-hid-roccat-pyra
new file mode 100644 (file)
index 0000000..ad1125b
--- /dev/null
@@ -0,0 +1,98 @@
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/actual_cpi
+Date:          August 2010
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   It is possible to switch the cpi setting of the mouse with the
+               press of a button.
+               When read, this file returns the raw number of the actual cpi
+               setting reported by the mouse. This number has to be further
+               processed to receive the real dpi value.
+
+               VALUE DPI
+               1     400
+               2     800
+               4     1600
+
+               This file is readonly.
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/actual_profile
+Date:          August 2010
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When read, this file returns the number of the actual profile in
+               range 0-4.
+               This file is readonly.
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/firmware_version
+Date:          August 2010
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When read, this file returns the raw integer version number of the
+               firmware reported by the mouse. Using the integer value eases
+               further usage in other programs. To receive the real version
+               number the decimal point has to be shifted 2 positions to the
+               left. E.g. a returned value of 138 means 1.38
+               This file is readonly.
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile_settings
+Date:          August 2010
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   The mouse can store 5 profiles which can be switched by the
+               press of a button. A profile is split in settings and buttons.
+               profile_settings holds informations like resolution, sensitivity
+               and light effects.
+               When written, this file lets one write the respective profile
+               settings back to the mouse. The data has to be 13 bytes long.
+               The mouse will reject invalid data.
+               Which profile to write is determined by the profile number
+               contained in the data.
+               This file is writeonly.
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile[1-5]_settings
+Date:          August 2010
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   The mouse can store 5 profiles which can be switched by the
+               press of a button. A profile is split in settings and buttons.
+               profile_settings holds informations like resolution, sensitivity
+               and light effects.
+               When read, these files return the respective profile settings.
+               The returned data is 13 bytes in size.
+               This file is readonly.
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile_buttons
+Date:          August 2010
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   The mouse can store 5 profiles which can be switched by the
+               press of a button. A profile is split in settings and buttons.
+               profile_buttons holds informations about button layout.
+               When written, this file lets one write the respective profile
+               buttons back to the mouse. The data has to be 19 bytes long.
+               The mouse will reject invalid data.
+               Which profile to write is determined by the profile number
+               contained in the data.
+               This file is writeonly.
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile[1-5]_buttons
+Date:          August 2010
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   The mouse can store 5 profiles which can be switched by the
+               press of a button. A profile is split in settings and buttons.
+               profile_buttons holds informations about button layout.
+               When read, these files return the respective profile buttons.
+               The returned data is 19 bytes in size.
+               This file is readonly.
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/startup_profile
+Date:          August 2010
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   The integer value of this attribute ranges from 0-4.
+                When read, this attribute returns the number of the profile
+                that's active when the mouse is powered on.
+               This file is readonly.
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/settings
+Date:          August 2010
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When read, this file returns the settings stored in the mouse.
+               The size of the data is 3 bytes and holds information on the
+               startup_profile.
+               When written, this file lets write settings back to the mouse.
+               The data has to be 3 bytes long. The mouse will reject invalid
+               data.
diff --git a/Documentation/input/ntrig.txt b/Documentation/input/ntrig.txt
new file mode 100644 (file)
index 0000000..be1fd98
--- /dev/null
@@ -0,0 +1,126 @@
+N-Trig touchscreen Driver
+-------------------------
+       Copyright (c) 2008-2010 Rafi Rubin <rafi@seas.upenn.edu>
+       Copyright (c) 2009-2010 Stephane Chatty
+
+This driver provides support for N-Trig pen and multi-touch sensors.  Single
+and multi-touch events are translated to the appropriate protocols for
+the hid and input systems.  Pen events are sufficiently hid compliant and
+are left to the hid core.  The driver also provides additional filtering
+and utility functions accessible with sysfs and module parameters.
+
+This driver has been reported to work properly with multiple N-Trig devices
+attached.
+
+
+Parameters
+----------
+
+Note: values set at load time are global and will apply to all applicable
+devices.  Adjusting parameters with sysfs will override the load time values,
+but only for that one device.
+
+The following parameters are used to configure filters to reduce noise:
+
+activate_slack         number of fingers to ignore before processing events
+
+activation_height      size threshold to activate immediately
+activation_width
+
+min_height             size threshold bellow which fingers are ignored
+min_width              both to decide activation and during activity
+
+deactivate_slack       the number of "no contact" frames to ignore before
+                       propagating the end of activity events
+
+When the last finger is removed from the device, it sends a number of empty
+frames.  By holding off on deactivation for a few frames we can tolerate false
+erroneous disconnects, where the sensor may mistakenly not detect a finger that
+is still present.  Thus deactivate_slack addresses problems where a users might
+see breaks in lines during drawing, or drop an object during a long drag.
+
+
+Additional sysfs items
+----------------------
+
+These nodes just provide easy access to the ranges reported by the device.
+sensor_logical_height  the range for positions reported during activity
+sensor_logical_width
+
+sensor_physical_height internal ranges not used for normal events but
+sensor_physical_width  useful for tuning
+
+All N-Trig devices with product id of 1 report events in the ranges of
+X: 0-9600
+Y: 0-7200
+However not all of these devices have the same physical dimensions.  Most
+seem to be 12" sensors (Dell Latitude XT and XT2 and the HP TX2), and
+at least one model (Dell Studio 17) has a 17" sensor.  The ratio of physical
+to logical sizes is used to adjust the size based filter parameters.
+
+
+Filtering
+---------
+
+With the release of the early multi-touch firmwares it became increasingly
+obvious that these sensors were prone to erroneous events.  Users reported
+seeing both inappropriately dropped contact and ghosts, contacts reported
+where no finger was actually touching the screen.
+
+Deactivation slack helps prevent dropped contact for single touch use, but does
+not address the problem of dropping one of more contacts while other contacts
+are still active.  Drops in the multi-touch context require additional
+processing and should be handled in tandem with tacking.
+
+As observed ghost contacts are similar to actual use of the sensor, but they
+seem to have different profiles.  Ghost activity typically shows up as small
+short lived touches.  As such, I assume that the longer the continuous stream
+of events the more likely those events are from a real contact, and that the
+larger the size of each contact the more likely it is real.  Balancing the
+goals of preventing ghosts and accepting real events quickly (to minimize
+user observable latency), the filter accumulates confidence for incoming
+events until it hits thresholds and begins propagating.  In the interest in
+minimizing stored state as well as the cost of operations to make a decision,
+I've kept that decision simple.
+
+Time is measured in terms of the number of fingers reported, not frames since
+the probability of multiple simultaneous ghosts is expected to drop off
+dramatically with increasing numbers.  Rather than accumulate weight as a
+function of size, I just use it as a binary threshold.  A sufficiently large
+contact immediately overrides the waiting period and leads to activation.
+
+Setting the activation size thresholds to large values will result in deciding
+primarily on activation slack.  If you see longer lived ghosts, turning up the
+activation slack while reducing the size thresholds may suffice to eliminate
+the ghosts while keeping the screen quite responsive to firm taps.
+
+Contacts continue to be filtered with min_height and min_width even after
+the initial activation filter is satisfied.  The intent is to provide
+a mechanism for filtering out ghosts in the form of an extra finger while
+you actually are using the screen.  In practice this sort of ghost has
+been far less problematic or relatively rare and I've left the defaults
+set to 0 for both parameters, effectively turning off that filter.
+
+I don't know what the optimal values are for these filters.  If the defaults
+don't work for you, please play with the parameters.  If you do find other
+values more comfortable, I would appreciate feedback.
+
+The calibration of these devices does drift over time.  If ghosts or contact
+dropping worsen and interfere with the normal usage of your device, try
+recalibrating it.
+
+
+Calibration
+-----------
+
+The N-Trig windows tools provide calibration and testing routines.  Also an
+unofficial unsupported set of user space tools including a calibrator is
+available at:
+http://code.launchpad.net/~rafi-seas/+junk/ntrig_calib
+
+
+Tracking
+--------
+
+As of yet, all tested N-Trig firmwares do not track fingers.  When multiple
+contacts are active they seem to be sorted primarily by Y position.
index 6369ba7f96f8c0cb2bce6ae0301fd2962027f914..3052e2969ad03c049f77f4956ea804e60eea8123 100644 (file)
@@ -56,20 +56,20 @@ menu "Special HID drivers"
        depends on HID
 
 config HID_3M_PCT
-       tristate "3M PCT"
+       tristate "3M PCT touchscreen"
        depends on USB_HID
        ---help---
        Support for 3M PCT touch screens.
 
 config HID_A4TECH
-       tristate "A4 tech" if EMBEDDED
+       tristate "A4 tech mice" if EMBEDDED
        depends on USB_HID
        default !EMBEDDED
        ---help---
        Support for A4 tech X5 and WOP-35 / Trust 450L mice.
 
 config HID_ACRUX_FF
-       tristate "ACRUX force feedback support"
+       tristate "ACRUX force feedback"
        depends on USB_HID
        select INPUT_FF_MEMLESS
        ---help---
@@ -77,7 +77,7 @@ config HID_ACRUX_FF
        game controllers.
 
 config HID_APPLE
-       tristate "Apple" if EMBEDDED
+       tristate "Apple {i,Power,Mac}Books" if EMBEDDED
        depends on (USB_HID || BT_HIDP)
        default !EMBEDDED
        ---help---
@@ -88,7 +88,7 @@ config HID_APPLE
        MacBooks, MacBook Pros and Apple Aluminum.
 
 config HID_BELKIN
-       tristate "Belkin" if EMBEDDED
+       tristate "Belkin Flip KVM and Wireless keyboard" if EMBEDDED
        depends on USB_HID
        default !EMBEDDED
        ---help---
@@ -101,14 +101,14 @@ config HID_CANDO
        Support for Cando dual touch panel.
 
 config HID_CHERRY
-       tristate "Cherry" if EMBEDDED
+       tristate "Cherry Cymotion keyboard" if EMBEDDED
        depends on USB_HID
        default !EMBEDDED
        ---help---
        Support for Cherry Cymotion keyboard.
 
 config HID_CHICONY
-       tristate "Chicony" if EMBEDDED
+       tristate "Chicony Tactical pad" if EMBEDDED
        depends on USB_HID
        default !EMBEDDED
        ---help---
@@ -130,20 +130,20 @@ config HID_PRODIKEYS
          and some additional multimedia keys.
 
 config HID_CYPRESS
-       tristate "Cypress" if EMBEDDED
+       tristate "Cypress mouse and barcode readers" if EMBEDDED
        depends on USB_HID
        default !EMBEDDED
        ---help---
        Support for cypress mouse and barcode readers.
 
 config HID_DRAGONRISE
-       tristate "DragonRise Inc. support"
+       tristate "DragonRise Inc. game controller"
        depends on USB_HID
        ---help---
        Say Y here if you have DragonRise Inc.game controllers.
 
 config DRAGONRISE_FF
-       bool "DragonRise Inc. force feedback support"
+       bool "DragonRise Inc. force feedback"
        depends on HID_DRAGONRISE
        select INPUT_FF_MEMLESS
        ---help---
@@ -157,46 +157,58 @@ config HID_EGALAX
        Support for the eGalax dual-touch panel.
 
 config HID_ELECOM
-       tristate "ELECOM"
+       tristate "ELECOM BM084 bluetooth mouse"
        depends on BT_HIDP
        ---help---
        Support for the ELECOM BM084 (bluetooth mouse).
 
 config HID_EZKEY
-       tristate "Ezkey" if EMBEDDED
+       tristate "Ezkey BTC 8193 keyboard" if EMBEDDED
        depends on USB_HID
        default !EMBEDDED
        ---help---
        Support for Ezkey BTC 8193 keyboard.
 
 config HID_KYE
-       tristate "Kye" if EMBEDDED
+       tristate "Kye/Genius Ergo Mouse" if EMBEDDED
        depends on USB_HID
        default !EMBEDDED
        ---help---
        Support for Kye/Genius Ergo Mouse.
 
+config HID_UCLOGIC
+       tristate "UC-Logic"
+       depends on USB_HID
+       ---help---
+       Support for UC-Logic tablets.
+
+config HID_WALTOP
+       tristate "Waltop"
+       depends on USB_HID
+       ---help---
+       Support for Waltop tablets.
+
 config HID_GYRATION
-       tristate "Gyration"
+       tristate "Gyration remote control"
        depends on USB_HID
        ---help---
        Support for Gyration remote control.
 
 config HID_TWINHAN
-       tristate "Twinhan"
+       tristate "Twinhan IR remote control"
        depends on USB_HID
        ---help---
        Support for Twinhan IR remote control.
 
 config HID_KENSINGTON
-       tristate "Kensington" if EMBEDDED
+       tristate "Kensington Slimblade Trackball" if EMBEDDED
        depends on USB_HID
        default !EMBEDDED
        ---help---
        Support for Kensington Slimblade Trackball.
 
 config HID_LOGITECH
-       tristate "Logitech" if EMBEDDED
+       tristate "Logitech devices" if EMBEDDED
        depends on USB_HID
        default !EMBEDDED
        ---help---
@@ -220,12 +232,12 @@ config LOGITECH_FF
          force feedback.
 
 config LOGIRUMBLEPAD2_FF
-       bool "Logitech Rumblepad 2 force feedback support"
+       bool "Logitech RumblePad/Rumblepad 2 force feedback support"
        depends on HID_LOGITECH
        select INPUT_FF_MEMLESS
        help
          Say Y here if you want to enable force feedback support for Logitech
-         Rumblepad 2 devices.
+         RumblePad and Rumblepad 2 devices.
 
 config LOGIG940_FF
        bool "Logitech Flight System G940 force feedback support"
@@ -235,6 +247,14 @@ config LOGIG940_FF
          Say Y here if you want to enable force feedback support for Logitech
          Flight System G940 devices.
 
+config LOGIWII_FF
+       bool "Logitech Speed Force Wireless force feedback support"
+       depends on HID_LOGITECH
+       select INPUT_FF_MEMLESS
+       help
+         Say Y here if you want to enable force feedback support for Logitech
+         Speed Force Wireless (Wii) devices.
+
 config HID_MAGICMOUSE
        tristate "Apple MagicMouse multi-touch support"
        depends on BT_HIDP
@@ -245,39 +265,39 @@ config HID_MAGICMOUSE
        Apple Wireless "Magic" Mouse.
 
 config HID_MICROSOFT
-       tristate "Microsoft" if EMBEDDED
+       tristate "Microsoft non-fully HID-compliant devices" if EMBEDDED
        depends on USB_HID
        default !EMBEDDED
        ---help---
        Support for Microsoft devices that are not fully compliant with HID standard.
 
 config HID_MOSART
-       tristate "MosArt"
+       tristate "MosArt dual-touch panels"
        depends on USB_HID
        ---help---
        Support for MosArt dual-touch panels.
 
 config HID_MONTEREY
-       tristate "Monterey" if EMBEDDED
+       tristate "Monterey Genius KB29E keyboard" if EMBEDDED
        depends on USB_HID
        default !EMBEDDED
        ---help---
        Support for Monterey Genius KB29E.
 
 config HID_NTRIG
-       tristate "NTrig"
+       tristate "N-Trig touch screen"
        depends on USB_HID
        ---help---
        Support for N-Trig touch screen.
 
 config HID_ORTEK
-       tristate "Ortek"
+       tristate "Ortek WKB-2000 wireless keyboard and mouse trackpad"
        depends on USB_HID
        ---help---
        Support for Ortek WKB-2000 wireless keyboard + mouse trackpad.
 
 config HID_PANTHERLORD
-       tristate "Pantherlord support"
+       tristate "Pantherlord/GreenAsia game controller"
        depends on USB_HID
        ---help---
          Say Y here if you have a PantherLord/GreenAsia based game controller
@@ -292,7 +312,7 @@ config PANTHERLORD_FF
          or adapter and want to enable force feedback support for it.
 
 config HID_PETALYNX
-       tristate "Petalynx"
+       tristate "Petalynx Maxter remote control"
        depends on USB_HID
        ---help---
        Support for Petalynx Maxter remote control.
@@ -356,7 +376,7 @@ config HID_PICOLCD_LEDS
          Provide access to PicoLCD's GPO pins via leds class.
 
 config HID_QUANTA
-       tristate "Quanta Optical Touch"
+       tristate "Quanta Optical Touch panels"
        depends on USB_HID
        ---help---
        Support for Quanta Optical Touch dual-touch panels.
@@ -376,32 +396,39 @@ config HID_ROCCAT_KONE
        ---help---
        Support for Roccat Kone mouse.
 
+config HID_ROCCAT_PYRA
+       tristate "Roccat Pyra mouse support"
+       depends on USB_HID
+       select HID_ROCCAT
+       ---help---
+       Support for Roccat Pyra mouse.
+
 config HID_SAMSUNG
-       tristate "Samsung"
+       tristate "Samsung InfraRed remote control or keyboards"
        depends on USB_HID
        ---help---
        Support for Samsung InfraRed remote control or keyboards.
 
 config HID_SONY
-       tristate "Sony"
+       tristate "Sony PS3 controller"
        depends on USB_HID
        ---help---
        Support for Sony PS3 controller.
 
 config HID_STANTUM
-       tristate "Stantum"
+       tristate "Stantum multitouch panel"
        depends on USB_HID
        ---help---
        Support for Stantum multitouch panel.
 
 config HID_SUNPLUS
-       tristate "Sunplus"
+       tristate "Sunplus wireless desktop"
        depends on USB_HID
        ---help---
        Support for Sunplus wireless desktop.
 
 config HID_GREENASIA
-       tristate "GreenAsia (Product ID 0x12) support"
+       tristate "GreenAsia (Product ID 0x12) game controller support"
        depends on USB_HID
        ---help---
          Say Y here if you have a GreenAsia (Product ID 0x12) based game
index 46f037f3df80ae4eefc30b64df583ff48b894534..c335605b920038098886b927cdf3340effbd0fd0 100644 (file)
@@ -21,6 +21,9 @@ endif
 ifdef CONFIG_LOGIG940_FF
        hid-logitech-objs       += hid-lg3ff.o
 endif
+ifdef CONFIG_LOGIWII_FF
+       hid-logitech-objs       += hid-lg4ff.o
+endif
 
 obj-$(CONFIG_HID_3M_PCT)       += hid-3m-pct.o
 obj-$(CONFIG_HID_A4TECH)       += hid-a4tech.o
@@ -52,6 +55,7 @@ obj-$(CONFIG_HID_PETALYNX)    += hid-petalynx.o
 obj-$(CONFIG_HID_PICOLCD)      += hid-picolcd.o
 obj-$(CONFIG_HID_ROCCAT)       += hid-roccat.o
 obj-$(CONFIG_HID_ROCCAT_KONE)  += hid-roccat-kone.o
+obj-$(CONFIG_HID_ROCCAT_PYRA)  += hid-roccat-pyra.o
 obj-$(CONFIG_HID_SAMSUNG)      += hid-samsung.o
 obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
 obj-$(CONFIG_HID_SONY)         += hid-sony.o
@@ -61,9 +65,11 @@ obj-$(CONFIG_HID_GREENASIA)  += hid-gaff.o
 obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
 obj-$(CONFIG_HID_TOPSEED)      += hid-topseed.o
 obj-$(CONFIG_HID_TWINHAN)      += hid-twinhan.o
+obj-$(CONFIG_HID_UCLOGIC)      += hid-uclogic.o
 obj-$(CONFIG_HID_ZEROPLUS)     += hid-zpff.o
 obj-$(CONFIG_HID_ZYDACRON)     += hid-zydacron.o
 obj-$(CONFIG_HID_WACOM)                += hid-wacom.o
+obj-$(CONFIG_HID_WALTOP)       += hid-waltop.o
 
 obj-$(CONFIG_USB_HID)          += usbhid/
 obj-$(CONFIG_USB_MOUSE)                += usbhid/
index 2a0d56b7a02be34fe0b5d09eff67148730424a9f..02d8cd3b1b1b8aed47644f48b3830da657e4fbd7 100644 (file)
@@ -2,6 +2,8 @@
  *  HID driver for 3M PCT multitouch panels
  *
  *  Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr>
+ *  Copyright (c) 2010      Henrik Rydberg <rydberg@euromail.se>
+ *  Copyright (c) 2010      Canonical, Ltd.
  *
  */
 
@@ -24,15 +26,26 @@ MODULE_LICENSE("GPL");
 
 #include "hid-ids.h"
 
+#define MAX_SLOTS              60
+#define MAX_TRKID              USHRT_MAX
+#define MAX_EVENTS             360
+
+/* estimated signal-to-noise ratios */
+#define SN_MOVE                        2048
+#define SN_WIDTH               128
+
 struct mmm_finger {
        __s32 x, y, w, h;
-       __u8 rank;
+       __u16 id;
+       bool prev_touch;
        bool touch, valid;
 };
 
 struct mmm_data {
-       struct mmm_finger f[10];
-       __u8 curid, num;
+       struct mmm_finger f[MAX_SLOTS];
+       __u16 id;
+       __u8 curid;
+       __u8 nexp, nreal;
        bool touch, valid;
 };
 
@@ -40,6 +53,10 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                struct hid_field *field, struct hid_usage *usage,
                unsigned long **bit, int *max)
 {
+       int f1 = field->logical_minimum;
+       int f2 = field->logical_maximum;
+       int df = f2 - f1;
+
        switch (usage->hid & HID_USAGE_PAGE) {
 
        case HID_UP_BUTTON:
@@ -50,18 +67,20 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                case HID_GD_X:
                        hid_map_usage(hi, usage, bit, max,
                                        EV_ABS, ABS_MT_POSITION_X);
+                       input_set_abs_params(hi->input, ABS_MT_POSITION_X,
+                                            f1, f2, df / SN_MOVE, 0);
                        /* touchscreen emulation */
                        input_set_abs_params(hi->input, ABS_X,
-                                               field->logical_minimum,
-                                               field->logical_maximum, 0, 0);
+                                            f1, f2, df / SN_MOVE, 0);
                        return 1;
                case HID_GD_Y:
                        hid_map_usage(hi, usage, bit, max,
                                        EV_ABS, ABS_MT_POSITION_Y);
+                       input_set_abs_params(hi->input, ABS_MT_POSITION_Y,
+                                            f1, f2, df / SN_MOVE, 0);
                        /* touchscreen emulation */
                        input_set_abs_params(hi->input, ABS_Y,
-                                               field->logical_minimum,
-                                               field->logical_maximum, 0, 0);
+                                            f1, f2, df / SN_MOVE, 0);
                        return 1;
                }
                return 0;
@@ -81,21 +100,31 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                case HID_DG_TIPSWITCH:
                        /* touchscreen emulation */
                        hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
+                       input_set_capability(hi->input, EV_KEY, BTN_TOUCH);
                        return 1;
                case HID_DG_WIDTH:
                        hid_map_usage(hi, usage, bit, max,
                                        EV_ABS, ABS_MT_TOUCH_MAJOR);
+                       input_set_abs_params(hi->input, ABS_MT_TOUCH_MAJOR,
+                                            f1, f2, df / SN_WIDTH, 0);
                        return 1;
                case HID_DG_HEIGHT:
                        hid_map_usage(hi, usage, bit, max,
                                        EV_ABS, ABS_MT_TOUCH_MINOR);
+                       input_set_abs_params(hi->input, ABS_MT_TOUCH_MINOR,
+                                            f1, f2, df / SN_WIDTH, 0);
                        input_set_abs_params(hi->input, ABS_MT_ORIENTATION,
-                                       1, 1, 0, 0);
+                                       0, 1, 0, 0);
                        return 1;
                case HID_DG_CONTACTID:
-                       field->logical_maximum = 59;
+                       field->logical_maximum = MAX_TRKID;
                        hid_map_usage(hi, usage, bit, max,
                                        EV_ABS, ABS_MT_TRACKING_ID);
+                       input_set_abs_params(hi->input, ABS_MT_TRACKING_ID,
+                                            0, MAX_TRKID, 0, 0);
+                       if (!hi->input->mt)
+                               input_mt_create_slots(hi->input, MAX_SLOTS);
+                       input_set_events_per_packet(hi->input, MAX_EVENTS);
                        return 1;
                }
                /* let hid-input decide for the others */
@@ -113,10 +142,10 @@ static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi,
                struct hid_field *field, struct hid_usage *usage,
                unsigned long **bit, int *max)
 {
+       /* tell hid-input to skip setup of these event types */
        if (usage->type == EV_KEY || usage->type == EV_ABS)
-               clear_bit(usage->code, *bit);
-
-       return 0;
+               set_bit(usage->type, hi->input->evbit);
+       return -1;
 }
 
 /*
@@ -126,70 +155,49 @@ static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi,
 static void mmm_filter_event(struct mmm_data *md, struct input_dev *input)
 {
        struct mmm_finger *oldest = 0;
-       bool pressed = false, released = false;
        int i;
-
-       /*
-        * we need to iterate on all fingers to decide if we have a press
-        * or a release event in our touchscreen emulation.
-        */
-       for (i = 0; i < 10; ++i) {
+       for (i = 0; i < MAX_SLOTS; ++i) {
                struct mmm_finger *f = &md->f[i];
                if (!f->valid) {
                        /* this finger is just placeholder data, ignore */
-               } else if (f->touch) {
+                       continue;
+               }
+               input_mt_slot(input, i);
+               if (f->touch) {
                        /* this finger is on the screen */
                        int wide = (f->w > f->h);
-                       input_event(input, EV_ABS, ABS_MT_TRACKING_ID, i);
+                       /* divided by two to match visual scale of touch */
+                       int major = max(f->w, f->h) >> 1;
+                       int minor = min(f->w, f->h) >> 1;
+
+                       if (!f->prev_touch)
+                               f->id = md->id++;
+                       input_event(input, EV_ABS, ABS_MT_TRACKING_ID, f->id);
                        input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x);
                        input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y);
                        input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide);
-                       input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR,
-                                               wide ? f->w : f->h);
-                       input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR,
-                                               wide ? f->h : f->w);
-                       input_mt_sync(input);
-                       /*
-                        * touchscreen emulation: maintain the age rank
-                        * of this finger, decide if we have a press
-                        */
-                       if (f->rank == 0) {
-                               f->rank = ++(md->num);
-                               if (f->rank == 1)
-                                       pressed = true;
-                       }
-                       if (f->rank == 1)
+                       input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
+                       input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
+                       /* touchscreen emulation: pick the oldest contact */
+                       if (!oldest || ((f->id - oldest->id) & (SHRT_MAX + 1)))
                                oldest = f;
                } else {
                        /* this finger took off the screen */
-                       /* touchscreen emulation: maintain age rank of others */
-                       int j;
-
-                       for (j = 0; j < 10; ++j) {
-                               struct mmm_finger *g = &md->f[j];
-                               if (g->rank > f->rank) {
-                                       g->rank--;
-                                       if (g->rank == 1)
-                                               oldest = g;
-                               }
-                       }
-                       f->rank = 0;
-                       --(md->num);
-                       if (md->num == 0)
-                               released = true;
+                       input_event(input, EV_ABS, ABS_MT_TRACKING_ID, -1);
                }
+               f->prev_touch = f->touch;
                f->valid = 0;
        }
 
        /* touchscreen emulation */
        if (oldest) {
-               if (pressed)
-                       input_event(input, EV_KEY, BTN_TOUCH, 1);
+               input_event(input, EV_KEY, BTN_TOUCH, 1);
                input_event(input, EV_ABS, ABS_X, oldest->x);
                input_event(input, EV_ABS, ABS_Y, oldest->y);
-       } else if (released) {
+       } else {
                input_event(input, EV_KEY, BTN_TOUCH, 0);
        }
+       input_sync(input);
 }
 
 /*
@@ -223,10 +231,12 @@ static int mmm_event(struct hid_device *hid, struct hid_field *field,
                                md->f[md->curid].h = value;
                        break;
                case HID_DG_CONTACTID:
+                       value = clamp_val(value, 0, MAX_SLOTS - 1);
                        if (md->valid) {
                                md->curid = value;
                                md->f[value].touch = md->touch;
                                md->f[value].valid = 1;
+                               md->nreal++;
                        }
                        break;
                case HID_GD_X:
@@ -238,7 +248,12 @@ static int mmm_event(struct hid_device *hid, struct hid_field *field,
                                md->f[md->curid].y = value;
                        break;
                case HID_DG_CONTACTCOUNT:
-                       mmm_filter_event(md, input);
+                       if (value)
+                               md->nexp = value;
+                       if (md->nreal >= md->nexp) {
+                               mmm_filter_event(md, input);
+                               md->nreal = 0;
+                       }
                        break;
                }
        }
@@ -255,6 +270,8 @@ static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id)
        int ret;
        struct mmm_data *md;
 
+       hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
+
        md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL);
        if (!md) {
                dev_err(&hdev->dev, "cannot allocate 3M data\n");
index 3a2b223c1da4a23e3fb346116cba22ed5278d92a..1666c1684e79d922858ff8c6c4e6f530e50446f5 100644 (file)
@@ -133,6 +133,8 @@ static const struct hid_device_id a4_devices[] = {
                .driver_data = A4_2WHEEL_MOUSE_HACK_7 },
        { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D),
                .driver_data = A4_2WHEEL_MOUSE_HACK_B8 },
+       { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649),
+               .driver_data = A4_2WHEEL_MOUSE_HACK_B8 },
        { }
 };
 MODULE_DEVICE_TABLE(hid, a4_devices);
index bba05d0a8980e1b43aa8ec6548400b7cf8d174b8..eaeca564a8d3e6643f1222f9be032740a158ee0a 100644 (file)
@@ -246,17 +246,18 @@ static int apple_event(struct hid_device *hdev, struct hid_field *field,
 /*
  * MacBook JIS keyboard has wrong logical maximum
  */
-static void apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-               unsigned int rsize)
+static __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
 {
        struct apple_sc *asc = hid_get_drvdata(hdev);
 
-       if ((asc->quirks & APPLE_RDESC_JIS) && rsize >= 60 &&
+       if ((asc->quirks & APPLE_RDESC_JIS) && *rsize >= 60 &&
                        rdesc[53] == 0x65 && rdesc[59] == 0x65) {
                dev_info(&hdev->dev, "fixing up MacBook JIS keyboard report "
                                "descriptor\n");
                rdesc[53] = rdesc[59] = 0xe7;
        }
+       return rdesc;
 }
 
 static void apple_setup_input(struct input_dev *input)
index 24663a8717b13bbc4e39657c082bf84b07a506bc..e880086c2311224d7d7ea9fc1ba7d0592bbaf29d 100644 (file)
  * Cherry Cymotion keyboard have an invalid HID report descriptor,
  * that needs fixing before we can parse it.
  */
-static void ch_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-               unsigned int rsize)
+static __u8 *ch_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
 {
-       if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
+       if (*rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) {
                dev_info(&hdev->dev, "fixing up Cherry Cymotion report "
                                "descriptor\n");
                rdesc[11] = rdesc[16] = 0xff;
                rdesc[12] = rdesc[17] = 0x03;
        }
+       return rdesc;
 }
 
 #define ch_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
index 3cb6632d451890f58361f3fff80385b0e6c69011..7832b6e2478b09c9f51ae9abc31ce4c98d708c01 100644 (file)
@@ -388,12 +388,6 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
        __u32 data;
        unsigned n;
 
-       /* Local delimiter could have value 0, which allows size to be 0 */
-       if (item->size == 0 && item->tag != HID_LOCAL_ITEM_TAG_DELIMITER) {
-               dbg_hid("item data expected for local item\n");
-               return -1;
-       }
-
        data = item_udata(item);
 
        switch (item->tag) {
@@ -651,7 +645,7 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
        };
 
        if (device->driver->report_fixup)
-               device->driver->report_fixup(device, start, size);
+               start = device->driver->report_fixup(device, start, &size);
 
        device->rdesc = kmemdup(start, size, GFP_KERNEL);
        if (device->rdesc == NULL)
@@ -1241,6 +1235,7 @@ static const struct hid_device_id hid_blacklist[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) },
        { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
        { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) },
 #if defined(CONFIG_HID_ACRUX_FF) || defined(CONFIG_HID_ACRUX_FF_MODULE)
        { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) },
 #endif
@@ -1248,6 +1243,7 @@ static const struct hid_device_id hid_blacklist[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) },
@@ -1327,6 +1323,7 @@ static const struct hid_device_id hid_blacklist[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) },
@@ -1336,6 +1333,7 @@ static const struct hid_device_id hid_blacklist[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) },
@@ -1371,12 +1369,15 @@ static const struct hid_device_id hid_blacklist[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
        { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, USB_DEVICE_ID_MTP) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_MTP_STM) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX, USB_DEVICE_ID_MTP_SITRONIX) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
        { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
        { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) },
@@ -1388,8 +1389,16 @@ static const struct hid_device_id hid_blacklist[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
        { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
        { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) },
index 998b6f443d7dc062fff6263030209850d0c43221..4cd0e2345991bd6faf5055286b23a8c093c66ac5 100644 (file)
  * Some USB barcode readers from cypress have usage min and usage max in
  * the wrong order
  */
-static void cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-               unsigned int rsize)
+static __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
 {
        unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
        unsigned int i;
 
        if (!(quirks & CP_RDESC_SWAPPED_MIN_MAX))
-               return;
+               return rdesc;
 
-       for (i = 0; i < rsize - 4; i++)
+       for (i = 0; i < *rsize - 4; i++)
                if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) {
                        __u8 tmp;
 
@@ -50,6 +50,7 @@ static void cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                        rdesc[i + 3] = rdesc[i + 1];
                        rdesc[i + 1] = tmp;
                }
+       return rdesc;
 }
 
 static int cp_input_mapped(struct hid_device *hdev, struct hid_input *hi,
index 61a3e572224aa4b578002e5699a37dfc26a7d243..75c5e23d09d2074d1869731722b89615cb896607 100644 (file)
@@ -570,6 +570,8 @@ void hid_debug_event(struct hid_device *hdev, char *buf)
                                buf[i];
                list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE;
         }
+
+       wake_up_interruptible(&hdev->debug_wait);
 }
 EXPORT_SYMBOL_GPL(hid_debug_event);
 
index 8ca7f65cf2f804d32120fadfa81cc9b900dfd94a..54b017ad258d29cf5e726ad04ee3297235a22366 100644 (file)
@@ -31,7 +31,7 @@ struct egalax_data {
        bool first;             /* is this the first finger in the frame? */
        bool valid;             /* valid finger data, or just placeholder? */
        bool activity;          /* at least one active finger previously? */
-       __u16 lastx, lasty;     /* latest valid (x, y) in the frame */
+       __u16 lastx, lasty, lastz;      /* latest valid (x, y, z) in the frame */
 };
 
 static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi,
@@ -79,6 +79,10 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                case HID_DG_TIPPRESSURE:
                        hid_map_usage(hi, usage, bit, max,
                                        EV_ABS, ABS_MT_PRESSURE);
+                       /* touchscreen emulation */
+                       input_set_abs_params(hi->input, ABS_PRESSURE,
+                                               field->logical_minimum,
+                                               field->logical_maximum, 0, 0);
                        return 1;
                }
                return 0;
@@ -109,8 +113,8 @@ static void egalax_filter_event(struct egalax_data *td, struct input_dev *input)
        if (td->valid) {
                /* emit multitouch events */
                input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id);
-               input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x);
-               input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y);
+               input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x >> 3);
+               input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y >> 3);
                input_event(input, EV_ABS, ABS_MT_PRESSURE, td->z);
 
                input_mt_sync(input);
@@ -121,6 +125,7 @@ static void egalax_filter_event(struct egalax_data *td, struct input_dev *input)
                 */
                td->lastx = td->x;
                td->lasty = td->y;
+               td->lastz = td->z;
        }
 
        /*
@@ -129,8 +134,9 @@ static void egalax_filter_event(struct egalax_data *td, struct input_dev *input)
         * the oldest on the panel, the one we want for single touch
         */
        if (!td->first && td->activity) {
-               input_event(input, EV_ABS, ABS_X, td->lastx);
-               input_event(input, EV_ABS, ABS_Y, td->lasty);
+               input_event(input, EV_ABS, ABS_X, td->lastx >> 3);
+               input_event(input, EV_ABS, ABS_Y, td->lasty >> 3);
+               input_event(input, EV_ABS, ABS_PRESSURE, td->lastz);
        }
 
        if (!td->valid) {
index 7a40878f46b4920d23619d4ee1fc3ea7c691c235..6e31f305397d1b60bb750efd1f81f17724e175a8 100644 (file)
 
 #include "hid-ids.h"
 
-static void elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-               unsigned int rsize)
+static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
 {
-       if (rsize >= 48 && rdesc[46] == 0x05 && rdesc[47] == 0x0c) {
+       if (*rsize >= 48 && rdesc[46] == 0x05 && rdesc[47] == 0x0c) {
                dev_info(&hdev->dev, "Fixing up Elecom BM084 "
                                "report descriptor.\n");
                rdesc[47] = 0x00;
        }
+    return rdesc;
 }
 
 static const struct hid_device_id elecom_devices[] = {
index 855aa8e355f4556b65e78c057d41d700d3671fe5..3ee999d33004a64ea5801987bb7d3016960f0990 100644 (file)
@@ -25,6 +25,7 @@
 #define USB_VENDOR_ID_A4TECH           0x09da
 #define USB_DEVICE_ID_A4TECH_WCP32PU   0x0006
 #define USB_DEVICE_ID_A4TECH_X5_005D   0x000a
+#define USB_DEVICE_ID_A4TECH_RP_649    0x001a
 
 #define USB_VENDOR_ID_AASHIMA          0x06d6
 #define USB_DEVICE_ID_AASHIMA_GAMEPAD  0x0025
@@ -63,6 +64,7 @@
 #define USB_VENDOR_ID_APPLE            0x05ac
 #define USB_DEVICE_ID_APPLE_MIGHTYMOUSE        0x0304
 #define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d
+#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD      0x030e
 #define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI      0x020e
 #define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO       0x020f
 #define USB_DEVICE_ID_APPLE_GEYSER_ANSI        0x0214
 #define USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE       0x0051
 #define USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE       0x00ff
 #define USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK   0x00d3
+#define USB_DEVICE_ID_CH_AXIS_295      0x001c
 
 #define USB_VENDOR_ID_CHERRY           0x046a
 #define USB_DEVICE_ID_CHERRY_CYMOTION  0x0023
 #define USB_DEVICE_ID_LOGITECH_RECEIVER        0xc101
 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST  0xc110
 #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
+#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD  0xc20a
 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD       0xc211
 #define USB_DEVICE_ID_LOGITECH_EXTREME_3D      0xc215
 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2      0xc218
 #define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG     0xc293
 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL      0xc295
 #define USB_DEVICE_ID_LOGITECH_G25_WHEEL       0xc299
+#define USB_DEVICE_ID_LOGITECH_WII_WHEEL       0xc29c
 #define USB_DEVICE_ID_LOGITECH_ELITE_KBD       0xc30a
 #define USB_DEVICE_ID_S510_RECEIVER    0xc50c
 #define USB_DEVICE_ID_S510_RECEIVER_2  0xc517
 
 #define USB_VENDOR_ID_ROCCAT           0x1e7d
 #define USB_DEVICE_ID_ROCCAT_KONE      0x2ced
+#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED        0x2c24
+#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS     0x2cf6
 
 #define USB_VENDOR_ID_SAITEK           0x06a3
 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
 #define USB_VENDOR_ID_STANTUM          0x1f87
 #define USB_DEVICE_ID_MTP              0x0002
 
+#define USB_VENDOR_ID_STANTUM_STM              0x0483
+#define USB_DEVICE_ID_MTP_STM          0x3261
+
+#define USB_VENDOR_ID_STANTUM_SITRONIX         0x1403
+#define USB_DEVICE_ID_MTP_SITRONIX             0x5001
+
 #define USB_VENDOR_ID_SUN              0x0430
 #define USB_DEVICE_ID_RARITAN_KVM_DONGLE       0xcdab
 
 
 #define USB_VENDOR_ID_UCLOGIC          0x5543
 #define USB_DEVICE_ID_UCLOGIC_TABLET_PF1209    0x0042
-#define USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U   0x0003
 #define USB_DEVICE_ID_UCLOGIC_TABLET_KNA5      0x6001
+#define USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U   0x0003
+#define USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U   0x0004
+#define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U   0x0005
 
 #define USB_VENDOR_ID_VERNIER          0x08f7
 #define USB_DEVICE_ID_VERNIER_LABPRO   0x0001
 #define USB_VENDOR_ID_WACOM            0x056a
 #define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81
 
+#define USB_VENDOR_ID_WALTOP                           0x172f
+#define USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH      0x0032
+#define USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH     0x0034
+#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH    0x0501
+#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH    0x0500
+
 #define USB_VENDOR_ID_WISEGROUP                0x0925
 #define USB_DEVICE_ID_SMARTJOY_PLUS    0x0005
 #define USB_DEVICE_ID_1_PHIDGETSERVO_20        0x8101
index 6c03dcc5760a277097bb17d866cc33aa17db484a..834ef47b76d6f456184dff1adc9663160bebfa28 100644 (file)
@@ -149,6 +149,83 @@ static int hidinput_setkeycode(struct input_dev *dev,
 }
 
 
+/**
+ * hidinput_calc_abs_res - calculate an absolute axis resolution
+ * @field: the HID report field to calculate resolution for
+ * @code: axis code
+ *
+ * The formula is:
+ *                         (logical_maximum - logical_minimum)
+ * resolution = ----------------------------------------------------------
+ *              (physical_maximum - physical_minimum) * 10 ^ unit_exponent
+ *
+ * as seen in the HID specification v1.11 6.2.2.7 Global Items.
+ *
+ * Only exponent 1 length units are processed. Centimeters are converted to
+ * inches. Degrees are converted to radians.
+ */
+static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
+{
+       __s32 unit_exponent = field->unit_exponent;
+       __s32 logical_extents = field->logical_maximum -
+                                       field->logical_minimum;
+       __s32 physical_extents = field->physical_maximum -
+                                       field->physical_minimum;
+       __s32 prev;
+
+       /* Check if the extents are sane */
+       if (logical_extents <= 0 || physical_extents <= 0)
+               return 0;
+
+       /*
+        * Verify and convert units.
+        * See HID specification v1.11 6.2.2.7 Global Items for unit decoding
+        */
+       if (code == ABS_X || code == ABS_Y || code == ABS_Z) {
+               if (field->unit == 0x11) {              /* If centimeters */
+                       /* Convert to inches */
+                       prev = logical_extents;
+                       logical_extents *= 254;
+                       if (logical_extents < prev)
+                               return 0;
+                       unit_exponent += 2;
+               } else if (field->unit != 0x13) {       /* If not inches */
+                       return 0;
+               }
+       } else if (code == ABS_RX || code == ABS_RY || code == ABS_RZ) {
+               if (field->unit == 0x14) {              /* If degrees */
+                       /* Convert to radians */
+                       prev = logical_extents;
+                       logical_extents *= 573;
+                       if (logical_extents < prev)
+                               return 0;
+                       unit_exponent += 1;
+               } else if (field->unit != 0x12) {       /* If not radians */
+                       return 0;
+               }
+       } else {
+               return 0;
+       }
+
+       /* Apply negative unit exponent */
+       for (; unit_exponent < 0; unit_exponent++) {
+               prev = logical_extents;
+               logical_extents *= 10;
+               if (logical_extents < prev)
+                       return 0;
+       }
+       /* Apply positive unit exponent */
+       for (; unit_exponent > 0; unit_exponent--) {
+               prev = physical_extents;
+               physical_extents *= 10;
+               if (physical_extents < prev)
+                       return 0;
+       }
+
+       /* Calculate resolution */
+       return logical_extents / physical_extents;
+}
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
                                     struct hid_usage *usage)
 {
@@ -336,6 +413,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                        map_key_clear(BTN_STYLUS);
                        break;
 
+               case 0x46: /* TabletPick */
+                       map_key_clear(BTN_STYLUS2);
+                       break;
+
                default:  goto unknown;
                }
                break;
@@ -537,6 +618,9 @@ mapped:
                        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);
 
+               input_abs_set_res(input, usage->code,
+                                 hidinput_calc_abs_res(field, usage->code));
+
                /* use a larger default input buffer for MT devices */
                if (usage->code == ABS_MT_POSITION_X && input->hint_events_per_packet == 0)
                        input_set_events_per_packet(input, 60);
@@ -659,6 +743,9 @@ void hidinput_report_event(struct hid_device *hid, struct hid_report *report)
 {
        struct hid_input *hidinput;
 
+       if (hid->quirks & HID_QUIRK_NO_INPUT_SYNC)
+               return;
+
        list_for_each_entry(hidinput, &hid->inputs, list)
                input_sync(hidinput->input);
 }
index f8871712b7b53cbbe4e5dac5c7b651691a38c99d..817247ee006c88119405e0df8c48413f68c2b1eb 100644 (file)
  *   - report size 8 count 1 must be size 1 count 8 for button bitfield
  *   - change the button usage range to 4-7 for the extra buttons
  */
-static void kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-               unsigned int rsize)
+static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
 {
-       if (rsize >= 74 &&
+       if (*rsize >= 74 &&
                rdesc[61] == 0x05 && rdesc[62] == 0x08 &&
                rdesc[63] == 0x19 && rdesc[64] == 0x08 &&
                rdesc[65] == 0x29 && rdesc[66] == 0x0f &&
@@ -40,6 +40,7 @@ static void kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                rdesc[72] = 0x01;
                rdesc[74] = 0x08;
        }
+       return rdesc;
 }
 
 static const struct hid_device_id kye_devices[] = {
index f6433d8050a96a0c3bb6951aa8ead889a5b02b2c..b629fba5a05757c5743fc9cfa06d69280b3173ef 100644 (file)
@@ -7,6 +7,7 @@
  *  Copyright (c) 2006-2007 Jiri Kosina
  *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby
+ *  Copyright (c) 2010 Hendrik Iben
  */
 
 /*
@@ -19,6 +20,9 @@
 #include <linux/device.h>
 #include <linux/hid.h>
 #include <linux/module.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
 
 #include "hid-ids.h"
 #include "hid-lg.h"
 #define LG_FF2                 0x400
 #define LG_RDESC_REL_ABS       0x800
 #define LG_FF3                 0x1000
+#define LG_FF4                 0x2000
 
 /*
  * Certain Logitech keyboards send in report #3 keys which are far
  * above the logical maximum described in descriptor. This extends
  * the original value of 0x28c of logical maximum to 0x104d
  */
-static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-               unsigned int rsize)
+static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
 {
        unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
 
-       if ((quirks & LG_RDESC) && rsize >= 90 && rdesc[83] == 0x26 &&
+       if ((quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 &&
                        rdesc[84] == 0x8c && rdesc[85] == 0x02) {
                dev_info(&hdev->dev, "fixing up Logitech keyboard report "
                                "descriptor\n");
                rdesc[84] = rdesc[89] = 0x4d;
                rdesc[85] = rdesc[90] = 0x10;
        }
-       if ((quirks & LG_RDESC_REL_ABS) && rsize >= 50 &&
+       if ((quirks & LG_RDESC_REL_ABS) && *rsize >= 50 &&
                        rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
                        rdesc[49] == 0x81 && rdesc[50] == 0x06) {
                dev_info(&hdev->dev, "fixing up rel/abs in Logitech "
                                "report descriptor\n");
                rdesc[33] = rdesc[50] = 0x02;
        }
+       if ((quirks & LG_FF4) && *rsize >= 101 &&
+                       rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
+                       rdesc[47] == 0x05 && rdesc[48] == 0x09) {
+               dev_info(&hdev->dev, "fixing up Logitech Speed Force Wireless "
+                       "button descriptor\n");
+               rdesc[41] = 0x05;
+               rdesc[42] = 0x09;
+               rdesc[47] = 0x95;
+               rdesc[48] = 0x0B;
+       }
+       return rdesc;
 }
 
 #define lg_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
@@ -285,12 +301,33 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
                goto err_free;
        }
 
+       if (quirks & LG_FF4) {
+               unsigned char buf[] = { 0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+               ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
+
+               if (ret >= 0) {
+                       /* insert a little delay of 10 jiffies ~ 40ms */
+                       wait_queue_head_t wait;
+                       init_waitqueue_head (&wait);
+                       wait_event_interruptible_timeout(wait, 0, 10);
+
+                       /* Select random Address */
+                       buf[1] = 0xB2;
+                       get_random_bytes(&buf[2], 2);
+
+                       ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
+               }
+       }
+
        if (quirks & LG_FF)
                lgff_init(hdev);
        if (quirks & LG_FF2)
                lg2ff_init(hdev);
        if (quirks & LG_FF3)
                lg3ff_init(hdev);
+       if (quirks & LG_FF4)
+               lg4ff_init(hdev);
 
        return 0;
 err_free:
@@ -325,6 +362,8 @@ static const struct hid_device_id lg_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
                .driver_data = LG_NOGET | LG_FF },
 
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
+               .driver_data = LG_FF2 },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
                .driver_data = LG_FF },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
@@ -339,6 +378,8 @@ static const struct hid_device_id lg_devices[] = {
                .driver_data = LG_FF },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
                .driver_data = LG_FF },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
+               .driver_data = LG_FF4 },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ),
                .driver_data = LG_FF },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
index ce2ac86726245e984829680ba64a77d5bd38a5f2..b0100ba2ae0b2f8b16b4f84b64e2f40fea7f40b6 100644 (file)
@@ -19,4 +19,10 @@ int lg3ff_init(struct hid_device *hdev);
 static inline int lg3ff_init(struct hid_device *hdev) { return -1; }
 #endif
 
+#ifdef CONFIG_LOGIWII_FF
+int lg4ff_init(struct hid_device *hdev);
+#else
+static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
+#endif
+
 #endif
index d888f1e6794f5a48e4ddebf053d40c0826f8af9c..4258253c36b35d9fa1f4530c03e5ab6da23f0f2a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Force feedback support for Logitech Rumblepad 2
+ *  Force feedback support for Logitech RumblePad and Rumblepad 2
  *
  *  Copyright (c) 2008 Anssi Hannula <anssi.hannula@gmail.com>
  */
@@ -110,7 +110,7 @@ int lg2ff_init(struct hid_device *hid)
 
        usbhid_submit_report(hid, report, USB_DIR_OUT);
 
-       dev_info(&hid->dev, "Force feedback for Logitech Rumblepad 2 by "
+       dev_info(&hid->dev, "Force feedback for Logitech RumblePad/Rumblepad 2 by "
               "Anssi Hannula <anssi.hannula@gmail.com>\n");
 
        return 0;
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
new file mode 100644 (file)
index 0000000..7eef5a2
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ *  Force feedback support for Logitech Speed Force Wireless
+ *
+ *  http://wiibrew.org/wiki/Logitech_USB_steering_wheel
+ *
+ *  Copyright (c) 2010 Simon Wood <simon@mungewell.org>
+ */
+
+/*
+ * 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/usb.h>
+#include <linux/hid.h>
+
+#include "usbhid/usbhid.h"
+#include "hid-lg.h"
+
+struct lg4ff_device {
+       struct hid_report *report;
+};
+
+static const signed short ff4_wheel_ac[] = {
+       FF_CONSTANT,
+       FF_AUTOCENTER,
+       -1
+};
+
+static int hid_lg4ff_play(struct input_dev *dev, void *data,
+                        struct ff_effect *effect)
+{
+       struct hid_device *hid = input_get_drvdata(dev);
+       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+       struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+       int x;
+
+#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
+
+       switch (effect->type) {
+       case FF_CONSTANT:
+               x = effect->u.ramp.start_level + 0x80;  /* 0x80 is no force */
+               CLAMP(x);
+               report->field[0]->value[0] = 0x11;      /* Slot 1 */
+               report->field[0]->value[1] = 0x10;
+               report->field[0]->value[2] = x;
+               report->field[0]->value[3] = 0x00;
+               report->field[0]->value[4] = 0x00;
+               report->field[0]->value[5] = 0x08;
+               report->field[0]->value[6] = 0x00;
+               dbg_hid("Autocenter, x=0x%02X\n", x);
+
+               usbhid_submit_report(hid, report, USB_DIR_OUT);
+               break;
+       }
+       return 0;
+}
+
+static void hid_lg4ff_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+       struct hid_device *hid = input_get_drvdata(dev);
+       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+       struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+       __s32 *value = report->field[0]->value;
+
+       *value++ = 0xfe;
+       *value++ = 0x0d;
+       *value++ = 0x07;
+       *value++ = 0x07;
+       *value++ = (magnitude >> 8) & 0xff;
+       *value++ = 0x00;
+       *value = 0x00;
+
+       usbhid_submit_report(hid, report, USB_DIR_OUT);
+}
+
+
+int lg4ff_init(struct hid_device *hid)
+{
+       struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+       struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+       struct input_dev *dev = hidinput->input;
+       struct hid_report *report;
+       struct hid_field *field;
+       const signed short *ff_bits = ff4_wheel_ac;
+       int error;
+       int i;
+
+       /* Find the report to use */
+       if (list_empty(report_list)) {
+               err_hid("No output report found");
+               return -1;
+       }
+
+       /* Check that the report looks ok */
+       report = list_entry(report_list->next, struct hid_report, list);
+       if (!report) {
+               err_hid("NULL output report");
+               return -1;
+       }
+
+       field = report->field[0];
+       if (!field) {
+               err_hid("NULL field");
+               return -1;
+       }
+
+       for (i = 0; ff_bits[i] >= 0; i++)
+               set_bit(ff_bits[i], dev->ffbit);
+
+       error = input_ff_create_memless(dev, NULL, hid_lg4ff_play);
+
+       if (error)
+               return error;
+
+       if (test_bit(FF_AUTOCENTER, dev->ffbit))
+               dev->ff->set_autocenter = hid_lg4ff_set_autocenter;
+
+       dev_info(&hid->dev, "Force feedback for Logitech Speed Force Wireless by "
+                       "Simon Wood <simon@mungewell.org>\n");
+       return 0;
+}
+
index 319b0e57ee41f1cdbebafb833989b7a3507c7440..e6dc151716643f0997e4eac4dc3d079c368dd15f 100644 (file)
@@ -2,6 +2,7 @@
  *   Apple "Magic" Wireless Mouse driver
  *
  *   Copyright (c) 2010 Michael Poole <mdpoole@troilus.org>
+ *   Copyright (c) 2010 Chase Douglas <chase.douglas@canonical.com>
  */
 
 /*
@@ -53,7 +54,9 @@ static bool report_undeciphered;
 module_param(report_undeciphered, bool, 0644);
 MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event");
 
-#define TOUCH_REPORT_ID   0x29
+#define TRACKPAD_REPORT_ID 0x28
+#define MOUSE_REPORT_ID    0x29
+#define DOUBLE_REPORT_ID   0xf7
 /* These definitions are not precise, but they're close enough.  (Bits
  * 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem
  * to be some kind of bit mask -- 0x20 may be a near-field reading,
@@ -67,15 +70,19 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
 
 #define SCROLL_ACCEL_DEFAULT 7
 
+/* Single touch emulation should only begin when no touches are currently down.
+ * This is true when single_touch_id is equal to NO_TOUCHES. If multiple touches
+ * are down and the touch providing for single touch emulation is lifted,
+ * single_touch_id is equal to SINGLE_TOUCH_UP. While single touch emulation is
+ * occuring, single_touch_id corresponds with the tracking id of the touch used.
+ */
+#define NO_TOUCHES -1
+#define SINGLE_TOUCH_UP -2
+
 /**
  * struct magicmouse_sc - Tracks Magic Mouse-specific data.
  * @input: Input device through which we report events.
  * @quirks: Currently unused.
- * @last_timestamp: Timestamp from most recent (18-bit) touch report
- *     (units of milliseconds over short windows, but seems to
- *     increase faster when there are no touches).
- * @delta_time: 18-bit difference between the two most recent touch
- *     reports from the mouse.
  * @ntouches: Number of touches in most recent touch report.
  * @scroll_accel: Number of consecutive scroll motions.
  * @scroll_jiffies: Time of last scroll motion.
@@ -86,8 +93,6 @@ struct magicmouse_sc {
        struct input_dev *input;
        unsigned long quirks;
 
-       int last_timestamp;
-       int delta_time;
        int ntouches;
        int scroll_accel;
        unsigned long scroll_jiffies;
@@ -98,9 +103,9 @@ struct magicmouse_sc {
                short scroll_x;
                short scroll_y;
                u8 size;
-               u8 down;
        } touches[16];
        int tracking_ids[16];
+       int single_touch_id;
 };
 
 static int magicmouse_firm_touch(struct magicmouse_sc *msc)
@@ -166,18 +171,35 @@ static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state)
 static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata)
 {
        struct input_dev *input = msc->input;
-       __s32 x_y = tdata[0] << 8 | tdata[1] << 16 | tdata[2] << 24;
-       int misc = tdata[5] | tdata[6] << 8;
-       int id = (misc >> 6) & 15;
-       int x = x_y << 12 >> 20;
-       int y = -(x_y >> 20);
-       int down = (tdata[7] & TOUCH_STATE_MASK) != TOUCH_STATE_NONE;
+       int id, x, y, size, orientation, touch_major, touch_minor, state, down;
+
+       if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
+               id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf;
+               x = (tdata[1] << 28 | tdata[0] << 20) >> 20;
+               y = -((tdata[2] << 24 | tdata[1] << 16) >> 20);
+               size = tdata[5] & 0x3f;
+               orientation = (tdata[6] >> 2) - 32;
+               touch_major = tdata[3];
+               touch_minor = tdata[4];
+               state = tdata[7] & TOUCH_STATE_MASK;
+               down = state != TOUCH_STATE_NONE;
+       } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+               id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf;
+               x = (tdata[1] << 27 | tdata[0] << 19) >> 19;
+               y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19);
+               size = tdata[6] & 0x3f;
+               orientation = (tdata[7] >> 2) - 32;
+               touch_major = tdata[4];
+               touch_minor = tdata[5];
+               state = tdata[8] & TOUCH_STATE_MASK;
+               down = state != TOUCH_STATE_NONE;
+       }
 
        /* Store tracking ID and other fields. */
        msc->tracking_ids[raw_id] = id;
        msc->touches[id].x = x;
        msc->touches[id].y = y;
-       msc->touches[id].size = misc & 63;
+       msc->touches[id].size = size;
 
        /* If requested, emulate a scroll wheel by detecting small
         * vertical touch motions.
@@ -188,7 +210,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
                int step_y = msc->touches[id].scroll_y - y;
 
                /* Calculate and apply the scroll motion. */
-               switch (tdata[7] & TOUCH_STATE_MASK) {
+               switch (state) {
                case TOUCH_STATE_START:
                        msc->touches[id].scroll_x = x;
                        msc->touches[id].scroll_y = y;
@@ -222,21 +244,28 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
                }
        }
 
+       if (down) {
+               msc->ntouches++;
+               if (msc->single_touch_id == NO_TOUCHES)
+                       msc->single_touch_id = id;
+       } else if (msc->single_touch_id == id)
+               msc->single_touch_id = SINGLE_TOUCH_UP;
+
        /* Generate the input events for this touch. */
        if (report_touches && down) {
-               int orientation = (misc >> 10) - 32;
-
-               msc->touches[id].down = 1;
-
                input_report_abs(input, ABS_MT_TRACKING_ID, id);
-               input_report_abs(input, ABS_MT_TOUCH_MAJOR, tdata[3]);
-               input_report_abs(input, ABS_MT_TOUCH_MINOR, tdata[4]);
+               input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2);
+               input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor << 2);
                input_report_abs(input, ABS_MT_ORIENTATION, orientation);
                input_report_abs(input, ABS_MT_POSITION_X, x);
                input_report_abs(input, ABS_MT_POSITION_Y, y);
 
-               if (report_undeciphered)
-                       input_event(input, EV_MSC, MSC_RAW, tdata[7]);
+               if (report_undeciphered) {
+                       if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
+                               input_event(input, EV_MSC, MSC_RAW, tdata[7]);
+                       else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+                               input_event(input, EV_MSC, MSC_RAW, tdata[8]);
+               }
 
                input_mt_sync(input);
        }
@@ -247,39 +276,43 @@ static int magicmouse_raw_event(struct hid_device *hdev,
 {
        struct magicmouse_sc *msc = hid_get_drvdata(hdev);
        struct input_dev *input = msc->input;
-       int x, y, ts, ii, clicks, last_up;
+       int x = 0, y = 0, ii, clicks = 0, npoints;
 
        switch (data[0]) {
-       case 0x10:
-               if (size != 6)
+       case TRACKPAD_REPORT_ID:
+               /* Expect four bytes of prefix, and N*9 bytes of touch data. */
+               if (size < 4 || ((size - 4) % 9) != 0)
                        return 0;
-               x = (__s16)(data[2] | data[3] << 8);
-               y = (__s16)(data[4] | data[5] << 8);
+               npoints = (size - 4) / 9;
+               msc->ntouches = 0;
+               for (ii = 0; ii < npoints; ii++)
+                       magicmouse_emit_touch(msc, ii, data + ii * 9 + 4);
+
+               /* We don't need an MT sync here because trackpad emits a
+                * BTN_TOUCH event in a new frame when all touches are released.
+                */
+               if (msc->ntouches == 0)
+                       msc->single_touch_id = NO_TOUCHES;
+
                clicks = data[1];
+
+               /* The following bits provide a device specific timestamp. They
+                * are unused here.
+                *
+                * ts = data[1] >> 6 | data[2] << 2 | data[3] << 10;
+                */
                break;
-       case TOUCH_REPORT_ID:
+       case MOUSE_REPORT_ID:
                /* Expect six bytes of prefix, and N*8 bytes of touch data. */
                if (size < 6 || ((size - 6) % 8) != 0)
                        return 0;
-               ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
-               msc->delta_time = (ts - msc->last_timestamp) & 0x3ffff;
-               msc->last_timestamp = ts;
-               msc->ntouches = (size - 6) / 8;
-               for (ii = 0; ii < msc->ntouches; ii++)
+               npoints = (size - 6) / 8;
+               msc->ntouches = 0;
+               for (ii = 0; ii < npoints; ii++)
                        magicmouse_emit_touch(msc, ii, data + ii * 8 + 6);
 
-               if (report_touches) {
-                       last_up = 1;
-                       for (ii = 0; ii < ARRAY_SIZE(msc->touches); ii++) {
-                               if (msc->touches[ii].down) {
-                                       last_up = 0;
-                                       msc->touches[ii].down = 0;
-                               }
-                       }
-                       if (last_up) {
-                               input_mt_sync(input);
-                       }
-               }
+               if (report_touches && msc->ntouches == 0)
+                       input_mt_sync(input);
 
                /* When emulating three-button mode, it is important
                 * to have the current touch information before
@@ -288,68 +321,72 @@ static int magicmouse_raw_event(struct hid_device *hdev,
                x = (int)(((data[3] & 0x0c) << 28) | (data[1] << 22)) >> 22;
                y = (int)(((data[3] & 0x30) << 26) | (data[2] << 22)) >> 22;
                clicks = data[3];
+
+               /* The following bits provide a device specific timestamp. They
+                * are unused here.
+                *
+                * ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
+                */
+               break;
+       case DOUBLE_REPORT_ID:
+               /* Sometimes the trackpad sends two touch reports in one
+                * packet.
+                */
+               magicmouse_raw_event(hdev, report, data + 2, data[1]);
+               magicmouse_raw_event(hdev, report, data + 2 + data[1],
+                       size - 2 - data[1]);
                break;
-       case 0x20: /* Theoretically battery status (0-100), but I have
-                   * never seen it -- maybe it is only upon request.
-                   */
-       case 0x60: /* Unknown, maybe laser on/off. */
-       case 0x61: /* Laser reflection status change.
-                   * data[1]: 0 = spotted, 1 = lost
-                   */
        default:
                return 0;
        }
 
-       magicmouse_emit_buttons(msc, clicks & 3);
-       input_report_rel(input, REL_X, x);
-       input_report_rel(input, REL_Y, y);
+       if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
+               magicmouse_emit_buttons(msc, clicks & 3);
+               input_report_rel(input, REL_X, x);
+               input_report_rel(input, REL_Y, y);
+       } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+               input_report_key(input, BTN_MOUSE, clicks & 1);
+               input_report_key(input, BTN_TOUCH, msc->ntouches > 0);
+               input_report_key(input, BTN_TOOL_FINGER, msc->ntouches == 1);
+               input_report_key(input, BTN_TOOL_DOUBLETAP, msc->ntouches == 2);
+               input_report_key(input, BTN_TOOL_TRIPLETAP, msc->ntouches == 3);
+               input_report_key(input, BTN_TOOL_QUADTAP, msc->ntouches == 4);
+               if (msc->single_touch_id >= 0) {
+                       input_report_abs(input, ABS_X,
+                               msc->touches[msc->single_touch_id].x);
+                       input_report_abs(input, ABS_Y,
+                               msc->touches[msc->single_touch_id].y);
+               }
+       }
+
        input_sync(input);
        return 1;
 }
 
-static int magicmouse_input_open(struct input_dev *dev)
-{
-       struct hid_device *hid = input_get_drvdata(dev);
-
-       return hid->ll_driver->open(hid);
-}
-
-static void magicmouse_input_close(struct input_dev *dev)
-{
-       struct hid_device *hid = input_get_drvdata(dev);
-
-       hid->ll_driver->close(hid);
-}
-
 static void magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev)
 {
-       input_set_drvdata(input, hdev);
-       input->event = hdev->ll_driver->hidinput_input_event;
-       input->open = magicmouse_input_open;
-       input->close = magicmouse_input_close;
-
-       input->name = hdev->name;
-       input->phys = hdev->phys;
-       input->uniq = hdev->uniq;
-       input->id.bustype = hdev->bus;
-       input->id.vendor = hdev->vendor;
-       input->id.product = hdev->product;
-       input->id.version = hdev->version;
-       input->dev.parent = hdev->dev.parent;
-
        __set_bit(EV_KEY, input->evbit);
-       __set_bit(BTN_LEFT, input->keybit);
-       __set_bit(BTN_RIGHT, input->keybit);
-       if (emulate_3button)
-               __set_bit(BTN_MIDDLE, input->keybit);
-       __set_bit(BTN_TOOL_FINGER, input->keybit);
-
-       __set_bit(EV_REL, input->evbit);
-       __set_bit(REL_X, input->relbit);
-       __set_bit(REL_Y, input->relbit);
-       if (emulate_scroll_wheel) {
-               __set_bit(REL_WHEEL, input->relbit);
-               __set_bit(REL_HWHEEL, input->relbit);
+
+       if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
+               __set_bit(BTN_LEFT, input->keybit);
+               __set_bit(BTN_RIGHT, input->keybit);
+               if (emulate_3button)
+                       __set_bit(BTN_MIDDLE, input->keybit);
+
+               __set_bit(EV_REL, input->evbit);
+               __set_bit(REL_X, input->relbit);
+               __set_bit(REL_Y, input->relbit);
+               if (emulate_scroll_wheel) {
+                       __set_bit(REL_WHEEL, input->relbit);
+                       __set_bit(REL_HWHEEL, input->relbit);
+               }
+       } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+               __set_bit(BTN_MOUSE, input->keybit);
+               __set_bit(BTN_TOOL_FINGER, input->keybit);
+               __set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
+               __set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
+               __set_bit(BTN_TOOL_QUADTAP, input->keybit);
+               __set_bit(BTN_TOUCH, input->keybit);
        }
 
        if (report_touches) {
@@ -359,16 +396,26 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h
                input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 4, 0);
                input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 4, 0);
                input_set_abs_params(input, ABS_MT_ORIENTATION, -32, 31, 1, 0);
-               input_set_abs_params(input, ABS_MT_POSITION_X, -1100, 1358,
-                               4, 0);
+
                /* Note: Touch Y position from the device is inverted relative
                 * to how pointer motion is reported (and relative to how USB
                 * HID recommends the coordinates work).  This driver keeps
                 * the origin at the same position, and just uses the additive
                 * inverse of the reported Y.
                 */
-               input_set_abs_params(input, ABS_MT_POSITION_Y, -1589, 2047,
-                               4, 0);
+               if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
+                       input_set_abs_params(input, ABS_MT_POSITION_X, -1100,
+                               1358, 4, 0);
+                       input_set_abs_params(input, ABS_MT_POSITION_Y, -1589,
+                               2047, 4, 0);
+               } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+                       input_set_abs_params(input, ABS_X, -2909, 3167, 4, 0);
+                       input_set_abs_params(input, ABS_Y, -2456, 2565, 4, 0);
+                       input_set_abs_params(input, ABS_MT_POSITION_X, -2909,
+                               3167, 4, 0);
+                       input_set_abs_params(input, ABS_MT_POSITION_Y, -2456,
+                               2565, 4, 0);
+               }
        }
 
        if (report_undeciphered) {
@@ -377,12 +424,22 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h
        }
 }
 
+static int magicmouse_input_mapping(struct hid_device *hdev,
+               struct hid_input *hi, struct hid_field *field,
+               struct hid_usage *usage, unsigned long **bit, int *max)
+{
+       struct magicmouse_sc *msc = hid_get_drvdata(hdev);
+
+       if (!msc->input)
+               msc->input = hi->input;
+
+       return 0;
+}
+
 static int magicmouse_probe(struct hid_device *hdev,
        const struct hid_device_id *id)
 {
-       __u8 feature_1[] = { 0xd7, 0x01 };
-       __u8 feature_2[] = { 0xf8, 0x01, 0x32 };
-       struct input_dev *input;
+       __u8 feature[] = { 0xd7, 0x01 };
        struct magicmouse_sc *msc;
        struct hid_report *report;
        int ret;
@@ -398,6 +455,8 @@ static int magicmouse_probe(struct hid_device *hdev,
        msc->quirks = id->driver_data;
        hid_set_drvdata(hdev, msc);
 
+       msc->single_touch_id = NO_TOUCHES;
+
        ret = hid_parse(hdev);
        if (ret) {
                dev_err(&hdev->dev, "magicmouse hid parse failed\n");
@@ -410,10 +469,22 @@ static int magicmouse_probe(struct hid_device *hdev,
                goto err_free;
        }
 
-       /* we are handling the input ourselves */
-       hidinput_disconnect(hdev);
+       /* We do this after hid-input is done parsing reports so that
+        * hid-input uses the most natural button and axis IDs.
+        */
+       if (msc->input)
+               magicmouse_setup_input(msc->input, hdev);
+
+       if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
+               report = hid_register_report(hdev, HID_INPUT_REPORT,
+                       MOUSE_REPORT_ID);
+       else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+               report = hid_register_report(hdev, HID_INPUT_REPORT,
+                       TRACKPAD_REPORT_ID);
+               report = hid_register_report(hdev, HID_INPUT_REPORT,
+                       DOUBLE_REPORT_ID);
+       }
 
-       report = hid_register_report(hdev, HID_INPUT_REPORT, TOUCH_REPORT_ID);
        if (!report) {
                dev_err(&hdev->dev, "unable to register touch report\n");
                ret = -ENOMEM;
@@ -421,39 +492,15 @@ static int magicmouse_probe(struct hid_device *hdev,
        }
        report->size = 6;
 
-       ret = hdev->hid_output_raw_report(hdev, feature_1, sizeof(feature_1),
+       ret = hdev->hid_output_raw_report(hdev, feature, sizeof(feature),
                        HID_FEATURE_REPORT);
-       if (ret != sizeof(feature_1)) {
-               dev_err(&hdev->dev, "unable to request touch data (1:%d)\n",
-                               ret);
-               goto err_stop_hw;
-       }
-       ret = hdev->hid_output_raw_report(hdev, feature_2,
-                       sizeof(feature_2), HID_FEATURE_REPORT);
-       if (ret != sizeof(feature_2)) {
-               dev_err(&hdev->dev, "unable to request touch data (2:%d)\n",
+       if (ret != sizeof(feature)) {
+               dev_err(&hdev->dev, "unable to request touch data (%d)\n",
                                ret);
                goto err_stop_hw;
        }
 
-       input = input_allocate_device();
-       if (!input) {
-               dev_err(&hdev->dev, "can't alloc input device\n");
-               ret = -ENOMEM;
-               goto err_stop_hw;
-       }
-       magicmouse_setup_input(input, hdev);
-
-       ret = input_register_device(input);
-       if (ret) {
-               dev_err(&hdev->dev, "input device registration failed\n");
-               goto err_input;
-       }
-       msc->input = input;
-
        return 0;
-err_input:
-       input_free_device(input);
 err_stop_hw:
        hid_hw_stop(hdev);
 err_free:
@@ -466,13 +513,14 @@ static void magicmouse_remove(struct hid_device *hdev)
        struct magicmouse_sc *msc = hid_get_drvdata(hdev);
 
        hid_hw_stop(hdev);
-       input_unregister_device(msc->input);
        kfree(msc);
 }
 
 static const struct hid_device_id magic_mice[] = {
-       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE),
-               .driver_data = 0 },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+               USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+               USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 },
        { }
 };
 MODULE_DEVICE_TABLE(hid, magic_mice);
@@ -483,6 +531,7 @@ static struct hid_driver magicmouse_driver = {
        .probe = magicmouse_probe,
        .remove = magicmouse_remove,
        .raw_event = magicmouse_raw_event,
+       .input_mapping = magicmouse_input_mapping,
 };
 
 static int __init magicmouse_init(void)
index 359cc447c6c600d61387e5b8cc6679dc1d4d6b75..dc618c33d0a2631cb9bb0085a06f6504cf6b2513 100644 (file)
  * Microsoft Wireless Desktop Receiver (Model 1028) has
  * 'Usage Min/Max' where it ought to have 'Physical Min/Max'
  */
-static void ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-               unsigned int rsize)
+static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
 {
        unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
 
-       if ((quirks & MS_RDESC) && rsize == 571 && rdesc[557] == 0x19 &&
+       if ((quirks & MS_RDESC) && *rsize == 571 && rdesc[557] == 0x19 &&
                        rdesc[559] == 0x29) {
                dev_info(&hdev->dev, "fixing up Microsoft Wireless Receiver "
                                "Model 1028 report descriptor\n");
                rdesc[557] = 0x35;
                rdesc[559] = 0x45;
        }
+       return rdesc;
 }
 
 #define ms_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
index 2cd05aa244b9cfb78739bb1c0f7c5da71f43f0ff..c95c31e2d86923bfc2b5fd4374f4f406a005d860 100644 (file)
 
 #include "hid-ids.h"
 
-static void mr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-               unsigned int rsize)
+static __u8 *mr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
 {
-       if (rsize >= 30 && rdesc[29] == 0x05 && rdesc[30] == 0x09) {
+       if (*rsize >= 30 && rdesc[29] == 0x05 && rdesc[30] == 0x09) {
                dev_info(&hdev->dev, "fixing up button/consumer in HID report "
                                "descriptor\n");
                rdesc[30] = 0x0c;
        }
+       return rdesc;
 }
 
 #define mr_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
index fb69b8c4953fda88f8c6340216faf0bf9194b223..69169efa1e16eee2653bdf11e28bd0684c2fd80d 100644 (file)
@@ -90,6 +90,55 @@ struct ntrig_data {
 };
 
 
+/*
+ * This function converts the 4 byte raw firmware code into
+ * a string containing 5 comma separated numbers.
+ */
+static int ntrig_version_string(unsigned char *raw, char *buf)
+{
+       __u8 a =  (raw[1] & 0x0e) >> 1;
+       __u8 b =  (raw[0] & 0x3c) >> 2;
+       __u8 c = ((raw[0] & 0x03) << 3) | ((raw[3] & 0xe0) >> 5);
+       __u8 d = ((raw[3] & 0x07) << 3) | ((raw[2] & 0xe0) >> 5);
+       __u8 e =   raw[2] & 0x07;
+
+       /*
+        * As yet unmapped bits:
+        * 0b11000000 0b11110001 0b00011000 0b00011000
+        */
+
+       return sprintf(buf, "%u.%u.%u.%u.%u", a, b, c, d, e);
+}
+
+static void ntrig_report_version(struct hid_device *hdev)
+{
+       int ret;
+       char buf[20];
+       struct usb_device *usb_dev = hid_to_usb_dev(hdev);
+       unsigned char *data = kmalloc(8, GFP_KERNEL);
+
+       if (!data)
+               goto err_free;
+
+       ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
+                             USB_REQ_CLEAR_FEATURE,
+                             USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+                             USB_DIR_IN,
+                             0x30c, 1, data, 8,
+                             USB_CTRL_SET_TIMEOUT);
+
+       if (ret == 8) {
+               ret = ntrig_version_string(&data[2], buf);
+
+               dev_info(&hdev->dev,
+                        "Firmware version: %s (%02x%02x %02x%02x)\n",
+                        buf, data[2], data[3], data[4], data[5]);
+       }
+
+err_free:
+       kfree(data);
+}
+
 static ssize_t show_phys_width(struct device *dev,
                               struct device_attribute *attr,
                               char *buf)
@@ -377,8 +426,8 @@ static struct attribute_group ntrig_attribute_group = {
  */
 
 static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
-               struct hid_field *field, struct hid_usage *usage,
-               unsigned long **bit, int *max)
+                              struct hid_field *field, struct hid_usage *usage,
+                              unsigned long **bit, int *max)
 {
        struct ntrig_data *nd = hid_get_drvdata(hdev);
 
@@ -448,13 +497,13 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                /* width/height mapped on TouchMajor/TouchMinor/Orientation */
                case HID_DG_WIDTH:
                        hid_map_usage(hi, usage, bit, max,
-                                       EV_ABS, ABS_MT_TOUCH_MAJOR);
+                                     EV_ABS, ABS_MT_TOUCH_MAJOR);
                        return 1;
                case HID_DG_HEIGHT:
                        hid_map_usage(hi, usage, bit, max,
-                                       EV_ABS, ABS_MT_TOUCH_MINOR);
+                                     EV_ABS, ABS_MT_TOUCH_MINOR);
                        input_set_abs_params(hi->input, ABS_MT_ORIENTATION,
-                                       0, 1, 0, 0);
+                                            0, 1, 0, 0);
                        return 1;
                }
                return 0;
@@ -468,8 +517,8 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 }
 
 static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi,
-               struct hid_field *field, struct hid_usage *usage,
-               unsigned long **bit, int *max)
+                             struct hid_field *field, struct hid_usage *usage,
+                             unsigned long **bit, int *max)
 {
        /* No special mappings needed for the pen and single touch */
        if (field->physical)
@@ -489,7 +538,7 @@ static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi,
  * and call input_mt_sync after each point if necessary
  */
 static int ntrig_event (struct hid_device *hid, struct hid_field *field,
-                                       struct hid_usage *usage, __s32 value)
+                       struct hid_usage *usage, __s32 value)
 {
        struct input_dev *input = field->hidinput->input;
        struct ntrig_data *nd = hid_get_drvdata(hid);
@@ -848,6 +897,8 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
        if (report)
                usbhid_submit_report(hdev, report, USB_DIR_OUT);
 
+       ntrig_report_version(hdev);
+
        ret = sysfs_create_group(&hdev->dev.kobj,
                        &ntrig_attribute_group);
 
@@ -860,7 +911,7 @@ err_free:
 static void ntrig_remove(struct hid_device *hdev)
 {
        sysfs_remove_group(&hdev->dev.kobj,
-                       &ntrig_attribute_group);
+                          &ntrig_attribute_group);
        hid_hw_stop(hdev);
        kfree(hid_get_drvdata(hdev));
 }
index aa9a960f73a413f59ded643eadbd5a1d77d81601..2e79716dca31e424243e6d6ca4d7dba78e4e0702 100644 (file)
 
 #include "hid-ids.h"
 
-static void ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-               unsigned int rsize)
+static __u8 *ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
 {
-       if (rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x01) {
+       if (*rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x01) {
                dev_info(&hdev->dev, "Fixing up Ortek WKB-2000 "
                                "report descriptor.\n");
                rdesc[55] = 0x92;
        }
+       return rdesc;
 }
 
 static const struct hid_device_id ortek_devices[] = {
index 500fbd0652dc4be5354743dab0961aef43cf6989..308d6ae48a3e4b5baa10a402eae47d00acfd7cfb 100644 (file)
 #include "hid-ids.h"
 
 /* Petalynx Maxter Remote has maximum for consumer page set too low */
-static void pl_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-               unsigned int rsize)
+static __u8 *pl_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
 {
-       if (rsize >= 60 && rdesc[39] == 0x2a && rdesc[40] == 0xf5 &&
+       if (*rsize >= 60 && rdesc[39] == 0x2a && rdesc[40] == 0xf5 &&
                        rdesc[41] == 0x00 && rdesc[59] == 0x26 &&
                        rdesc[60] == 0xf9 && rdesc[61] == 0x00) {
                dev_info(&hdev->dev, "fixing up Petalynx Maxter Remote report "
@@ -34,6 +34,7 @@ static void pl_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                rdesc[60] = 0xfa;
                rdesc[40] = 0xfa;
        }
+       return rdesc;
 }
 
 #define pl_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
index 845f428b8090191aa3ab75952feb010fb1e8ab79..48eab84f53b5467acac72ad1ff57b2f3c79f4c9d 100644 (file)
@@ -740,10 +740,10 @@ int pcmidi_snd_terminate(struct pcmidi_snd *pm)
 /*
  * PC-MIDI report descriptor for report id is wrong.
  */
-static void pk_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-               unsigned int rsize)
+static __u8 *pk_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
 {
-       if (rsize == 178 &&
+       if (*rsize == 178 &&
              rdesc[111] == 0x06 && rdesc[112] == 0x00 &&
              rdesc[113] == 0xff) {
                dev_info(&hdev->dev, "fixing up pc-midi keyboard report "
@@ -751,6 +751,7 @@ static void pk_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 
                rdesc[144] = 0x18; /* report 4: was 0x10 report count */
        }
+       return rdesc;
 }
 
 static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi,
diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c
new file mode 100644 (file)
index 0000000..9bf2304
--- /dev/null
@@ -0,0 +1,968 @@
+/*
+ * Roccat Pyra driver for Linux
+ *
+ * Copyright (c) 2010 Stefan Achatz <erazor_de@users.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.
+ */
+
+/*
+ * Roccat Pyra is a mobile gamer mouse which comes in wired and wireless
+ * variant. Wireless variant is not tested.
+ * Userland tools can be found at http://sourceforge.net/projects/roccat
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/usb.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "hid-ids.h"
+#include "hid-roccat.h"
+#include "hid-roccat-pyra.h"
+
+static void profile_activated(struct pyra_device *pyra,
+               unsigned int new_profile)
+{
+       pyra->actual_profile = new_profile;
+       pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi;
+}
+
+static int pyra_send_control(struct usb_device *usb_dev, int value,
+               enum pyra_control_requests request)
+{
+       int len;
+       struct pyra_control control;
+
+       if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS ||
+                       request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) &&
+                       (value < 0 || value > 4))
+               return -EINVAL;
+
+       control.command = PYRA_COMMAND_CONTROL;
+       control.value = value;
+       control.request = request;
+
+       len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+                       USB_REQ_SET_CONFIGURATION,
+                       USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                       PYRA_USB_COMMAND_CONTROL, 0, (char *)&control,
+                       sizeof(struct pyra_control),
+                       USB_CTRL_SET_TIMEOUT);
+
+       if (len != sizeof(struct pyra_control))
+               return len;
+
+       return 0;
+}
+
+static int pyra_receive_control_status(struct usb_device *usb_dev)
+{
+       int len;
+       struct pyra_control control;
+
+       do {
+               msleep(10);
+
+               len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
+                               USB_REQ_CLEAR_FEATURE,
+                               USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+                               USB_DIR_IN,
+                               PYRA_USB_COMMAND_CONTROL, 0, (char *)&control,
+                               sizeof(struct pyra_control),
+                               USB_CTRL_SET_TIMEOUT);
+
+               /* requested too early, try again */
+       } while (len == -EPROTO);
+
+       if (len == sizeof(struct pyra_control) &&
+                       control.command == PYRA_COMMAND_CONTROL &&
+                       control.request == PYRA_CONTROL_REQUEST_STATUS &&
+                       control.value == 1)
+                       return 0;
+       else {
+               dev_err(&usb_dev->dev, "receive control status: "
+                               "unknown response 0x%x 0x%x\n",
+                               control.request, control.value);
+               return -EINVAL;
+       }
+}
+
+static int pyra_get_profile_settings(struct usb_device *usb_dev,
+               struct pyra_profile_settings *buf, int number)
+{
+       int retval;
+
+       retval = pyra_send_control(usb_dev, number,
+                       PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
+
+       if (retval)
+               return retval;
+
+       retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
+                       USB_REQ_CLEAR_FEATURE,
+                       USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+                       PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)buf,
+                       sizeof(struct pyra_profile_settings),
+                       USB_CTRL_SET_TIMEOUT);
+
+       if (retval != sizeof(struct pyra_profile_settings))
+               return retval;
+
+       return 0;
+}
+
+static int pyra_get_profile_buttons(struct usb_device *usb_dev,
+               struct pyra_profile_buttons *buf, int number)
+{
+       int retval;
+
+       retval = pyra_send_control(usb_dev, number,
+                       PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
+
+       if (retval)
+               return retval;
+
+       retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
+                       USB_REQ_CLEAR_FEATURE,
+                       USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+                       PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buf,
+                       sizeof(struct pyra_profile_buttons),
+                       USB_CTRL_SET_TIMEOUT);
+
+       if (retval != sizeof(struct pyra_profile_buttons))
+               return retval;
+
+       return 0;
+}
+
+static int pyra_get_settings(struct usb_device *usb_dev,
+               struct pyra_settings *buf)
+{
+       int len;
+       len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
+                       USB_REQ_CLEAR_FEATURE,
+                       USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+                       PYRA_USB_COMMAND_SETTINGS, 0, buf,
+                       sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT);
+       if (len != sizeof(struct pyra_settings))
+               return -EIO;
+       return 0;
+}
+
+static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf)
+{
+       int len;
+       len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
+                       USB_REQ_CLEAR_FEATURE,
+                       USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+                       PYRA_USB_COMMAND_INFO, 0, buf,
+                       sizeof(struct pyra_info), USB_CTRL_SET_TIMEOUT);
+       if (len != sizeof(struct pyra_info))
+               return -EIO;
+       return 0;
+}
+
+static int pyra_set_profile_settings(struct usb_device *usb_dev,
+               struct pyra_profile_settings const *settings)
+{
+       int len;
+       len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+                       USB_REQ_SET_CONFIGURATION,
+                       USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                       PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)settings,
+                       sizeof(struct pyra_profile_settings),
+                       USB_CTRL_SET_TIMEOUT);
+       if (len != sizeof(struct pyra_profile_settings))
+               return -EIO;
+       if (pyra_receive_control_status(usb_dev))
+               return -EIO;
+       return 0;
+}
+
+static int pyra_set_profile_buttons(struct usb_device *usb_dev,
+               struct pyra_profile_buttons const *buttons)
+{
+       int len;
+       len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+                       USB_REQ_SET_CONFIGURATION,
+                       USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                       PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buttons,
+                       sizeof(struct pyra_profile_buttons),
+                       USB_CTRL_SET_TIMEOUT);
+       if (len != sizeof(struct pyra_profile_buttons))
+               return -EIO;
+       if (pyra_receive_control_status(usb_dev))
+               return -EIO;
+       return 0;
+}
+
+static int pyra_set_settings(struct usb_device *usb_dev,
+               struct pyra_settings const *settings)
+{
+       int len;
+       len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+                       USB_REQ_SET_CONFIGURATION,
+                       USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                       PYRA_USB_COMMAND_SETTINGS, 0, (char *)settings,
+                       sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT);
+       if (len != sizeof(struct pyra_settings))
+               return -EIO;
+       if (pyra_receive_control_status(usb_dev))
+               return -EIO;
+       return 0;
+}
+
+static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count, int number)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
+
+       if (off >= sizeof(struct pyra_profile_settings))
+               return 0;
+
+       if (off + count > sizeof(struct pyra_profile_settings))
+               count = sizeof(struct pyra_profile_settings) - off;
+
+       mutex_lock(&pyra->pyra_lock);
+       memcpy(buf, ((char const *)&pyra->profile_settings[number]) + off,
+                       count);
+       mutex_unlock(&pyra->pyra_lock);
+
+       return count;
+}
+
+static ssize_t pyra_sysfs_read_profile1_settings(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count)
+{
+       return pyra_sysfs_read_profilex_settings(fp, kobj,
+                       attr, buf, off, count, 0);
+}
+
+static ssize_t pyra_sysfs_read_profile2_settings(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count)
+{
+       return pyra_sysfs_read_profilex_settings(fp, kobj,
+                       attr, buf, off, count, 1);
+}
+
+static ssize_t pyra_sysfs_read_profile3_settings(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count)
+{
+       return pyra_sysfs_read_profilex_settings(fp, kobj,
+                       attr, buf, off, count, 2);
+}
+
+static ssize_t pyra_sysfs_read_profile4_settings(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count)
+{
+       return pyra_sysfs_read_profilex_settings(fp, kobj,
+                       attr, buf, off, count, 3);
+}
+
+static ssize_t pyra_sysfs_read_profile5_settings(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count)
+{
+       return pyra_sysfs_read_profilex_settings(fp, kobj,
+                       attr, buf, off, count, 4);
+}
+
+static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count, int number)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
+
+       if (off >= sizeof(struct pyra_profile_buttons))
+               return 0;
+
+       if (off + count > sizeof(struct pyra_profile_buttons))
+               count = sizeof(struct pyra_profile_buttons) - off;
+
+       mutex_lock(&pyra->pyra_lock);
+       memcpy(buf, ((char const *)&pyra->profile_buttons[number]) + off,
+                       count);
+       mutex_unlock(&pyra->pyra_lock);
+
+       return count;
+}
+
+static ssize_t pyra_sysfs_read_profile1_buttons(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count)
+{
+       return pyra_sysfs_read_profilex_buttons(fp, kobj,
+                       attr, buf, off, count, 0);
+}
+
+static ssize_t pyra_sysfs_read_profile2_buttons(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count)
+{
+       return pyra_sysfs_read_profilex_buttons(fp, kobj,
+                       attr, buf, off, count, 1);
+}
+
+static ssize_t pyra_sysfs_read_profile3_buttons(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count)
+{
+       return pyra_sysfs_read_profilex_buttons(fp, kobj,
+                       attr, buf, off, count, 2);
+}
+
+static ssize_t pyra_sysfs_read_profile4_buttons(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count)
+{
+       return pyra_sysfs_read_profilex_buttons(fp, kobj,
+                       attr, buf, off, count, 3);
+}
+
+static ssize_t pyra_sysfs_read_profile5_buttons(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count)
+{
+       return pyra_sysfs_read_profilex_buttons(fp, kobj,
+                       attr, buf, off, count, 4);
+}
+
+static ssize_t pyra_sysfs_write_profile_settings(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
+       struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
+       int retval = 0;
+       int difference;
+       int profile_number;
+       struct pyra_profile_settings *profile_settings;
+
+       if (off != 0 || count != sizeof(struct pyra_profile_settings))
+               return -EINVAL;
+
+       profile_number = ((struct pyra_profile_settings const *)buf)->number;
+       profile_settings = &pyra->profile_settings[profile_number];
+
+       mutex_lock(&pyra->pyra_lock);
+       difference = memcmp(buf, profile_settings,
+                       sizeof(struct pyra_profile_settings));
+       if (difference) {
+               retval = pyra_set_profile_settings(usb_dev,
+                               (struct pyra_profile_settings const *)buf);
+               if (!retval)
+                       memcpy(profile_settings, buf,
+                                       sizeof(struct pyra_profile_settings));
+       }
+       mutex_unlock(&pyra->pyra_lock);
+
+       if (retval)
+               return retval;
+
+       return sizeof(struct pyra_profile_settings);
+}
+
+static ssize_t pyra_sysfs_write_profile_buttons(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
+       struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
+       int retval = 0;
+       int difference;
+       int profile_number;
+       struct pyra_profile_buttons *profile_buttons;
+
+       if (off != 0 || count != sizeof(struct pyra_profile_buttons))
+               return -EINVAL;
+
+       profile_number = ((struct pyra_profile_buttons const *)buf)->number;
+       profile_buttons = &pyra->profile_buttons[profile_number];
+
+       mutex_lock(&pyra->pyra_lock);
+       difference = memcmp(buf, profile_buttons,
+                       sizeof(struct pyra_profile_buttons));
+       if (difference) {
+               retval = pyra_set_profile_buttons(usb_dev,
+                               (struct pyra_profile_buttons const *)buf);
+               if (!retval)
+                       memcpy(profile_buttons, buf,
+                                       sizeof(struct pyra_profile_buttons));
+       }
+       mutex_unlock(&pyra->pyra_lock);
+
+       if (retval)
+               return retval;
+
+       return sizeof(struct pyra_profile_buttons);
+}
+
+static ssize_t pyra_sysfs_read_settings(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
+
+       if (off >= sizeof(struct pyra_settings))
+               return 0;
+
+       if (off + count > sizeof(struct pyra_settings))
+               count = sizeof(struct pyra_settings) - off;
+
+       mutex_lock(&pyra->pyra_lock);
+       memcpy(buf, ((char const *)&pyra->settings) + off, count);
+       mutex_unlock(&pyra->pyra_lock);
+
+       return count;
+}
+
+static ssize_t pyra_sysfs_write_settings(struct file *fp,
+               struct kobject *kobj, struct bin_attribute *attr, char *buf,
+               loff_t off, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
+       struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
+       int retval = 0;
+       int difference;
+
+       if (off != 0 || count != sizeof(struct pyra_settings))
+               return -EINVAL;
+
+       mutex_lock(&pyra->pyra_lock);
+       difference = memcmp(buf, &pyra->settings, sizeof(struct pyra_settings));
+       if (difference) {
+               retval = pyra_set_settings(usb_dev,
+                               (struct pyra_settings const *)buf);
+               if (!retval)
+                       memcpy(&pyra->settings, buf,
+                                       sizeof(struct pyra_settings));
+       }
+       mutex_unlock(&pyra->pyra_lock);
+
+       if (retval)
+               return retval;
+
+       profile_activated(pyra, pyra->settings.startup_profile);
+
+       return sizeof(struct pyra_settings);
+}
+
+
+static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
+       return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi);
+}
+
+static ssize_t pyra_sysfs_show_actual_profile(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
+       return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_profile);
+}
+
+static ssize_t pyra_sysfs_show_firmware_version(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
+       return snprintf(buf, PAGE_SIZE, "%d\n", pyra->firmware_version);
+}
+
+static ssize_t pyra_sysfs_show_startup_profile(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
+       return snprintf(buf, PAGE_SIZE, "%d\n", pyra->settings.startup_profile);
+}
+
+static DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL);
+
+static DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL);
+
+static DEVICE_ATTR(firmware_version, 0440,
+               pyra_sysfs_show_firmware_version, NULL);
+
+static DEVICE_ATTR(startup_profile, 0440,
+               pyra_sysfs_show_startup_profile, NULL);
+
+static struct attribute *pyra_attributes[] = {
+               &dev_attr_actual_cpi.attr,
+               &dev_attr_actual_profile.attr,
+               &dev_attr_firmware_version.attr,
+               &dev_attr_startup_profile.attr,
+               NULL
+};
+
+static struct attribute_group pyra_attribute_group = {
+               .attrs = pyra_attributes
+};
+
+static struct bin_attribute pyra_profile_settings_attr = {
+               .attr = { .name = "profile_settings", .mode = 0220 },
+               .size = sizeof(struct pyra_profile_settings),
+               .write = pyra_sysfs_write_profile_settings
+};
+
+static struct bin_attribute pyra_profile1_settings_attr = {
+               .attr = { .name = "profile1_settings", .mode = 0440 },
+               .size = sizeof(struct pyra_profile_settings),
+               .read = pyra_sysfs_read_profile1_settings
+};
+
+static struct bin_attribute pyra_profile2_settings_attr = {
+               .attr = { .name = "profile2_settings", .mode = 0440 },
+               .size = sizeof(struct pyra_profile_settings),
+               .read = pyra_sysfs_read_profile2_settings
+};
+
+static struct bin_attribute pyra_profile3_settings_attr = {
+               .attr = { .name = "profile3_settings", .mode = 0440 },
+               .size = sizeof(struct pyra_profile_settings),
+               .read = pyra_sysfs_read_profile3_settings
+};
+
+static struct bin_attribute pyra_profile4_settings_attr = {
+               .attr = { .name = "profile4_settings", .mode = 0440 },
+               .size = sizeof(struct pyra_profile_settings),
+               .read = pyra_sysfs_read_profile4_settings
+};
+
+static struct bin_attribute pyra_profile5_settings_attr = {
+               .attr = { .name = "profile5_settings", .mode = 0440 },
+               .size = sizeof(struct pyra_profile_settings),
+               .read = pyra_sysfs_read_profile5_settings
+};
+
+static struct bin_attribute pyra_profile_buttons_attr = {
+               .attr = { .name = "profile_buttons", .mode = 0220 },
+               .size = sizeof(struct pyra_profile_buttons),
+               .write = pyra_sysfs_write_profile_buttons
+};
+
+static struct bin_attribute pyra_profile1_buttons_attr = {
+               .attr = { .name = "profile1_buttons", .mode = 0440 },
+               .size = sizeof(struct pyra_profile_buttons),
+               .read = pyra_sysfs_read_profile1_buttons
+};
+
+static struct bin_attribute pyra_profile2_buttons_attr = {
+               .attr = { .name = "profile2_buttons", .mode = 0440 },
+               .size = sizeof(struct pyra_profile_buttons),
+               .read = pyra_sysfs_read_profile2_buttons
+};
+
+static struct bin_attribute pyra_profile3_buttons_attr = {
+               .attr = { .name = "profile3_buttons", .mode = 0440 },
+               .size = sizeof(struct pyra_profile_buttons),
+               .read = pyra_sysfs_read_profile3_buttons
+};
+
+static struct bin_attribute pyra_profile4_buttons_attr = {
+               .attr = { .name = "profile4_buttons", .mode = 0440 },
+               .size = sizeof(struct pyra_profile_buttons),
+               .read = pyra_sysfs_read_profile4_buttons
+};
+
+static struct bin_attribute pyra_profile5_buttons_attr = {
+               .attr = { .name = "profile5_buttons", .mode = 0440 },
+               .size = sizeof(struct pyra_profile_buttons),
+               .read = pyra_sysfs_read_profile5_buttons
+};
+
+static struct bin_attribute pyra_settings_attr = {
+               .attr = { .name = "settings", .mode = 0660 },
+               .size = sizeof(struct pyra_settings),
+               .read = pyra_sysfs_read_settings,
+               .write = pyra_sysfs_write_settings
+};
+
+static int pyra_create_sysfs_attributes(struct usb_interface *intf)
+{
+       int retval;
+
+       retval = sysfs_create_group(&intf->dev.kobj, &pyra_attribute_group);
+       if (retval)
+               goto exit_1;
+
+       retval = sysfs_create_bin_file(&intf->dev.kobj,
+                       &pyra_profile_settings_attr);
+       if (retval)
+               goto exit_2;
+
+       retval = sysfs_create_bin_file(&intf->dev.kobj,
+                       &pyra_profile1_settings_attr);
+       if (retval)
+               goto exit_3;
+
+       retval = sysfs_create_bin_file(&intf->dev.kobj,
+                       &pyra_profile2_settings_attr);
+       if (retval)
+               goto exit_4;
+
+       retval = sysfs_create_bin_file(&intf->dev.kobj,
+                       &pyra_profile3_settings_attr);
+       if (retval)
+               goto exit_5;
+
+       retval = sysfs_create_bin_file(&intf->dev.kobj,
+                       &pyra_profile4_settings_attr);
+       if (retval)
+               goto exit_6;
+
+       retval = sysfs_create_bin_file(&intf->dev.kobj,
+                       &pyra_profile5_settings_attr);
+       if (retval)
+               goto exit_7;
+
+       retval = sysfs_create_bin_file(&intf->dev.kobj,
+                       &pyra_profile_buttons_attr);
+       if (retval)
+               goto exit_8;
+
+       retval = sysfs_create_bin_file(&intf->dev.kobj,
+                       &pyra_profile1_buttons_attr);
+       if (retval)
+               goto exit_9;
+
+       retval = sysfs_create_bin_file(&intf->dev.kobj,
+                       &pyra_profile2_buttons_attr);
+       if (retval)
+               goto exit_10;
+
+       retval = sysfs_create_bin_file(&intf->dev.kobj,
+                       &pyra_profile3_buttons_attr);
+       if (retval)
+               goto exit_11;
+
+       retval = sysfs_create_bin_file(&intf->dev.kobj,
+                       &pyra_profile4_buttons_attr);
+       if (retval)
+               goto exit_12;
+
+       retval = sysfs_create_bin_file(&intf->dev.kobj,
+                       &pyra_profile5_buttons_attr);
+       if (retval)
+               goto exit_13;
+
+       retval = sysfs_create_bin_file(&intf->dev.kobj,
+                       &pyra_settings_attr);
+       if (retval)
+               goto exit_14;
+
+       return 0;
+
+exit_14:
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_buttons_attr);
+exit_13:
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_buttons_attr);
+exit_12:
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_buttons_attr);
+exit_11:
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_buttons_attr);
+exit_10:
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_buttons_attr);
+exit_9:
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_buttons_attr);
+exit_8:
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_settings_attr);
+exit_7:
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_settings_attr);
+exit_6:
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_settings_attr);
+exit_5:
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_settings_attr);
+exit_4:
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_settings_attr);
+exit_3:
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_settings_attr);
+exit_2:
+       sysfs_remove_group(&intf->dev.kobj, &pyra_attribute_group);
+exit_1:
+       return retval;
+}
+
+static void pyra_remove_sysfs_attributes(struct usb_interface *intf)
+{
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_settings_attr);
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_buttons_attr);
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_buttons_attr);
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_buttons_attr);
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_buttons_attr);
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_buttons_attr);
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_buttons_attr);
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_settings_attr);
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_settings_attr);
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_settings_attr);
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_settings_attr);
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_settings_attr);
+       sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_settings_attr);
+       sysfs_remove_group(&intf->dev.kobj, &pyra_attribute_group);
+}
+
+static int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
+               struct pyra_device *pyra)
+{
+       struct pyra_info *info;
+       int retval, i;
+
+       mutex_init(&pyra->pyra_lock);
+
+       info = kmalloc(sizeof(struct pyra_info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+       retval = pyra_get_info(usb_dev, info);
+       if (retval) {
+               kfree(info);
+               return retval;
+       }
+       pyra->firmware_version = info->firmware_version;
+       kfree(info);
+
+       retval = pyra_get_settings(usb_dev, &pyra->settings);
+       if (retval)
+               return retval;
+
+       for (i = 0; i < 5; ++i) {
+               retval = pyra_get_profile_settings(usb_dev,
+                               &pyra->profile_settings[i], i);
+               if (retval)
+                       return retval;
+
+               retval = pyra_get_profile_buttons(usb_dev,
+                               &pyra->profile_buttons[i], i);
+               if (retval)
+                       return retval;
+       }
+
+       profile_activated(pyra, pyra->settings.startup_profile);
+
+       return 0;
+}
+
+static int pyra_init_specials(struct hid_device *hdev)
+{
+       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+       struct usb_device *usb_dev = interface_to_usbdev(intf);
+       struct pyra_device *pyra;
+       int retval;
+
+       if (intf->cur_altsetting->desc.bInterfaceProtocol
+                       == USB_INTERFACE_PROTOCOL_MOUSE) {
+
+               pyra = kzalloc(sizeof(*pyra), GFP_KERNEL);
+               if (!pyra) {
+                       dev_err(&hdev->dev, "can't alloc device descriptor\n");
+                       return -ENOMEM;
+               }
+               hid_set_drvdata(hdev, pyra);
+
+               retval = pyra_init_pyra_device_struct(usb_dev, pyra);
+               if (retval) {
+                       dev_err(&hdev->dev,
+                                       "couldn't init struct pyra_device\n");
+                       goto exit_free;
+               }
+
+               retval = roccat_connect(hdev);
+               if (retval < 0) {
+                       dev_err(&hdev->dev, "couldn't init char dev\n");
+               } else {
+                       pyra->chrdev_minor = retval;
+                       pyra->roccat_claimed = 1;
+               }
+
+               retval = pyra_create_sysfs_attributes(intf);
+               if (retval) {
+                       dev_err(&hdev->dev, "cannot create sysfs files\n");
+                       goto exit_free;
+               }
+       } else {
+               hid_set_drvdata(hdev, NULL);
+       }
+
+       return 0;
+exit_free:
+       kfree(pyra);
+       return retval;
+}
+
+static void pyra_remove_specials(struct hid_device *hdev)
+{
+       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+       struct pyra_device *pyra;
+
+       if (intf->cur_altsetting->desc.bInterfaceProtocol
+                       == USB_INTERFACE_PROTOCOL_MOUSE) {
+               pyra_remove_sysfs_attributes(intf);
+               pyra = hid_get_drvdata(hdev);
+               if (pyra->roccat_claimed)
+                       roccat_disconnect(pyra->chrdev_minor);
+               kfree(hid_get_drvdata(hdev));
+       }
+}
+
+static int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int retval;
+
+       retval = hid_parse(hdev);
+       if (retval) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto exit;
+       }
+
+       retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (retval) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto exit;
+       }
+
+       retval = pyra_init_specials(hdev);
+       if (retval) {
+               dev_err(&hdev->dev, "couldn't install mouse\n");
+               goto exit_stop;
+       }
+       return 0;
+
+exit_stop:
+       hid_hw_stop(hdev);
+exit:
+       return retval;
+}
+
+static void pyra_remove(struct hid_device *hdev)
+{
+       pyra_remove_specials(hdev);
+       hid_hw_stop(hdev);
+}
+
+static void pyra_keep_values_up_to_date(struct pyra_device *pyra,
+               u8 const *data)
+{
+       struct pyra_mouse_event_button const *button_event;
+
+       switch (data[0]) {
+       case PYRA_MOUSE_REPORT_NUMBER_BUTTON:
+               button_event = (struct pyra_mouse_event_button const *)data;
+               switch (button_event->type) {
+               case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
+                       profile_activated(pyra, button_event->data1 - 1);
+                       break;
+               case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
+                       pyra->actual_cpi = button_event->data1;
+                       break;
+               }
+               break;
+       }
+}
+
+static void pyra_report_to_chrdev(struct pyra_device const *pyra,
+               u8 const *data)
+{
+       struct pyra_roccat_report roccat_report;
+       struct pyra_mouse_event_button const *button_event;
+
+       if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON)
+               return;
+
+       button_event = (struct pyra_mouse_event_button const *)data;
+
+       switch (button_event->type) {
+       case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
+       case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
+               roccat_report.type = button_event->type;
+               roccat_report.value = button_event->data1;
+               roccat_report.key = 0;
+               roccat_report_event(pyra->chrdev_minor,
+                               (uint8_t const *)&roccat_report,
+                               sizeof(struct pyra_roccat_report));
+               break;
+       case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO:
+       case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT:
+       case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH:
+               if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) {
+                       roccat_report.type = button_event->type;
+                       roccat_report.key = button_event->data1;
+                       /*
+                        * pyra reports profile numbers with range 1-5.
+                        * Keeping this behaviour.
+                        */
+                       roccat_report.value = pyra->actual_profile + 1;
+                       roccat_report_event(pyra->chrdev_minor,
+                                       (uint8_t const *)&roccat_report,
+                                       sizeof(struct pyra_roccat_report));
+               }
+               break;
+       }
+}
+
+static int pyra_raw_event(struct hid_device *hdev, struct hid_report *report,
+               u8 *data, int size)
+{
+       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+       struct pyra_device *pyra = hid_get_drvdata(hdev);
+
+       if (intf->cur_altsetting->desc.bInterfaceProtocol
+                       != USB_INTERFACE_PROTOCOL_MOUSE)
+               return 0;
+
+       pyra_keep_values_up_to_date(pyra, data);
+
+       if (pyra->roccat_claimed)
+               pyra_report_to_chrdev(pyra, data);
+
+       return 0;
+}
+
+static const struct hid_device_id pyra_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
+                       USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
+       /* TODO add USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS after testing */
+       { }
+};
+
+MODULE_DEVICE_TABLE(hid, pyra_devices);
+
+static struct hid_driver pyra_driver = {
+               .name = "pyra",
+               .id_table = pyra_devices,
+               .probe = pyra_probe,
+               .remove = pyra_remove,
+               .raw_event = pyra_raw_event
+};
+
+static int __init pyra_init(void)
+{
+       return hid_register_driver(&pyra_driver);
+}
+
+static void __exit pyra_exit(void)
+{
+       hid_unregister_driver(&pyra_driver);
+}
+
+module_init(pyra_init);
+module_exit(pyra_exit);
+
+MODULE_AUTHOR("Stefan Achatz");
+MODULE_DESCRIPTION("USB Roccat Pyra driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hid/hid-roccat-pyra.h b/drivers/hid/hid-roccat-pyra.h
new file mode 100644 (file)
index 0000000..22f80a8
--- /dev/null
@@ -0,0 +1,186 @@
+#ifndef __HID_ROCCAT_PYRA_H
+#define __HID_ROCCAT_PYRA_H
+
+/*
+ * Copyright (c) 2010 Stefan Achatz <erazor_de@users.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.
+ */
+
+#include <linux/types.h>
+
+#pragma pack(push)
+#pragma pack(1)
+
+struct pyra_b {
+       uint8_t command; /* PYRA_COMMAND_B */
+       uint8_t size; /* always 3 */
+       uint8_t unknown; /* 1 */
+};
+
+struct pyra_control {
+       uint8_t command; /* PYRA_COMMAND_CONTROL */
+       /*
+        * value is profile number for request_settings and request_buttons
+        * 1 if status ok for request_status
+        */
+       uint8_t value; /* Range 0-4 */
+       uint8_t request;
+};
+
+enum pyra_control_requests {
+       PYRA_CONTROL_REQUEST_STATUS = 0x00,
+       PYRA_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10,
+       PYRA_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20
+};
+
+struct pyra_settings {
+       uint8_t command; /* PYRA_COMMAND_SETTINGS */
+       uint8_t size; /* always 3 */
+       uint8_t startup_profile; /* Range 0-4! */
+};
+
+struct pyra_profile_settings {
+       uint8_t command; /* PYRA_COMMAND_PROFILE_SETTINGS */
+       uint8_t size; /* always 0xd */
+       uint8_t number; /* Range 0-4 */
+       uint8_t xysync;
+       uint8_t x_sensitivity; /* 0x1-0xa */
+       uint8_t y_sensitivity;
+       uint8_t x_cpi; /* unused */
+       uint8_t y_cpi; /* this value is for x and y */
+       uint8_t lightswitch; /* 0 = off, 1 = on */
+       uint8_t light_effect;
+       uint8_t handedness;
+       uint16_t checksum; /* byte sum */
+};
+
+struct pyra_profile_buttons {
+       uint8_t command; /* PYRA_COMMAND_PROFILE_BUTTONS */
+       uint8_t size; /* always 0x13 */
+       uint8_t number; /* Range 0-4 */
+       uint8_t buttons[14];
+       uint16_t checksum; /* byte sum */
+};
+
+struct pyra_info {
+       uint8_t command; /* PYRA_COMMAND_INFO */
+       uint8_t size; /* always 6 */
+       uint8_t firmware_version;
+       uint8_t unknown1; /* always 0 */
+       uint8_t unknown2; /* always 1 */
+       uint8_t unknown3; /* always 0 */
+};
+
+enum pyra_commands {
+       PYRA_COMMAND_CONTROL = 0x4,
+       PYRA_COMMAND_SETTINGS = 0x5,
+       PYRA_COMMAND_PROFILE_SETTINGS = 0x6,
+       PYRA_COMMAND_PROFILE_BUTTONS = 0x7,
+       PYRA_COMMAND_INFO = 0x9,
+       PYRA_COMMAND_B = 0xb
+};
+
+enum pyra_usb_commands {
+       PYRA_USB_COMMAND_CONTROL = 0x304,
+       PYRA_USB_COMMAND_SETTINGS = 0x305,
+       PYRA_USB_COMMAND_PROFILE_SETTINGS = 0x306,
+       PYRA_USB_COMMAND_PROFILE_BUTTONS = 0x307,
+       PYRA_USB_COMMAND_INFO = 0x309,
+       PYRA_USB_COMMAND_B = 0x30b /* writes 3 bytes */
+};
+
+enum pyra_mouse_report_numbers {
+       PYRA_MOUSE_REPORT_NUMBER_HID = 1,
+       PYRA_MOUSE_REPORT_NUMBER_AUDIO = 2,
+       PYRA_MOUSE_REPORT_NUMBER_BUTTON = 3,
+};
+
+struct pyra_mouse_event_button {
+       uint8_t report_number; /* always 3 */
+       uint8_t unknown; /* always 0 */
+       uint8_t type;
+       uint8_t data1;
+       uint8_t data2;
+};
+
+struct pyra_mouse_event_audio {
+       uint8_t report_number; /* always 2 */
+       uint8_t type;
+       uint8_t unused; /* always 0 */
+};
+
+/* hid audio controls */
+enum pyra_mouse_event_audio_types {
+       PYRA_MOUSE_EVENT_AUDIO_TYPE_MUTE = 0xe2,
+       PYRA_MOUSE_EVENT_AUDIO_TYPE_VOLUME_UP = 0xe9,
+       PYRA_MOUSE_EVENT_AUDIO_TYPE_VOLUME_DOWN = 0xea,
+};
+
+enum pyra_mouse_event_button_types {
+       /*
+        * Mouse sends tilt events on report_number 1 and 3
+        * Tilt events are sent repeatedly with 0.94s between first and second
+        * event and 0.22s on subsequent
+        */
+       PYRA_MOUSE_EVENT_BUTTON_TYPE_TILT = 0x10,
+
+       /*
+        * These are sent sequentially
+        * data1 contains new profile number in range 1-5
+        */
+       PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_1 = 0x20,
+       PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2 = 0x30,
+
+       /*
+        * data1 = button_number (rmp index)
+        * data2 = pressed/released
+        */
+       PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO = 0x40,
+       PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT = 0x50,
+
+       /*
+        * data1 = button_number (rmp index)
+        */
+       PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH = 0x60,
+
+       /* data1 = new cpi */
+       PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI = 0xb0,
+
+       /* data1 and data2 = new sensitivity */
+       PYRA_MOUSE_EVENT_BUTTON_TYPE_SENSITIVITY = 0xc0,
+
+       PYRA_MOUSE_EVENT_BUTTON_TYPE_MULTIMEDIA = 0xf0,
+};
+
+enum {
+       PYRA_MOUSE_EVENT_BUTTON_PRESS = 0,
+       PYRA_MOUSE_EVENT_BUTTON_RELEASE = 1,
+};
+
+struct pyra_roccat_report {
+       uint8_t type;
+       uint8_t value;
+       uint8_t key;
+};
+
+#pragma pack(pop)
+
+struct pyra_device {
+       int actual_profile;
+       int actual_cpi;
+       int firmware_version;
+       int roccat_claimed;
+       int chrdev_minor;
+       struct mutex pyra_lock;
+       struct pyra_settings settings;
+       struct pyra_profile_settings profile_settings[5];
+       struct pyra_profile_buttons profile_buttons[5];
+};
+
+#endif
index bda0fd60c98d28014258768d7711c3db25b391e5..35894444e00067640532c17f2cc5468c1f9cef14 100644 (file)
@@ -61,10 +61,10 @@ static inline void samsung_irda_dev_trace(struct hid_device *hdev,
                        "descriptor\n", rsize);
 }
 
-static void samsung_irda_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-               unsigned int rsize)
+static __u8 *samsung_irda_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
 {
-       if (rsize == 184 && rdesc[175] == 0x25 && rdesc[176] == 0x40 &&
+       if (*rsize == 184 && rdesc[175] == 0x25 && rdesc[176] == 0x40 &&
                        rdesc[177] == 0x75 && rdesc[178] == 0x30 &&
                        rdesc[179] == 0x95 && rdesc[180] == 0x01 &&
                        rdesc[182] == 0x40) {
@@ -74,24 +74,25 @@ static void samsung_irda_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                rdesc[180] = 0x06;
                rdesc[182] = 0x42;
        } else
-       if (rsize == 203 && rdesc[192] == 0x15 && rdesc[193] == 0x0 &&
+       if (*rsize == 203 && rdesc[192] == 0x15 && rdesc[193] == 0x0 &&
                        rdesc[194] == 0x25 && rdesc[195] == 0x12) {
                samsung_irda_dev_trace(hdev, 203);
                rdesc[193] = 0x1;
                rdesc[195] = 0xf;
        } else
-       if (rsize == 135 && rdesc[124] == 0x15 && rdesc[125] == 0x0 &&
+       if (*rsize == 135 && rdesc[124] == 0x15 && rdesc[125] == 0x0 &&
                        rdesc[126] == 0x25 && rdesc[127] == 0x11) {
                samsung_irda_dev_trace(hdev, 135);
                rdesc[125] = 0x1;
                rdesc[127] = 0xe;
        } else
-       if (rsize == 171 && rdesc[160] == 0x15 && rdesc[161] == 0x0 &&
+       if (*rsize == 171 && rdesc[160] == 0x15 && rdesc[161] == 0x0 &&
                        rdesc[162] == 0x25 && rdesc[163] == 0x01) {
                samsung_irda_dev_trace(hdev, 171);
                rdesc[161] = 0x1;
                rdesc[163] = 0x3;
        }
+       return rdesc;
 }
 
 #define samsung_kbd_mouse_map_key_clear(c) \
@@ -130,11 +131,12 @@ static int samsung_kbd_mouse_input_mapping(struct hid_device *hdev,
        return 1;
 }
 
-static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-       unsigned int rsize)
+static __u8 *samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+       unsigned int *rsize)
 {
        if (USB_DEVICE_ID_SAMSUNG_IR_REMOTE == hdev->product)
-               samsung_irda_report_fixup(hdev, rdesc, rsize);
+               rdesc = samsung_irda_report_fixup(hdev, rdesc, rsize);
+       return rdesc;
 }
 
 static int samsung_input_mapping(struct hid_device *hdev, struct hid_input *hi,
index 402d5574b5747a6e39b2b6c3bc3f1ec29e35a9f9..677bb3da10e8d4149d5d6583f943f6a767c9d3c1 100644 (file)
 
 #include "hid-ids.h"
 
-#define VAIO_RDESC_CONSTANT 0x0001
+#define VAIO_RDESC_CONSTANT     (1 << 0)
+#define SIXAXIS_CONTROLLER_USB  (1 << 1)
+#define SIXAXIS_CONTROLLER_BT   (1 << 2)
 
 struct sony_sc {
        unsigned long quirks;
 };
 
 /* Sony Vaio VGX has wrongly mouse pointer declared as constant */
-static void sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-               unsigned int rsize)
+static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
 {
        struct sony_sc *sc = hid_get_drvdata(hdev);
 
        if ((sc->quirks & VAIO_RDESC_CONSTANT) &&
-                       rsize >= 56 && rdesc[54] == 0x81 && rdesc[55] == 0x07) {
+                       *rsize >= 56 && rdesc[54] == 0x81 && rdesc[55] == 0x07) {
                dev_info(&hdev->dev, "Fixing up Sony Vaio VGX report "
                                "descriptor\n");
                rdesc[55] = 0x06;
        }
+       return rdesc;
+}
+
+static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf,
+               size_t count, unsigned char report_type)
+{
+       struct usb_interface *intf = to_usb_interface(hid->dev.parent);
+       struct usb_device *dev = interface_to_usbdev(intf);
+       struct usb_host_interface *interface = intf->cur_altsetting;
+       int report_id = buf[0];
+       int ret;
+
+       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+               HID_REQ_SET_REPORT,
+               USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+               ((report_type + 1) << 8) | report_id,
+               interface->desc.bInterfaceNumber, buf, count,
+               USB_CTRL_SET_TIMEOUT);
+
+       return ret;
 }
 
 /*
@@ -49,7 +71,7 @@ static void sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
  * to "operational".  Without this, the ps3 controller will not report any
  * events.
  */
-static int sony_set_operational_usb(struct hid_device *hdev)
+static int sixaxis_set_operational_usb(struct hid_device *hdev)
 {
        struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
        struct usb_device *dev = interface_to_usbdev(intf);
@@ -74,7 +96,7 @@ static int sony_set_operational_usb(struct hid_device *hdev)
        return ret;
 }
 
-static int sony_set_operational_bt(struct hid_device *hdev)
+static int sixaxis_set_operational_bt(struct hid_device *hdev)
 {
        unsigned char buf[] = { 0xf4,  0x42, 0x03, 0x00, 0x00 };
        return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
@@ -108,16 +130,14 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                goto err_free;
        }
 
-       switch (hdev->bus) {
-       case BUS_USB:
-               ret = sony_set_operational_usb(hdev);
-               break;
-       case BUS_BLUETOOTH:
-               ret = sony_set_operational_bt(hdev);
-               break;
-       default:
-               ret = 0;
+       if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
+               hdev->hid_output_raw_report = sixaxis_usb_output_raw_report;
+               ret = sixaxis_set_operational_usb(hdev);
        }
+       else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
+               ret = sixaxis_set_operational_bt(hdev);
+       else
+               ret = 0;
 
        if (ret < 0)
                goto err_stop;
@@ -137,8 +157,10 @@ static void sony_remove(struct hid_device *hdev)
 }
 
 static const struct hid_device_id sony_devices[] = {
-       { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
-       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
+               .driver_data = SIXAXIS_CONTROLLER_USB },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER),
+               .driver_data = SIXAXIS_CONTROLLER_BT },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE),
                .driver_data = VAIO_RDESC_CONSTANT },
        { }
index 90df886c5e048023f6839f8fcea1c62f00e6004f..3171be28c3d534daf8d3a8f245de79a1137fc9a8 100644 (file)
@@ -249,6 +249,8 @@ static void stantum_remove(struct hid_device *hdev)
 
 static const struct hid_device_id stantum_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, USB_DEVICE_ID_MTP) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_MTP_STM) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX, USB_DEVICE_ID_MTP_SITRONIX) },
        { }
 };
 MODULE_DEVICE_TABLE(hid, stantum_devices);
index 438107d9f1b2a3b5d11be27c2f9e1deca5690108..164ed568f6cf73bf4640b6f4080d99dde37d6d91 100644 (file)
 
 #include "hid-ids.h"
 
-static void sp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-               unsigned int rsize)
+static __u8 *sp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
 {
-       if (rsize >= 107 && rdesc[104] == 0x26 && rdesc[105] == 0x80 &&
+       if (*rsize >= 107 && rdesc[104] == 0x26 && rdesc[105] == 0x80 &&
                        rdesc[106] == 0x03) {
                dev_info(&hdev->dev, "fixing up Sunplus Wireless Desktop "
                                "report descriptor\n");
                rdesc[105] = rdesc[110] = 0x03;
                rdesc[106] = rdesc[111] = 0x21;
        }
+       return rdesc;
 }
 
 #define sp_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c
new file mode 100644 (file)
index 0000000..05fdc85
--- /dev/null
@@ -0,0 +1,623 @@
+/*
+ *  HID driver for UC-Logic devices not fully compliant with HID standard
+ *
+ *  Copyright (c) 2010 Nikolai Kondrashov
+ */
+
+/*
+ * 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/*
+ * The original descriptors of WPXXXXU tablets have three report IDs, of
+ * which only two are used (8 and 9), and the remaining (7) seems to have
+ * the originally intended pen description which was abandoned for some
+ * reason.  From this unused description it is possible to extract the
+ * actual physical extents and resolution. All the models use the same
+ * descriptor with different extents for the unused report ID.
+ *
+ * Here it is:
+ *
+ *  Usage Page (Digitizer),         ; Digitizer (0Dh)
+ *  Usage (Pen),                    ; Pen (02h, application collection)
+ *  Collection (Application),
+ *    Report ID (7),
+ *    Usage (Stylus),               ; Stylus (20h, logical collection)
+ *    Collection (Physical),
+ *      Usage (Tip Switch),         ; Tip switch (42h, momentary control)
+ *      Usage (Barrel Switch),      ; Barrel switch (44h, momentary control)
+ *      Usage (Eraser),             ; Eraser (45h, momentary control)
+ *      Logical Minimum (0),
+ *      Logical Maximum (1),
+ *      Report Size (1),
+ *      Report Count (3),
+ *      Input (Variable),
+ *      Report Count (3),
+ *      Input (Constant, Variable),
+ *      Usage (In Range),           ; In range (32h, momentary control)
+ *      Report Count (1),
+ *      Input (Variable),
+ *      Report Count (1),
+ *      Input (Constant, Variable),
+ *      Usage Page (Desktop),       ; Generic desktop controls (01h)
+ *      Usage (X),                  ; X (30h, dynamic value)
+ *      Report Size (16),
+ *      Report Count (1),
+ *      Push,
+ *      Unit Exponent (13),
+ *      Unit (Inch^3),
+ *      Physical Minimum (0),
+ *      Physical Maximum (Xpm),
+ *      Logical Maximum (Xlm),
+ *      Input (Variable),
+ *      Usage (Y),                  ; Y (31h, dynamic value)
+ *      Physical Maximum (Ypm),
+ *      Logical Maximum (Ylm),
+ *      Input (Variable),
+ *      Pop,
+ *      Usage Page (Digitizer),     ; Digitizer (0Dh)
+ *      Usage (Tip Pressure),       ; Tip pressure (30h, dynamic value)
+ *      Logical Maximum (1023),
+ *      Input (Variable),
+ *      Report Size (16),
+ *    End Collection,
+ *  End Collection,
+ *  Usage Page (Desktop),           ; Generic desktop controls (01h)
+ *  Usage (Mouse),                  ; Mouse (02h, application collection)
+ *  Collection (Application),
+ *    Report ID (8),
+ *    Usage (Pointer),              ; Pointer (01h, physical collection)
+ *    Collection (Physical),
+ *      Usage Page (Button),        ; Button (09h)
+ *      Usage Minimum (01h),
+ *      Usage Maximum (03h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (1),
+ *      Report Count (3),
+ *      Report Size (1),
+ *      Input (Variable),
+ *      Report Count (5),
+ *      Input (Constant),
+ *      Usage Page (Desktop),       ; Generic desktop controls (01h)
+ *      Usage (X),                  ; X (30h, dynamic value)
+ *      Usage (Y),                  ; Y (31h, dynamic value)
+ *      Usage (Wheel),              ; Wheel (38h, dynamic value)
+ *      Usage (00h),
+ *      Logical Minimum (-127),
+ *      Logical Maximum (127),
+ *      Report Size (8),
+ *      Report Count (4),
+ *      Input (Variable, Relative),
+ *    End Collection,
+ *  End Collection,
+ *  Usage Page (Desktop),           ; Generic desktop controls (01h)
+ *  Usage (Mouse),                  ; Mouse (02h, application collection)
+ *  Collection (Application),
+ *    Report ID (9),
+ *    Usage (Pointer),              ; Pointer (01h, physical collection)
+ *    Collection (Physical),
+ *      Usage Page (Button),        ; Button (09h)
+ *      Usage Minimum (01h),
+ *      Usage Maximum (03h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (1),
+ *      Report Count (3),
+ *      Report Size (1),
+ *      Input (Variable),
+ *      Report Count (5),
+ *      Input (Constant),
+ *      Usage Page (Desktop),       ; Generic desktop controls (01h)
+ *      Usage (X),                  ; X (30h, dynamic value)
+ *      Usage (Y),                  ; Y (31h, dynamic value)
+ *      Logical Minimum (0),
+ *      Logical Maximum (32767),
+ *      Physical Minimum (0),
+ *      Physical Maximum (32767),
+ *      Report Count (2),
+ *      Report Size (16),
+ *      Input (Variable),
+ *      Usage Page (Digitizer),     ; Digitizer (0Dh)
+ *      Usage (Tip Pressure),       ; Tip pressure (30h, dynamic value)
+ *      Logical Maximum (1023),
+ *      Report Count (1),
+ *      Report Size (16),
+ *      Input (Variable),
+ *    End Collection,
+ *  End Collection
+ *
+ * Here are the extents values for the WPXXXXU models:
+ *
+ *              Xpm     Xlm     Ypm     Ylm
+ *  WP4030U     4000    8000    3000    6000
+ *  WP5540U     5500    11000   4000    8000
+ *  WP8060U     8000    16000   6000    12000
+ *
+ * This suggests that all of them have 2000 LPI resolution, as advertised.
+ */
+
+/* Size of the original descriptor of WPXXXXU tablets */
+#define WPXXXXU_RDESC_ORIG_SIZE        212
+
+/*
+ * Fixed WP4030U report descriptor.
+ * Although the hardware might actually support it, the mouse description
+ * has been removed, since there seems to be no devices having one and it
+ * wouldn't make much sense because of the working area size.
+ */
+static __u8 wp4030u_rdesc_fixed[] = {
+       0x05, 0x0D,         /*  Usage Page (Digitizer),             */
+       0x09, 0x02,         /*  Usage (Pen),                        */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x09,         /*      Report ID (9),                  */
+       0x09, 0x20,         /*      Usage (Stylus),                 */
+       0xA0,               /*      Collection (Physical),          */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x09, 0x42,         /*          Usage (Tip Switch),         */
+       0x09, 0x44,         /*          Usage (Barrel Switch),      */
+       0x09, 0x46,         /*          Usage (Tablet Pick),        */
+       0x14,               /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x95, 0x03,         /*          Report Count (3),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x05,         /*          Report Count (5),           */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0x75, 0x10,         /*          Report Size (16),           */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x14,               /*          Logical Minimum (0),        */
+       0xA4,               /*          Push,                       */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x55, 0xFD,         /*          Unit Exponent (-3),         */
+       0x65, 0x13,         /*          Unit (Inch),                */
+       0x34,               /*          Physical Minimum (0),       */
+       0x09, 0x30,         /*          Usage (X),                  */
+       0x46, 0xA0, 0x0F,   /*          Physical Maximum (4000),    */
+       0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x09, 0x31,         /*          Usage (Y),                  */
+       0x46, 0xB8, 0x0B,   /*          Physical Maximum (3000),    */
+       0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xB4,               /*          Pop,                        */
+       0x09, 0x30,         /*          Usage (Tip Pressure),       */
+       0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xC0,               /*      End Collection,                 */
+       0xC0                /*  End Collection                      */
+};
+
+/* Fixed WP5540U report descriptor */
+static __u8 wp5540u_rdesc_fixed[] = {
+       0x05, 0x0D,         /*  Usage Page (Digitizer),             */
+       0x09, 0x02,         /*  Usage (Pen),                        */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x09,         /*      Report ID (9),                  */
+       0x09, 0x20,         /*      Usage (Stylus),                 */
+       0xA0,               /*      Collection (Physical),          */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x09, 0x42,         /*          Usage (Tip Switch),         */
+       0x09, 0x44,         /*          Usage (Barrel Switch),      */
+       0x09, 0x46,         /*          Usage (Tablet Pick),        */
+       0x14,               /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x95, 0x03,         /*          Report Count (3),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x05,         /*          Report Count (5),           */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0x75, 0x10,         /*          Report Size (16),           */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x14,               /*          Logical Minimum (0),        */
+       0xA4,               /*          Push,                       */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x55, 0xFD,         /*          Unit Exponent (-3),         */
+       0x65, 0x13,         /*          Unit (Inch),                */
+       0x34,               /*          Physical Minimum (0),       */
+       0x09, 0x30,         /*          Usage (X),                  */
+       0x46, 0x7C, 0x15,   /*          Physical Maximum (5500),    */
+       0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x09, 0x31,         /*          Usage (Y),                  */
+       0x46, 0xA0, 0x0F,   /*          Physical Maximum (4000),    */
+       0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xB4,               /*          Pop,                        */
+       0x09, 0x30,         /*          Usage (Tip Pressure),       */
+       0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xC0,               /*      End Collection,                 */
+       0xC0,               /*  End Collection,                     */
+       0x05, 0x01,         /*  Usage Page (Desktop),               */
+       0x09, 0x02,         /*  Usage (Mouse),                      */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x08,         /*      Report ID (8),                  */
+       0x09, 0x01,         /*      Usage (Pointer),                */
+       0xA0,               /*      Collection (Physical),          */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x05, 0x09,         /*          Usage Page (Button),        */
+       0x19, 0x01,         /*          Usage Minimum (01h),        */
+       0x29, 0x03,         /*          Usage Maximum (03h),        */
+       0x14,               /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x95, 0x03,         /*          Report Count (3),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x05,         /*          Report Count (5),           */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x09, 0x30,         /*          Usage (X),                  */
+       0x09, 0x31,         /*          Usage (Y),                  */
+       0x15, 0x81,         /*          Logical Minimum (-127),     */
+       0x25, 0x7F,         /*          Logical Maximum (127),      */
+       0x95, 0x02,         /*          Report Count (2),           */
+       0x81, 0x06,         /*          Input (Variable, Relative), */
+       0x09, 0x38,         /*          Usage (Wheel),              */
+       0x15, 0xFF,         /*          Logical Minimum (-1),       */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x81, 0x06,         /*          Input (Variable, Relative), */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0xC0,               /*      End Collection,                 */
+       0xC0                /*  End Collection                      */
+};
+
+/* Fixed WP8060U report descriptor */
+static __u8 wp8060u_rdesc_fixed[] = {
+       0x05, 0x0D,         /*  Usage Page (Digitizer),             */
+       0x09, 0x02,         /*  Usage (Pen),                        */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x09,         /*      Report ID (9),                  */
+       0x09, 0x20,         /*      Usage (Stylus),                 */
+       0xA0,               /*      Collection (Physical),          */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x09, 0x42,         /*          Usage (Tip Switch),         */
+       0x09, 0x44,         /*          Usage (Barrel Switch),      */
+       0x09, 0x46,         /*          Usage (Tablet Pick),        */
+       0x14,               /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x95, 0x03,         /*          Report Count (3),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x05,         /*          Report Count (5),           */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0x75, 0x10,         /*          Report Size (16),           */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x14,               /*          Logical Minimum (0),        */
+       0xA4,               /*          Push,                       */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x55, 0xFD,         /*          Unit Exponent (-3),         */
+       0x65, 0x13,         /*          Unit (Inch),                */
+       0x34,               /*          Physical Minimum (0),       */
+       0x09, 0x30,         /*          Usage (X),                  */
+       0x46, 0x40, 0x1F,   /*          Physical Maximum (8000),    */
+       0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x09, 0x31,         /*          Usage (Y),                  */
+       0x46, 0x70, 0x17,   /*          Physical Maximum (6000),    */
+       0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xB4,               /*          Pop,                        */
+       0x09, 0x30,         /*          Usage (Tip Pressure),       */
+       0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xC0,               /*      End Collection,                 */
+       0xC0,               /*  End Collection,                     */
+       0x05, 0x01,         /*  Usage Page (Desktop),               */
+       0x09, 0x02,         /*  Usage (Mouse),                      */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x08,         /*      Report ID (8),                  */
+       0x09, 0x01,         /*      Usage (Pointer),                */
+       0xA0,               /*      Collection (Physical),          */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x05, 0x09,         /*          Usage Page (Button),        */
+       0x19, 0x01,         /*          Usage Minimum (01h),        */
+       0x29, 0x03,         /*          Usage Maximum (03h),        */
+       0x14,               /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x95, 0x03,         /*          Report Count (3),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x05,         /*          Report Count (5),           */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x09, 0x30,         /*          Usage (X),                  */
+       0x09, 0x31,         /*          Usage (Y),                  */
+       0x15, 0x81,         /*          Logical Minimum (-127),     */
+       0x25, 0x7F,         /*          Logical Maximum (127),      */
+       0x95, 0x02,         /*          Report Count (2),           */
+       0x81, 0x06,         /*          Input (Variable, Relative), */
+       0x09, 0x38,         /*          Usage (Wheel),              */
+       0x15, 0xFF,         /*          Logical Minimum (-1),       */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x81, 0x06,         /*          Input (Variable, Relative), */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0xC0,               /*      End Collection,                 */
+       0xC0                /*  End Collection                      */
+};
+
+/*
+ * Original PF1209 report descriptor.
+ *
+ * The descriptor is similar to WPXXXXU descriptors, with an addition of a
+ * feature report (ID 4) of unknown purpose.
+ *
+ * Although the advertised resolution is 4000 LPI the unused report ID
+ * (taken from WPXXXXU, it seems) states 2000 LPI, but it is probably
+ * incorrect and is a result of blind copying without understanding. Anyway
+ * the real logical extents are always scaled to 0..32767, which IMHO spoils
+ * the precision.
+ *
+ *  Usage Page (Digitizer),         ; Digitizer (0Dh)
+ *  Usage (Pen),                    ; Pen (02h, application collection)
+ *  Collection (Application),
+ *    Report ID (7),
+ *    Usage (Stylus),               ; Stylus (20h, logical collection)
+ *    Collection (Physical),
+ *      Usage (Tip Switch),         ; Tip switch (42h, momentary control)
+ *      Usage (Barrel Switch),      ; Barrel switch (44h, momentary control)
+ *      Usage (Eraser),             ; Eraser (45h, momentary control)
+ *      Logical Minimum (0),
+ *      Logical Maximum (1),
+ *      Report Size (1),
+ *      Report Count (3),
+ *      Input (Variable),
+ *      Report Count (3),
+ *      Input (Constant, Variable),
+ *      Usage (In Range),           ; In range (32h, momentary control)
+ *      Report Count (1),
+ *      Input (Variable),
+ *      Report Count (1),
+ *      Input (Constant, Variable),
+ *      Usage Page (Desktop),       ; Generic desktop controls (01h)
+ *      Usage (X),                  ; X (30h, dynamic value)
+ *      Report Size (16),
+ *      Report Count (1),
+ *      Push,
+ *      Unit Exponent (13),
+ *      Unit (Inch^3),
+ *      Physical Minimum (0),
+ *      Physical Maximum (12000),
+ *      Logical Maximum (24000),
+ *      Input (Variable),
+ *      Usage (Y),                  ; Y (31h, dynamic value)
+ *      Physical Maximum (9000),
+ *      Logical Maximum (18000),
+ *      Input (Variable),
+ *      Pop,
+ *      Usage Page (Digitizer),     ; Digitizer (0Dh)
+ *      Usage (Tip Pressure),       ; Tip pressure (30h, dynamic value)
+ *      Logical Maximum (1023),
+ *      Input (Variable),
+ *      Report Size (16),
+ *    End Collection,
+ *  End Collection,
+ *  Usage Page (Desktop),           ; Generic desktop controls (01h)
+ *  Usage (Mouse),                  ; Mouse (02h, application collection)
+ *  Collection (Application),
+ *    Report ID (8),
+ *    Usage (Pointer),              ; Pointer (01h, physical collection)
+ *    Collection (Physical),
+ *      Usage Page (Button),        ; Button (09h)
+ *      Usage Minimum (01h),
+ *      Usage Maximum (03h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (1),
+ *      Report Count (3),
+ *      Report Size (1),
+ *      Input (Variable),
+ *      Report Count (5),
+ *      Input (Constant),
+ *      Usage Page (Desktop),       ; Generic desktop controls (01h)
+ *      Usage (X),                  ; X (30h, dynamic value)
+ *      Usage (Y),                  ; Y (31h, dynamic value)
+ *      Usage (Wheel),              ; Wheel (38h, dynamic value)
+ *      Usage (00h),
+ *      Logical Minimum (-127),
+ *      Logical Maximum (127),
+ *      Report Size (8),
+ *      Report Count (4),
+ *      Input (Variable, Relative),
+ *    End Collection,
+ *  End Collection,
+ *  Usage Page (Desktop),           ; Generic desktop controls (01h)
+ *  Usage (Mouse),                  ; Mouse (02h, application collection)
+ *  Collection (Application),
+ *    Report ID (9),
+ *    Usage (Pointer),              ; Pointer (01h, physical collection)
+ *    Collection (Physical),
+ *      Usage Page (Button),        ; Button (09h)
+ *      Usage Minimum (01h),
+ *      Usage Maximum (03h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (1),
+ *      Report Count (3),
+ *      Report Size (1),
+ *      Input (Variable),
+ *      Report Count (5),
+ *      Input (Constant),
+ *      Usage Page (Desktop),       ; Generic desktop controls (01h)
+ *      Usage (X),                  ; X (30h, dynamic value)
+ *      Usage (Y),                  ; Y (31h, dynamic value)
+ *      Logical Minimum (0),
+ *      Logical Maximum (32767),
+ *      Physical Minimum (0),
+ *      Physical Maximum (32767),
+ *      Report Count (2),
+ *      Report Size (16),
+ *      Input (Variable),
+ *      Usage Page (Digitizer),     ; Digitizer (0Dh)
+ *      Usage (Tip Pressure),       ; Tip pressure (30h, dynamic value)
+ *      Logical Maximum (1023),
+ *      Report Count (1),
+ *      Report Size (16),
+ *      Input (Variable),
+ *    End Collection,
+ *  End Collection,
+ *  Usage Page (Desktop),           ; Generic desktop controls (01h)
+ *  Usage (00h),
+ *  Collection (Application),
+ *    Report ID (4),
+ *    Logical Minimum (0),
+ *    Logical Maximum (255),
+ *    Usage (00h),
+ *    Report Size (8),
+ *    Report Count (3),
+ *    Feature (Variable),
+ *  End Collection
+ */
+
+/* Size of the original descriptor of PF1209 tablet */
+#define PF1209_RDESC_ORIG_SIZE 234
+
+/*
+ * Fixed PF1209 report descriptor
+ *
+ * The descriptor is fixed similarly to WP5540U and WP8060U, plus the
+ * feature report is removed, because its purpose is unknown and it is of no
+ * use to the generic HID driver anyway for now.
+ */
+static __u8 pf1209_rdesc_fixed[] = {
+       0x05, 0x0D,         /*  Usage Page (Digitizer),             */
+       0x09, 0x02,         /*  Usage (Pen),                        */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x09,         /*      Report ID (9),                  */
+       0x09, 0x20,         /*      Usage (Stylus),                 */
+       0xA0,               /*      Collection (Physical),          */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x09, 0x42,         /*          Usage (Tip Switch),         */
+       0x09, 0x44,         /*          Usage (Barrel Switch),      */
+       0x09, 0x46,         /*          Usage (Tablet Pick),        */
+       0x14,               /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x95, 0x03,         /*          Report Count (3),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x05,         /*          Report Count (5),           */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0x75, 0x10,         /*          Report Size (16),           */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x14,               /*          Logical Minimum (0),        */
+       0xA4,               /*          Push,                       */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x55, 0xFD,         /*          Unit Exponent (-3),         */
+       0x65, 0x13,         /*          Unit (Inch),                */
+       0x34,               /*          Physical Minimum (0),       */
+       0x09, 0x30,         /*          Usage (X),                  */
+       0x46, 0xE0, 0x2E,   /*          Physical Maximum (12000),   */
+       0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x09, 0x31,         /*          Usage (Y),                  */
+       0x46, 0x28, 0x23,   /*          Physical Maximum (9000),    */
+       0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xB4,               /*          Pop,                        */
+       0x09, 0x30,         /*          Usage (Tip Pressure),       */
+       0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xC0,               /*      End Collection,                 */
+       0xC0,               /*  End Collection,                     */
+       0x05, 0x01,         /*  Usage Page (Desktop),               */
+       0x09, 0x02,         /*  Usage (Mouse),                      */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x08,         /*      Report ID (8),                  */
+       0x09, 0x01,         /*      Usage (Pointer),                */
+       0xA0,               /*      Collection (Physical),          */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x05, 0x09,         /*          Usage Page (Button),        */
+       0x19, 0x01,         /*          Usage Minimum (01h),        */
+       0x29, 0x03,         /*          Usage Maximum (03h),        */
+       0x14,               /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x95, 0x03,         /*          Report Count (3),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x05,         /*          Report Count (5),           */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x09, 0x30,         /*          Usage (X),                  */
+       0x09, 0x31,         /*          Usage (Y),                  */
+       0x15, 0x81,         /*          Logical Minimum (-127),     */
+       0x25, 0x7F,         /*          Logical Maximum (127),      */
+       0x95, 0x02,         /*          Report Count (2),           */
+       0x81, 0x06,         /*          Input (Variable, Relative), */
+       0x09, 0x38,         /*          Usage (Wheel),              */
+       0x15, 0xFF,         /*          Logical Minimum (-1),       */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x81, 0x06,         /*          Input (Variable, Relative), */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0xC0,               /*      End Collection,                 */
+       0xC0                /*  End Collection                      */
+};
+
+static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+                                       unsigned int *rsize)
+{
+       switch (hdev->product) {
+       case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209:
+               if (*rsize == PF1209_RDESC_ORIG_SIZE) {
+                       rdesc = pf1209_rdesc_fixed;
+                       *rsize = sizeof(pf1209_rdesc_fixed);
+               }
+               break;
+       case USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U:
+               if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) {
+                       rdesc = wp4030u_rdesc_fixed;
+                       *rsize = sizeof(wp4030u_rdesc_fixed);
+               }
+               break;
+       case USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U:
+               if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) {
+                       rdesc = wp5540u_rdesc_fixed;
+                       *rsize = sizeof(wp5540u_rdesc_fixed);
+               }
+               break;
+       case USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U:
+               if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) {
+                       rdesc = wp8060u_rdesc_fixed;
+                       *rsize = sizeof(wp8060u_rdesc_fixed);
+               }
+               break;
+       }
+
+       return rdesc;
+}
+
+static const struct hid_device_id uclogic_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
+                               USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
+                               USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
+                               USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
+                               USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, uclogic_devices);
+
+static struct hid_driver uclogic_driver = {
+       .name = "uclogic",
+       .id_table = uclogic_devices,
+       .report_fixup = uclogic_report_fixup,
+};
+
+static int __init uclogic_init(void)
+{
+       return hid_register_driver(&uclogic_driver);
+}
+
+static void __exit uclogic_exit(void)
+{
+       hid_unregister_driver(&uclogic_driver);
+}
+
+module_init(uclogic_init);
+module_exit(uclogic_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-waltop.c b/drivers/hid/hid-waltop.c
new file mode 100644 (file)
index 0000000..b3a4163
--- /dev/null
@@ -0,0 +1,1099 @@
+/*
+ *  HID driver for Waltop devices not fully compliant with HID standard
+ *
+ *  Copyright (c) 2010 Nikolai Kondrashov
+ */
+
+/*
+ * 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/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/*
+ * There exists an official driver on the manufacturer's website, which
+ * wasn't submitted to the kernel, for some reason. The official driver
+ * doesn't seem to support extra features of some tablets, like wheels.
+ *
+ * It shows that the feature report ID 2 could be used to control any waltop
+ * tablet input mode, switching it between "default", "tablet" and "ink".
+ *
+ * This driver only uses "default" mode for all the supported tablets. This
+ * mode tries to be HID-compatible (not very successfully), but cripples the
+ * resolution of some tablets.
+ *
+ * The "tablet" mode uses some proprietary, yet decipherable protocol, which
+ * represents the correct resolution, but is possibly HID-incompatible (i.e.
+ * indescribable by a report descriptor).
+ *
+ * The purpose of the "ink" mode is unknown.
+ *
+ * The feature reports needed for switching to each mode are these:
+ *
+ * 02 16 00     default
+ * 02 16 01     tablet
+ * 02 16 02     ink
+ */
+
+/*
+ * Original Slim Tablet 5.8 inch report descriptor.
+ *
+ * All the reports except the report with ID 16 (the stylus) are unused,
+ * possibly because the tablet is not configured to, or because they were
+ * just copied from a more capable model. The full purpose of features
+ * described for report ID 2 is unknown.
+ *
+ * The stylus buttons are described as three bit fields, whereas actually
+ * it's an "array", i.e. they're reported as button numbers (1, 2 and 3).
+ * The "eraser" field is not used. There is also a "push" without a "pop" in
+ * the stylus description.
+ *
+ *  Usage Page (Desktop),           ; Generic desktop controls (01h)
+ *  Usage (Mouse),                  ; Mouse (02h, application collection)
+ *  Collection (Application),
+ *    Report ID (1),
+ *    Usage (Pointer),              ; Pointer (01h, physical collection)
+ *    Collection (Physical),
+ *      Usage Page (Button),        ; Button (09h)
+ *      Usage Minimum (01h),
+ *      Usage Maximum (05h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (1),
+ *      Report Size (1),
+ *      Report Count (5),
+ *      Input (Variable),
+ *      Report Size (3),
+ *      Report Count (1),
+ *      Input (Constant, Variable),
+ *      Usage Page (Desktop),       ; Generic desktop controls (01h)
+ *      Usage (X),                  ; X (30h, dynamic value)
+ *      Usage (Y),                  ; Y (31h, dynamic value)
+ *      Usage (Wheel),              ; Wheel (38h, dynamic value)
+ *      Logical Minimum (-127),
+ *      Logical Maximum (127),
+ *      Report Size (8),
+ *      Report Count (3),
+ *      Input (Variable, Relative),
+ *    End Collection,
+ *  End Collection,
+ *  Usage Page (Digitizer),         ; Digitizer (0Dh)
+ *  Usage (Pen),                    ; Pen (02h, application collection)
+ *  Collection (Application),
+ *    Report ID (2),
+ *    Usage (Stylus),               ; Stylus (20h, logical collection)
+ *    Collection (Physical),
+ *      Usage (00h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (7),
+ *      Input (Variable),
+ *      Usage (Azimuth),            ; Azimuth (3Fh, dynamic value)
+ *      Usage (Altitude),           ; Altitude (40h, dynamic value)
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (2),
+ *      Feature (Variable),
+ *    End Collection,
+ *    Report ID (5),
+ *    Usage Page (Digitizer),       ; Digitizer (0Dh)
+ *    Usage (Stylus),               ; Stylus (20h, logical collection)
+ *    Collection (Physical),
+ *      Usage (00h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (7),
+ *      Input (Variable),
+ *    End Collection,
+ *    Report ID (10),
+ *    Usage Page (Digitizer),       ; Digitizer (0Dh)
+ *    Usage (Stylus),               ; Stylus (20h, logical collection)
+ *    Collection (Physical),
+ *      Usage (00h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (3),
+ *      Input (Variable),
+ *    End Collection,
+ *    Report ID (16),
+ *    Usage (Stylus),               ; Stylus (20h, logical collection)
+ *    Collection (Physical),
+ *      Usage (Tip Switch),         ; Tip switch (42h, momentary control)
+ *      Usage (Barrel Switch),      ; Barrel switch (44h, momentary control)
+ *      Usage (Invert),             ; Invert (3Ch, momentary control)
+ *      Usage (Eraser),             ; Eraser (45h, momentary control)
+ *      Usage (In Range),           ; In range (32h, momentary control)
+ *      Logical Minimum (0),
+ *      Logical Maximum (1),
+ *      Report Size (1),
+ *      Report Count (5),
+ *      Input (Variable),
+ *      Report Count (3),
+ *      Input (Constant, Variable),
+ *      Usage Page (Desktop),       ; Generic desktop controls (01h)
+ *      Usage (X),                  ; X (30h, dynamic value)
+ *      Report Size (16),
+ *      Report Count (1),
+ *      Push,
+ *      Unit Exponent (13),
+ *      Unit (Inch^3),
+ *      Logical Minimum (0),
+ *      Logical Maximum (10000),
+ *      Physical Minimum (0),
+ *      Physical Maximum (10000),
+ *      Input (Variable),
+ *      Usage (Y),                  ; Y (31h, dynamic value)
+ *      Logical Maximum (6000),
+ *      Physical Maximum (6000),
+ *      Input (Variable),
+ *      Usage Page (Digitizer),     ; Digitizer (0Dh)
+ *      Usage (Tip Pressure),       ; Tip pressure (30h, dynamic value)
+ *      Logical Minimum (0),
+ *      Logical Maximum (1023),
+ *      Physical Minimum (0),
+ *      Physical Maximum (1023),
+ *      Input (Variable),
+ *    End Collection,
+ *  End Collection
+ */
+
+/* Size of the original report descriptor of Slim Tablet 5.8 inch */
+#define SLIM_TABLET_5_8_INCH_RDESC_ORIG_SIZE   222
+
+/*
+ * Fixed Slim Tablet 5.8 inch descriptor.
+ *
+ * All the reports except the stylus report (ID 16) were removed as unused.
+ * The stylus buttons description was fixed.
+ */
+static __u8 slim_tablet_5_8_inch_rdesc_fixed[] = {
+       0x05, 0x0D,         /*  Usage Page (Digitizer),             */
+       0x09, 0x02,         /*  Usage (Pen),                        */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x10,         /*      Report ID (16),                 */
+       0x09, 0x20,         /*      Usage (Stylus),                 */
+       0xA0,               /*      Collection (Physical),          */
+       0x09, 0x42,         /*          Usage (Tip Switch),         */
+       0x09, 0x44,         /*          Usage (Barrel Switch),      */
+       0x09, 0x46,         /*          Usage (Tablet Pick),        */
+       0x15, 0x01,         /*          Logical Minimum (1),        */
+       0x25, 0x03,         /*          Logical Maximum (3),        */
+       0x75, 0x04,         /*          Report Size (4),            */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x80,               /*          Input,                      */
+       0x09, 0x32,         /*          Usage (In Range),           */
+       0x14,               /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x03,         /*          Report Count (3),           */
+       0x81, 0x03,         /*          Input (Constant, Variable), */
+       0x75, 0x10,         /*          Report Size (16),           */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x14,               /*          Logical Minimum (0),        */
+       0xA4,               /*          Push,                       */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x65, 0x13,         /*          Unit (Inch),                */
+       0x55, 0xFD,         /*          Unit Exponent (-3),         */
+       0x34,               /*          Physical Minimum (0),       */
+       0x09, 0x30,         /*          Usage (X),                  */
+       0x46, 0x88, 0x13,   /*          Physical Maximum (5000),    */
+       0x26, 0x10, 0x27,   /*          Logical Maximum (10000),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x09, 0x31,         /*          Usage (Y),                  */
+       0x46, 0xB8, 0x0B,   /*          Physical Maximum (3000),    */
+       0x26, 0x70, 0x17,   /*          Logical Maximum (6000),     */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xB4,               /*          Pop,                        */
+       0x09, 0x30,         /*          Usage (Tip Pressure),       */
+       0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xC0,               /*      End Collection,                 */
+       0xC0                /*  End Collection                      */
+};
+
+/*
+ * Original Slim Tablet 12.1 inch report descriptor.
+ *
+ * The descriptor is similar to the Slim Tablet 5.8 inch descriptor with the
+ * addition of a keyboard report, seemingly unused. It may have get here
+ * from a Media Tablet - probably an unimplemented feature.
+ *
+ *  Usage Page (Desktop),             ; Generic desktop controls (01h)
+ *  Usage (Mouse),                    ; Mouse (02h, application collection)
+ *  Collection (Application),
+ *    Report ID (1),
+ *    Usage (Pointer),                ; Pointer (01h, physical collection)
+ *    Collection (Physical),
+ *      Usage Page (Button),          ; Button (09h)
+ *      Usage Minimum (01h),
+ *      Usage Maximum (05h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (1),
+ *      Report Size (1),
+ *      Report Count (5),
+ *      Input (Variable),
+ *      Report Size (3),
+ *      Report Count (1),
+ *      Input (Constant, Variable),
+ *      Usage Page (Desktop),         ; Generic desktop controls (01h)
+ *      Usage (X),                    ; X (30h, dynamic value)
+ *      Usage (Y),                    ; Y (31h, dynamic value)
+ *      Usage (Wheel),                ; Wheel (38h, dynamic value)
+ *      Logical Minimum (-127),
+ *      Logical Maximum (127),
+ *      Report Size (8),
+ *      Report Count (3),
+ *      Input (Variable, Relative),
+ *    End Collection,
+ *  End Collection,
+ *  Usage Page (Digitizer),           ; Digitizer (0Dh)
+ *  Usage (Pen),                      ; Pen (02h, application collection)
+ *  Collection (Application),
+ *    Report ID (2),
+ *    Usage (Stylus),                 ; Stylus (20h, logical collection)
+ *    Collection (Physical),
+ *      Usage (00h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (7),
+ *      Input (Variable),
+ *      Usage (Azimuth),              ; Azimuth (3Fh, dynamic value)
+ *      Usage (Altitude),             ; Altitude (40h, dynamic value)
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (2),
+ *      Feature (Variable),
+ *    End Collection,
+ *    Report ID (5),
+ *    Usage Page (Digitizer),         ; Digitizer (0Dh)
+ *    Usage (Stylus),                 ; Stylus (20h, logical collection)
+ *    Collection (Physical),
+ *      Usage (00h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (7),
+ *      Input (Variable),
+ *    End Collection,
+ *    Report ID (10),
+ *    Usage Page (Digitizer),         ; Digitizer (0Dh)
+ *    Usage (Stylus),                 ; Stylus (20h, logical collection)
+ *    Collection (Physical),
+ *      Usage (00h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (3),
+ *      Input (Variable),
+ *    End Collection,
+ *    Report ID (16),
+ *    Usage (Stylus),                 ; Stylus (20h, logical collection)
+ *    Collection (Physical),
+ *      Usage (Tip Switch),           ; Tip switch (42h, momentary control)
+ *      Usage (Barrel Switch),        ; Barrel switch (44h, momentary control)
+ *      Usage (Invert),               ; Invert (3Ch, momentary control)
+ *      Usage (Eraser),               ; Eraser (45h, momentary control)
+ *      Usage (In Range),             ; In range (32h, momentary control)
+ *      Logical Minimum (0),
+ *      Logical Maximum (1),
+ *      Report Size (1),
+ *      Report Count (5),
+ *      Input (Variable),
+ *      Report Count (3),
+ *      Input (Constant, Variable),
+ *      Usage Page (Desktop),         ; Generic desktop controls (01h)
+ *      Usage (X),                    ; X (30h, dynamic value)
+ *      Report Size (16),
+ *      Report Count (1),
+ *      Push,
+ *      Unit Exponent (13),
+ *      Unit (Inch^3),
+ *      Logical Minimum (0),
+ *      Logical Maximum (20000),
+ *      Physical Minimum (0),
+ *      Physical Maximum (20000),
+ *      Input (Variable),
+ *      Usage (Y),                    ; Y (31h, dynamic value)
+ *      Logical Maximum (12500),
+ *      Physical Maximum (12500),
+ *      Input (Variable),
+ *      Usage Page (Digitizer),       ; Digitizer (0Dh)
+ *      Usage (Tip Pressure),         ; Tip pressure (30h, dynamic value)
+ *      Logical Minimum (0),
+ *      Logical Maximum (1023),
+ *      Physical Minimum (0),
+ *      Physical Maximum (1023),
+ *      Input (Variable),
+ *    End Collection,
+ *  End Collection,
+ *  Usage Page (Desktop),             ; Generic desktop controls (01h)
+ *  Usage (Keyboard),                 ; Keyboard (06h, application collection)
+ *  Collection (Application),
+ *    Report ID (13),
+ *    Usage Page (Keyboard),          ; Keyboard/keypad (07h)
+ *    Usage Minimum (KB Leftcontrol), ; Keyboard left control
+ *                                    ; (E0h, dynamic value)
+ *    Usage Maximum (KB Right GUI),   ; Keyboard right GUI (E7h, dynamic value)
+ *    Logical Minimum (0),
+ *    Logical Maximum (1),
+ *    Report Size (1),
+ *    Report Count (8),
+ *    Input (Variable),
+ *    Report Size (8),
+ *    Report Count (1),
+ *    Input (Constant),
+ *    Usage Page (Keyboard),          ; Keyboard/keypad (07h)
+ *    Usage Minimum (None),           ; No event (00h, selector)
+ *    Usage Maximum (KB Application), ; Keyboard Application (65h, selector)
+ *    Logical Minimum (0),
+ *    Logical Maximum (101),
+ *    Report Size (8),
+ *    Report Count (5),
+ *    Input,
+ *  End Collection
+ */
+
+/* Size of the original report descriptor of Slim Tablet 12.1 inch */
+#define SLIM_TABLET_12_1_INCH_RDESC_ORIG_SIZE  269
+
+/*
+ * Fixed Slim Tablet 12.1 inch descriptor.
+ *
+ * All the reports except the stylus report (ID 16) were removed as unused.
+ * The stylus buttons description was fixed.
+ */
+static __u8 slim_tablet_12_1_inch_rdesc_fixed[] = {
+       0x05, 0x0D,         /*  Usage Page (Digitizer),             */
+       0x09, 0x02,         /*  Usage (Pen),                        */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x10,         /*      Report ID (16),                 */
+       0x09, 0x20,         /*      Usage (Stylus),                 */
+       0xA0,               /*      Collection (Physical),          */
+       0x09, 0x42,         /*          Usage (Tip Switch),         */
+       0x09, 0x44,         /*          Usage (Barrel Switch),      */
+       0x09, 0x46,         /*          Usage (Tablet Pick),        */
+       0x15, 0x01,         /*          Logical Minimum (1),        */
+       0x25, 0x03,         /*          Logical Maximum (3),        */
+       0x75, 0x04,         /*          Report Size (4),            */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x80,               /*          Input,                      */
+       0x09, 0x32,         /*          Usage (In Range),           */
+       0x14,               /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x03,         /*          Report Count (3),           */
+       0x81, 0x03,         /*          Input (Constant, Variable), */
+       0x75, 0x10,         /*          Report Size (16),           */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x14,               /*          Logical Minimum (0),        */
+       0xA4,               /*          Push,                       */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x65, 0x13,         /*          Unit (Inch),                */
+       0x55, 0xFD,         /*          Unit Exponent (-3),         */
+       0x34,               /*          Physical Minimum (0),       */
+       0x09, 0x30,         /*          Usage (X),                  */
+       0x46, 0x10, 0x27,   /*          Physical Maximum (10000),   */
+       0x26, 0x20, 0x4E,   /*          Logical Maximum (20000),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x09, 0x31,         /*          Usage (Y),                  */
+       0x46, 0x6A, 0x18,   /*          Physical Maximum (6250),    */
+       0x26, 0xD4, 0x30,   /*          Logical Maximum (12500),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xB4,               /*          Pop,                        */
+       0x09, 0x30,         /*          Usage (Tip Pressure),       */
+       0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xC0,               /*      End Collection,                 */
+       0xC0                /*  End Collection                      */
+};
+
+/*
+ * Original Media Tablet 10.6 inch report descriptor.
+ *
+ * There are at least two versions of this model in the wild. They are
+ * represented by Genius G-Pen M609 (older version) and Genius G-Pen M609X
+ * (newer version).
+ *
+ * Both versions have the usual pen with two barrel buttons and two
+ * identical wheels with center buttons in the top corners of the tablet
+ * base. They also have buttons on the top, between the wheels, for
+ * selecting the wheels' functions and wide/standard mode. In the wide mode
+ * the whole working surface is sensed, in the standard mode a narrower area
+ * is sensed, but the logical report extents remain the same. These modes
+ * correspond roughly to 16:9 and 4:3 aspect ratios respectively.
+ *
+ * The older version has three wheel function buttons ("scroll", "zoom" and
+ * "volume") and two separate buttons for wide and standard mode. The newer
+ * version has four wheel function buttons (plus "brush") and only one
+ * button is used for selecting wide/standard mode. So, the total number of
+ * buttons remains the same, but one of the mode buttons is repurposed as a
+ * wheels' function button in the newer version.
+ *
+ * The wheel functions are:
+ * scroll   - the wheels act as scroll wheels, the center buttons switch
+ *            between vertical and horizontal scrolling;
+ * zoom     - the wheels zoom in/out, the buttons supposedly reset to 100%;
+ * volume   - the wheels control the sound volume, the buttons mute;
+ * brush    - the wheels are supposed to control brush width in a graphics
+ *            editor, the buttons do nothing.
+ *
+ * Below is the newer version's report descriptor. It may very well be that
+ * the older version's descriptor is different and thus it won't be
+ * supported.
+ *
+ * The mouse report (ID 1) only uses the wheel field for reporting the tablet
+ * wheels' scroll mode. The keyboard report (ID 13) is used to report the
+ * wheels' zoom and brush control functions as key presses. The report ID 12
+ * is used to report the wheels' volume control functions. The stylus report
+ * (ID 16) has the same problems as the Slim Tablet 5.8 inch report has.
+ *
+ * The rest of the reports are unused, at least in the default configuration.
+ * The purpose of the features is unknown.
+ *
+ *  Usage Page (Desktop),
+ *  Usage (Mouse),
+ *  Collection (Application),
+ *    Report ID (1),
+ *    Usage (Pointer),
+ *    Collection (Physical),
+ *      Usage Page (Button),
+ *      Usage Minimum (01h),
+ *      Usage Maximum (05h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (1),
+ *      Report Size (1),
+ *      Report Count (5),
+ *      Input (Variable),
+ *      Report Size (3),
+ *      Report Count (1),
+ *      Input (Constant, Variable),
+ *      Usage Page (Desktop),
+ *      Usage (X),
+ *      Usage (Y),
+ *      Usage (Wheel),
+ *      Logical Minimum (-127),
+ *      Logical Maximum (127),
+ *      Report Size (8),
+ *      Report Count (3),
+ *      Input (Variable, Relative),
+ *    End Collection,
+ *  End Collection,
+ *  Usage Page (Digitizer),
+ *  Usage (Pen),
+ *  Collection (Application),
+ *    Report ID (2),
+ *    Usage (Stylus),
+ *    Collection (Physical),
+ *      Usage (00h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (7),
+ *      Input (Variable),
+ *      Usage (Azimuth),
+ *      Usage (Altitude),
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (2),
+ *      Feature (Variable),
+ *    End Collection,
+ *    Report ID (5),
+ *    Usage Page (Digitizer),
+ *    Usage (Stylus),
+ *    Collection (Physical),
+ *      Usage (00h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (7),
+ *      Input (Variable),
+ *    End Collection,
+ *    Report ID (10),
+ *    Usage Page (Digitizer),
+ *    Usage (Stylus),
+ *    Collection (Physical),
+ *      Usage (00h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (7),
+ *      Input (Variable),
+ *    End Collection,
+ *    Report ID (16),
+ *    Usage (Stylus),
+ *    Collection (Physical),
+ *      Usage (Tip Switch),
+ *      Usage (Barrel Switch),
+ *      Usage (Invert),
+ *      Usage (Eraser),
+ *      Usage (In Range),
+ *      Logical Minimum (0),
+ *      Logical Maximum (1),
+ *      Report Size (1),
+ *      Report Count (5),
+ *      Input (Variable),
+ *      Report Count (3),
+ *      Input (Constant, Variable),
+ *      Usage Page (Desktop),
+ *      Usage (X),
+ *      Report Size (16),
+ *      Report Count (1),
+ *      Push,
+ *      Unit Exponent (13),
+ *      Unit (Inch^3),
+ *      Logical Minimum (0),
+ *      Logical Maximum (18000),
+ *      Physical Minimum (0),
+ *      Physical Maximum (18000),
+ *      Input (Variable),
+ *      Usage (Y),
+ *      Logical Maximum (11000),
+ *      Physical Maximum (11000),
+ *      Input (Variable),
+ *      Usage Page (Digitizer),
+ *      Usage (Tip Pressure),
+ *      Logical Minimum (0),
+ *      Logical Maximum (1023),
+ *      Physical Minimum (0),
+ *      Physical Maximum (1023),
+ *      Input (Variable),
+ *    End Collection,
+ *  End Collection,
+ *  Usage Page (Desktop),
+ *  Usage (Keyboard),
+ *  Collection (Application),
+ *    Report ID (13),
+ *    Usage Page (Keyboard),
+ *    Usage Minimum (KB Leftcontrol),
+ *    Usage Maximum (KB Right GUI),
+ *    Logical Minimum (0),
+ *    Logical Maximum (1),
+ *    Report Size (1),
+ *    Report Count (8),
+ *    Input (Variable),
+ *    Report Size (8),
+ *    Report Count (1),
+ *    Input (Constant),
+ *    Usage Page (Keyboard),
+ *    Usage Minimum (None),
+ *    Usage Maximum (KB Application),
+ *    Logical Minimum (0),
+ *    Logical Maximum (101),
+ *    Report Size (8),
+ *    Report Count (5),
+ *    Input,
+ *  End Collection,
+ *  Usage Page (Consumer),
+ *  Usage (Consumer Control),
+ *  Collection (Application),
+ *    Report ID (12),
+ *    Usage (Volume Inc),
+ *    Usage (Volume Dec),
+ *    Usage (Mute),
+ *    Logical Minimum (0),
+ *    Logical Maximum (1),
+ *    Report Size (1),
+ *    Report Count (3),
+ *    Input (Variable, Relative),
+ *    Report Size (5),
+ *    Report Count (1),
+ *    Input (Constant, Variable, Relative),
+ *  End Collection
+ */
+
+/* Size of the original report descriptor of Media Tablet 10.6 inch */
+#define MEDIA_TABLET_10_6_INCH_RDESC_ORIG_SIZE 300
+
+/*
+ * Fixed Media Tablet 10.6 inch descriptor.
+ *
+ * The descriptions of reports unused in the default configuration are
+ * removed. The stylus report (ID 16) is fixed similarly to Slim Tablet 5.8
+ * inch.  The unused mouse report (ID 1) fields are replaced with constant
+ * padding.
+ *
+ * The keyboard report (ID 13) is hacked to instead have an "array" field
+ * reporting consumer page controls, and all the unused bits are masked out
+ * with constant padding. The "brush" wheels' function is represented as "Scan
+ * Previous/Next Track" controls due to the lack of brush controls in the
+ * usage tables specification.
+ */
+static __u8 media_tablet_10_6_inch_rdesc_fixed[] = {
+       0x05, 0x0D,         /*  Usage Page (Digitizer),             */
+       0x09, 0x02,         /*  Usage (Pen),                        */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x10,         /*      Report ID (16),                 */
+       0x09, 0x20,         /*      Usage (Stylus),                 */
+       0xA0,               /*      Collection (Physical),          */
+       0x09, 0x42,         /*          Usage (Tip Switch),         */
+       0x09, 0x44,         /*          Usage (Barrel Switch),      */
+       0x09, 0x46,         /*          Usage (Tablet Pick),        */
+       0x15, 0x01,         /*          Logical Minimum (1),        */
+       0x25, 0x03,         /*          Logical Maximum (3),        */
+       0x75, 0x04,         /*          Report Size (4),            */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x80,               /*          Input,                      */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x09, 0x32,         /*          Usage (In Range),           */
+       0x14,               /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x03,         /*          Report Count (3),           */
+       0x81, 0x03,         /*          Input (Constant, Variable), */
+       0x75, 0x10,         /*          Report Size (16),           */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x14,               /*          Logical Minimum (0),        */
+       0xA4,               /*          Push,                       */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x65, 0x13,         /*          Unit (Inch),                */
+       0x55, 0xFD,         /*          Unit Exponent (-3),         */
+       0x34,               /*          Physical Minimum (0),       */
+       0x09, 0x30,         /*          Usage (X),                  */
+       0x46, 0x28, 0x23,   /*          Physical Maximum (9000),    */
+       0x26, 0x50, 0x46,   /*          Logical Maximum (18000),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x09, 0x31,         /*          Usage (Y),                  */
+       0x46, 0x7C, 0x15,   /*          Physical Maximum (5500),    */
+       0x26, 0xF8, 0x2A,   /*          Logical Maximum (11000),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xB4,               /*          Pop,                        */
+       0x09, 0x30,         /*          Usage (Tip Pressure),       */
+       0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xC0,               /*      End Collection,                 */
+       0xC0,               /*  End Collection,                     */
+       0x05, 0x01,         /*  Usage Page (Desktop),               */
+       0x09, 0x02,         /*  Usage (Mouse),                      */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x01,         /*      Report ID (1),                  */
+       0x09, 0x01,         /*      Usage (Pointer),                */
+       0xA0,               /*      Collection (Physical),          */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x03,         /*          Report Count (3),           */
+       0x81, 0x03,         /*          Input (Constant, Variable), */
+       0x95, 0x02,         /*          Report Count (2),           */
+       0x15, 0xFF,         /*          Logical Minimum (-1),       */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x09, 0x38,         /*          Usage (Wheel),              */
+       0x0B, 0x38, 0x02,   /*          Usage (Consumer AC Pan),    */
+               0x0C, 0x00,
+       0x81, 0x06,         /*          Input (Variable, Relative), */
+       0x95, 0x02,         /*          Report Count (2),           */
+       0x81, 0x03,         /*          Input (Constant, Variable), */
+       0xC0,               /*      End Collection,                 */
+       0xC0,               /*  End Collection,                     */
+       0x05, 0x0C,         /*  Usage Page (Consumer),              */
+       0x09, 0x01,         /*  Usage (Consumer Control),           */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x0D,         /*      Report ID (13),                 */
+       0x95, 0x01,         /*      Report Count (1),               */
+       0x75, 0x10,         /*      Report Size (16),               */
+       0x81, 0x03,         /*      Input (Constant, Variable),     */
+       0x0A, 0x2F, 0x02,   /*      Usage (AC Zoom),                */
+       0x0A, 0x2E, 0x02,   /*      Usage (AC Zoom Out),            */
+       0x0A, 0x2D, 0x02,   /*      Usage (AC Zoom In),             */
+       0x09, 0xB6,         /*      Usage (Scan Previous Track),    */
+       0x09, 0xB5,         /*      Usage (Scan Next Track),        */
+       0x08,               /*      Usage (00h),                    */
+       0x08,               /*      Usage (00h),                    */
+       0x08,               /*      Usage (00h),                    */
+       0x08,               /*      Usage (00h),                    */
+       0x08,               /*      Usage (00h),                    */
+       0x0A, 0x2E, 0x02,   /*      Usage (AC Zoom Out),            */
+       0x0A, 0x2D, 0x02,   /*      Usage (AC Zoom In),             */
+       0x15, 0x0C,         /*      Logical Minimum (12),           */
+       0x25, 0x17,         /*      Logical Maximum (23),           */
+       0x75, 0x05,         /*      Report Size (5),                */
+       0x80,               /*      Input,                          */
+       0x75, 0x03,         /*      Report Size (3),                */
+       0x81, 0x03,         /*      Input (Constant, Variable),     */
+       0x75, 0x20,         /*      Report Size (32),               */
+       0x81, 0x03,         /*      Input (Constant, Variable),     */
+       0xC0,               /*  End Collection,                     */
+       0x09, 0x01,         /*  Usage (Consumer Control),           */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x0C,         /*      Report ID (12),                 */
+       0x75, 0x01,         /*      Report Size (1),                */
+       0x09, 0xE9,         /*      Usage (Volume Inc),             */
+       0x09, 0xEA,         /*      Usage (Volume Dec),             */
+       0x09, 0xE2,         /*      Usage (Mute),                   */
+       0x14,               /*      Logical Minimum (0),            */
+       0x25, 0x01,         /*      Logical Maximum (1),            */
+       0x95, 0x03,         /*      Report Count (3),               */
+       0x81, 0x06,         /*      Input (Variable, Relative),     */
+       0x95, 0x35,         /*      Report Count (53),              */
+       0x81, 0x03,         /*      Input (Constant, Variable),     */
+       0xC0                /*  End Collection                      */
+};
+
+/*
+ * Original Media Tablet 14.1 inch report descriptor.
+ *
+ * There are at least two versions of this model in the wild. They are
+ * represented by Genius G-Pen M712 (older version) and Genius G-Pen M712X
+ * (newer version). The hardware difference between these versions is the same
+ * as between older and newer versions of Media Tablet 10.6 inch. The report
+ * descriptors are identical for both versions.
+ *
+ * The function, behavior and report descriptor of this tablet is similar to
+ * that of Media Tablet 10.6 inch. However, there is one more field (with
+ * Consumer AC Pan usage) in the mouse description. Then the tablet X and Y
+ * logical extents both get scaled to 0..16383 range (a hardware limit?),
+ * which kind of defeats the advertised 4000 LPI resolution, considering the
+ * physical extents of 12x7.25 inches. Plus, reports 5, 10 and 255 are used
+ * sometimes (while moving the pen) with unknown purpose. Also, the key codes
+ * generated for zoom in/out are different.
+ *
+ *  Usage Page (Desktop),
+ *  Usage (Mouse),
+ *  Collection (Application),
+ *    Report ID (1),
+ *    Usage (Pointer),
+ *    Collection (Physical),
+ *      Usage Page (Button),
+ *      Usage Minimum (01h),
+ *      Usage Maximum (05h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (1),
+ *      Report Size (1),
+ *      Report Count (5),
+ *      Input (Variable),
+ *      Report Size (3),
+ *      Report Count (1),
+ *      Input (Constant, Variable),
+ *      Usage Page (Desktop),
+ *      Usage (X),
+ *      Usage (Y),
+ *      Usage (Wheel),
+ *      Logical Minimum (-127),
+ *      Logical Maximum (127),
+ *      Report Size (8),
+ *      Report Count (3),
+ *      Input (Variable, Relative),
+ *      Usage Page (Consumer),
+ *      Logical Minimum (-127),
+ *      Logical Maximum (127),
+ *      Report Size (8),
+ *      Report Count (1),
+ *      Usage (AC Pan),
+ *      Input (Variable, Relative),
+ *    End Collection,
+ *  End Collection,
+ *  Usage Page (Digitizer),
+ *  Usage (Pen),
+ *  Collection (Application),
+ *    Report ID (2),
+ *    Usage (Stylus),
+ *    Collection (Physical),
+ *      Usage (00h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (7),
+ *      Input (Variable),
+ *      Usage (Azimuth),
+ *      Usage (Altitude),
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (2),
+ *      Feature (Variable),
+ *    End Collection,
+ *    Report ID (5),
+ *    Usage Page (Digitizer),
+ *    Usage (Stylus),
+ *    Collection (Physical),
+ *      Usage (00h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (7),
+ *      Input (Variable),
+ *    End Collection,
+ *    Report ID (10),
+ *    Usage Page (Digitizer),
+ *    Usage (Stylus),
+ *    Collection (Physical),
+ *      Usage (00h),
+ *      Logical Minimum (0),
+ *      Logical Maximum (255),
+ *      Report Size (8),
+ *      Report Count (7),
+ *      Input (Variable),
+ *    End Collection,
+ *    Report ID (16),
+ *    Usage (Stylus),
+ *    Collection (Physical),
+ *      Usage (Tip Switch),
+ *      Usage (Barrel Switch),
+ *      Usage (Invert),
+ *      Usage (Eraser),
+ *      Usage (In Range),
+ *      Logical Minimum (0),
+ *      Logical Maximum (1),
+ *      Report Size (1),
+ *      Report Count (5),
+ *      Input (Variable),
+ *      Report Count (3),
+ *      Input (Constant, Variable),
+ *      Usage Page (Desktop),
+ *      Usage (X),
+ *      Report Size (16),
+ *      Report Count (1),
+ *      Push,
+ *      Unit Exponent (13),
+ *      Unit (Inch^3),
+ *      Logical Minimum (0),
+ *      Logical Maximum (16383),
+ *      Physical Minimum (0),
+ *      Physical Maximum (16383),
+ *      Input (Variable),
+ *      Usage (Y),
+ *      Input (Variable),
+ *      Usage Page (Digitizer),
+ *      Usage (Tip Pressure),
+ *      Logical Minimum (0),
+ *      Logical Maximum (1023),
+ *      Physical Minimum (0),
+ *      Physical Maximum (1023),
+ *      Input (Variable),
+ *    End Collection,
+ *  End Collection,
+ *  Usage Page (Desktop),
+ *  Usage (Keyboard),
+ *  Collection (Application),
+ *    Report ID (13),
+ *    Usage Page (Keyboard),
+ *    Usage Minimum (KB Leftcontrol),
+ *    Usage Maximum (KB Right GUI),
+ *    Logical Minimum (0),
+ *    Logical Maximum (1),
+ *    Report Size (1),
+ *    Report Count (8),
+ *    Input (Variable),
+ *    Report Size (8),
+ *    Report Count (1),
+ *    Input (Constant),
+ *    Usage Page (Keyboard),
+ *    Usage Minimum (None),
+ *    Usage Maximum (KB Application),
+ *    Logical Minimum (0),
+ *    Logical Maximum (101),
+ *    Report Size (8),
+ *    Report Count (5),
+ *    Input,
+ *  End Collection,
+ *  Usage Page (Consumer),
+ *  Usage (Consumer Control),
+ *  Collection (Application),
+ *    Report ID (12),
+ *    Usage (Volume Inc),
+ *    Usage (Volume Dec),
+ *    Usage (Mute),
+ *    Logical Minimum (0),
+ *    Logical Maximum (1),
+ *    Report Size (1),
+ *    Report Count (3),
+ *    Input (Variable, Relative),
+ *    Report Size (5),
+ *    Report Count (1),
+ *    Input (Constant, Variable, Relative),
+ *  End Collection
+ */
+
+/* Size of the original report descriptor of Media Tablet 14.1 inch */
+#define MEDIA_TABLET_14_1_INCH_RDESC_ORIG_SIZE 309
+
+/*
+ * Fixed Media Tablet 14.1 inch descriptor.
+ * It is fixed similarly to the Media Tablet 10.6 inch descriptor.
+ */
+static __u8 media_tablet_14_1_inch_rdesc_fixed[] = {
+       0x05, 0x0D,         /*  Usage Page (Digitizer),             */
+       0x09, 0x02,         /*  Usage (Pen),                        */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x10,         /*      Report ID (16),                 */
+       0x09, 0x20,         /*      Usage (Stylus),                 */
+       0xA0,               /*      Collection (Physical),          */
+       0x09, 0x42,         /*          Usage (Tip Switch),         */
+       0x09, 0x44,         /*          Usage (Barrel Switch),      */
+       0x09, 0x46,         /*          Usage (Tablet Pick),        */
+       0x15, 0x01,         /*          Logical Minimum (1),        */
+       0x25, 0x03,         /*          Logical Maximum (3),        */
+       0x75, 0x04,         /*          Report Size (4),            */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x80,               /*          Input,                      */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x09, 0x32,         /*          Usage (In Range),           */
+       0x14,               /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x03,         /*          Report Count (3),           */
+       0x81, 0x03,         /*          Input (Constant, Variable), */
+       0x75, 0x10,         /*          Report Size (16),           */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x14,               /*          Logical Minimum (0),        */
+       0xA4,               /*          Push,                       */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x65, 0x13,         /*          Unit (Inch),                */
+       0x55, 0xFD,         /*          Unit Exponent (-3),         */
+       0x34,               /*          Physical Minimum (0),       */
+       0x09, 0x30,         /*          Usage (X),                  */
+       0x46, 0xE0, 0x2E,   /*          Physical Maximum (12000),   */
+       0x26, 0xFF, 0x3F,   /*          Logical Maximum (16383),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x09, 0x31,         /*          Usage (Y),                  */
+       0x46, 0x52, 0x1C,   /*          Physical Maximum (7250),    */
+       0x26, 0xFF, 0x3F,   /*          Logical Maximum (16383),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xB4,               /*          Pop,                        */
+       0x09, 0x30,         /*          Usage (Tip Pressure),       */
+       0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xC0,               /*      End Collection,                 */
+       0xC0,               /*  End Collection,                     */
+       0x05, 0x01,         /*  Usage Page (Desktop),               */
+       0x09, 0x02,         /*  Usage (Mouse),                      */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x01,         /*      Report ID (1),                  */
+       0x09, 0x01,         /*      Usage (Pointer),                */
+       0xA0,               /*      Collection (Physical),          */
+       0x75, 0x08,         /*          Report Size (8),            */
+       0x95, 0x03,         /*          Report Count (3),           */
+       0x81, 0x03,         /*          Input (Constant, Variable), */
+       0x95, 0x02,         /*          Report Count (2),           */
+       0x15, 0xFF,         /*          Logical Minimum (-1),       */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x09, 0x38,         /*          Usage (Wheel),              */
+       0x0B, 0x38, 0x02,   /*          Usage (Consumer AC Pan),    */
+               0x0C, 0x00,
+       0x81, 0x06,         /*          Input (Variable, Relative), */
+       0xC0,               /*      End Collection,                 */
+       0xC0,               /*  End Collection,                     */
+       0x05, 0x0C,         /*  Usage Page (Consumer),              */
+       0x09, 0x01,         /*  Usage (Consumer Control),           */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x0D,         /*      Report ID (13),                 */
+       0x95, 0x01,         /*      Report Count (1),               */
+       0x75, 0x10,         /*      Report Size (16),               */
+       0x81, 0x03,         /*      Input (Constant, Variable),     */
+       0x0A, 0x2F, 0x02,   /*      Usage (AC Zoom),                */
+       0x0A, 0x2E, 0x02,   /*      Usage (AC Zoom Out),            */
+       0x0A, 0x2D, 0x02,   /*      Usage (AC Zoom In),             */
+       0x09, 0xB6,         /*      Usage (Scan Previous Track),    */
+       0x09, 0xB5,         /*      Usage (Scan Next Track),        */
+       0x08,               /*      Usage (00h),                    */
+       0x08,               /*      Usage (00h),                    */
+       0x08,               /*      Usage (00h),                    */
+       0x08,               /*      Usage (00h),                    */
+       0x08,               /*      Usage (00h),                    */
+       0x0A, 0x2E, 0x02,   /*      Usage (AC Zoom Out),            */
+       0x0A, 0x2D, 0x02,   /*      Usage (AC Zoom In),             */
+       0x15, 0x0C,         /*      Logical Minimum (12),           */
+       0x25, 0x17,         /*      Logical Maximum (23),           */
+       0x75, 0x05,         /*      Report Size (5),                */
+       0x80,               /*      Input,                          */
+       0x75, 0x03,         /*      Report Size (3),                */
+       0x81, 0x03,         /*      Input (Constant, Variable),     */
+       0x75, 0x20,         /*      Report Size (32),               */
+       0x81, 0x03,         /*      Input (Constant, Variable),     */
+       0xC0,               /*  End Collection,                     */
+       0x09, 0x01,         /*  Usage (Consumer Control),           */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x0C,         /*      Report ID (12),                 */
+       0x75, 0x01,         /*      Report Size (1),                */
+       0x09, 0xE9,         /*      Usage (Volume Inc),             */
+       0x09, 0xEA,         /*      Usage (Volume Dec),             */
+       0x09, 0xE2,         /*      Usage (Mute),                   */
+       0x14,               /*      Logical Minimum (0),            */
+       0x25, 0x01,         /*      Logical Maximum (1),            */
+       0x95, 0x03,         /*      Report Count (3),               */
+       0x81, 0x06,         /*      Input (Variable, Relative),     */
+       0x75, 0x05,         /*      Report Size (5),                */
+       0x81, 0x03,         /*      Input (Constant, Variable),     */
+       0xC0                /*  End Collection                      */
+};
+
+static __u8 *waltop_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
+{
+       switch (hdev->product) {
+       case USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH:
+               if (*rsize == SLIM_TABLET_5_8_INCH_RDESC_ORIG_SIZE) {
+                       rdesc = slim_tablet_5_8_inch_rdesc_fixed;
+                       *rsize = sizeof(slim_tablet_5_8_inch_rdesc_fixed);
+               }
+               break;
+       case USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH:
+               if (*rsize == SLIM_TABLET_12_1_INCH_RDESC_ORIG_SIZE) {
+                       rdesc = slim_tablet_12_1_inch_rdesc_fixed;
+                       *rsize = sizeof(slim_tablet_12_1_inch_rdesc_fixed);
+               }
+               break;
+       case USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH:
+               if (*rsize == MEDIA_TABLET_10_6_INCH_RDESC_ORIG_SIZE) {
+                       rdesc = media_tablet_10_6_inch_rdesc_fixed;
+                       *rsize = sizeof(media_tablet_10_6_inch_rdesc_fixed);
+               }
+               break;
+       case USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH:
+               if (*rsize == MEDIA_TABLET_14_1_INCH_RDESC_ORIG_SIZE) {
+                       rdesc = media_tablet_14_1_inch_rdesc_fixed;
+                       *rsize = sizeof(media_tablet_14_1_inch_rdesc_fixed);
+               }
+               break;
+       }
+       return rdesc;
+}
+
+static const struct hid_device_id waltop_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP,
+                               USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP,
+                               USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP,
+                               USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP,
+                               USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, waltop_devices);
+
+static struct hid_driver waltop_driver = {
+       .name = "waltop",
+       .id_table = waltop_devices,
+       .report_fixup = waltop_report_fixup,
+};
+
+static int __init waltop_init(void)
+{
+       return hid_register_driver(&waltop_driver);
+}
+
+static void __exit waltop_exit(void)
+{
+       hid_unregister_driver(&waltop_driver);
+}
+
+module_init(waltop_init);
+module_exit(waltop_exit);
+MODULE_LICENSE("GPL");
index 9e8d35a203e4adefc2c9d8370bc5b74efeb7d73a..aac1f92731491083db68e0c212dd1292aed67b74 100644 (file)
@@ -27,10 +27,10 @@ struct zc_device {
 * Zydacron remote control has an invalid HID report descriptor,
 * that needs fixing before we can parse it.
 */
-static void zc_report_fixup(struct hid_device *hdev, __u8 *rdesc,
-       unsigned int rsize)
+static __u8 *zc_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+       unsigned int *rsize)
 {
-       if (rsize >= 253 &&
+       if (*rsize >= 253 &&
                rdesc[0x96] == 0xbc && rdesc[0x97] == 0xff &&
                rdesc[0xca] == 0xbc && rdesc[0xcb] == 0xff &&
                rdesc[0xe1] == 0xbc && rdesc[0xe2] == 0xff) {
@@ -40,6 +40,7 @@ static void zc_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                        rdesc[0x96] = rdesc[0xca] = rdesc[0xe1] = 0x0c;
                        rdesc[0x97] = rdesc[0xcb] = rdesc[0xe2] = 0x00;
                }
+       return rdesc;
 }
 
 #define zc_map_key_clear(c) \
index 925992f549f09abdefd1ba6ab8f280529910920f..8a4b32dca9f74fd03621f3deffe0912929ee5cf3 100644 (file)
@@ -218,9 +218,13 @@ static int hidraw_release(struct inode * inode, struct file * file)
        unsigned int minor = iminor(inode);
        struct hidraw *dev;
        struct hidraw_list *list = file->private_data;
+       int ret;
 
-       if (!hidraw_table[minor])
-               return -ENODEV;
+       mutex_lock(&minors_lock);
+       if (!hidraw_table[minor]) {
+               ret = -ENODEV;
+               goto unlock;
+       }
 
        list_del(&list->node);
        dev = hidraw_table[minor];
@@ -233,10 +237,12 @@ static int hidraw_release(struct inode * inode, struct file * file)
                        kfree(list->hidraw);
                }
        }
-
        kfree(list);
+       ret = 0;
+unlock:
+       mutex_unlock(&minors_lock);
 
-       return 0;
+       return ret;
 }
 
 static long hidraw_ioctl(struct file *file, unsigned int cmd,
index 599041a7f670a9f105e00da3272d79642aefb78c..5489eab3a6bd09e9aaf443cc7961f52267aff4a6 100644 (file)
@@ -807,9 +807,10 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co
        struct usb_host_interface *interface = intf->cur_altsetting;
        int ret;
 
-       if (usbhid->urbout) {
+       if (usbhid->urbout && report_type != HID_FEATURE_REPORT) {
                int actual_length;
                int skipped_report_id = 0;
+
                if (buf[0] == 0x0) {
                        /* Don't send the Report ID */
                        buf++;
@@ -1469,9 +1470,6 @@ static int __init hid_init(void)
        retval = usbhid_quirks_init(quirks_param);
        if (retval)
                goto usbhid_quirks_init_fail;
-       retval = hiddev_init();
-       if (retval)
-               goto hiddev_init_fail;
        retval = usb_register(&hid_driver);
        if (retval)
                goto usb_register_fail;
@@ -1479,8 +1477,6 @@ static int __init hid_init(void)
 
        return 0;
 usb_register_fail:
-       hiddev_exit();
-hiddev_init_fail:
        usbhid_quirks_exit();
 usbhid_quirks_init_fail:
        hid_unregister_driver(&hid_usb_driver);
@@ -1493,7 +1489,6 @@ no_queue:
 static void __exit hid_exit(void)
 {
        usb_deregister(&hid_driver);
-       hiddev_exit();
        usbhid_quirks_exit();
        hid_unregister_driver(&hid_usb_driver);
        destroy_workqueue(resumption_waker);
index f0260c699adb45ac9d5b11f119c1491a95e04504..2c185477eeb3f7a61bdcdcd757ef5fcb728443f5 100644 (file)
@@ -34,7 +34,6 @@ static const struct hid_blacklist {
        { 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_DWAV, USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER, HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET },
-       { USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_MOJO, USB_DEVICE_ID_RETRO_ADAPTER, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
@@ -63,6 +62,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_PEDALS, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET },
+       { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET },
@@ -72,6 +72,10 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_KNA5, HID_QUIRK_MULTI_INPUT },
+       { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U, HID_QUIRK_MULTI_INPUT },
+       { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U, HID_QUIRK_MULTI_INPUT },
+       { USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH, HID_QUIRK_MULTI_INPUT },
+       { USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
        { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
 
index dfcb27613ec54005033f5fe4309550bfb2539998..fedd88df9a18477de2699cb07e48feade407312f 100644 (file)
@@ -67,8 +67,6 @@ struct hiddev_list {
        struct mutex thread_lock;
 };
 
-static struct usb_driver hiddev_driver;
-
 /*
  * 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
@@ -926,41 +924,3 @@ void hiddev_disconnect(struct hid_device *hid)
                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 = {
-       .name =         "hiddev",
-       .probe =        hiddev_usbd_probe,
-};
-
-int __init hiddev_init(void)
-{
-       return usb_register(&hiddev_driver);
-}
-
-void hiddev_exit(void)
-{
-       usb_deregister(&hiddev_driver);
-}
index 42a0f1d1136540b5aada9f816a2289f687d01c57..bb0f56f5c01ef81c51808e7cd97fe9543567ae1e 100644 (file)
@@ -316,6 +316,7 @@ struct hid_item {
 #define HID_QUIRK_FULLSPEED_INTERVAL           0x10000000
 #define HID_QUIRK_NO_INIT_REPORTS              0x20000000
 #define HID_QUIRK_NO_IGNORE                    0x40000000
+#define HID_QUIRK_NO_INPUT_SYNC                        0x80000000
 
 /*
  * This is the global environment of the parser. This information is
@@ -626,8 +627,8 @@ struct hid_driver {
        int (*event)(struct hid_device *hdev, struct hid_field *field,
                        struct hid_usage *usage, __s32 value);
 
-       void (*report_fixup)(struct hid_device *hdev, __u8 *buf,
-                       unsigned int size);
+       __u8 *(*report_fixup)(struct hid_device *hdev, __u8 *buf,
+                       unsigned int *size);
 
        int (*input_mapping)(struct hid_device *hdev,
                        struct hid_input *hidinput, struct hid_field *field,
index bb6f58baf319918121880b604a0c1c004eab5e03..a3f481a3063b5d1cb14ec91aca8ff386592ae506 100644 (file)
@@ -226,8 +226,6 @@ void hiddev_disconnect(struct hid_device *);
 void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
                      struct hid_usage *usage, __s32 value);
 void hiddev_report_event(struct hid_device *hid, struct hid_report *report);
-int __init hiddev_init(void);
-void hiddev_exit(void);
 #else
 static inline int hiddev_connect(struct hid_device *hid,
                unsigned int force)
@@ -236,8 +234,6 @@ static inline void hiddev_disconnect(struct hid_device *hid) { }
 static inline void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
                      struct hid_usage *usage, __s32 value) { }
 static inline void hiddev_report_event(struct hid_device *hid, struct hid_report *report) { }
-static inline int hiddev_init(void) { return 0; }
-static inline void hiddev_exit(void) { }
 #endif
 
 #endif