]> git.openfabrics.org - ~shefty/rdma-dev.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 10 Jan 2012 18:48:28 +0000 (10:48 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 10 Jan 2012 18:48:28 +0000 (10:48 -0800)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (68 commits)
  hid-input/battery: add FEATURE quirk
  hid-input/battery: remove battery_val
  hid-input/battery: power-supply type really *is* a battery
  hid-input/battery: make the battery setup common for INPUTs and FEATUREs
  hid-input/battery: deal with both FEATURE and INPUT report batteries
  hid-input/battery: add quirks for battery
  hid-input/battery: remove apparently redundant kmalloc
  hid-input: add support for HID devices reporting Battery Strength
  HID: hid-multitouch: add support 9 new Xiroku devices
  HID: multitouch: add support for 3M 32"
  HID: multitouch: add support of Atmel multitouch panels
  HID: usbhid: defer LED setting to a workqueue
  HID: usbhid: hid-core: submit queued urbs before suspend
  HID: usbhid: remove LED_ON
  HID: emsff: use symbolic name instead of hardcoded PID constant
  HID: Enable HID_QUIRK_MULTI_INPUT for Trio Linker Plus II
  HID: Kconfig: fix syntax
  HID: introduce proper dependency of HID_BATTERY on POWER_SUPPLY
  HID: multitouch: support PixArt optical touch screen
  HID: make parser more verbose about parsing errors by default
  ...

Fix up rename/delete conflict in drivers/hid/hid-hyperv.c (removed in
staging, moved in this branch) and similarly for the rules for same file
in drivers/staging/hv/{Kconfig,Makefile}.

31 files changed:
Documentation/ABI/testing/sysfs-driver-hid-multitouch [new file with mode: 0644]
Documentation/ABI/testing/sysfs-driver-hid-roccat-isku [new file with mode: 0644]
Documentation/ABI/testing/sysfs-driver-hid-wiimote
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-core.c
drivers/hid/hid-debug.c
drivers/hid/hid-emsff.c
drivers/hid/hid-hyperv.c [new file with mode: 0644]
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-lg4ff.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-picolcd.c
drivers/hid/hid-quanta.c [deleted file]
drivers/hid/hid-roccat-common.c
drivers/hid/hid-roccat-isku.c [new file with mode: 0644]
drivers/hid/hid-roccat-isku.h [new file with mode: 0644]
drivers/hid/hid-roccat-kone.c
drivers/hid/hid-wacom.c
drivers/hid/hid-wiimote-core.c [new file with mode: 0644]
drivers/hid/hid-wiimote-debug.c [new file with mode: 0644]
drivers/hid/hid-wiimote-ext.c [new file with mode: 0644]
drivers/hid/hid-wiimote.c [deleted file]
drivers/hid/hid-wiimote.h [new file with mode: 0644]
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/hid-quirks.c
drivers/hid/usbhid/usbhid.h
drivers/hid/usbhid/usbkbd.c
drivers/usb/misc/usbled.c
include/linux/hid.h

diff --git a/Documentation/ABI/testing/sysfs-driver-hid-multitouch b/Documentation/ABI/testing/sysfs-driver-hid-multitouch
new file mode 100644 (file)
index 0000000..f79839d
--- /dev/null
@@ -0,0 +1,9 @@
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/quirks
+Date:          November 2011
+Contact:       Benjamin Tissoires <benjamin.tissoires@gmail.com>
+Description:   The integer value of this attribute corresponds to the
+               quirks actually in place to handle the device's protocol.
+               When read, this attribute returns the current settings (see
+               MT_QUIRKS_* in hid-multitouch.c).
+               When written this attribute change on the fly the quirks, then
+               the protocol to handle the device.
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku b/Documentation/ABI/testing/sysfs-driver-hid-roccat-isku
new file mode 100644 (file)
index 0000000..189dc43
--- /dev/null
@@ -0,0 +1,135 @@
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/actual_profile
+Date:          June 2011
+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 actual
+               profile. This value is persistent, so its equivalent to the
+               profile that's active when the device is powered on next time.
+               When written, this file sets the number of the startup profile
+               and the device activates this profile immediately.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/info
+Date:          June 2011
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When read, this file returns general data like firmware version.
+               The data is 6 bytes long.
+               This file is readonly.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/key_mask
+Date:          June 2011
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one deactivate certain keys like
+               windows and application keys, to prevent accidental presses.
+               Profile number for which this settings occur is included in
+               written data. The data has to be 6 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/keys_capslock
+Date:          June 2011
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the function of the
+               capslock key for a specific profile. Profile number is included
+               in written data. The data has to be 6 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/keys_easyzone
+Date:          June 2011
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the function of the
+               easyzone keys for a specific profile. Profile number is included
+               in written data. The data has to be 65 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/keys_function
+Date:          June 2011
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the function of the
+               function keys for a specific profile. Profile number is included
+               in written data. The data has to be 41 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/keys_macro
+Date:          June 2011
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the function of the macro
+               keys for a specific profile. Profile number is included in
+               written data. The data has to be 35 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/keys_media
+Date:          June 2011
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the function of the media
+               keys for a specific profile. Profile number is included in
+               written data. The data has to be 29 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/keys_thumbster
+Date:          June 2011
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the function of the
+               thumbster keys for a specific profile. Profile number is included
+               in written data. The data has to be 23 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/last_set
+Date:          June 2011
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the time in secs since
+               epoch in which the last configuration took place.
+               The data has to be 20 bytes long.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/light
+Date:          June 2011
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one set the backlight intensity for
+               a specific profile. Profile number is included in written data.
+               The data has to be 10 bytes long.
+               Before reading this file, control has to be written to select
+               which profile to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/macro
+Date:          June 2011
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one store macros with max 500
+               keystrokes for a specific button for a specific profile.
+               Button and profile numbers are included in written data.
+               The data has to be 2083 bytes long.
+               Before reading this file, control has to be written to select
+               which profile and key to read.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/control
+Date:          June 2011
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one select which data from which
+               profile will be read next. The data has to be 3 bytes long.
+               This file is writeonly.
+Users:         http://roccat.sourceforge.net
+
+What:          /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/isku/roccatisku<minor>/talk
+Date:          June 2011
+Contact:       Stefan Achatz <erazor_de@users.sourceforge.net>
+Description:   When written, this file lets one trigger easyshift functionality
+               from the host.
+               The data has to be 16 bytes long.
+               This file is writeonly.
+Users:         http://roccat.sourceforge.net
index 5d5a16ea57c612fb87075658e618e27715149926..3d98009f447a67447956e36e585965403a33610c 100644 (file)
@@ -8,3 +8,15 @@ Contact:       David Herrmann <dh.herrmann@googlemail.com>
 Description:   Make it possible to set/get current led state. Reading from it
                returns 0 if led is off and 1 if it is on. Writing 0 to it
                disables the led, writing 1 enables it.
+
+What:          /sys/bus/hid/drivers/wiimote/<dev>/extension
+Date:          August 2011
+KernelVersion: 3.2
+Contact:       David Herrmann <dh.herrmann@googlemail.com>
+Description:   This file contains the currently connected and initialized
+               extensions. It can be one of: none, motionp, nunchuck, classic,
+               motionp+nunchuck, motionp+classic
+               motionp is the official Nintendo Motion+ extension, nunchuck is
+               the official Nintendo Nunchuck extension and classic is the
+               Nintendo Classic Controller extension. The motionp extension can
+               be combined with the other two.
index 22a4a051f221e885d647d6db2e0a0f046074efae..a421abdd1ab7968e696d91f8f1b10ca1ff6a31a2 100644 (file)
@@ -31,6 +31,11 @@ config HID
 
          If unsure, say Y.
 
+config HID_BATTERY_STRENGTH
+       bool
+       depends on HID && POWER_SUPPLY && HID = POWER_SUPPLY
+       default y
+
 config HIDRAW
        bool "/dev/hidraw raw HID device support"
        depends on HID
@@ -335,6 +340,7 @@ config HID_MULTITOUCH
          Say Y here if you have one of the following devices:
          - 3M PCT touch screens
          - ActionStar dual touch panels
+         - Atmel panels
          - Cando dual touch panels
          - Chunghwa panels
          - CVTouch panels
@@ -349,12 +355,15 @@ config HID_MULTITOUCH
          - Lumio CrystalTouch panels
          - MosArt dual-touch panels
          - PenMount dual touch panels
+         - PixArt optical touch screen
          - Pixcir dual touch panels
+         - Quanta panels
          - eGalax dual-touch panels, including the Joojoo and Wetab tablets
          - Stantum multitouch panels
          - Touch International Panels
          - Unitec Panels
          - XAT optical touch panels
+         - Xiroku optical touch panels
 
          If unsure, say N.
 
@@ -466,12 +475,6 @@ config HID_PRIMAX
        Support for Primax devices that are not fully compliant with the
        HID standard.
 
-config HID_QUANTA
-       tristate "Quanta Optical Touch panels"
-       depends on USB_HID
-       ---help---
-       Support for Quanta Optical Touch dual-touch panels.
-
 config HID_ROCCAT
        tristate "Roccat special event support"
        depends on USB_HID
@@ -492,6 +495,13 @@ config HID_ROCCAT_ARVO
        ---help---
        Support for Roccat Arvo keyboard.
 
+config HID_ROCCAT_ISKU
+       tristate "Roccat Isku keyboard support"
+       depends on USB_HID
+       depends on HID_ROCCAT
+       ---help---
+       Support for Roccat Isku keyboard.
+
 config HID_ROCCAT_KONE
        tristate "Roccat Kone Mouse support"
        depends on USB_HID
@@ -560,6 +570,12 @@ config GREENASIA_FF
        (like MANTA Warrior MM816 and SpeedLink Strike2 SL-6635) or adapter
        and want to enable force feedback support for it.
 
+config HID_HYPERV_MOUSE
+       tristate "Microsoft Hyper-V mouse driver"
+       depends on HYPERV
+       ---help---
+       Select this option to enable the Hyper-V mouse driver.
+
 config HID_SMARTJOYPLUS
        tristate "SmartJoy PLUS PS2/USB adapter support"
        depends on USB_HID
@@ -620,9 +636,19 @@ config HID_WIIMOTE
        depends on BT_HIDP
        depends on LEDS_CLASS
        select POWER_SUPPLY
+       select INPUT_FF_MEMLESS
        ---help---
        Support for the Nintendo Wii Remote bluetooth device.
 
+config HID_WIIMOTE_EXT
+       bool "Nintendo Wii Remote Extension support"
+       depends on HID_WIIMOTE
+       default HID_WIIMOTE
+       ---help---
+       Support for extension controllers of the Nintendo Wii Remote. Say yes
+       here if you want to use the Nintendo Motion+, Nunchuck or Classic
+       extension controllers with your Wii Remote.
+
 config HID_ZEROPLUS
        tristate "Zeroplus based game controller support"
        depends on USB_HID
index 1e0d2a638b28d71bdf0f0340e1e4bab062f17a62..8aefdc963cce319b41838e7655e036a9b8c5a775 100644 (file)
@@ -25,6 +25,14 @@ ifdef CONFIG_LOGIWHEELS_FF
        hid-logitech-y  += hid-lg4ff.o
 endif
 
+hid-wiimote-y          := hid-wiimote-core.o
+ifdef CONFIG_HID_WIIMOTE_EXT
+       hid-wiimote-y   += hid-wiimote-ext.o
+endif
+ifdef CONFIG_DEBUG_FS
+       hid-wiimote-y   += hid-wiimote-debug.o
+endif
+
 obj-$(CONFIG_HID_A4TECH)       += hid-a4tech.o
 obj-$(CONFIG_HID_ACRUX)                += hid-axff.o
 obj-$(CONFIG_HID_APPLE)                += hid-apple.o
@@ -38,6 +46,7 @@ obj-$(CONFIG_HID_ELECOM)      += hid-elecom.o
 obj-$(CONFIG_HID_EZKEY)                += hid-ezkey.o
 obj-$(CONFIG_HID_GYRATION)     += hid-gyration.o
 obj-$(CONFIG_HID_HOLTEK)       += hid-holtekff.o
+obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o
 obj-$(CONFIG_HID_KENSINGTON)   += hid-kensington.o
 obj-$(CONFIG_HID_KEYTOUCH)     += hid-keytouch.o
 obj-$(CONFIG_HID_KYE)          += hid-kye.o
@@ -51,7 +60,6 @@ obj-$(CONFIG_HID_MULTITOUCH)  += hid-multitouch.o
 obj-$(CONFIG_HID_NTRIG)                += hid-ntrig.o
 obj-$(CONFIG_HID_ORTEK)                += hid-ortek.o
 obj-$(CONFIG_HID_PRODIKEYS)    += hid-prodikeys.o
-obj-$(CONFIG_HID_QUANTA)       += hid-quanta.o
 obj-$(CONFIG_HID_PANTHERLORD)  += hid-pl.o
 obj-$(CONFIG_HID_PETALYNX)     += hid-petalynx.o
 obj-$(CONFIG_HID_PICOLCD)      += hid-picolcd.o
@@ -59,6 +67,7 @@ obj-$(CONFIG_HID_PRIMAX)      += hid-primax.o
 obj-$(CONFIG_HID_ROCCAT)       += hid-roccat.o
 obj-$(CONFIG_HID_ROCCAT_COMMON)        += hid-roccat-common.o
 obj-$(CONFIG_HID_ROCCAT_ARVO)  += hid-roccat-arvo.o
+obj-$(CONFIG_HID_ROCCAT_ISKU)  += hid-roccat-isku.o
 obj-$(CONFIG_HID_ROCCAT_KONE)  += hid-roccat-kone.o
 obj-$(CONFIG_HID_ROCCAT_KONEPLUS)      += hid-roccat-koneplus.o
 obj-$(CONFIG_HID_ROCCAT_KOVAPLUS)      += hid-roccat-kovaplus.o
index af353842f75feaceadeedcc547eeb880519f86df..af08ce7207d972f797d6c2f6d2dbee3c038e0898 100644 (file)
@@ -90,7 +90,7 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
        struct hid_field *field;
 
        if (report->maxfield == HID_MAX_FIELDS) {
-               dbg_hid("too many fields in report\n");
+               hid_err(report->device, "too many fields in report\n");
                return NULL;
        }
 
@@ -121,7 +121,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
        usage = parser->local.usage[0];
 
        if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
-               dbg_hid("collection stack overflow\n");
+               hid_err(parser->device, "collection stack overflow\n");
                return -1;
        }
 
@@ -129,7 +129,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
                collection = kmalloc(sizeof(struct hid_collection) *
                                parser->device->collection_size * 2, GFP_KERNEL);
                if (collection == NULL) {
-                       dbg_hid("failed to reallocate collection array\n");
+                       hid_err(parser->device, "failed to reallocate collection array\n");
                        return -1;
                }
                memcpy(collection, parser->device->collection,
@@ -165,7 +165,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
 static int close_collection(struct hid_parser *parser)
 {
        if (!parser->collection_stack_ptr) {
-               dbg_hid("collection stack underflow\n");
+               hid_err(parser->device, "collection stack underflow\n");
                return -1;
        }
        parser->collection_stack_ptr--;
@@ -197,7 +197,7 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
 static int hid_add_usage(struct hid_parser *parser, unsigned usage)
 {
        if (parser->local.usage_index >= HID_MAX_USAGES) {
-               dbg_hid("usage index exceeded\n");
+               hid_err(parser->device, "usage index exceeded\n");
                return -1;
        }
        parser->local.usage[parser->local.usage_index] = usage;
@@ -222,12 +222,13 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
 
        report = hid_register_report(parser->device, report_type, parser->global.report_id);
        if (!report) {
-               dbg_hid("hid_register_report failed\n");
+               hid_err(parser->device, "hid_register_report failed\n");
                return -1;
        }
 
        if (parser->global.logical_maximum < parser->global.logical_minimum) {
-               dbg_hid("logical range invalid %d %d\n", parser->global.logical_minimum, parser->global.logical_maximum);
+               hid_err(parser->device, "logical range invalid %d %d\n",
+                               parser->global.logical_minimum, parser->global.logical_maximum);
                return -1;
        }
 
@@ -307,7 +308,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
        case HID_GLOBAL_ITEM_TAG_PUSH:
 
                if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) {
-                       dbg_hid("global environment stack overflow\n");
+                       hid_err(parser->device, "global environment stack overflow\n");
                        return -1;
                }
 
@@ -318,7 +319,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
        case HID_GLOBAL_ITEM_TAG_POP:
 
                if (!parser->global_stack_ptr) {
-                       dbg_hid("global environment stack underflow\n");
+                       hid_err(parser->device, "global environment stack underflow\n");
                        return -1;
                }
 
@@ -362,8 +363,8 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
 
        case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
                parser->global.report_size = item_udata(item);
-               if (parser->global.report_size > 32) {
-                       dbg_hid("invalid report_size %d\n",
+               if (parser->global.report_size > 96) {
+                       hid_err(parser->device, "invalid report_size %d\n",
                                        parser->global.report_size);
                        return -1;
                }
@@ -372,7 +373,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
        case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
                parser->global.report_count = item_udata(item);
                if (parser->global.report_count > HID_MAX_USAGES) {
-                       dbg_hid("invalid report_count %d\n",
+                       hid_err(parser->device, "invalid report_count %d\n",
                                        parser->global.report_count);
                        return -1;
                }
@@ -381,13 +382,13 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
        case HID_GLOBAL_ITEM_TAG_REPORT_ID:
                parser->global.report_id = item_udata(item);
                if (parser->global.report_id == 0) {
-                       dbg_hid("report_id 0 is invalid\n");
+                       hid_err(parser->device, "report_id 0 is invalid\n");
                        return -1;
                }
                return 0;
 
        default:
-               dbg_hid("unknown global tag 0x%x\n", item->tag);
+               hid_err(parser->device, "unknown global tag 0x%x\n", item->tag);
                return -1;
        }
 }
@@ -414,14 +415,14 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
                         * items and the first delimiter set.
                         */
                        if (parser->local.delimiter_depth != 0) {
-                               dbg_hid("nested delimiters\n");
+                               hid_err(parser->device, "nested delimiters\n");
                                return -1;
                        }
                        parser->local.delimiter_depth++;
                        parser->local.delimiter_branch++;
                } else {
                        if (parser->local.delimiter_depth < 1) {
-                               dbg_hid("bogus close delimiter\n");
+                               hid_err(parser->device, "bogus close delimiter\n");
                                return -1;
                        }
                        parser->local.delimiter_depth--;
@@ -506,7 +507,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
                ret = hid_add_field(parser, HID_FEATURE_REPORT, data);
                break;
        default:
-               dbg_hid("unknown main item tag 0x%x\n", item->tag);
+               hid_err(parser->device, "unknown main item tag 0x%x\n", item->tag);
                ret = 0;
        }
 
@@ -678,12 +679,12 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
        while ((start = fetch_item(start, end, &item)) != NULL) {
 
                if (item.format != HID_ITEM_FORMAT_SHORT) {
-                       dbg_hid("unexpected long global item\n");
+                       hid_err(device, "unexpected long global item\n");
                        goto err;
                }
 
                if (dispatch_type[item.type](parser, &item)) {
-                       dbg_hid("item %u %u %u %u parsing failed\n",
+                       hid_err(device, "item %u %u %u %u parsing failed\n",
                                item.format, (unsigned)item.size,
                                (unsigned)item.type, (unsigned)item.tag);
                        goto err;
@@ -691,11 +692,11 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
 
                if (start == end) {
                        if (parser->collection_stack_ptr) {
-                               dbg_hid("unbalanced collection at end of report description\n");
+                               hid_err(device, "unbalanced collection at end of report description\n");
                                goto err;
                        }
                        if (parser->local.delimiter_depth) {
-                               dbg_hid("unbalanced delimiter at end of report description\n");
+                               hid_err(device, "unbalanced delimiter at end of report description\n");
                                goto err;
                        }
                        vfree(parser);
@@ -703,7 +704,7 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
                }
        }
 
-       dbg_hid("item fetching failed at offset %d\n", (int)(end - start));
+       hid_err(device, "item fetching failed at offset %d\n", (int)(end - start));
 err:
        vfree(parser);
        return ret;
@@ -873,7 +874,7 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field,
                ret = hdrv->event(hid, field, usage, value);
                if (ret != 0) {
                        if (ret < 0)
-                               dbg_hid("%s's event failed with %d\n",
+                               hid_err(hid, "%s's event failed with %d\n",
                                                hdrv->name, ret);
                        return;
                }
@@ -995,12 +996,13 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
        hid_dump_input(field->report->device, field->usage + offset, value);
 
        if (offset >= field->report_count) {
-               dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count);
+               hid_err(field->report->device, "offset (%d) exceeds report_count (%d)\n",
+                               offset, field->report_count);
                return -1;
        }
        if (field->logical_minimum < 0) {
                if (value != snto32(s32ton(value, size), size)) {
-                       dbg_hid("value %d is out of range\n", value);
+                       hid_err(field->report->device, "value %d is out of range\n", value);
                        return -1;
                }
        }
@@ -1157,7 +1159,7 @@ static bool hid_match_one_id(struct hid_device *hdev,
                (id->product == HID_ANY_ID || id->product == hdev->product);
 }
 
-static const struct hid_device_id *hid_match_id(struct hid_device *hdev,
+const struct hid_device_id *hid_match_id(struct hid_device *hdev,
                const struct hid_device_id *id)
 {
        for (; id->bus; id++)
@@ -1404,11 +1406,13 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_TRUETOUCH) },
        { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
        { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2515) },
        { HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) },
@@ -1423,6 +1427,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) },
        { HID_USB_DEVICE(USB_VENDOR_ID_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT, USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) },
        { HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6650) },
        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ILITEK, USB_DEVICE_ID_ILITEK_MULTITOUCH) },
@@ -1498,11 +1503,15 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
        { HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_PCI) },
        { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
        { 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_ARVO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
@@ -1544,11 +1553,21 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_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_XAT, USB_DEVICE_ID_XAT_CSR) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX1) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX1) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR1) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
@@ -1768,6 +1787,7 @@ static const struct hid_device_id hid_ignore_list[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
        { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x0004) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x000a) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC4UM) },
index ee80d733801d30cc4abe2d5909e682835013b551..01dd9a7daf7a7f27d58599ee2d1315210239a7da 100644 (file)
@@ -114,6 +114,14 @@ static const struct hid_usage_entry hid_usage_table[] = {
       {0, 0xbd, "FlareRelease"},
       {0, 0xbe, "LandingGear"},
       {0, 0xbf, "ToeBrake"},
+  {  6, 0, "GenericDeviceControls" },
+      {0, 0x20, "BatteryStrength" },
+      {0, 0x21, "WirelessChannel" },
+      {0, 0x22, "WirelessID" },
+      {0, 0x23, "DiscoverWirelessControl" },
+      {0, 0x24, "SecurityCodeCharacterEntered" },
+      {0, 0x25, "SecurityCodeCharactedErased" },
+      {0, 0x26, "SecurityCodeCleared" },
   {  7, 0, "Keyboard" },
   {  8, 0, "LED" },
       {0, 0x01, "NumLock"},
index 9bdde867a02fbad7a6b163d82babb9376dc1f78f..2630d483d2623a08eaf357fe41bef4bd29b7aaf0 100644 (file)
@@ -140,7 +140,7 @@ err:
 }
 
 static const struct hid_device_id ems_devices[] = {
-       { HID_USB_DEVICE(USB_VENDOR_ID_EMS, 0x118) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) },
        { }
 };
 MODULE_DEVICE_TABLE(hid, ems_devices);
diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c
new file mode 100644 (file)
index 0000000..0c33ae9
--- /dev/null
@@ -0,0 +1,586 @@
+/*
+ *  Copyright (c) 2009, Citrix Systems, Inc.
+ *  Copyright (c) 2010, Microsoft Corporation.
+ *  Copyright (c) 2011, Novell Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms and conditions of the GNU General Public License,
+ *  version 2, as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope 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.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/completion.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/hiddev.h>
+#include <linux/hyperv.h>
+
+
+struct hv_input_dev_info {
+       unsigned int size;
+       unsigned short vendor;
+       unsigned short product;
+       unsigned short version;
+       unsigned short reserved[11];
+};
+
+/* The maximum size of a synthetic input message. */
+#define SYNTHHID_MAX_INPUT_REPORT_SIZE 16
+
+/*
+ * Current version
+ *
+ * History:
+ * Beta, RC < 2008/1/22        1,0
+ * RC > 2008/1/22              2,0
+ */
+#define SYNTHHID_INPUT_VERSION_MAJOR   2
+#define SYNTHHID_INPUT_VERSION_MINOR   0
+#define SYNTHHID_INPUT_VERSION         (SYNTHHID_INPUT_VERSION_MINOR | \
+                                        (SYNTHHID_INPUT_VERSION_MAJOR << 16))
+
+
+#pragma pack(push, 1)
+/*
+ * Message types in the synthetic input protocol
+ */
+enum synthhid_msg_type {
+       SYNTH_HID_PROTOCOL_REQUEST,
+       SYNTH_HID_PROTOCOL_RESPONSE,
+       SYNTH_HID_INITIAL_DEVICE_INFO,
+       SYNTH_HID_INITIAL_DEVICE_INFO_ACK,
+       SYNTH_HID_INPUT_REPORT,
+       SYNTH_HID_MAX
+};
+
+/*
+ * Basic message structures.
+ */
+struct synthhid_msg_hdr {
+       enum synthhid_msg_type type;
+       u32 size;
+};
+
+struct synthhid_msg {
+       struct synthhid_msg_hdr header;
+       char data[1]; /* Enclosed message */
+};
+
+union synthhid_version {
+       struct {
+               u16 minor_version;
+               u16 major_version;
+       };
+       u32 version;
+};
+
+/*
+ * Protocol messages
+ */
+struct synthhid_protocol_request {
+       struct synthhid_msg_hdr header;
+       union synthhid_version version_requested;
+};
+
+struct synthhid_protocol_response {
+       struct synthhid_msg_hdr header;
+       union synthhid_version version_requested;
+       unsigned char approved;
+};
+
+struct synthhid_device_info {
+       struct synthhid_msg_hdr header;
+       struct hv_input_dev_info hid_dev_info;
+       struct hid_descriptor hid_descriptor;
+};
+
+struct synthhid_device_info_ack {
+       struct synthhid_msg_hdr header;
+       unsigned char reserved;
+};
+
+struct synthhid_input_report {
+       struct synthhid_msg_hdr header;
+       char buffer[1];
+};
+
+#pragma pack(pop)
+
+#define INPUTVSC_SEND_RING_BUFFER_SIZE         (10*PAGE_SIZE)
+#define INPUTVSC_RECV_RING_BUFFER_SIZE         (10*PAGE_SIZE)
+
+
+enum pipe_prot_msg_type {
+       PIPE_MESSAGE_INVALID,
+       PIPE_MESSAGE_DATA,
+       PIPE_MESSAGE_MAXIMUM
+};
+
+
+struct pipe_prt_msg {
+       enum pipe_prot_msg_type type;
+       u32 size;
+       char data[1];
+};
+
+struct  mousevsc_prt_msg {
+       enum pipe_prot_msg_type type;
+       u32 size;
+       union {
+               struct synthhid_protocol_request request;
+               struct synthhid_protocol_response response;
+               struct synthhid_device_info_ack ack;
+       };
+};
+
+/*
+ * Represents an mousevsc device
+ */
+struct mousevsc_dev {
+       struct hv_device        *device;
+       bool                    init_complete;
+       bool                    connected;
+       struct mousevsc_prt_msg protocol_req;
+       struct mousevsc_prt_msg protocol_resp;
+       /* Synchronize the request/response if needed */
+       struct completion       wait_event;
+       int                     dev_info_status;
+
+       struct hid_descriptor   *hid_desc;
+       unsigned char           *report_desc;
+       u32                     report_desc_size;
+       struct hv_input_dev_info hid_dev_info;
+       struct hid_device       *hid_device;
+};
+
+
+static struct mousevsc_dev *mousevsc_alloc_device(struct hv_device *device)
+{
+       struct mousevsc_dev *input_dev;
+
+       input_dev = kzalloc(sizeof(struct mousevsc_dev), GFP_KERNEL);
+
+       if (!input_dev)
+               return NULL;
+
+       input_dev->device = device;
+       hv_set_drvdata(device, input_dev);
+       init_completion(&input_dev->wait_event);
+       input_dev->init_complete = false;
+
+       return input_dev;
+}
+
+static void mousevsc_free_device(struct mousevsc_dev *device)
+{
+       kfree(device->hid_desc);
+       kfree(device->report_desc);
+       hv_set_drvdata(device->device, NULL);
+       kfree(device);
+}
+
+static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device,
+                               struct synthhid_device_info *device_info)
+{
+       int ret = 0;
+       struct hid_descriptor *desc;
+       struct mousevsc_prt_msg ack;
+
+       input_device->dev_info_status = -ENOMEM;
+
+       input_device->hid_dev_info = device_info->hid_dev_info;
+       desc = &device_info->hid_descriptor;
+       if (desc->bLength == 0)
+               goto cleanup;
+
+       input_device->hid_desc = kzalloc(desc->bLength, GFP_ATOMIC);
+
+       if (!input_device->hid_desc)
+               goto cleanup;
+
+       memcpy(input_device->hid_desc, desc, desc->bLength);
+
+       input_device->report_desc_size = desc->desc[0].wDescriptorLength;
+       if (input_device->report_desc_size == 0) {
+               input_device->dev_info_status = -EINVAL;
+               goto cleanup;
+       }
+
+       input_device->report_desc = kzalloc(input_device->report_desc_size,
+                                         GFP_ATOMIC);
+
+       if (!input_device->report_desc) {
+               input_device->dev_info_status = -ENOMEM;
+               goto cleanup;
+       }
+
+       memcpy(input_device->report_desc,
+              ((unsigned char *)desc) + desc->bLength,
+              desc->desc[0].wDescriptorLength);
+
+       /* Send the ack */
+       memset(&ack, 0, sizeof(struct mousevsc_prt_msg));
+
+       ack.type = PIPE_MESSAGE_DATA;
+       ack.size = sizeof(struct synthhid_device_info_ack);
+
+       ack.ack.header.type = SYNTH_HID_INITIAL_DEVICE_INFO_ACK;
+       ack.ack.header.size = 1;
+       ack.ack.reserved = 0;
+
+       ret = vmbus_sendpacket(input_device->device->channel,
+                       &ack,
+                       sizeof(struct pipe_prt_msg) - sizeof(unsigned char) +
+                       sizeof(struct synthhid_device_info_ack),
+                       (unsigned long)&ack,
+                       VM_PKT_DATA_INBAND,
+                       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+
+       if (!ret)
+               input_device->dev_info_status = 0;
+
+cleanup:
+       complete(&input_device->wait_event);
+
+       return;
+}
+
+static void mousevsc_on_receive(struct hv_device *device,
+                               struct vmpacket_descriptor *packet)
+{
+       struct pipe_prt_msg *pipe_msg;
+       struct synthhid_msg *hid_msg;
+       struct mousevsc_dev *input_dev = hv_get_drvdata(device);
+       struct synthhid_input_report *input_report;
+
+       pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet +
+                                               (packet->offset8 << 3));
+
+       if (pipe_msg->type != PIPE_MESSAGE_DATA)
+               return;
+
+       hid_msg = (struct synthhid_msg *)pipe_msg->data;
+
+       switch (hid_msg->header.type) {
+       case SYNTH_HID_PROTOCOL_RESPONSE:
+               /*
+                * While it will be impossible for us to protect against
+                * malicious/buggy hypervisor/host, add a check here to
+                * ensure we don't corrupt memory.
+                */
+               if ((pipe_msg->size + sizeof(struct pipe_prt_msg)
+                       - sizeof(unsigned char))
+                       > sizeof(struct mousevsc_prt_msg)) {
+                       WARN_ON(1);
+                       break;
+               }
+
+               memcpy(&input_dev->protocol_resp, pipe_msg,
+                      pipe_msg->size + sizeof(struct pipe_prt_msg) -
+                      sizeof(unsigned char));
+               complete(&input_dev->wait_event);
+               break;
+
+       case SYNTH_HID_INITIAL_DEVICE_INFO:
+               WARN_ON(pipe_msg->size < sizeof(struct hv_input_dev_info));
+
+               /*
+                * Parse out the device info into device attr,
+                * hid desc and report desc
+                */
+               mousevsc_on_receive_device_info(input_dev,
+                       (struct synthhid_device_info *)pipe_msg->data);
+               break;
+       case SYNTH_HID_INPUT_REPORT:
+               input_report =
+                       (struct synthhid_input_report *)pipe_msg->data;
+               if (!input_dev->init_complete)
+                       break;
+               hid_input_report(input_dev->hid_device,
+                               HID_INPUT_REPORT, input_report->buffer,
+                               input_report->header.size, 1);
+               break;
+       default:
+               pr_err("unsupported hid msg type - type %d len %d",
+                      hid_msg->header.type, hid_msg->header.size);
+               break;
+       }
+
+}
+
+static void mousevsc_on_channel_callback(void *context)
+{
+       const int packet_size = 0x100;
+       int ret;
+       struct hv_device *device = context;
+       u32 bytes_recvd;
+       u64 req_id;
+       struct vmpacket_descriptor *desc;
+       unsigned char   *buffer;
+       int     bufferlen = packet_size;
+
+       buffer = kmalloc(bufferlen, GFP_ATOMIC);
+       if (!buffer)
+               return;
+
+       do {
+               ret = vmbus_recvpacket_raw(device->channel, buffer,
+                                       bufferlen, &bytes_recvd, &req_id);
+
+               switch (ret) {
+               case 0:
+                       if (bytes_recvd <= 0) {
+                               kfree(buffer);
+                               return;
+                       }
+                       desc = (struct vmpacket_descriptor *)buffer;
+
+                       switch (desc->type) {
+                       case VM_PKT_COMP:
+                               break;
+
+                       case VM_PKT_DATA_INBAND:
+                               mousevsc_on_receive(device, desc);
+                               break;
+
+                       default:
+                               pr_err("unhandled packet type %d, tid %llx len %d\n",
+                                       desc->type, req_id, bytes_recvd);
+                               break;
+                       }
+
+                       break;
+
+               case -ENOBUFS:
+                       kfree(buffer);
+                       /* Handle large packet */
+                       bufferlen = bytes_recvd;
+                       buffer = kmalloc(bytes_recvd, GFP_ATOMIC);
+
+                       if (!buffer)
+                               return;
+
+                       break;
+               }
+       } while (1);
+
+}
+
+static int mousevsc_connect_to_vsp(struct hv_device *device)
+{
+       int ret = 0;
+       int t;
+       struct mousevsc_dev *input_dev = hv_get_drvdata(device);
+       struct mousevsc_prt_msg *request;
+       struct mousevsc_prt_msg *response;
+
+       request = &input_dev->protocol_req;
+       memset(request, 0, sizeof(struct mousevsc_prt_msg));
+
+       request->type = PIPE_MESSAGE_DATA;
+       request->size = sizeof(struct synthhid_protocol_request);
+       request->request.header.type = SYNTH_HID_PROTOCOL_REQUEST;
+       request->request.header.size = sizeof(unsigned int);
+       request->request.version_requested.version = SYNTHHID_INPUT_VERSION;
+
+       ret = vmbus_sendpacket(device->channel, request,
+                               sizeof(struct pipe_prt_msg) -
+                               sizeof(unsigned char) +
+                               sizeof(struct synthhid_protocol_request),
+                               (unsigned long)request,
+                               VM_PKT_DATA_INBAND,
+                               VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       if (ret)
+               goto cleanup;
+
+       t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ);
+       if (!t) {
+               ret = -ETIMEDOUT;
+               goto cleanup;
+       }
+
+       response = &input_dev->protocol_resp;
+
+       if (!response->response.approved) {
+               pr_err("synthhid protocol request failed (version %d)\n",
+                      SYNTHHID_INPUT_VERSION);
+               ret = -ENODEV;
+               goto cleanup;
+       }
+
+       t = wait_for_completion_timeout(&input_dev->wait_event, 5*HZ);
+       if (!t) {
+               ret = -ETIMEDOUT;
+               goto cleanup;
+       }
+
+       /*
+        * We should have gotten the device attr, hid desc and report
+        * desc at this point
+        */
+       ret = input_dev->dev_info_status;
+
+cleanup:
+       return ret;
+}
+
+static int mousevsc_hid_open(struct hid_device *hid)
+{
+       return 0;
+}
+
+static int mousevsc_hid_start(struct hid_device *hid)
+{
+       return 0;
+}
+
+static void mousevsc_hid_close(struct hid_device *hid)
+{
+}
+
+static void mousevsc_hid_stop(struct hid_device *hid)
+{
+}
+
+static struct hid_ll_driver mousevsc_ll_driver = {
+       .open = mousevsc_hid_open,
+       .close = mousevsc_hid_close,
+       .start = mousevsc_hid_start,
+       .stop = mousevsc_hid_stop,
+};
+
+static struct hid_driver mousevsc_hid_driver;
+
+static int mousevsc_probe(struct hv_device *device,
+                       const struct hv_vmbus_device_id *dev_id)
+{
+       int ret;
+       struct mousevsc_dev *input_dev;
+       struct hid_device *hid_dev;
+
+       input_dev = mousevsc_alloc_device(device);
+
+       if (!input_dev)
+               return -ENOMEM;
+
+       ret = vmbus_open(device->channel,
+               INPUTVSC_SEND_RING_BUFFER_SIZE,
+               INPUTVSC_RECV_RING_BUFFER_SIZE,
+               NULL,
+               0,
+               mousevsc_on_channel_callback,
+               device
+               );
+
+       if (ret)
+               goto probe_err0;
+
+       ret = mousevsc_connect_to_vsp(device);
+
+       if (ret)
+               goto probe_err1;
+
+       /* workaround SA-167 */
+       if (input_dev->report_desc[14] == 0x25)
+               input_dev->report_desc[14] = 0x29;
+
+       hid_dev = hid_allocate_device();
+       if (IS_ERR(hid_dev)) {
+               ret = PTR_ERR(hid_dev);
+               goto probe_err1;
+       }
+
+       hid_dev->ll_driver = &mousevsc_ll_driver;
+       hid_dev->driver = &mousevsc_hid_driver;
+       hid_dev->bus = BUS_VIRTUAL;
+       hid_dev->vendor = input_dev->hid_dev_info.vendor;
+       hid_dev->product = input_dev->hid_dev_info.product;
+       hid_dev->version = input_dev->hid_dev_info.version;
+       input_dev->hid_device = hid_dev;
+
+       sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse");
+
+       ret = hid_add_device(hid_dev);
+       if (ret)
+               goto probe_err1;
+
+       ret = hid_parse_report(hid_dev, input_dev->report_desc,
+                               input_dev->report_desc_size);
+
+       if (ret) {
+               hid_err(hid_dev, "parse failed\n");
+               goto probe_err2;
+       }
+
+       ret = hid_hw_start(hid_dev, HID_CONNECT_HIDINPUT | HID_CONNECT_HIDDEV);
+
+       if (ret) {
+               hid_err(hid_dev, "hw start failed\n");
+               goto probe_err2;
+       }
+
+       input_dev->connected = true;
+       input_dev->init_complete = true;
+
+       return ret;
+
+probe_err2:
+       hid_destroy_device(hid_dev);
+
+probe_err1:
+       vmbus_close(device->channel);
+
+probe_err0:
+       mousevsc_free_device(input_dev);
+
+       return ret;
+}
+
+
+static int mousevsc_remove(struct hv_device *dev)
+{
+       struct mousevsc_dev *input_dev = hv_get_drvdata(dev);
+
+       vmbus_close(dev->channel);
+       hid_destroy_device(input_dev->hid_device);
+       mousevsc_free_device(input_dev);
+
+       return 0;
+}
+
+static const struct hv_vmbus_device_id id_table[] = {
+       /* Mouse guid */
+       { VMBUS_DEVICE(0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c,
+                      0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A) },
+       { },
+};
+
+MODULE_DEVICE_TABLE(vmbus, id_table);
+
+static struct  hv_driver mousevsc_drv = {
+       .name = KBUILD_MODNAME,
+       .id_table = id_table,
+       .probe = mousevsc_probe,
+       .remove = mousevsc_remove,
+};
+
+static int __init mousevsc_init(void)
+{
+       return vmbus_driver_register(&mousevsc_drv);
+}
+
+static void __exit mousevsc_exit(void)
+{
+       vmbus_driver_unregister(&mousevsc_drv);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION(HV_DRV_VERSION);
+module_init(mousevsc_init);
+module_exit(mousevsc_exit);
index 4a441a6f996748a923204fbcb53c4850eb3af061..b8574cddd95352a360ef7ff67200527185b3c1fb 100644 (file)
@@ -21,6 +21,7 @@
 #define USB_VENDOR_ID_3M               0x0596
 #define USB_DEVICE_ID_3M1968           0x0500
 #define USB_DEVICE_ID_3M2256           0x0502
+#define USB_DEVICE_ID_3M3266           0x0506
 
 #define USB_VENDOR_ID_A4TECH           0x09da
 #define USB_DEVICE_ID_A4TECH_WCP32PU   0x0006
 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI  0x0239
 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO   0x023a
 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS   0x023b
+#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI  0x0255
 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO   0x0256
 #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY   0x030a
 #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY    0x030b
 #define USB_DEVICE_ID_ATEN_4PORTKVM    0x2205
 #define USB_DEVICE_ID_ATEN_4PORTKVMC   0x2208
 
+#define USB_VENDOR_ID_ATMEL            0x03eb
+#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c
+
 #define USB_VENDOR_ID_AVERMEDIA                0x07ca
 #define USB_DEVICE_ID_AVER_FM_MR800    0xb800
 
 
 #define USB_VENDOR_ID_DWAV             0x0eef
 #define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER   0x0001
-#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH   0x480d
-#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1  0x720c
-#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2  0x72a1
-#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3  0x480e
-#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4  0x726b
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D      0x480d
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E      0x480e
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C      0x720c
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B      0x726b
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1      0x72a1
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA      0x72fa
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302      0x7302
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001      0xa001
 
 #define USB_VENDOR_ID_ELECOM           0x056e
 #define USB_DEVICE_ID_ELECOM_BM084     0x0061
 #define USB_VENDOR_ID_HANVON           0x20b3
 #define USB_DEVICE_ID_HANVON_MULTITOUCH        0x0a18
 
+#define USB_VENDOR_ID_HANVON_ALT       0x22ed
+#define USB_DEVICE_ID_HANVON_ALT_MULTITOUCH    0x1010
+
 #define USB_VENDOR_ID_HAPP             0x078b
 #define USB_DEVICE_ID_UGCI_DRIVING     0x0010
 #define USB_DEVICE_ID_UGCI_FLYING      0x0020
 #define USB_VENDOR_ID_PI_ENGINEERING   0x05f3
 #define USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL 0xff
 
+#define USB_VENDOR_ID_PIXART                           0x093a
+#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN      0x8001
+#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1     0x8002
+#define USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2     0x8003
+
 #define USB_VENDOR_ID_PLAYDOTCOM       0x0b43
 #define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII     0x0003
 
 #define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062
 
 #define USB_VENDOR_ID_QUANTA           0x0408
-#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH     0x3000
+#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH             0x3000
+#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001                0x3001
+#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008                0x3008
 #define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN  0x3001
 
 #define USB_VENDOR_ID_ROCCAT           0x1e7d
 #define USB_DEVICE_ID_ROCCAT_ARVO      0x30d4
+#define USB_DEVICE_ID_ROCCAT_ISKU      0x319c
 #define USB_DEVICE_ID_ROCCAT_KONE      0x2ced
 #define USB_DEVICE_ID_ROCCAT_KONEPLUS  0x2d51
 #define USB_DEVICE_ID_ROCCAT_KOVAPLUS  0x2d50
 
 #define USB_VENDOR_ID_WACOM            0x056a
 #define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH 0x81
+#define USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH   0x00BD
 
 #define USB_VENDOR_ID_WALTOP                           0x172f
 #define USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH      0x0032
 #define USB_VENDOR_ID_XAT      0x2505
 #define USB_DEVICE_ID_XAT_CSR  0x0220
 
+#define USB_VENDOR_ID_XIROKU           0x1477
+#define USB_DEVICE_ID_XIROKU_SPX       0x1006
+#define USB_DEVICE_ID_XIROKU_MPX       0x1007
+#define USB_DEVICE_ID_XIROKU_CSR       0x100e
+#define USB_DEVICE_ID_XIROKU_SPX1      0x1021
+#define USB_DEVICE_ID_XIROKU_CSR1      0x1022
+#define USB_DEVICE_ID_XIROKU_MPX1      0x1023
+#define USB_DEVICE_ID_XIROKU_SPX2      0x1024
+#define USB_DEVICE_ID_XIROKU_CSR2      0x1025
+#define USB_DEVICE_ID_XIROKU_MPX2      0x1026
+
 #define USB_VENDOR_ID_YEALINK          0x6993
 #define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K      0xb001
 
index f333139d1a4823b325f4ab6c5063cf926198ce26..9333d692a786b03df115ebe87d920e748b607dc6 100644 (file)
@@ -32,6 +32,8 @@
 #include <linux/hid.h>
 #include <linux/hid-debug.h>
 
+#include "hid-ids.h"
+
 #define unk    KEY_UNKNOWN
 
 static const unsigned char hid_keyboard[256] = {
@@ -271,6 +273,161 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
        return logical_extents / physical_extents;
 }
 
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+static enum power_supply_property hidinput_battery_props[] = {
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_MODEL_NAME,
+       POWER_SUPPLY_PROP_STATUS
+};
+
+#define HID_BATTERY_QUIRK_PERCENT      (1 << 0) /* always reports percent */
+#define HID_BATTERY_QUIRK_FEATURE      (1 << 1) /* ask for feature report */
+
+static const struct hid_device_id hid_battery_quirks[] = {
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+                              USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI),
+         HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE },
+       {}
+};
+
+static unsigned find_battery_quirk(struct hid_device *hdev)
+{
+       unsigned quirks = 0;
+       const struct hid_device_id *match;
+
+       match = hid_match_id(hdev, hid_battery_quirks);
+       if (match != NULL)
+               quirks = match->driver_data;
+
+       return quirks;
+}
+
+static int hidinput_get_battery_property(struct power_supply *psy,
+                                        enum power_supply_property prop,
+                                        union power_supply_propval *val)
+{
+       struct hid_device *dev = container_of(psy, struct hid_device, battery);
+       int ret = 0;
+       __u8 buf[2] = {};
+
+       switch (prop) {
+       case POWER_SUPPLY_PROP_PRESENT:
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = 1;
+               break;
+
+       case POWER_SUPPLY_PROP_CAPACITY:
+               ret = dev->hid_get_raw_report(dev, dev->battery_report_id,
+                                             buf, sizeof(buf),
+                                             dev->battery_report_type);
+
+               if (ret != 2) {
+                       if (ret >= 0)
+                               ret = -EINVAL;
+                       break;
+               }
+
+               if (dev->battery_min < dev->battery_max &&
+                   buf[1] >= dev->battery_min &&
+                   buf[1] <= dev->battery_max)
+                       val->intval = (100 * (buf[1] - dev->battery_min)) /
+                               (dev->battery_max - dev->battery_min);
+               break;
+
+       case POWER_SUPPLY_PROP_MODEL_NAME:
+               val->strval = dev->name;
+               break;
+
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               break;
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field)
+{
+       struct power_supply *battery = &dev->battery;
+       int ret;
+       unsigned quirks;
+       s32 min, max;
+
+       if (field->usage->hid != HID_DC_BATTERYSTRENGTH)
+               return false;   /* no match */
+
+       if (battery->name != NULL)
+               goto out;       /* already initialized? */
+
+       battery->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq);
+       if (battery->name == NULL)
+               goto out;
+
+       battery->type = POWER_SUPPLY_TYPE_BATTERY;
+       battery->properties = hidinput_battery_props;
+       battery->num_properties = ARRAY_SIZE(hidinput_battery_props);
+       battery->use_for_apm = 0;
+       battery->get_property = hidinput_get_battery_property;
+
+       quirks = find_battery_quirk(dev);
+
+       hid_dbg(dev, "device %x:%x:%x %d quirks %d\n",
+               dev->bus, dev->vendor, dev->product, dev->version, quirks);
+
+       min = field->logical_minimum;
+       max = field->logical_maximum;
+
+       if (quirks & HID_BATTERY_QUIRK_PERCENT) {
+               min = 0;
+               max = 100;
+       }
+
+       if (quirks & HID_BATTERY_QUIRK_FEATURE)
+               report_type = HID_FEATURE_REPORT;
+
+       dev->battery_min = min;
+       dev->battery_max = max;
+       dev->battery_report_type = report_type;
+       dev->battery_report_id = field->report->id;
+
+       ret = power_supply_register(&dev->dev, battery);
+       if (ret != 0) {
+               hid_warn(dev, "can't register power supply: %d\n", ret);
+               kfree(battery->name);
+               battery->name = NULL;
+       }
+
+out:
+       return true;
+}
+
+static void hidinput_cleanup_battery(struct hid_device *dev)
+{
+       if (!dev->battery.name)
+               return;
+
+       power_supply_unregister(&dev->battery);
+       kfree(dev->battery.name);
+       dev->battery.name = NULL;
+}
+#else  /* !CONFIG_HID_BATTERY_STRENGTH */
+static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
+                                  struct hid_field *field)
+{
+       return false;
+}
+
+static void hidinput_cleanup_battery(struct hid_device *dev)
+{
+}
+#endif /* CONFIG_HID_BATTERY_STRENGTH */
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
                                     struct hid_usage *usage)
 {
@@ -629,6 +786,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                }
                break;
 
+       case HID_UP_GENDEVCTRLS:
+               if (hidinput_setup_battery(device, HID_INPUT_REPORT, field))
+                       goto ignore;
+               else
+                       goto unknown;
+               break;
+
        case HID_UP_HPVENDOR:   /* Reported on a Dutch layout HP5308 */
                set_bit(EV_REP, input->evbit);
                switch (usage->hid & HID_USAGE) {
@@ -822,6 +986,12 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
                return;
        }
 
+       /* Ignore out-of-range values as per HID specification, section 5.10 */
+       if (value < field->logical_minimum || value > field->logical_maximum) {
+               dbg_hid("Ignoring out-of-range value %x\n", value);
+               return;
+       }
+
        /* report the usage code as scancode if the key status has changed */
        if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value)
                input_event(input, EV_MSC, MSC_SCAN, usage->hid);
@@ -861,6 +1031,48 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int
 }
 EXPORT_SYMBOL_GPL(hidinput_find_field);
 
+struct hid_field *hidinput_get_led_field(struct hid_device *hid)
+{
+       struct hid_report *report;
+       struct hid_field *field;
+       int i, j;
+
+       list_for_each_entry(report,
+                           &hid->report_enum[HID_OUTPUT_REPORT].report_list,
+                           list) {
+               for (i = 0; i < report->maxfield; i++) {
+                       field = report->field[i];
+                       for (j = 0; j < field->maxusage; j++)
+                               if (field->usage[j].type == EV_LED)
+                                       return field;
+               }
+       }
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(hidinput_get_led_field);
+
+unsigned int hidinput_count_leds(struct hid_device *hid)
+{
+       struct hid_report *report;
+       struct hid_field *field;
+       int i, j;
+       unsigned int count = 0;
+
+       list_for_each_entry(report,
+                           &hid->report_enum[HID_OUTPUT_REPORT].report_list,
+                           list) {
+               for (i = 0; i < report->maxfield; i++) {
+                       field = report->field[i];
+                       for (j = 0; j < field->maxusage; j++)
+                               if (field->usage[j].type == EV_LED &&
+                                   field->value[j])
+                                       count += 1;
+               }
+       }
+       return count;
+}
+EXPORT_SYMBOL_GPL(hidinput_count_leds);
+
 static int hidinput_open(struct input_dev *dev)
 {
        struct hid_device *hid = input_get_drvdata(dev);
@@ -882,15 +1094,17 @@ static void report_features(struct hid_device *hid)
        struct hid_report *rep;
        int i, j;
 
-       if (!drv->feature_mapping)
-               return;
-
        rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
        list_for_each_entry(rep, &rep_enum->report_list, list)
                for (i = 0; i < rep->maxfield; i++)
-                       for (j = 0; j < rep->field[i]->maxusage; j++)
-                               drv->feature_mapping(hid, rep->field[i],
-                                                    rep->field[i]->usage + j);
+                       for (j = 0; j < rep->field[i]->maxusage; j++) {
+                               /* Verify if Battery Strength feature is available */
+                               hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]);
+
+                               if (drv->feature_mapping)
+                                       drv->feature_mapping(hid, rep->field[i],
+                                                            rep->field[i]->usage + j);
+                       }
 }
 
 /*
@@ -1010,6 +1224,8 @@ void hidinput_disconnect(struct hid_device *hid)
 {
        struct hid_input *hidinput, *next;
 
+       hidinput_cleanup_battery(hid);
+
        list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
                list_del(&hidinput->list);
                input_unregister_device(hidinput->input);
index 103f30d93f76adb154518f5656bba6bbd98e24e5..6ecc9e2204409c1451e90086a4edd7f0639cdb01 100644 (file)
@@ -430,7 +430,7 @@ int lg4ff_init(struct hid_device *hid)
        }
 
        /* Add the device to device_list */
-       entry = (struct lg4ff_device_entry *)kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL);
+       entry = kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL);
        if (!entry) {
                hid_err(hid, "Cannot add device, insufficient memory.\n");
                return -ENOMEM;
index f1c909f1b23998f91d9b7b76d8cf31e53f65aadd..24fc4423b9373f749dab0f743f62692280d7db24 100644 (file)
@@ -50,7 +50,6 @@ MODULE_LICENSE("GPL");
 #define MT_QUIRK_ALWAYS_VALID          (1 << 4)
 #define MT_QUIRK_VALID_IS_INRANGE      (1 << 5)
 #define MT_QUIRK_VALID_IS_CONFIDENCE   (1 << 6)
-#define MT_QUIRK_EGALAX_XYZ_FIXUP      (1 << 7)
 #define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE   (1 << 8)
 
 struct mt_slot {
@@ -60,9 +59,19 @@ struct mt_slot {
        bool seen_in_this_frame;/* has this slot been updated */
 };
 
+struct mt_class {
+       __s32 name;     /* MT_CLS */
+       __s32 quirks;
+       __s32 sn_move;  /* Signal/noise ratio for move events */
+       __s32 sn_width; /* Signal/noise ratio for width events */
+       __s32 sn_height;        /* Signal/noise ratio for height events */
+       __s32 sn_pressure;      /* Signal/noise ratio for pressure events */
+       __u8 maxcontacts;
+};
+
 struct mt_device {
        struct mt_slot curdata; /* placeholder of incoming data */
-       struct mt_class *mtclass;       /* our mt device class */
+       struct mt_class mtclass;        /* our mt device class */
        unsigned last_field_index;      /* last field index of the report */
        unsigned last_slot_field;       /* the last field of a slot */
        int last_mt_collection; /* last known mt-related collection */
@@ -74,30 +83,23 @@ struct mt_device {
        struct mt_slot *slots;
 };
 
-struct mt_class {
-       __s32 name;     /* MT_CLS */
-       __s32 quirks;
-       __s32 sn_move;  /* Signal/noise ratio for move events */
-       __s32 sn_width; /* Signal/noise ratio for width events */
-       __s32 sn_height;        /* Signal/noise ratio for height events */
-       __s32 sn_pressure;      /* Signal/noise ratio for pressure events */
-       __u8 maxcontacts;
-};
-
 /* classes of device behavior */
 #define MT_CLS_DEFAULT                         0x0001
 
 #define MT_CLS_SERIAL                          0x0002
 #define MT_CLS_CONFIDENCE                      0x0003
-#define MT_CLS_CONFIDENCE_MINUS_ONE            0x0004
-#define MT_CLS_DUAL_INRANGE_CONTACTID          0x0005
-#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER      0x0006
-#define MT_CLS_DUAL_NSMU_CONTACTID             0x0007
+#define MT_CLS_CONFIDENCE_CONTACT_ID           0x0004
+#define MT_CLS_CONFIDENCE_MINUS_ONE            0x0005
+#define MT_CLS_DUAL_INRANGE_CONTACTID          0x0006
+#define MT_CLS_DUAL_INRANGE_CONTACTNUMBER      0x0007
+#define MT_CLS_DUAL_NSMU_CONTACTID             0x0008
+#define MT_CLS_INRANGE_CONTACTNUMBER           0x0009
 
 /* vendor specific classes */
 #define MT_CLS_3M                              0x0101
 #define MT_CLS_CYPRESS                         0x0102
 #define MT_CLS_EGALAX                          0x0103
+#define MT_CLS_EGALAX_SERIAL                   0x0104
 
 #define MT_DEFAULT_MAXCONTACT  10
 
@@ -133,13 +135,16 @@ static int find_slot_from_contactid(struct mt_device *td)
        return -1;
 }
 
-struct mt_class mt_classes[] = {
+static struct mt_class mt_classes[] = {
        { .name = MT_CLS_DEFAULT,
                .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP },
        { .name = MT_CLS_SERIAL,
                .quirks = MT_QUIRK_ALWAYS_VALID},
        { .name = MT_CLS_CONFIDENCE,
                .quirks = MT_QUIRK_VALID_IS_CONFIDENCE },
+       { .name = MT_CLS_CONFIDENCE_CONTACT_ID,
+               .quirks = MT_QUIRK_VALID_IS_CONFIDENCE |
+                       MT_QUIRK_SLOT_IS_CONTACTID },
        { .name = MT_CLS_CONFIDENCE_MINUS_ONE,
                .quirks = MT_QUIRK_VALID_IS_CONFIDENCE |
                        MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE },
@@ -155,6 +160,9 @@ struct mt_class mt_classes[] = {
                .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
                        MT_QUIRK_SLOT_IS_CONTACTID,
                .maxcontacts = 2 },
+       { .name = MT_CLS_INRANGE_CONTACTNUMBER,
+               .quirks = MT_QUIRK_VALID_IS_INRANGE |
+                       MT_QUIRK_SLOT_IS_CONTACTNUMBER },
 
        /*
         * vendor specific classes
@@ -171,9 +179,13 @@ struct mt_class mt_classes[] = {
                .maxcontacts = 10 },
        { .name = MT_CLS_EGALAX,
                .quirks =  MT_QUIRK_SLOT_IS_CONTACTID |
-                       MT_QUIRK_VALID_IS_INRANGE |
-                       MT_QUIRK_EGALAX_XYZ_FIXUP,
-               .maxcontacts = 2,
+                       MT_QUIRK_VALID_IS_INRANGE,
+               .sn_move = 4096,
+               .sn_pressure = 32,
+       },
+       { .name = MT_CLS_EGALAX_SERIAL,
+               .quirks =  MT_QUIRK_SLOT_IS_CONTACTID |
+                       MT_QUIRK_ALWAYS_VALID,
                .sn_move = 4096,
                .sn_pressure = 32,
        },
@@ -181,6 +193,44 @@ struct mt_class mt_classes[] = {
        { }
 };
 
+static ssize_t mt_show_quirks(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct mt_device *td = hid_get_drvdata(hdev);
+
+       return sprintf(buf, "%u\n", td->mtclass.quirks);
+}
+
+static ssize_t mt_set_quirks(struct device *dev,
+                         struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct mt_device *td = hid_get_drvdata(hdev);
+
+       unsigned long val;
+
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       td->mtclass.quirks = val;
+
+       return count;
+}
+
+static DEVICE_ATTR(quirks, S_IWUSR | S_IRUGO, mt_show_quirks, mt_set_quirks);
+
+static struct attribute *sysfs_attrs[] = {
+       &dev_attr_quirks.attr,
+       NULL
+};
+
+static struct attribute_group mt_attribute_group = {
+       .attrs = sysfs_attrs
+};
+
 static void mt_feature_mapping(struct hid_device *hdev,
                struct hid_field *field, struct hid_usage *usage)
 {
@@ -192,9 +242,9 @@ static void mt_feature_mapping(struct hid_device *hdev,
                break;
        case HID_DG_CONTACTMAX:
                td->maxcontacts = field->value[0];
-               if (td->mtclass->maxcontacts)
+               if (td->mtclass.maxcontacts)
                        /* check if the maxcontacts is given by the class */
-                       td->maxcontacts = td->mtclass->maxcontacts;
+                       td->maxcontacts = td->mtclass.maxcontacts;
 
                break;
        }
@@ -214,8 +264,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                unsigned long **bit, int *max)
 {
        struct mt_device *td = hid_get_drvdata(hdev);
-       struct mt_class *cls = td->mtclass;
-       __s32 quirks = cls->quirks;
+       struct mt_class *cls = &td->mtclass;
 
        /* Only map fields from TouchScreen or TouchPad collections.
          * We need to ignore fields that belong to other collections
@@ -227,13 +276,17 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
        else
                return 0;
 
+       /* eGalax devices provide a Digitizer.Stylus input which overrides
+        * the correct Digitizers.Finger X/Y ranges.
+        * Let's just ignore this input. */
+       if (field->physical == HID_DG_STYLUS)
+               return -1;
+
        switch (usage->hid & HID_USAGE_PAGE) {
 
        case HID_UP_GENDESK:
                switch (usage->hid) {
                case HID_GD_X:
-                       if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP)
-                               field->logical_maximum = 32760;
                        hid_map_usage(hi, usage, bit, max,
                                        EV_ABS, ABS_MT_POSITION_X);
                        set_abs(hi->input, ABS_MT_POSITION_X, field,
@@ -246,8 +299,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                        }
                        return 1;
                case HID_GD_Y:
-                       if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP)
-                               field->logical_maximum = 32760;
                        hid_map_usage(hi, usage, bit, max,
                                        EV_ABS, ABS_MT_POSITION_Y);
                        set_abs(hi->input, ABS_MT_POSITION_Y, field,
@@ -315,8 +366,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                        }
                        return 1;
                case HID_DG_TIPPRESSURE:
-                       if (quirks & MT_QUIRK_EGALAX_XYZ_FIXUP)
-                               field->logical_minimum = 0;
                        hid_map_usage(hi, usage, bit, max,
                                        EV_ABS, ABS_MT_PRESSURE);
                        set_abs(hi->input, ABS_MT_PRESSURE, field,
@@ -363,7 +412,7 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi,
 
 static int mt_compute_slot(struct mt_device *td)
 {
-       __s32 quirks = td->mtclass->quirks;
+       __s32 quirks = td->mtclass.quirks;
 
        if (quirks & MT_QUIRK_SLOT_IS_CONTACTID)
                return td->curdata.contactid;
@@ -407,7 +456,7 @@ static void mt_emit_event(struct mt_device *td, struct input_dev *input)
 
        for (i = 0; i < td->maxcontacts; ++i) {
                struct mt_slot *s = &(td->slots[i]);
-               if ((td->mtclass->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) &&
+               if ((td->mtclass.quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) &&
                        !s->seen_in_this_frame) {
                        s->touch_state = false;
                }
@@ -444,7 +493,7 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
                                struct hid_usage *usage, __s32 value)
 {
        struct mt_device *td = hid_get_drvdata(hid);
-       __s32 quirks = td->mtclass->quirks;
+       __s32 quirks = td->mtclass.quirks;
 
        if (hid->claimed & HID_CLAIMED_INPUT && td->slots) {
                switch (usage->hid) {
@@ -552,7 +601,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
                dev_err(&hdev->dev, "cannot allocate multitouch data\n");
                return -ENOMEM;
        }
-       td->mtclass = mtclass;
+       td->mtclass = *mtclass;
        td->inputmode = -1;
        td->last_mt_collection = -1;
        hid_set_drvdata(hdev, td);
@@ -574,6 +623,8 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
                goto fail;
        }
 
+       ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
+
        mt_set_input_mode(hdev);
 
        return 0;
@@ -594,6 +645,7 @@ static int mt_reset_resume(struct hid_device *hdev)
 static void mt_remove(struct hid_device *hdev)
 {
        struct mt_device *td = hid_get_drvdata(hdev);
+       sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
        hid_hw_stop(hdev);
        kfree(td->slots);
        kfree(td);
@@ -609,12 +661,20 @@ static const struct hid_device_id mt_devices[] = {
        { .driver_data = MT_CLS_3M,
                HID_USB_DEVICE(USB_VENDOR_ID_3M,
                        USB_DEVICE_ID_3M2256) },
+       { .driver_data = MT_CLS_3M,
+               HID_USB_DEVICE(USB_VENDOR_ID_3M,
+                       USB_DEVICE_ID_3M3266) },
 
        /* ActionStar panels */
        { .driver_data = MT_CLS_DEFAULT,
                HID_USB_DEVICE(USB_VENDOR_ID_ACTIONSTAR,
                        USB_DEVICE_ID_ACTIONSTAR_1011) },
 
+       /* Atmel panels */
+       { .driver_data = MT_CLS_SERIAL,
+               HID_USB_DEVICE(USB_VENDOR_ID_ATMEL,
+                       USB_DEVICE_ID_ATMEL_MULTITOUCH) },
+
        /* Cando panels */
        { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER,
                HID_USB_DEVICE(USB_VENDOR_ID_CANDO,
@@ -645,23 +705,32 @@ static const struct hid_device_id mt_devices[] = {
                        USB_DEVICE_ID_CYPRESS_TRUETOUCH) },
 
        /* eGalax devices (resistive) */
-       {  .driver_data = MT_CLS_EGALAX,
+       { .driver_data = MT_CLS_EGALAX,
                HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
-                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) },
-       {  .driver_data = MT_CLS_EGALAX,
+                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D) },
+       { .driver_data = MT_CLS_EGALAX,
                HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
-                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH3) },
+                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) },
 
        /* eGalax devices (capacitive) */
-       {  .driver_data = MT_CLS_EGALAX,
+       { .driver_data = MT_CLS_EGALAX,
                HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
-                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) },
-       {  .driver_data = MT_CLS_EGALAX,
+                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) },
+       { .driver_data = MT_CLS_EGALAX,
                HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
-                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH2) },
-       {  .driver_data = MT_CLS_EGALAX,
+                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) },
+       { .driver_data = MT_CLS_EGALAX,
                HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
-                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH4) },
+                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) },
+       { .driver_data = MT_CLS_EGALAX,
+               HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
+                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA) },
+       { .driver_data = MT_CLS_EGALAX,
+               HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
+                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) },
+       { .driver_data = MT_CLS_EGALAX_SERIAL,
+               HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
+                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) },
 
        /* Elo TouchSystems IntelliTouch Plus panel */
        { .driver_data = MT_CLS_DUAL_NSMU_CONTACTID,
@@ -678,6 +747,11 @@ static const struct hid_device_id mt_devices[] = {
                HID_USB_DEVICE(USB_VENDOR_ID_GOODTOUCH,
                        USB_DEVICE_ID_GOODTOUCH_000f) },
 
+       /* Hanvon panels */
+       { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID,
+               HID_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT,
+                       USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) },
+
        /* Ideacom panel */
        { .driver_data = MT_CLS_SERIAL,
                HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM,
@@ -722,6 +796,17 @@ static const struct hid_device_id mt_devices[] = {
                HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT,
                        USB_DEVICE_ID_PENMOUNT_PCI) },
 
+       /* PixArt optical touch screen */
+       { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER,
+               HID_USB_DEVICE(USB_VENDOR_ID_PIXART,
+                       USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN) },
+       { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER,
+               HID_USB_DEVICE(USB_VENDOR_ID_PIXART,
+                       USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1) },
+       { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER,
+               HID_USB_DEVICE(USB_VENDOR_ID_PIXART,
+                       USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2) },
+
        /* PixCir-based panels */
        { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID,
                HID_USB_DEVICE(USB_VENDOR_ID_HANVON,
@@ -730,6 +815,17 @@ static const struct hid_device_id mt_devices[] = {
                HID_USB_DEVICE(USB_VENDOR_ID_CANDO,
                        USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) },
 
+       /* Quanta-based panels */
+       { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID,
+               HID_USB_DEVICE(USB_VENDOR_ID_QUANTA,
+                       USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
+       { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID,
+               HID_USB_DEVICE(USB_VENDOR_ID_QUANTA,
+                       USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001) },
+       { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID,
+               HID_USB_DEVICE(USB_VENDOR_ID_QUANTA,
+                       USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008) },
+
        /* Stantum panels */
        { .driver_data = MT_CLS_CONFIDENCE,
                HID_USB_DEVICE(USB_VENDOR_ID_STANTUM,
@@ -758,6 +854,35 @@ static const struct hid_device_id mt_devices[] = {
                HID_USB_DEVICE(USB_VENDOR_ID_XAT,
                        USB_DEVICE_ID_XAT_CSR) },
 
+       /* Xiroku */
+       { .driver_data = MT_CLS_DEFAULT,
+               HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
+                       USB_DEVICE_ID_XIROKU_SPX) },
+       { .driver_data = MT_CLS_DEFAULT,
+               HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
+                       USB_DEVICE_ID_XIROKU_MPX) },
+       { .driver_data = MT_CLS_DEFAULT,
+               HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
+                       USB_DEVICE_ID_XIROKU_CSR) },
+       { .driver_data = MT_CLS_DEFAULT,
+               HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
+                       USB_DEVICE_ID_XIROKU_SPX1) },
+       { .driver_data = MT_CLS_DEFAULT,
+               HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
+                       USB_DEVICE_ID_XIROKU_MPX1) },
+       { .driver_data = MT_CLS_DEFAULT,
+               HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
+                       USB_DEVICE_ID_XIROKU_CSR1) },
+       { .driver_data = MT_CLS_DEFAULT,
+               HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
+                       USB_DEVICE_ID_XIROKU_SPX2) },
+       { .driver_data = MT_CLS_DEFAULT,
+               HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
+                       USB_DEVICE_ID_XIROKU_MPX2) },
+       { .driver_data = MT_CLS_DEFAULT,
+               HID_USB_DEVICE(USB_VENDOR_ID_XIROKU,
+                       USB_DEVICE_ID_XIROKU_CSR2) },
+
        { }
 };
 MODULE_DEVICE_TABLE(hid, mt_devices);
index 01e7d2cd7c26a55170d2f7eda4e0a83cb37cb55d..12f9777c385d4038825d2fddcd59a18182e13b3c 100644 (file)
@@ -633,7 +633,7 @@ struct picolcd_fb_cleanup_item {
        struct picolcd_fb_cleanup_item *next;
 };
 static struct picolcd_fb_cleanup_item *fb_pending;
-DEFINE_SPINLOCK(fb_pending_lock);
+static DEFINE_SPINLOCK(fb_pending_lock);
 
 static void picolcd_fb_do_cleanup(struct work_struct *data)
 {
@@ -658,7 +658,7 @@ static void picolcd_fb_do_cleanup(struct work_struct *data)
        } while (item);
 }
 
-DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup);
+static DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup);
 
 static int picolcd_fb_open(struct fb_info *info, int u)
 {
diff --git a/drivers/hid/hid-quanta.c b/drivers/hid/hid-quanta.c
deleted file mode 100644 (file)
index 87a54df..0000000
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- *  HID driver for Quanta Optical Touch dual-touch panels
- *
- *  Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr>
- *
- */
-
-/*
- * 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 <linux/slab.h>
-
-MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>");
-MODULE_DESCRIPTION("Quanta dual-touch panel");
-MODULE_LICENSE("GPL");
-
-#include "hid-ids.h"
-
-struct quanta_data {
-       __u16 x, y;
-       __u8 id;
-       bool valid;             /* valid finger data, or just placeholder? */
-       bool first;             /* is this the first finger in this frame? */
-       bool activity_now;      /* at least one active finger in this frame? */
-       bool activity;          /* at least one active finger previously? */
-};
-
-static int quanta_input_mapping(struct hid_device *hdev, struct hid_input *hi,
-               struct hid_field *field, struct hid_usage *usage,
-               unsigned long **bit, int *max)
-{
-       switch (usage->hid & HID_USAGE_PAGE) {
-
-       case HID_UP_GENDESK:
-               switch (usage->hid) {
-               case HID_GD_X:
-                       hid_map_usage(hi, usage, bit, max,
-                                       EV_ABS, ABS_MT_POSITION_X);
-                       /* touchscreen emulation */
-                       input_set_abs_params(hi->input, ABS_X,
-                                               field->logical_minimum,
-                                               field->logical_maximum, 0, 0);
-                       return 1;
-               case HID_GD_Y:
-                       hid_map_usage(hi, usage, bit, max,
-                                       EV_ABS, ABS_MT_POSITION_Y);
-                       /* touchscreen emulation */
-                       input_set_abs_params(hi->input, ABS_Y,
-                                               field->logical_minimum,
-                                               field->logical_maximum, 0, 0);
-                       return 1;
-               }
-               return 0;
-
-       case HID_UP_DIGITIZER:
-               switch (usage->hid) {
-               case HID_DG_CONFIDENCE:
-               case HID_DG_TIPSWITCH:
-               case HID_DG_INPUTMODE:
-               case HID_DG_DEVICEINDEX:
-               case HID_DG_CONTACTCOUNT:
-               case HID_DG_CONTACTMAX:
-               case HID_DG_TIPPRESSURE:
-               case HID_DG_WIDTH:
-               case HID_DG_HEIGHT:
-                       return -1;
-               case HID_DG_INRANGE:
-                       /* touchscreen emulation */
-                       hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
-                       return 1;
-               case HID_DG_CONTACTID:
-                       hid_map_usage(hi, usage, bit, max,
-                                       EV_ABS, ABS_MT_TRACKING_ID);
-                       return 1;
-               }
-               return 0;
-
-       case 0xff000000:
-               /* ignore vendor-specific features */
-               return -1;
-       }
-
-       return 0;
-}
-
-static int quanta_input_mapped(struct hid_device *hdev, struct hid_input *hi,
-               struct hid_field *field, struct hid_usage *usage,
-               unsigned long **bit, int *max)
-{
-       if (usage->type == EV_KEY || usage->type == EV_ABS)
-               clear_bit(usage->code, *bit);
-
-       return 0;
-}
-
-/*
- * this function is called when a whole finger has been parsed,
- * so that it can decide what to send to the input layer.
- */
-static void quanta_filter_event(struct quanta_data *td, struct input_dev *input)
-{
-       
-       td->first = !td->first; /* touchscreen emulation */
-
-       if (!td->valid) {
-               /*
-                * touchscreen emulation: if no finger in this frame is valid
-                * and there previously was finger activity, this is a release
-                */ 
-               if (!td->first && !td->activity_now && td->activity) {
-                       input_event(input, EV_KEY, BTN_TOUCH, 0);
-                       td->activity = false;
-               }
-               return;
-       }
-
-       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_mt_sync(input);
-       td->valid = false;
-
-       /* touchscreen emulation: if first active finger in this frame... */
-       if (!td->activity_now) {
-               /* if there was no previous activity, emit touch event */
-               if (!td->activity) {
-                       input_event(input, EV_KEY, BTN_TOUCH, 1);
-                       td->activity = true;
-               }
-               td->activity_now = true;
-               /* and in any case this is our preferred finger */
-               input_event(input, EV_ABS, ABS_X, td->x);
-               input_event(input, EV_ABS, ABS_Y, td->y);
-       }
-}
-
-
-static int quanta_event(struct hid_device *hid, struct hid_field *field,
-                               struct hid_usage *usage, __s32 value)
-{
-       struct quanta_data *td = hid_get_drvdata(hid);
-
-       if (hid->claimed & HID_CLAIMED_INPUT) {
-               struct input_dev *input = field->hidinput->input;
-
-               switch (usage->hid) {
-               case HID_DG_INRANGE:
-                       td->valid = !!value;
-                       break;
-               case HID_GD_X:
-                       td->x = value;
-                       break;
-               case HID_GD_Y:
-                       td->y = value;
-                       quanta_filter_event(td, input);
-                       break;
-               case HID_DG_CONTACTID:
-                       td->id = value;
-                       break;
-               case HID_DG_CONTACTCOUNT:
-                       /* touch emulation: this is the last field in a frame */
-                       td->first = false;
-                       td->activity_now = false;
-                       break;
-               case HID_DG_CONFIDENCE:
-               case HID_DG_TIPSWITCH:
-                       /* avoid interference from generic hidinput handling */
-                       break;
-
-               default:
-                       /* fallback to the generic hidinput handling */
-                       return 0;
-               }
-       }
-
-       /* we have handled the hidinput part, now remains hiddev */
-       if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event)
-               hid->hiddev_hid_event(hid, field, usage, value);
-
-       return 1;
-}
-
-static int quanta_probe(struct hid_device *hdev, const struct hid_device_id *id)
-{
-       int ret;
-       struct quanta_data *td;
-
-       td = kmalloc(sizeof(struct quanta_data), GFP_KERNEL);
-       if (!td) {
-               hid_err(hdev, "cannot allocate Quanta Touch data\n");
-               return -ENOMEM;
-       }
-       td->valid = false;
-       td->activity = false;
-       td->activity_now = false;
-       td->first = false;
-       hid_set_drvdata(hdev, td);
-
-       ret = hid_parse(hdev);
-       if (!ret)
-               ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
-
-       if (ret)
-               kfree(td);
-
-       return ret;
-}
-
-static void quanta_remove(struct hid_device *hdev)
-{
-       hid_hw_stop(hdev);
-       kfree(hid_get_drvdata(hdev));
-       hid_set_drvdata(hdev, NULL);
-}
-
-static const struct hid_device_id quanta_devices[] = {
-       { 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) },
-       { }
-};
-MODULE_DEVICE_TABLE(hid, quanta_devices);
-
-static const struct hid_usage_id quanta_grabbed_usages[] = {
-       { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
-       { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
-};
-
-static struct hid_driver quanta_driver = {
-       .name = "quanta-touch",
-       .id_table = quanta_devices,
-       .probe = quanta_probe,
-       .remove = quanta_remove,
-       .input_mapping = quanta_input_mapping,
-       .input_mapped = quanta_input_mapped,
-       .usage_table = quanta_grabbed_usages,
-       .event = quanta_event,
-};
-
-static int __init quanta_init(void)
-{
-       return hid_register_driver(&quanta_driver);
-}
-
-static void __exit quanta_exit(void)
-{
-       hid_unregister_driver(&quanta_driver);
-}
-
-module_init(quanta_init);
-module_exit(quanta_exit);
-
index b07e7f96a3580e0168bce0a89e86b8091c94cd7a..a6d93992c75a4ee8d593729f867ca582975e0629 100644 (file)
@@ -49,12 +49,10 @@ int roccat_common_send(struct usb_device *usb_dev, uint report_id,
        char *buf;
        int len;
 
-       buf = kmalloc(size, GFP_KERNEL);
+       buf = kmemdup(data, size, GFP_KERNEL);
        if (buf == NULL)
                return -ENOMEM;
 
-       memcpy(buf, data, size);
-
        len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
                        HID_REQ_SET_REPORT,
                        USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c
new file mode 100644 (file)
index 0000000..0e4a0ab
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ * Roccat Isku driver for Linux
+ *
+ * Copyright (c) 2011 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 Isku is a gamer keyboard with macro keys that can be configured in
+ * 5 profiles.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/hid-roccat.h>
+#include "hid-ids.h"
+#include "hid-roccat-common.h"
+#include "hid-roccat-isku.h"
+
+static struct class *isku_class;
+
+static void isku_profile_activated(struct isku_device *isku, uint new_profile)
+{
+       isku->actual_profile = new_profile;
+}
+
+static int isku_receive(struct usb_device *usb_dev, uint command,
+               void *buf, uint size)
+{
+       return roccat_common_receive(usb_dev, command, buf, size);
+}
+
+static int isku_receive_control_status(struct usb_device *usb_dev)
+{
+       int retval;
+       struct isku_control control;
+
+       do {
+               msleep(50);
+               retval = isku_receive(usb_dev, ISKU_COMMAND_CONTROL,
+                               &control, sizeof(struct isku_control));
+
+               if (retval)
+                       return retval;
+
+               switch (control.value) {
+               case ISKU_CONTROL_VALUE_STATUS_OK:
+                       return 0;
+               case ISKU_CONTROL_VALUE_STATUS_WAIT:
+                       continue;
+               case ISKU_CONTROL_VALUE_STATUS_INVALID:
+               /* seems to be critical - replug necessary */
+               case ISKU_CONTROL_VALUE_STATUS_OVERLOAD:
+                       return -EINVAL;
+               default:
+                       hid_err(usb_dev, "isku_receive_control_status: "
+                                       "unknown response value 0x%x\n",
+                                       control.value);
+                       return -EINVAL;
+               }
+
+       } while (1);
+}
+
+static int isku_send(struct usb_device *usb_dev, uint command,
+               void const *buf, uint size)
+{
+       int retval;
+
+       retval = roccat_common_send(usb_dev, command, buf, size);
+       if (retval)
+               return retval;
+
+       return isku_receive_control_status(usb_dev);
+}
+
+static int isku_get_actual_profile(struct usb_device *usb_dev)
+{
+       struct isku_actual_profile buf;
+       int retval;
+
+       retval = isku_receive(usb_dev, ISKU_COMMAND_ACTUAL_PROFILE,
+                       &buf, sizeof(struct isku_actual_profile));
+       return retval ? retval : buf.actual_profile;
+}
+
+static int isku_set_actual_profile(struct usb_device *usb_dev, int new_profile)
+{
+       struct isku_actual_profile buf;
+
+       buf.command = ISKU_COMMAND_ACTUAL_PROFILE;
+       buf.size = sizeof(struct isku_actual_profile);
+       buf.actual_profile = new_profile;
+       return isku_send(usb_dev, ISKU_COMMAND_ACTUAL_PROFILE, &buf,
+                       sizeof(struct isku_actual_profile));
+}
+
+static ssize_t isku_sysfs_show_actual_profile(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct isku_device *isku =
+                       hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
+       return snprintf(buf, PAGE_SIZE, "%d\n", isku->actual_profile);
+}
+
+static ssize_t isku_sysfs_set_actual_profile(struct device *dev,
+               struct device_attribute *attr, char const *buf, size_t size)
+{
+       struct isku_device *isku;
+       struct usb_device *usb_dev;
+       unsigned long profile;
+       int retval;
+       struct isku_roccat_report roccat_report;
+
+       dev = dev->parent->parent;
+       isku = hid_get_drvdata(dev_get_drvdata(dev));
+       usb_dev = interface_to_usbdev(to_usb_interface(dev));
+
+       retval = strict_strtoul(buf, 10, &profile);
+       if (retval)
+               return retval;
+
+       if (profile > 4)
+               return -EINVAL;
+
+       mutex_lock(&isku->isku_lock);
+
+       retval = isku_set_actual_profile(usb_dev, profile);
+       if (retval) {
+               mutex_unlock(&isku->isku_lock);
+               return retval;
+       }
+
+       isku_profile_activated(isku, profile);
+
+       roccat_report.event = ISKU_REPORT_BUTTON_EVENT_PROFILE;
+       roccat_report.data1 = profile + 1;
+       roccat_report.data2 = 0;
+       roccat_report.profile = profile + 1;
+       roccat_report_event(isku->chrdev_minor, (uint8_t const *)&roccat_report);
+
+       mutex_unlock(&isku->isku_lock);
+
+       return size;
+}
+
+static struct device_attribute isku_attributes[] = {
+       __ATTR(actual_profile, 0660,
+                       isku_sysfs_show_actual_profile,
+                       isku_sysfs_set_actual_profile),
+       __ATTR_NULL
+};
+
+static ssize_t isku_sysfs_read(struct file *fp, struct kobject *kobj,
+               char *buf, loff_t off, size_t count,
+               size_t real_size, uint command)
+{
+       struct device *dev =
+                       container_of(kobj, struct device, kobj)->parent->parent;
+       struct isku_device *isku = hid_get_drvdata(dev_get_drvdata(dev));
+       struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
+       int retval;
+
+       if (off >= real_size)
+               return 0;
+
+       if (off != 0 || count != real_size)
+               return -EINVAL;
+
+       mutex_lock(&isku->isku_lock);
+       retval = isku_receive(usb_dev, command, buf, real_size);
+       mutex_unlock(&isku->isku_lock);
+
+       return retval ? retval : real_size;
+}
+
+static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj,
+               void const *buf, loff_t off, size_t count,
+               size_t real_size, uint command)
+{
+       struct device *dev =
+                       container_of(kobj, struct device, kobj)->parent->parent;
+       struct isku_device *isku = hid_get_drvdata(dev_get_drvdata(dev));
+       struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
+       int retval;
+
+       if (off != 0 || count != real_size)
+               return -EINVAL;
+
+       mutex_lock(&isku->isku_lock);
+       retval = isku_send(usb_dev, command, (void *)buf, real_size);
+       mutex_unlock(&isku->isku_lock);
+
+       return retval ? retval : real_size;
+}
+
+#define ISKU_SYSFS_W(thingy, THINGY) \
+static ssize_t isku_sysfs_write_ ## thingy(struct file *fp, struct kobject *kobj, \
+               struct bin_attribute *attr, char *buf, \
+               loff_t off, size_t count) \
+{ \
+       return isku_sysfs_write(fp, kobj, buf, off, count, \
+                       sizeof(struct isku_ ## thingy), ISKU_COMMAND_ ## THINGY); \
+}
+
+#define ISKU_SYSFS_R(thingy, THINGY) \
+static ssize_t isku_sysfs_read_ ## thingy(struct file *fp, struct kobject *kobj, \
+               struct bin_attribute *attr, char *buf, \
+               loff_t off, size_t count) \
+{ \
+       return isku_sysfs_read(fp, kobj, buf, off, count, \
+                       sizeof(struct isku_ ## thingy), ISKU_COMMAND_ ## THINGY); \
+}
+
+#define ISKU_SYSFS_RW(thingy, THINGY) \
+ISKU_SYSFS_R(thingy, THINGY) \
+ISKU_SYSFS_W(thingy, THINGY)
+
+#define ISKU_BIN_ATTR_RW(thingy) \
+{ \
+       .attr = { .name = #thingy, .mode = 0660 }, \
+       .size = sizeof(struct isku_ ## thingy), \
+       .read = isku_sysfs_read_ ## thingy, \
+       .write = isku_sysfs_write_ ## thingy \
+}
+
+#define ISKU_BIN_ATTR_R(thingy) \
+{ \
+       .attr = { .name = #thingy, .mode = 0440 }, \
+       .size = sizeof(struct isku_ ## thingy), \
+       .read = isku_sysfs_read_ ## thingy, \
+}
+
+#define ISKU_BIN_ATTR_W(thingy) \
+{ \
+       .attr = { .name = #thingy, .mode = 0220 }, \
+       .size = sizeof(struct isku_ ## thingy), \
+       .write = isku_sysfs_write_ ## thingy \
+}
+
+ISKU_SYSFS_RW(macro, MACRO)
+ISKU_SYSFS_RW(keys_function, KEYS_FUNCTION)
+ISKU_SYSFS_RW(keys_easyzone, KEYS_EASYZONE)
+ISKU_SYSFS_RW(keys_media, KEYS_MEDIA)
+ISKU_SYSFS_RW(keys_thumbster, KEYS_THUMBSTER)
+ISKU_SYSFS_RW(keys_macro, KEYS_MACRO)
+ISKU_SYSFS_RW(keys_capslock, KEYS_CAPSLOCK)
+ISKU_SYSFS_RW(light, LIGHT)
+ISKU_SYSFS_RW(key_mask, KEY_MASK)
+ISKU_SYSFS_RW(last_set, LAST_SET)
+ISKU_SYSFS_W(talk, TALK)
+ISKU_SYSFS_R(info, INFO)
+ISKU_SYSFS_W(control, CONTROL)
+
+static struct bin_attribute isku_bin_attributes[] = {
+       ISKU_BIN_ATTR_RW(macro),
+       ISKU_BIN_ATTR_RW(keys_function),
+       ISKU_BIN_ATTR_RW(keys_easyzone),
+       ISKU_BIN_ATTR_RW(keys_media),
+       ISKU_BIN_ATTR_RW(keys_thumbster),
+       ISKU_BIN_ATTR_RW(keys_macro),
+       ISKU_BIN_ATTR_RW(keys_capslock),
+       ISKU_BIN_ATTR_RW(light),
+       ISKU_BIN_ATTR_RW(key_mask),
+       ISKU_BIN_ATTR_RW(last_set),
+       ISKU_BIN_ATTR_W(talk),
+       ISKU_BIN_ATTR_R(info),
+       ISKU_BIN_ATTR_W(control),
+       __ATTR_NULL
+};
+
+static int isku_init_isku_device_struct(struct usb_device *usb_dev,
+               struct isku_device *isku)
+{
+       int retval;
+
+       mutex_init(&isku->isku_lock);
+
+       retval = isku_get_actual_profile(usb_dev);
+       if (retval < 0)
+               return retval;
+       isku_profile_activated(isku, retval);
+
+       return 0;
+}
+
+static int isku_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 isku_device *isku;
+       int retval;
+
+       if (intf->cur_altsetting->desc.bInterfaceProtocol
+                       != ISKU_USB_INTERFACE_PROTOCOL) {
+               hid_set_drvdata(hdev, NULL);
+               return 0;
+       }
+
+       isku = kzalloc(sizeof(*isku), GFP_KERNEL);
+       if (!isku) {
+               hid_err(hdev, "can't alloc device descriptor\n");
+               return -ENOMEM;
+       }
+       hid_set_drvdata(hdev, isku);
+
+       retval = isku_init_isku_device_struct(usb_dev, isku);
+       if (retval) {
+               hid_err(hdev, "couldn't init struct isku_device\n");
+               goto exit_free;
+       }
+
+       retval = roccat_connect(isku_class, hdev,
+                       sizeof(struct isku_roccat_report));
+       if (retval < 0) {
+               hid_err(hdev, "couldn't init char dev\n");
+       } else {
+               isku->chrdev_minor = retval;
+               isku->roccat_claimed = 1;
+       }
+
+       return 0;
+exit_free:
+       kfree(isku);
+       return retval;
+}
+
+static void isku_remove_specials(struct hid_device *hdev)
+{
+       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+       struct isku_device *isku;
+
+       if (intf->cur_altsetting->desc.bInterfaceProtocol
+                       != ISKU_USB_INTERFACE_PROTOCOL)
+               return;
+
+       isku = hid_get_drvdata(hdev);
+       if (isku->roccat_claimed)
+               roccat_disconnect(isku->chrdev_minor);
+       kfree(isku);
+}
+
+static int isku_probe(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       int retval;
+
+       retval = hid_parse(hdev);
+       if (retval) {
+               hid_err(hdev, "parse failed\n");
+               goto exit;
+       }
+
+       retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (retval) {
+               hid_err(hdev, "hw start failed\n");
+               goto exit;
+       }
+
+       retval = isku_init_specials(hdev);
+       if (retval) {
+               hid_err(hdev, "couldn't install keyboard\n");
+               goto exit_stop;
+       }
+
+       return 0;
+
+exit_stop:
+       hid_hw_stop(hdev);
+exit:
+       return retval;
+}
+
+static void isku_remove(struct hid_device *hdev)
+{
+       isku_remove_specials(hdev);
+       hid_hw_stop(hdev);
+}
+
+static void isku_keep_values_up_to_date(struct isku_device *isku,
+               u8 const *data)
+{
+       struct isku_report_button const *button_report;
+
+       switch (data[0]) {
+       case ISKU_REPORT_NUMBER_BUTTON:
+               button_report = (struct isku_report_button const *)data;
+               switch (button_report->event) {
+               case ISKU_REPORT_BUTTON_EVENT_PROFILE:
+                       isku_profile_activated(isku, button_report->data1 - 1);
+                       break;
+               }
+               break;
+       }
+}
+
+static void isku_report_to_chrdev(struct isku_device const *isku,
+               u8 const *data)
+{
+       struct isku_roccat_report roccat_report;
+       struct isku_report_button const *button_report;
+
+       if (data[0] != ISKU_REPORT_NUMBER_BUTTON)
+               return;
+
+       button_report = (struct isku_report_button const *)data;
+
+       roccat_report.event = button_report->event;
+       roccat_report.data1 = button_report->data1;
+       roccat_report.data2 = button_report->data2;
+       roccat_report.profile = isku->actual_profile + 1;
+       roccat_report_event(isku->chrdev_minor,
+                       (uint8_t const *)&roccat_report);
+}
+
+static int isku_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 isku_device *isku = hid_get_drvdata(hdev);
+
+       if (intf->cur_altsetting->desc.bInterfaceProtocol
+                       != ISKU_USB_INTERFACE_PROTOCOL)
+               return 0;
+
+       if (isku == NULL)
+               return 0;
+
+       isku_keep_values_up_to_date(isku, data);
+
+       if (isku->roccat_claimed)
+               isku_report_to_chrdev(isku, data);
+
+       return 0;
+}
+
+static const struct hid_device_id isku_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
+       { }
+};
+
+MODULE_DEVICE_TABLE(hid, isku_devices);
+
+static struct hid_driver isku_driver = {
+               .name = "isku",
+               .id_table = isku_devices,
+               .probe = isku_probe,
+               .remove = isku_remove,
+               .raw_event = isku_raw_event
+};
+
+static int __init isku_init(void)
+{
+       int retval;
+       isku_class = class_create(THIS_MODULE, "isku");
+       if (IS_ERR(isku_class))
+               return PTR_ERR(isku_class);
+       isku_class->dev_attrs = isku_attributes;
+       isku_class->dev_bin_attrs = isku_bin_attributes;
+
+       retval = hid_register_driver(&isku_driver);
+       if (retval)
+               class_destroy(isku_class);
+       return retval;
+}
+
+static void __exit isku_exit(void)
+{
+       hid_unregister_driver(&isku_driver);
+       class_destroy(isku_class);
+}
+
+module_init(isku_init);
+module_exit(isku_exit);
+
+MODULE_AUTHOR("Stefan Achatz");
+MODULE_DESCRIPTION("USB Roccat Isku driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hid/hid-roccat-isku.h b/drivers/hid/hid-roccat-isku.h
new file mode 100644 (file)
index 0000000..075f6ef
--- /dev/null
@@ -0,0 +1,147 @@
+#ifndef __HID_ROCCAT_ISKU_H
+#define __HID_ROCCAT_ISKU_H
+
+/*
+ * Copyright (c) 2011 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>
+
+enum {
+       ISKU_PROFILE_NUM = 5,
+       ISKU_USB_INTERFACE_PROTOCOL = 0,
+};
+
+struct isku_control {
+       uint8_t command; /* ISKU_COMMAND_CONTROL */
+       uint8_t value;
+       uint8_t request;
+} __packed;
+
+enum isku_control_values {
+       ISKU_CONTROL_VALUE_STATUS_OVERLOAD = 0,
+       ISKU_CONTROL_VALUE_STATUS_OK = 1,
+       ISKU_CONTROL_VALUE_STATUS_INVALID = 2,
+       ISKU_CONTROL_VALUE_STATUS_WAIT = 3,
+};
+
+struct isku_actual_profile {
+       uint8_t command; /* ISKU_COMMAND_ACTUAL_PROFILE */
+       uint8_t size; /* always 3 */
+       uint8_t actual_profile;
+} __packed;
+
+struct isku_key_mask {
+       uint8_t command; /* ISKU_COMMAND_KEY_MASK */
+       uint8_t size; /* 6 */
+       uint8_t profile_number; /* 0-4 */
+       uint8_t mask;
+       uint16_t checksum;
+} __packed;
+
+struct isku_keys_function {
+       uint8_t data[0x29];
+} __packed;
+
+struct isku_keys_easyzone {
+       uint8_t data[0x41];
+} __packed;
+
+struct isku_keys_media {
+       uint8_t data[0x1d];
+} __packed;
+
+struct isku_keys_thumbster {
+       uint8_t data[0x17];
+} __packed;
+
+struct isku_keys_macro {
+       uint8_t data[0x23];
+} __packed;
+
+struct isku_keys_capslock {
+       uint8_t data[0x6];
+} __packed;
+
+struct isku_macro {
+       uint8_t data[0x823];
+} __packed;
+
+struct isku_light {
+       uint8_t data[0xa];
+} __packed;
+
+struct isku_info {
+       uint8_t data[2];
+       uint8_t firmware_version;
+       uint8_t unknown[3];
+} __packed;
+
+struct isku_talk {
+       uint8_t data[0x10];
+} __packed;
+
+struct isku_last_set {
+       uint8_t data[0x14];
+} __packed;
+
+enum isku_commands {
+       ISKU_COMMAND_CONTROL = 0x4,
+       ISKU_COMMAND_ACTUAL_PROFILE = 0x5,
+       ISKU_COMMAND_KEY_MASK = 0x7,
+       ISKU_COMMAND_KEYS_FUNCTION = 0x8,
+       ISKU_COMMAND_KEYS_EASYZONE = 0x9,
+       ISKU_COMMAND_KEYS_MEDIA = 0xa,
+       ISKU_COMMAND_KEYS_THUMBSTER = 0xb,
+       ISKU_COMMAND_KEYS_MACRO = 0xd,
+       ISKU_COMMAND_MACRO = 0xe,
+       ISKU_COMMAND_INFO = 0xf,
+       ISKU_COMMAND_LIGHT = 0x10,
+       ISKU_COMMAND_KEYS_CAPSLOCK = 0x13,
+       ISKU_COMMAND_LAST_SET = 0x14,
+       ISKU_COMMAND_15 = 0x15,
+       ISKU_COMMAND_TALK = 0x16,
+       ISKU_COMMAND_FIRMWARE_WRITE = 0x1b,
+       ISKU_COMMAND_FIRMWARE_WRITE_CONTROL = 0x1c,
+};
+
+struct isku_report_button {
+       uint8_t number; /* ISKU_REPORT_NUMBER_BUTTON */
+       uint8_t zero;
+       uint8_t event;
+       uint8_t data1;
+       uint8_t data2;
+};
+
+enum isku_report_numbers {
+       ISKU_REPORT_NUMBER_BUTTON = 3,
+};
+
+enum isku_report_button_events {
+       ISKU_REPORT_BUTTON_EVENT_PROFILE = 0x2,
+};
+
+struct isku_roccat_report {
+       uint8_t event;
+       uint8_t data1;
+       uint8_t data2;
+       uint8_t profile;
+} __packed;
+
+struct isku_device {
+       int roccat_claimed;
+       int chrdev_minor;
+
+       struct mutex isku_lock;
+
+       int actual_profile;
+};
+
+#endif
index e2072afb34bbb7dce51c9bdb3b44f5b2867e9bd3..40090d602158bf06e03297edb40aa0c00edcd7f6 100644 (file)
@@ -78,12 +78,10 @@ static int kone_send(struct usb_device *usb_dev, uint usb_command,
        char *buf;
        int len;
 
-       buf = kmalloc(size, GFP_KERNEL);
+       buf = kmemdup(data, size, GFP_KERNEL);
        if (buf == NULL)
                return -ENOMEM;
 
-       memcpy(buf, data, size);
-
        len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
                        HID_REQ_SET_REPORT,
                        USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
index 17bb88f782b69b5cd0a44df7927742f412c6d1d0..f2183486a9b60882025d0eacc00070fa97253ce7 100644 (file)
@@ -9,6 +9,7 @@
  *  Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
  *  Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
  *  Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
+ *  Copyright (c) 2011 Przemys┼éaw Firszt <przemo@firszt.eu>
  */
 
 /*
@@ -33,6 +34,7 @@
 struct wacom_data {
        __u16 tool;
        unsigned char butstate;
+       __u8 features;
        unsigned char high_speed;
 #ifdef CONFIG_HID_WACOM_POWER_SUPPLY
        int battery_capacity;
@@ -107,6 +109,19 @@ static int wacom_ac_get_property(struct power_supply *psy,
 }
 #endif
 
+static void wacom_set_features(struct hid_device *hdev)
+{
+       int ret;
+       __u8 rep_data[2];
+
+       /*set high speed, tablet mode*/
+       rep_data[0] = 0x03;
+       rep_data[1] = 0x20;
+       ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
+                               HID_FEATURE_REPORT);
+       return;
+}
+
 static void wacom_poke(struct hid_device *hdev, u8 speed)
 {
        struct wacom_data *wdata = hid_get_drvdata(hdev);
@@ -177,26 +192,13 @@ static ssize_t wacom_store_speed(struct device *dev,
 static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP,
                wacom_show_speed, wacom_store_speed);
 
-static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
-               u8 *raw_data, int size)
+static int wacom_gr_parse_report(struct hid_device *hdev,
+                       struct wacom_data *wdata,
+                       struct input_dev *input, unsigned char *data)
 {
-       struct wacom_data *wdata = hid_get_drvdata(hdev);
-       struct hid_input *hidinput;
-       struct input_dev *input;
-       unsigned char *data = (unsigned char *) raw_data;
        int tool, x, y, rw;
 
-       if (!(hdev->claimed & HID_CLAIMED_INPUT))
-               return 0;
-
        tool = 0;
-       hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
-       input = hidinput->input;
-
-       /* Check if this is a tablet report */
-       if (data[0] != 0x03)
-               return 0;
-
        /* Get X & Y positions */
        x = le16_to_cpu(*(__le16 *) &data[2]);
        y = le16_to_cpu(*(__le16 *) &data[4]);
@@ -304,6 +306,121 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
        return 1;
 }
 
+static void wacom_i4_parse_pen_report(struct wacom_data *wdata,
+                       struct input_dev *input, unsigned char *data)
+{
+       __u16 x, y, pressure;
+       __u32 id;
+
+       switch (data[1]) {
+       case 0x80: /* Out of proximity report */
+               wdata->tool = 0;
+               input_report_key(input, BTN_TOUCH, 0);
+               input_report_abs(input, ABS_PRESSURE, 0);
+               input_report_key(input, wdata->tool, 0);
+               input_sync(input);
+               break;
+       case 0xC2: /* Tool report */
+               id = ((data[2] << 4) | (data[3] >> 4) |
+                       ((data[7] & 0x0f) << 20) |
+                       ((data[8] & 0xf0) << 12)) & 0xfffff;
+
+               switch (id) {
+               case 0x802:
+                       wdata->tool = BTN_TOOL_PEN;
+                       break;
+               case 0x80A:
+                       wdata->tool = BTN_TOOL_RUBBER;
+                       break;
+               }
+               break;
+       default: /* Position/pressure report */
+               x = data[2] << 9 | data[3] << 1 | ((data[9] & 0x02) >> 1);
+               y = data[4] << 9 | data[5] << 1 | (data[9] & 0x01);
+               pressure = (data[6] << 3) | ((data[7] & 0xC0) >> 5)
+                       | (data[1] & 0x01);
+
+               input_report_key(input, BTN_TOUCH, pressure > 1);
+
+               input_report_key(input, BTN_STYLUS, data[1] & 0x02);
+               input_report_key(input, BTN_STYLUS2, data[1] & 0x04);
+               input_report_key(input, wdata->tool, 1);
+               input_report_abs(input, ABS_X, x);
+               input_report_abs(input, ABS_Y, y);
+               input_report_abs(input, ABS_PRESSURE, pressure);
+               input_sync(input);
+               break;
+       }
+
+       return;
+}
+
+static void wacom_i4_parse_report(struct hid_device *hdev,
+                       struct wacom_data *wdata,
+                       struct input_dev *input, unsigned char *data)
+{
+       switch (data[0]) {
+       case 0x00: /* Empty report */
+               break;
+       case 0x02: /* Pen report */
+               wacom_i4_parse_pen_report(wdata, input, data);
+               break;
+       case 0x03: /* Features Report */
+               wdata->features = data[2];
+               break;
+       case 0x0C: /* Button report */
+               break;
+       default:
+               hid_err(hdev, "Unknown report: %d,%d\n", data[0], data[1]);
+               break;
+       }
+}
+
+static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
+               u8 *raw_data, int size)
+{
+       struct wacom_data *wdata = hid_get_drvdata(hdev);
+       struct hid_input *hidinput;
+       struct input_dev *input;
+       unsigned char *data = (unsigned char *) raw_data;
+       int i;
+
+       if (!(hdev->claimed & HID_CLAIMED_INPUT))
+               return 0;
+
+       hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
+       input = hidinput->input;
+
+       /* Check if this is a tablet report */
+       if (data[0] != 0x03)
+               return 0;
+
+       switch (hdev->product) {
+       case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
+               return wacom_gr_parse_report(hdev, wdata, input, data);
+               break;
+       case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
+               i = 1;
+
+               switch (data[0]) {
+               case 0x04:
+                       wacom_i4_parse_report(hdev, wdata, input, data + i);
+                       i += 10;
+                       /* fall through */
+               case 0x03:
+                       wacom_i4_parse_report(hdev, wdata, input, data + i);
+                       i += 10;
+                       wacom_i4_parse_report(hdev, wdata, input, data + i);
+                       break;
+               default:
+                       hid_err(hdev, "Unknown report: %d,%d size:%d\n",
+                                       data[0], data[1], size);
+                       return 0;
+               }
+       }
+       return 1;
+}
+
 static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi,
        struct hid_field *field, struct hid_usage *usage, unsigned long **bit,
                                                                int *max)
@@ -338,10 +455,19 @@ static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi,
        __set_bit(BTN_TOOL_RUBBER, input->keybit);
        __set_bit(BTN_TOOL_MOUSE, input->keybit);
 
-       input_set_abs_params(input, ABS_X, 0, 16704, 4, 0);
-       input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0);
-       input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0);
-       input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0);
+       switch (hdev->product) {
+       case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
+               input_set_abs_params(input, ABS_X, 0, 16704, 4, 0);
+               input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0);
+               input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0);
+               input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0);
+               break;
+       case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
+               input_set_abs_params(input, ABS_X, 0, 40640, 4, 0);
+               input_set_abs_params(input, ABS_Y, 0, 25400, 4, 0);
+               input_set_abs_params(input, ABS_PRESSURE, 0, 2047, 0, 0);
+               break;
+       }
 
        return 0;
 }
@@ -378,8 +504,16 @@ static int wacom_probe(struct hid_device *hdev,
                hid_warn(hdev,
                         "can't create sysfs speed attribute err: %d\n", ret);
 
-       /* Set Wacom mode 2 with high reporting speed */
-       wacom_poke(hdev, 1);
+       switch (hdev->product) {
+       case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
+               /* Set Wacom mode 2 with high reporting speed */
+               wacom_poke(hdev, 1);
+               break;
+       case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
+               wdata->features = 0;
+               wacom_set_features(hdev);
+               break;
+       }
 
 #ifdef CONFIG_HID_WACOM_POWER_SUPPLY
        wdata->battery.properties = wacom_battery_props;
@@ -441,6 +575,7 @@ static void wacom_remove(struct hid_device *hdev)
 
 static const struct hid_device_id wacom_devices[] = {
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
 
        { }
 };
diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c
new file mode 100644 (file)
index 0000000..61881b3
--- /dev/null
@@ -0,0 +1,1316 @@
+/*
+ * HID driver for Nintendo Wiimote devices
+ * Copyright (c) 2011 David Herrmann
+ */
+
+/*
+ * 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/completion.h>
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/power_supply.h>
+#include <linux/spinlock.h>
+#include "hid-ids.h"
+#include "hid-wiimote.h"
+
+enum wiiproto_keys {
+       WIIPROTO_KEY_LEFT,
+       WIIPROTO_KEY_RIGHT,
+       WIIPROTO_KEY_UP,
+       WIIPROTO_KEY_DOWN,
+       WIIPROTO_KEY_PLUS,
+       WIIPROTO_KEY_MINUS,
+       WIIPROTO_KEY_ONE,
+       WIIPROTO_KEY_TWO,
+       WIIPROTO_KEY_A,
+       WIIPROTO_KEY_B,
+       WIIPROTO_KEY_HOME,
+       WIIPROTO_KEY_COUNT
+};
+
+static __u16 wiiproto_keymap[] = {
+       KEY_LEFT,       /* WIIPROTO_KEY_LEFT */
+       KEY_RIGHT,      /* WIIPROTO_KEY_RIGHT */
+       KEY_UP,         /* WIIPROTO_KEY_UP */
+       KEY_DOWN,       /* WIIPROTO_KEY_DOWN */
+       KEY_NEXT,       /* WIIPROTO_KEY_PLUS */
+       KEY_PREVIOUS,   /* WIIPROTO_KEY_MINUS */
+       BTN_1,          /* WIIPROTO_KEY_ONE */
+       BTN_2,          /* WIIPROTO_KEY_TWO */
+       BTN_A,          /* WIIPROTO_KEY_A */
+       BTN_B,          /* WIIPROTO_KEY_B */
+       BTN_MODE,       /* WIIPROTO_KEY_HOME */
+};
+
+static enum power_supply_property wiimote_battery_props[] = {
+       POWER_SUPPLY_PROP_CAPACITY
+};
+
+static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
+                                                               size_t count)
+{
+       __u8 *buf;
+       ssize_t ret;
+
+       if (!hdev->hid_output_raw_report)
+               return -ENODEV;
+
+       buf = kmemdup(buffer, count, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = hdev->hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT);
+
+       kfree(buf);
+       return ret;
+}
+
+static void wiimote_worker(struct work_struct *work)
+{
+       struct wiimote_data *wdata = container_of(work, struct wiimote_data,
+                                                                       worker);
+       unsigned long flags;
+
+       spin_lock_irqsave(&wdata->qlock, flags);
+
+       while (wdata->head != wdata->tail) {
+               spin_unlock_irqrestore(&wdata->qlock, flags);
+               wiimote_hid_send(wdata->hdev, wdata->outq[wdata->tail].data,
+                                               wdata->outq[wdata->tail].size);
+               spin_lock_irqsave(&wdata->qlock, flags);
+
+               wdata->tail = (wdata->tail + 1) % WIIMOTE_BUFSIZE;
+       }
+
+       spin_unlock_irqrestore(&wdata->qlock, flags);
+}
+
+static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer,
+                                                               size_t count)
+{
+       unsigned long flags;
+       __u8 newhead;
+
+       if (count > HID_MAX_BUFFER_SIZE) {
+               hid_warn(wdata->hdev, "Sending too large output report\n");
+               return;
+       }
+
+       /*
+        * Copy new request into our output queue and check whether the
+        * queue is full. If it is full, discard this request.
+        * If it is empty we need to start a new worker that will
+        * send out the buffer to the hid device.
+        * If the queue is not empty, then there must be a worker
+        * that is currently sending out our buffer and this worker
+        * will reschedule itself until the queue is empty.
+        */
+
+       spin_lock_irqsave(&wdata->qlock, flags);
+
+       memcpy(wdata->outq[wdata->head].data, buffer, count);
+       wdata->outq[wdata->head].size = count;
+       newhead = (wdata->head + 1) % WIIMOTE_BUFSIZE;
+
+       if (wdata->head == wdata->tail) {
+               wdata->head = newhead;
+               schedule_work(&wdata->worker);
+       } else if (newhead != wdata->tail) {
+               wdata->head = newhead;
+       } else {
+               hid_warn(wdata->hdev, "Output queue is full");
+       }
+
+       spin_unlock_irqrestore(&wdata->qlock, flags);
+}
+
+/*
+ * This sets the rumble bit on the given output report if rumble is
+ * currently enabled.
+ * \cmd1 must point to the second byte in the output report => &cmd[1]
+ * This must be called on nearly every output report before passing it
+ * into the output queue!
+ */
+static inline void wiiproto_keep_rumble(struct wiimote_data *wdata, __u8 *cmd1)
+{
+       if (wdata->state.flags & WIIPROTO_FLAG_RUMBLE)
+               *cmd1 |= 0x01;
+}
+
+static void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble)
+{
+       __u8 cmd[2];
+
+       rumble = !!rumble;
+       if (rumble == !!(wdata->state.flags & WIIPROTO_FLAG_RUMBLE))
+               return;
+
+       if (rumble)
+               wdata->state.flags |= WIIPROTO_FLAG_RUMBLE;
+       else
+               wdata->state.flags &= ~WIIPROTO_FLAG_RUMBLE;
+
+       cmd[0] = WIIPROTO_REQ_RUMBLE;
+       cmd[1] = 0;
+
+       wiiproto_keep_rumble(wdata, &cmd[1]);
+       wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+static void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
+{
+       __u8 cmd[2];
+
+       leds &= WIIPROTO_FLAGS_LEDS;
+       if ((wdata->state.flags & WIIPROTO_FLAGS_LEDS) == leds)
+               return;
+       wdata->state.flags = (wdata->state.flags & ~WIIPROTO_FLAGS_LEDS) | leds;
+
+       cmd[0] = WIIPROTO_REQ_LED;
+       cmd[1] = 0;
+
+       if (leds & WIIPROTO_FLAG_LED1)
+               cmd[1] |= 0x10;
+       if (leds & WIIPROTO_FLAG_LED2)
+               cmd[1] |= 0x20;
+       if (leds & WIIPROTO_FLAG_LED3)
+               cmd[1] |= 0x40;
+       if (leds & WIIPROTO_FLAG_LED4)
+               cmd[1] |= 0x80;
+
+       wiiproto_keep_rumble(wdata, &cmd[1]);
+       wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+/*
+ * Check what peripherals of the wiimote are currently
+ * active and select a proper DRM that supports all of
+ * the requested data inputs.
+ */
+static __u8 select_drm(struct wiimote_data *wdata)
+{
+       __u8 ir = wdata->state.flags & WIIPROTO_FLAGS_IR;
+       bool ext = wiiext_active(wdata);
+
+       if (ir == WIIPROTO_FLAG_IR_BASIC) {
+               if (wdata->state.flags & WIIPROTO_FLAG_ACCEL)
+                       return WIIPROTO_REQ_DRM_KAIE;
+               else
+                       return WIIPROTO_REQ_DRM_KIE;
+       } else if (ir == WIIPROTO_FLAG_IR_EXT) {
+               return WIIPROTO_REQ_DRM_KAI;
+       } else if (ir == WIIPROTO_FLAG_IR_FULL) {
+               return WIIPROTO_REQ_DRM_SKAI1;
+       } else {
+               if (wdata->state.flags & WIIPROTO_FLAG_ACCEL) {
+                       if (ext)
+                               return WIIPROTO_REQ_DRM_KAE;
+                       else
+                               return WIIPROTO_REQ_DRM_KA;
+               } else {
+                       if (ext)
+                               return WIIPROTO_REQ_DRM_KE;
+                       else
+                               return WIIPROTO_REQ_DRM_K;
+               }
+       }
+}
+
+void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm)
+{
+       __u8 cmd[3];
+
+       if (drm == WIIPROTO_REQ_NULL)
+               drm = select_drm(wdata);
+
+       cmd[0] = WIIPROTO_REQ_DRM;
+       cmd[1] = 0;
+       cmd[2] = drm;
+
+       wdata->state.drm = drm;
+       wiiproto_keep_rumble(wdata, &cmd[1]);
+       wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+static void wiiproto_req_status(struct wiimote_data *wdata)
+{
+       __u8 cmd[2];
+
+       cmd[0] = WIIPROTO_REQ_SREQ;
+       cmd[1] = 0;
+
+       wiiproto_keep_rumble(wdata, &cmd[1]);
+       wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+static void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel)
+{
+       accel = !!accel;
+       if (accel == !!(wdata->state.flags & WIIPROTO_FLAG_ACCEL))
+               return;
+
+       if (accel)
+               wdata->state.flags |= WIIPROTO_FLAG_ACCEL;
+       else
+               wdata->state.flags &= ~WIIPROTO_FLAG_ACCEL;
+
+       wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+}
+
+static void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags)
+{
+       __u8 cmd[2];
+
+       cmd[0] = WIIPROTO_REQ_IR1;
+       cmd[1] = flags;
+
+       wiiproto_keep_rumble(wdata, &cmd[1]);
+       wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+static void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags)
+{
+       __u8 cmd[2];
+
+       cmd[0] = WIIPROTO_REQ_IR2;
+       cmd[1] = flags;
+
+       wiiproto_keep_rumble(wdata, &cmd[1]);
+       wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+#define wiiproto_req_wreg(wdata, os, buf, sz) \
+                       wiiproto_req_wmem((wdata), false, (os), (buf), (sz))
+
+#define wiiproto_req_weeprom(wdata, os, buf, sz) \
+                       wiiproto_req_wmem((wdata), true, (os), (buf), (sz))
+
+static void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom,
+                               __u32 offset, const __u8 *buf, __u8 size)
+{
+       __u8 cmd[22];
+
+       if (size > 16 || size == 0) {
+               hid_warn(wdata->hdev, "Invalid length %d wmem request\n", size);
+               return;
+       }
+
+       memset(cmd, 0, sizeof(cmd));
+       cmd[0] = WIIPROTO_REQ_WMEM;
+       cmd[2] = (offset >> 16) & 0xff;
+       cmd[3] = (offset >> 8) & 0xff;
+       cmd[4] = offset & 0xff;
+       cmd[5] = size;
+       memcpy(&cmd[6], buf, size);
+
+       if (!eeprom)
+               cmd[1] |= 0x04;
+
+       wiiproto_keep_rumble(wdata, &cmd[1]);
+       wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+void wiiproto_req_rmem(struct wiimote_data *wdata, bool eeprom, __u32 offset,
+                                                               __u16 size)
+{
+       __u8 cmd[7];
+
+       if (size == 0) {
+               hid_warn(wdata->hdev, "Invalid length %d rmem request\n", size);
+               return;
+       }
+
+       cmd[0] = WIIPROTO_REQ_RMEM;
+       cmd[1] = 0;
+       cmd[2] = (offset >> 16) & 0xff;
+       cmd[3] = (offset >> 8) & 0xff;
+       cmd[4] = offset & 0xff;
+       cmd[5] = (size >> 8) & 0xff;
+       cmd[6] = size & 0xff;
+
+       if (!eeprom)
+               cmd[1] |= 0x04;
+
+       wiiproto_keep_rumble(wdata, &cmd[1]);
+       wiimote_queue(wdata, cmd, sizeof(cmd));
+}
+
+/* requries the cmd-mutex to be held */
+int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
+                                               const __u8 *wmem, __u8 size)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wiimote_cmd_set(wdata, WIIPROTO_REQ_WMEM, 0);
+       wiiproto_req_wreg(wdata, offset, wmem, size);
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       ret = wiimote_cmd_wait(wdata);
+       if (!ret && wdata->state.cmd_err)
+               ret = -EIO;
+
+       return ret;
+}
+
+/* requries the cmd-mutex to be held */
+ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset, __u8 *rmem,
+                                                               __u8 size)
+{
+       unsigned long flags;
+       ssize_t ret;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wdata->state.cmd_read_size = size;
+       wdata->state.cmd_read_buf = rmem;
+       wiimote_cmd_set(wdata, WIIPROTO_REQ_RMEM, offset & 0xffff);
+       wiiproto_req_rreg(wdata, offset, size);
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       ret = wiimote_cmd_wait(wdata);
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wdata->state.cmd_read_buf = NULL;
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       if (!ret) {
+               if (wdata->state.cmd_read_size == 0)
+                       ret = -EIO;
+               else
+                       ret = wdata->state.cmd_read_size;
+       }
+
+       return ret;
+}
+
+static int wiimote_battery_get_property(struct power_supply *psy,
+                                               enum power_supply_property psp,
+                                               union power_supply_propval *val)
+{
+       struct wiimote_data *wdata = container_of(psy,
+                                               struct wiimote_data, battery);
+       int ret = 0, state;
+       unsigned long flags;
+
+       ret = wiimote_cmd_acquire(wdata);
+       if (ret)
+               return ret;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0);
+       wiiproto_req_status(wdata);
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       ret = wiimote_cmd_wait(wdata);
+       state = wdata->state.cmd_battery;
+       wiimote_cmd_release(wdata);
+
+       if (ret)
+               return ret;
+
+       switch (psp) {
+               case POWER_SUPPLY_PROP_CAPACITY:
+                       val->intval = state * 100 / 255;
+                       break;
+               default:
+                       ret = -EINVAL;
+                       break;
+       }
+
+       return ret;
+}
+
+static int wiimote_init_ir(struct wiimote_data *wdata, __u16 mode)
+{
+       int ret;
+       unsigned long flags;
+       __u8 format = 0;
+       static const __u8 data_enable[] = { 0x01 };
+       static const __u8 data_sens1[] = { 0x02, 0x00, 0x00, 0x71, 0x01,
+                                               0x00, 0xaa, 0x00, 0x64 };
+       static const __u8 data_sens2[] = { 0x63, 0x03 };
+       static const __u8 data_fin[] = { 0x08 };
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+
+       if (mode == (wdata->state.flags & WIIPROTO_FLAGS_IR)) {
+               spin_unlock_irqrestore(&wdata->state.lock, flags);
+               return 0;
+       }
+
+       if (mode == 0) {
+               wdata->state.flags &= ~WIIPROTO_FLAGS_IR;
+               wiiproto_req_ir1(wdata, 0);
+               wiiproto_req_ir2(wdata, 0);
+               wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+               spin_unlock_irqrestore(&wdata->state.lock, flags);
+               return 0;
+       }
+
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       ret = wiimote_cmd_acquire(wdata);
+       if (ret)
+               return ret;
+
+       /* send PIXEL CLOCK ENABLE cmd first */
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wiimote_cmd_set(wdata, WIIPROTO_REQ_IR1, 0);
+       wiiproto_req_ir1(wdata, 0x06);
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       ret = wiimote_cmd_wait(wdata);
+       if (ret)
+               goto unlock;
+       if (wdata->state.cmd_err) {
+               ret = -EIO;
+               goto unlock;
+       }
+
+       /* enable IR LOGIC */
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wiimote_cmd_set(wdata, WIIPROTO_REQ_IR2, 0);
+       wiiproto_req_ir2(wdata, 0x06);
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       ret = wiimote_cmd_wait(wdata);
+       if (ret)
+               goto unlock;
+       if (wdata->state.cmd_err) {
+               ret = -EIO;
+               goto unlock;
+       }
+
+       /* enable IR cam but do not make it send data, yet */
+       ret = wiimote_cmd_write(wdata, 0xb00030, data_enable,
+                                                       sizeof(data_enable));
+       if (ret)
+               goto unlock;
+
+       /* write first sensitivity block */
+       ret = wiimote_cmd_write(wdata, 0xb00000, data_sens1,
+                                                       sizeof(data_sens1));
+       if (ret)
+               goto unlock;
+
+       /* write second sensitivity block */
+       ret = wiimote_cmd_write(wdata, 0xb0001a, data_sens2,
+                                                       sizeof(data_sens2));
+       if (ret)
+               goto unlock;
+
+       /* put IR cam into desired state */
+       switch (mode) {
+               case WIIPROTO_FLAG_IR_FULL:
+                       format = 5;
+                       break;
+               case WIIPROTO_FLAG_IR_EXT:
+                       format = 3;
+                       break;
+               case WIIPROTO_FLAG_IR_BASIC:
+                       format = 1;
+                       break;
+       }
+       ret = wiimote_cmd_write(wdata, 0xb00033, &format, sizeof(format));
+       if (ret)
+               goto unlock;
+
+       /* make IR cam send data */
+       ret = wiimote_cmd_write(wdata, 0xb00030, data_fin, sizeof(data_fin));
+       if (ret)
+               goto unlock;
+
+       /* request new DRM mode compatible to IR mode */
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wdata->state.flags &= ~WIIPROTO_FLAGS_IR;
+       wdata->state.flags |= mode & WIIPROTO_FLAGS_IR;
+       wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+unlock:
+       wiimote_cmd_release(wdata);
+       return ret;
+}
+
+static enum led_brightness wiimote_leds_get(struct led_classdev *led_dev)
+{
+       struct wiimote_data *wdata;
+       struct device *dev = led_dev->dev->parent;
+       int i;
+       unsigned long flags;
+       bool value = false;
+
+       wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
+
+       for (i = 0; i < 4; ++i) {
+               if (wdata->leds[i] == led_dev) {
+                       spin_lock_irqsave(&wdata->state.lock, flags);
+                       value = wdata->state.flags & WIIPROTO_FLAG_LED(i + 1);
+                       spin_unlock_irqrestore(&wdata->state.lock, flags);
+                       break;
+               }
+       }
+
+       return value ? LED_FULL : LED_OFF;
+}
+
+static void wiimote_leds_set(struct led_classdev *led_dev,
+                                               enum led_brightness value)
+{
+       struct wiimote_data *wdata;
+       struct device *dev = led_dev->dev->parent;
+       int i;
+       unsigned long flags;
+       __u8 state, flag;
+
+       wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
+
+       for (i = 0; i < 4; ++i) {
+               if (wdata->leds[i] == led_dev) {
+                       flag = WIIPROTO_FLAG_LED(i + 1);
+                       spin_lock_irqsave(&wdata->state.lock, flags);
+                       state = wdata->state.flags;
+                       if (value == LED_OFF)
+                               wiiproto_req_leds(wdata, state & ~flag);
+                       else
+                               wiiproto_req_leds(wdata, state | flag);
+                       spin_unlock_irqrestore(&wdata->state.lock, flags);
+                       break;
+               }
+       }
+}
+
+static int wiimote_ff_play(struct input_dev *dev, void *data,
+                                                       struct ff_effect *eff)
+{
+       struct wiimote_data *wdata = input_get_drvdata(dev);
+       __u8 value;
+       unsigned long flags;
+
+       /*
+        * The wiimote supports only a single rumble motor so if any magnitude
+        * is set to non-zero then we start the rumble motor. If both are set to
+        * zero, we stop the rumble motor.
+        */
+
+       if (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude)
+               value = 1;
+       else
+               value = 0;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wiiproto_req_rumble(wdata, value);
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       return 0;
+}
+
+static int wiimote_input_open(struct input_dev *dev)
+{
+       struct wiimote_data *wdata = input_get_drvdata(dev);
+
+       return hid_hw_open(wdata->hdev);
+}
+
+static void wiimote_input_close(struct input_dev *dev)
+{
+       struct wiimote_data *wdata = input_get_drvdata(dev);
+
+       hid_hw_close(wdata->hdev);
+}
+
+static int wiimote_accel_open(struct input_dev *dev)
+{
+       struct wiimote_data *wdata = input_get_drvdata(dev);
+       int ret;
+       unsigned long flags;
+
+       ret = hid_hw_open(wdata->hdev);
+       if (ret)
+               return ret;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wiiproto_req_accel(wdata, true);
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       return 0;
+}
+
+static void wiimote_accel_close(struct input_dev *dev)
+{
+       struct wiimote_data *wdata = input_get_drvdata(dev);
+       unsigned long flags;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wiiproto_req_accel(wdata, false);
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       hid_hw_close(wdata->hdev);
+}
+
+static int wiimote_ir_open(struct input_dev *dev)
+{
+       struct wiimote_data *wdata = input_get_drvdata(dev);
+       int ret;
+
+       ret = hid_hw_open(wdata->hdev);
+       if (ret)
+               return ret;
+
+       ret = wiimote_init_ir(wdata, WIIPROTO_FLAG_IR_BASIC);
+       if (ret) {
+               hid_hw_close(wdata->hdev);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void wiimote_ir_close(struct input_dev *dev)
+{
+       struct wiimote_data *wdata = input_get_drvdata(dev);
+
+       wiimote_init_ir(wdata, 0);
+       hid_hw_close(wdata->hdev);
+}
+
+static void handler_keys(struct wiimote_data *wdata, const __u8 *payload)
+{
+       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_LEFT],
+                                                       !!(payload[0] & 0x01));
+       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_RIGHT],
+                                                       !!(payload[0] & 0x02));
+       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_DOWN],
+                                                       !!(payload[0] & 0x04));
+       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_UP],
+                                                       !!(payload[0] & 0x08));
+       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_PLUS],
+                                                       !!(payload[0] & 0x10));
+       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_TWO],
+                                                       !!(payload[1] & 0x01));
+       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_ONE],
+                                                       !!(payload[1] & 0x02));
+       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_B],
+                                                       !!(payload[1] & 0x04));
+       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_A],
+                                                       !!(payload[1] & 0x08));
+       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_MINUS],
+                                                       !!(payload[1] & 0x10));
+       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_HOME],
+                                                       !!(payload[1] & 0x80));
+       input_sync(wdata->input);
+}
+
+static void handler_accel(struct wiimote_data *wdata, const __u8 *payload)
+{
+       __u16 x, y, z;
+
+       if (!(wdata->state.flags & WIIPROTO_FLAG_ACCEL))
+               return;
+
+       /*
+        * payload is: BB BB XX YY ZZ
+        * Accelerometer data is encoded into 3 10bit values. XX, YY and ZZ
+        * contain the upper 8 bits of each value. The lower 2 bits are
+        * contained in the buttons data BB BB.
+        * Bits 6 and 7 of the first buttons byte BB is the lower 2 bits of the
+        * X accel value. Bit 5 of the second buttons byte is the 2nd bit of Y
+        * accel value and bit 6 is the second bit of the Z value.
+        * The first bit of Y and Z values is not available and always set to 0.
+        * 0x200 is returned on no movement.
+        */
+
+       x = payload[2] << 2;
+       y = payload[3] << 2;
+       z = payload[4] << 2;
+
+       x |= (payload[0] >> 5) & 0x3;
+       y |= (payload[1] >> 4) & 0x2;
+       z |= (payload[1] >> 5) & 0x2;
+
+       input_report_abs(wdata->accel, ABS_RX, x - 0x200);
+       input_report_abs(wdata->accel, ABS_RY, y - 0x200);
+       input_report_abs(wdata->accel, ABS_RZ, z - 0x200);
+       input_sync(wdata->accel);
+}
+
+#define ir_to_input0(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
+                                                       ABS_HAT0X, ABS_HAT0Y)
+#define ir_to_input1(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
+                                                       ABS_HAT1X, ABS_HAT1Y)
+#define ir_to_input2(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
+                                                       ABS_HAT2X, ABS_HAT2Y)
+#define ir_to_input3(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
+                                                       ABS_HAT3X, ABS_HAT3Y)
+
+static void __ir_to_input(struct wiimote_data *wdata, const __u8 *ir,
+                                               bool packed, __u8 xid, __u8 yid)
+{
+       __u16 x, y;
+
+       if (!(wdata->state.flags & WIIPROTO_FLAGS_IR))
+               return;
+
+       /*
+        * Basic IR data is encoded into 3 bytes. The first two bytes are the
+        * upper 8 bit of the X/Y data, the 3rd byte contains the lower 2 bits
+        * of both.
+        * If data is packed, then the 3rd byte is put first and slightly
+        * reordered. This allows to interleave packed and non-packed data to
+        * have two IR sets in 5 bytes instead of 6.
+        * The resulting 10bit X/Y values are passed to the ABS_HATXY input dev.
+        */
+
+       if (packed) {
+               x = ir[1] << 2;
+               y = ir[2] << 2;
+
+               x |= ir[0] & 0x3;
+               y |= (ir[0] >> 2) & 0x3;
+       } else {
+               x = ir[0] << 2;
+               y = ir[1] << 2;
+
+               x |= (ir[2] >> 4) & 0x3;
+               y |= (ir[2] >> 6) & 0x3;
+       }
+
+       input_report_abs(wdata->ir, xid, x);
+       input_report_abs(wdata->ir, yid, y);
+}
+
+static void handler_status(struct wiimote_data *wdata, const __u8 *payload)
+{
+       handler_keys(wdata, payload);
+
+       /* on status reports the drm is reset so we need to resend the drm */
+       wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
+
+       wiiext_event(wdata, payload[2] & 0x02);
+
+       if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0)) {
+               wdata->state.cmd_battery = payload[5];
+               wiimote_cmd_complete(wdata);
+       }
+}
+
+static void handler_data(struct wiimote_data *wdata, const __u8 *payload)
+{
+       __u16 offset = payload[3] << 8 | payload[4];
+       __u8 size = (payload[2] >> 4) + 1;
+       __u8 err = payload[2] & 0x0f;
+
+       handler_keys(wdata, payload);
+
+       if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_RMEM, offset)) {
+               if (err)
+                       size = 0;
+               else if (size > wdata->state.cmd_read_size)
+                       size = wdata->state.cmd_read_size;
+
+               wdata->state.cmd_read_size = size;
+               if (wdata->state.cmd_read_buf)
+                       memcpy(wdata->state.cmd_read_buf, &payload[5], size);
+               wiimote_cmd_complete(wdata);
+       }
+}
+
+static void handler_return(struct wiimote_data *wdata, const __u8 *payload)
+{
+       __u8 err = payload[3];
+       __u8 cmd = payload[2];
+
+       handler_keys(wdata, payload);
+
+       if (wiimote_cmd_pending(wdata, cmd, 0)) {
+               wdata->state.cmd_err = err;
+               wiimote_cmd_complete(wdata);
+       } else if (err) {
+               hid_warn(wdata->hdev, "Remote error %hhu on req %hhu\n", err,
+                                                                       cmd);
+       }
+}
+
+static void handler_drm_KA(struct wiimote_data *wdata, const __u8 *payload)
+{
+       handler_keys(wdata, payload);
+       handler_accel(wdata, payload);
+}
+
+static void handler_drm_KE(struct wiimote_data *wdata, const __u8 *payload)
+{
+       handler_keys(wdata, payload);
+       wiiext_handle(wdata, &payload[2]);
+}
+
+static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload)
+{
+       handler_keys(wdata, payload);
+       handler_accel(wdata, payload);
+       ir_to_input0(wdata, &payload[5], false);
+       ir_to_input1(wdata, &payload[8], false);
+       ir_to_input2(wdata, &payload[11], false);
+       ir_to_input3(wdata, &payload[14], false);
+       input_sync(wdata->ir);
+}
+
+static void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload)
+{
+       handler_keys(wdata, payload);
+       wiiext_handle(wdata, &payload[2]);
+}
+
+static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload)
+{
+       handler_keys(wdata, payload);
+       ir_to_input0(wdata, &payload[2], false);
+       ir_to_input1(wdata, &payload[4], true);
+       ir_to_input2(wdata, &payload[7], false);
+       ir_to_input3(wdata, &payload[9], true);
+       input_sync(wdata->ir);
+       wiiext_handle(wdata, &payload[12]);
+}
+
+static void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload)
+{
+       handler_keys(wdata, payload);
+       handler_accel(wdata, payload);
+       wiiext_handle(wdata, &payload[5]);
+}
+
+static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload)
+{
+       handler_keys(wdata, payload);
+       handler_accel(wdata, payload);
+       ir_to_input0(wdata, &payload[5], false);
+       ir_to_input1(wdata, &payload[7], true);
+       ir_to_input2(wdata, &payload[10], false);
+       ir_to_input3(wdata, &payload[12], true);
+       input_sync(wdata->ir);
+       wiiext_handle(wdata, &payload[15]);
+}
+
+static void handler_drm_E(struct wiimote_data *wdata, const __u8 *payload)
+{
+       wiiext_handle(wdata, payload);
+}
+
+static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload)
+{
+       handler_keys(wdata, payload);
+
+       wdata->state.accel_split[0] = payload[2];
+       wdata->state.accel_split[1] = (payload[0] >> 1) & (0x10 | 0x20);
+       wdata->state.accel_split[1] |= (payload[1] << 1) & (0x40 | 0x80);
+
+       ir_to_input0(wdata, &payload[3], false);
+       ir_to_input1(wdata, &payload[12], false);
+       input_sync(wdata->ir);
+}
+
+static void handler_drm_SKAI2(struct wiimote_data *wdata, const __u8 *payload)
+{
+       __u8 buf[5];
+
+       handler_keys(wdata, payload);
+
+       wdata->state.accel_split[1] |= (payload[0] >> 5) & (0x01 | 0x02);
+       wdata->state.accel_split[1] |= (payload[1] >> 3) & (0x04 | 0x08);
+
+       buf[0] = 0;
+       buf[1] = 0;
+       buf[2] = wdata->state.accel_split[0];
+       buf[3] = payload[2];
+       buf[4] = wdata->state.accel_split[1];
+       handler_accel(wdata, buf);
+
+       ir_to_input2(wdata, &payload[3], false);
+       ir_to_input3(wdata, &payload[12], false);
+       input_sync(wdata->ir);
+}
+
+struct wiiproto_handler {
+       __u8 id;
+       size_t size;
+       void (*func)(struct wiimote_data *wdata, const __u8 *payload);
+};
+
+static struct wiiproto_handler handlers[] = {
+       { .id = WIIPROTO_REQ_STATUS, .size = 6, .func = handler_status },
+       { .id = WIIPROTO_REQ_DATA, .size = 21, .func = handler_data },
+       { .id = WIIPROTO_REQ_RETURN, .size = 4, .func = handler_return },
+       { .id = WIIPROTO_REQ_DRM_K, .size = 2, .func = handler_keys },
+       { .id = WIIPROTO_REQ_DRM_KA, .size = 5, .func = handler_drm_KA },
+       { .id = WIIPROTO_REQ_DRM_KE, .size = 10, .func = handler_drm_KE },
+       { .id = WIIPROTO_REQ_DRM_KAI, .size = 17, .func = handler_drm_KAI },
+       { .id = WIIPROTO_REQ_DRM_KEE, .size = 21, .func = handler_drm_KEE },
+       { .id = WIIPROTO_REQ_DRM_KAE, .size = 21, .func = handler_drm_KAE },
+       { .id = WIIPROTO_REQ_DRM_KIE, .size = 21, .func = handler_drm_KIE },
+       { .id = WIIPROTO_REQ_DRM_KAIE, .size = 21, .func = handler_drm_KAIE },
+       { .id = WIIPROTO_REQ_DRM_E, .size = 21, .func = handler_drm_E },
+       { .id = WIIPROTO_REQ_DRM_SKAI1, .size = 21, .func = handler_drm_SKAI1 },
+       { .id = WIIPROTO_REQ_DRM_SKAI2, .size = 21, .func = handler_drm_SKAI2 },
+       { .id = 0 }
+};
+
+static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report,
+                                                       u8 *raw_data, int size)
+{
+       struct wiimote_data *wdata = hid_get_drvdata(hdev);
+       struct wiiproto_handler *h;
+       int i;
+       unsigned long flags;
+       bool handled = false;
+
+       if (size < 1)
+               return -EINVAL;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+
+       for (i = 0; handlers[i].id; ++i) {
+               h = &handlers[i];
+               if (h->id == raw_data[0] && h->size < size) {
+                       h->func(wdata, &raw_data[1]);
+                       handled = true;
+               }
+       }
+
+       if (!handled)
+               hid_warn(hdev, "Unhandled report %hhu size %d\n", raw_data[0],
+                                                                       size);
+
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       return 0;
+}
+
+static void wiimote_leds_destroy(struct wiimote_data *wdata)
+{
+       int i;
+       struct led_classdev *led;
+
+       for (i = 0; i < 4; ++i) {
+               if (wdata->leds[i]) {
+                       led = wdata->leds[i];
+                       wdata->leds[i] = NULL;
+                       led_classdev_unregister(led);
+                       kfree(led);
+               }
+       }
+}
+
+static int wiimote_leds_create(struct wiimote_data *wdata)
+{
+       int i, ret;
+       struct device *dev = &wdata->hdev->dev;
+       size_t namesz = strlen(dev_name(dev)) + 9;
+       struct led_classdev *led;
+       char *name;
+
+       for (i = 0; i < 4; ++i) {
+               led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL);
+               if (!led) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
+               name = (void*)&led[1];
+               snprintf(name, namesz, "%s:blue:p%d", dev_name(dev), i);
+               led->name = name;
+               led->brightness = 0;
+               led->max_brightness = 1;
+               led->brightness_get = wiimote_leds_get;
+               led->brightness_set = wiimote_leds_set;
+
+               ret = led_classdev_register(dev, led);
+               if (ret) {
+                       kfree(led);
+                       goto err;
+               }
+               wdata->leds[i] = led;
+       }
+
+       return 0;
+
+err:
+       wiimote_leds_destroy(wdata);
+       return ret;
+}
+
+static struct wiimote_data *wiimote_create(struct hid_device *hdev)
+{
+       struct wiimote_data *wdata;
+       int i;
+
+       wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
+       if (!wdata)
+               return NULL;
+
+       wdata->input = input_allocate_device();
+       if (!wdata->input)
+               goto err;
+
+       wdata->hdev = hdev;
+       hid_set_drvdata(hdev, wdata);
+
+       input_set_drvdata(wdata->input, wdata);
+       wdata->input->open = wiimote_input_open;
+       wdata->input->close = wiimote_input_close;
+       wdata->input->dev.parent = &wdata->hdev->dev;
+       wdata->input->id.bustype = wdata->hdev->bus;
+       wdata->input->id.vendor = wdata->hdev->vendor;
+       wdata->input->id.product = wdata->hdev->product;
+       wdata->input->id.version = wdata->hdev->version;
+       wdata->input->name = WIIMOTE_NAME;
+
+       set_bit(EV_KEY, wdata->input->evbit);
+       for (i = 0; i < WIIPROTO_KEY_COUNT; ++i)
+               set_bit(wiiproto_keymap[i], wdata->input->keybit);
+
+       set_bit(FF_RUMBLE, wdata->input->ffbit);
+       if (input_ff_create_memless(wdata->input, NULL, wiimote_ff_play))
+               goto err_input;
+
+       wdata->accel = input_allocate_device();
+       if (!wdata->accel)
+               goto err_input;
+
+       input_set_drvdata(wdata->accel, wdata);
+       wdata->accel->open = wiimote_accel_open;
+       wdata->accel->close = wiimote_accel_close;
+       wdata->accel->dev.parent = &wdata->hdev->dev;
+       wdata->accel->id.bustype = wdata->hdev->bus;
+       wdata->accel->id.vendor = wdata->hdev->vendor;
+       wdata->accel->id.product = wdata->hdev->product;
+       wdata->accel->id.version = wdata->hdev->version;
+       wdata->accel->name = WIIMOTE_NAME " Accelerometer";
+
+       set_bit(EV_ABS, wdata->accel->evbit);
+       set_bit(ABS_RX, wdata->accel->absbit);
+       set_bit(ABS_RY, wdata->accel->absbit);
+       set_bit(ABS_RZ, wdata->accel->absbit);
+       input_set_abs_params(wdata->accel, ABS_RX, -500, 500, 2, 4);
+       input_set_abs_params(wdata->accel, ABS_RY, -500, 500, 2, 4);
+       input_set_abs_params(wdata->accel, ABS_RZ, -500, 500, 2, 4);
+
+       wdata->ir = input_allocate_device();
+       if (!wdata->ir)
+               goto err_ir;
+
+       input_set_drvdata(wdata->ir, wdata);
+       wdata->ir->open = wiimote_ir_open;
+       wdata->ir->close = wiimote_ir_close;
+       wdata->ir->dev.parent = &wdata->hdev->dev;
+       wdata->ir->id.bustype = wdata->hdev->bus;
+       wdata->ir->id.vendor = wdata->hdev->vendor;
+       wdata->ir->id.product = wdata->hdev->product;
+       wdata->ir->id.version = wdata->hdev->version;
+       wdata->ir->name = WIIMOTE_NAME " IR";
+
+       set_bit(EV_ABS, wdata->ir->evbit);
+       set_bit(ABS_HAT0X, wdata->ir->absbit);
+       set_bit(ABS_HAT0Y, wdata->ir->absbit);
+       set_bit(ABS_HAT1X, wdata->ir->absbit);
+       set_bit(ABS_HAT1Y, wdata->ir->absbit);
+       set_bit(ABS_HAT2X, wdata->ir->absbit);
+       set_bit(ABS_HAT2Y, wdata->ir->absbit);
+       set_bit(ABS_HAT3X, wdata->ir->absbit);
+       set_bit(ABS_HAT3Y, wdata->ir->absbit);
+       input_set_abs_params(wdata->ir, ABS_HAT0X, 0, 1023, 2, 4);
+       input_set_abs_params(wdata->ir, ABS_HAT0Y, 0, 767, 2, 4);
+       input_set_abs_params(wdata->ir, ABS_HAT1X, 0, 1023, 2, 4);
+       input_set_abs_params(wdata->ir, ABS_HAT1Y, 0, 767, 2, 4);
+       input_set_abs_params(wdata->ir, ABS_HAT2X, 0, 1023, 2, 4);
+       input_set_abs_params(wdata->ir, ABS_HAT2Y, 0, 767, 2, 4);
+       input_set_abs_params(wdata->ir, ABS_HAT3X, 0, 1023, 2, 4);
+       input_set_abs_params(wdata->ir, ABS_HAT3Y, 0, 767, 2, 4);
+
+       spin_lock_init(&wdata->qlock);
+       INIT_WORK(&wdata->worker, wiimote_worker);
+
+       spin_lock_init(&wdata->state.lock);
+       init_completion(&wdata->state.ready);
+       mutex_init(&wdata->state.sync);
+       wdata->state.drm = WIIPROTO_REQ_DRM_K;
+
+       return wdata;
+
+err_ir:
+       input_free_device(wdata->accel);
+err_input:
+       input_free_device(wdata->input);
+err:
+       kfree(wdata);
+       return NULL;
+}
+
+static void wiimote_destroy(struct wiimote_data *wdata)
+{
+       wiidebug_deinit(wdata);
+       wiiext_deinit(wdata);
+       wiimote_leds_destroy(wdata);
+
+       power_supply_unregister(&wdata->battery);
+       input_unregister_device(wdata->accel);
+       input_unregister_device(wdata->ir);
+       input_unregister_device(wdata->input);
+       cancel_work_sync(&wdata->worker);
+       hid_hw_stop(wdata->hdev);
+
+       kfree(wdata);
+}
+
+static int wiimote_hid_probe(struct hid_device *hdev,
+                               const struct hid_device_id *id)
+{
+       struct wiimote_data *wdata;
+       int ret;
+
+       hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
+
+       wdata = wiimote_create(hdev);
+       if (!wdata) {
+               hid_err(hdev, "Can't alloc device\n");
+               return -ENOMEM;
+       }
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               hid_err(hdev, "HID parse failed\n");
+               goto err;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+       if (ret) {
+               hid_err(hdev, "HW start failed\n");
+               goto err;
+       }
+
+       ret = input_register_device(wdata->accel);
+       if (ret) {
+               hid_err(hdev, "Cannot register input device\n");
+               goto err_stop;
+       }
+
+       ret = input_register_device(wdata->ir);
+       if (ret) {
+               hid_err(hdev, "Cannot register input device\n");
+               goto err_ir;
+       }
+
+       ret = input_register_device(wdata->input);
+       if (ret) {
+               hid_err(hdev, "Cannot register input device\n");
+               goto err_input;
+       }
+
+       wdata->battery.properties = wiimote_battery_props;
+       wdata->battery.num_properties = ARRAY_SIZE(wiimote_battery_props);
+       wdata->battery.get_property = wiimote_battery_get_property;
+       wdata->battery.name = "wiimote_battery";
+       wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+       wdata->battery.use_for_apm = 0;
+
+       ret = power_supply_register(&wdata->hdev->dev, &wdata->battery);
+       if (ret) {
+               hid_err(hdev, "Cannot register battery device\n");
+               goto err_battery;
+       }
+
+       ret = wiimote_leds_create(wdata);
+       if (ret)
+               goto err_free;
+
+       ret = wiiext_init(wdata);
+       if (ret)
+               goto err_free;
+
+       ret = wiidebug_init(wdata);
+       if (ret)
+               goto err_free;
+
+       hid_info(hdev, "New device registered\n");
+
+       /* by default set led1 after device initialization */
+       spin_lock_irq(&wdata->state.lock);
+       wiiproto_req_leds(wdata, WIIPROTO_FLAG_LED1);
+       spin_unlock_irq(&wdata->state.lock);
+
+       return 0;
+
+err_free:
+       wiimote_destroy(wdata);
+       return ret;
+
+err_battery:
+       input_unregister_device(wdata->input);
+       wdata->input = NULL;
+err_input:
+       input_unregister_device(wdata->ir);
+       wdata->ir = NULL;
+err_ir:
+       input_unregister_device(wdata->accel);
+       wdata->accel = NULL;
+err_stop:
+       hid_hw_stop(hdev);
+err:
+       input_free_device(wdata->ir);
+       input_free_device(wdata->accel);
+       input_free_device(wdata->input);
+       kfree(wdata);
+       return ret;
+}
+
+static void wiimote_hid_remove(struct hid_device *hdev)
+{
+       struct wiimote_data *wdata = hid_get_drvdata(hdev);
+
+       hid_info(hdev, "Device removed\n");
+       wiimote_destroy(wdata);
+}
+
+static const struct hid_device_id wiimote_hid_devices[] = {
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
+                               USB_DEVICE_ID_NINTENDO_WIIMOTE) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, wiimote_hid_devices);
+
+static struct hid_driver wiimote_hid_driver = {
+       .name = "wiimote",
+       .id_table = wiimote_hid_devices,
+       .probe = wiimote_hid_probe,
+       .remove = wiimote_hid_remove,
+       .raw_event = wiimote_hid_event,
+};
+
+static int __init wiimote_init(void)
+{
+       int ret;
+
+       ret = hid_register_driver(&wiimote_hid_driver);
+       if (ret)
+               pr_err("Can't register wiimote hid driver\n");
+
+       return ret;
+}
+
+static void __exit wiimote_exit(void)
+{
+       hid_unregister_driver(&wiimote_hid_driver);
+}
+
+module_init(wiimote_init);
+module_exit(wiimote_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
+MODULE_DESCRIPTION(WIIMOTE_NAME " Device Driver");
diff --git a/drivers/hid/hid-wiimote-debug.c b/drivers/hid/hid-wiimote-debug.c
new file mode 100644 (file)
index 0000000..17dabc1
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Debug support for HID Nintendo Wiimote devices
+ * Copyright (c) 2011 David Herrmann
+ */
+
+/*
+ * 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/debugfs.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include "hid-wiimote.h"
+
+struct wiimote_debug {
+       struct wiimote_data *wdata;
+       struct dentry *eeprom;
+       struct dentry *drm;
+};
+
+static int wiidebug_eeprom_open(struct inode *i, struct file *f)
+{
+       f->private_data = i->i_private;
+       return 0;
+}
+
+static ssize_t wiidebug_eeprom_read(struct file *f, char __user *u, size_t s,
+                                                               loff_t *off)
+{
+       struct wiimote_debug *dbg = f->private_data;
+       struct wiimote_data *wdata = dbg->wdata;
+       unsigned long flags;
+       ssize_t ret;
+       char buf[16];
+       __u16 size;
+
+       if (s == 0)
+               return -EINVAL;
+       if (*off > 0xffffff)
+               return 0;
+       if (s > 16)
+               s = 16;
+
+       ret = wiimote_cmd_acquire(wdata);
+       if (ret)
+               return ret;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wdata->state.cmd_read_size = s;
+       wdata->state.cmd_read_buf = buf;
+       wiimote_cmd_set(wdata, WIIPROTO_REQ_RMEM, *off & 0xffff);
+       wiiproto_req_reeprom(wdata, *off, s);
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       ret = wiimote_cmd_wait(wdata);
+       if (!ret)
+               size = wdata->state.cmd_read_size;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wdata->state.cmd_read_buf = NULL;
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       wiimote_cmd_release(wdata);
+
+       if (ret)
+               return ret;
+       else if (size == 0)
+               return -EIO;
+
+       if (copy_to_user(u, buf, size))
+               return -EFAULT;
+
+       *off += size;
+       ret = size;
+
+       return ret;
+}
+
+static const struct file_operations wiidebug_eeprom_fops = {
+       .owner = THIS_MODULE,
+       .open = wiidebug_eeprom_open,
+       .read = wiidebug_eeprom_read,
+       .llseek = generic_file_llseek,
+};
+
+static const char *wiidebug_drmmap[] = {
+       [WIIPROTO_REQ_NULL] = "NULL",
+       [WIIPROTO_REQ_DRM_K] = "K",
+       [WIIPROTO_REQ_DRM_KA] = "KA",
+       [WIIPROTO_REQ_DRM_KE] = "KE",
+       [WIIPROTO_REQ_DRM_KAI] = "KAI",
+       [WIIPROTO_REQ_DRM_KEE] = "KEE",
+       [WIIPROTO_REQ_DRM_KAE] = "KAE",
+       [WIIPROTO_REQ_DRM_KIE] = "KIE",
+       [WIIPROTO_REQ_DRM_KAIE] = "KAIE",
+       [WIIPROTO_REQ_DRM_E] = "E",
+       [WIIPROTO_REQ_DRM_SKAI1] = "SKAI1",
+       [WIIPROTO_REQ_DRM_SKAI2] = "SKAI2",
+       [WIIPROTO_REQ_MAX] = NULL
+};
+
+static int wiidebug_drm_show(struct seq_file *f, void *p)
+{
+       struct wiimote_debug *dbg = f->private;
+       const char *str = NULL;
+       unsigned long flags;
+       __u8 drm;
+
+       spin_lock_irqsave(&dbg->wdata->state.lock, flags);
+       drm = dbg->wdata->state.drm;
+       spin_unlock_irqrestore(&dbg->wdata->state.lock, flags);
+
+       if (drm < WIIPROTO_REQ_MAX)
+               str = wiidebug_drmmap[drm];
+       if (!str)
+               str = "unknown";
+
+       seq_printf(f, "%s\n", str);
+
+       return 0;
+}
+
+static int wiidebug_drm_open(struct inode *i, struct file *f)
+{
+       return single_open(f, wiidebug_drm_show, i->i_private);
+}
+
+static ssize_t wiidebug_drm_write(struct file *f, const char __user *u,
+                                                       size_t s, loff_t *off)
+{
+       struct wiimote_debug *dbg = f->private_data;
+       unsigned long flags;
+       char buf[16];
+       ssize_t len;
+       int i;
+
+       if (s == 0)
+               return -EINVAL;
+
+       len = min((size_t) 15, s);
+       if (copy_from_user(buf, u, len))
+               return -EFAULT;
+
+       buf[15] = 0;
+
+       for (i = 0; i < WIIPROTO_REQ_MAX; ++i) {
+               if (!wiidebug_drmmap[i])
+                       continue;
+               if (!strcasecmp(buf, wiidebug_drmmap[i]))
+                       break;
+       }
+
+       if (i == WIIPROTO_REQ_MAX)
+               i = simple_strtoul(buf, NULL, 10);
+
+       spin_lock_irqsave(&dbg->wdata->state.lock, flags);
+       wiiproto_req_drm(dbg->wdata, (__u8) i);
+       spin_unlock_irqrestore(&dbg->wdata->state.lock, flags);
+
+       return len;
+}
+
+static const struct file_operations wiidebug_drm_fops = {
+       .owner = THIS_MODULE,
+       .open = wiidebug_drm_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .write = wiidebug_drm_write,
+       .release = single_release,
+};
+
+int wiidebug_init(struct wiimote_data *wdata)
+{
+       struct wiimote_debug *dbg;
+       unsigned long flags;
+       int ret = -ENOMEM;
+
+       dbg = kzalloc(sizeof(*dbg), GFP_KERNEL);
+       if (!dbg)
+               return -ENOMEM;
+
+       dbg->wdata = wdata;
+
+       dbg->eeprom = debugfs_create_file("eeprom", S_IRUSR,
+               dbg->wdata->hdev->debug_dir, dbg, &wiidebug_eeprom_fops);
+       if (!dbg->eeprom)
+               goto err;
+
+       dbg->drm = debugfs_create_file("drm", S_IRUSR,
+                       dbg->wdata->hdev->debug_dir, dbg, &wiidebug_drm_fops);
+       if (!dbg->drm)
+               goto err_drm;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wdata->debug = dbg;
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       return 0;
+
+err_drm:
+       debugfs_remove(dbg->eeprom);
+err:
+       kfree(dbg);
+       return ret;
+}
+
+void wiidebug_deinit(struct wiimote_data *wdata)
+{
+       struct wiimote_debug *dbg = wdata->debug;
+       unsigned long flags;
+
+       if (!dbg)
+               return;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wdata->debug = NULL;
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       debugfs_remove(dbg->drm);
+       debugfs_remove(dbg->eeprom);
+       kfree(dbg);
+}
diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c
new file mode 100644 (file)
index 0000000..aa95870
--- /dev/null
@@ -0,0 +1,752 @@
+/*
+ * HID driver for Nintendo Wiimote extension devices
+ * Copyright (c) 2011 David Herrmann
+ */
+
+/*
+ * 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/atomic.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include "hid-wiimote.h"
+
+struct wiimote_ext {
+       struct wiimote_data *wdata;
+       struct work_struct worker;
+       struct input_dev *input;
+       struct input_dev *mp_input;
+
+       atomic_t opened;
+       atomic_t mp_opened;
+       bool plugged;
+       bool mp_plugged;
+       bool motionp;
+       __u8 ext_type;
+};
+
+enum wiiext_type {
+       WIIEXT_NONE,            /* placeholder */
+       WIIEXT_CLASSIC,         /* Nintendo classic controller */
+       WIIEXT_NUNCHUCK,        /* Nintendo nunchuck controller */
+};
+
+enum wiiext_keys {
+       WIIEXT_KEY_C,
+       WIIEXT_KEY_Z,
+       WIIEXT_KEY_A,
+       WIIEXT_KEY_B,
+       WIIEXT_KEY_X,
+       WIIEXT_KEY_Y,
+       WIIEXT_KEY_ZL,
+       WIIEXT_KEY_ZR,
+       WIIEXT_KEY_PLUS,
+       WIIEXT_KEY_MINUS,
+       WIIEXT_KEY_HOME,
+       WIIEXT_KEY_LEFT,
+       WIIEXT_KEY_RIGHT,
+       WIIEXT_KEY_UP,
+       WIIEXT_KEY_DOWN,
+       WIIEXT_KEY_LT,
+       WIIEXT_KEY_RT,
+       WIIEXT_KEY_COUNT
+};
+
+static __u16 wiiext_keymap[] = {
+       BTN_C,          /* WIIEXT_KEY_C */
+       BTN_Z,          /* WIIEXT_KEY_Z */
+       BTN_A,          /* WIIEXT_KEY_A */
+       BTN_B,          /* WIIEXT_KEY_B */
+       BTN_X,          /* WIIEXT_KEY_X */
+       BTN_Y,          /* WIIEXT_KEY_Y */
+       BTN_TL2,        /* WIIEXT_KEY_ZL */
+       BTN_TR2,        /* WIIEXT_KEY_ZR */
+       KEY_NEXT,       /* WIIEXT_KEY_PLUS */
+       KEY_PREVIOUS,   /* WIIEXT_KEY_MINUS */
+       BTN_MODE,       /* WIIEXT_KEY_HOME */
+       KEY_LEFT,       /* WIIEXT_KEY_LEFT */
+       KEY_RIGHT,      /* WIIEXT_KEY_RIGHT */
+       KEY_UP,         /* WIIEXT_KEY_UP */
+       KEY_DOWN,       /* WIIEXT_KEY_DOWN */
+       BTN_TL,         /* WIIEXT_KEY_LT */
+       BTN_TR,         /* WIIEXT_KEY_RT */
+};
+
+/* diable all extensions */
+static void ext_disable(struct wiimote_ext *ext)
+{
+       unsigned long flags;
+       __u8 wmem = 0x55;
+
+       if (!wiimote_cmd_acquire(ext->wdata)) {
+               wiimote_cmd_write(ext->wdata, 0xa400f0, &wmem, sizeof(wmem));
+               wiimote_cmd_release(ext->wdata);
+       }
+
+       spin_lock_irqsave(&ext->wdata->state.lock, flags);
+       ext->motionp = false;
+       ext->ext_type = WIIEXT_NONE;
+       wiiproto_req_drm(ext->wdata, WIIPROTO_REQ_NULL);
+       spin_unlock_irqrestore(&ext->wdata->state.lock, flags);
+}
+
+static bool motionp_read(struct wiimote_ext *ext)
+{
+       __u8 rmem[2], wmem;
+       ssize_t ret;
+       bool avail = false;
+
+       if (!atomic_read(&ext->mp_opened))
+               return false;
+
+       if (wiimote_cmd_acquire(ext->wdata))
+               return false;
+
+       /* initialize motion plus */
+       wmem = 0x55;
+       ret = wiimote_cmd_write(ext->wdata, 0xa600f0, &wmem, sizeof(wmem));
+       if (ret)
+               goto error;
+
+       /* read motion plus ID */
+       ret = wiimote_cmd_read(ext->wdata, 0xa600fe, rmem, 2);
+       if (ret == 2 || rmem[1] == 0x5)
+               avail = true;
+
+error:
+       wiimote_cmd_release(ext->wdata);
+       return avail;
+}
+
+static __u8 ext_read(struct wiimote_ext *ext)
+{
+       ssize_t ret;
+       __u8 rmem[2], wmem;
+       __u8 type = WIIEXT_NONE;
+
+       if (!ext->plugged || !atomic_read(&ext->opened))
+               return WIIEXT_NONE;
+
+       if (wiimote_cmd_acquire(ext->wdata))
+               return WIIEXT_NONE;
+
+       /* initialize extension */
+       wmem = 0x55;
+       ret = wiimote_cmd_write(ext->wdata, 0xa400f0, &wmem, sizeof(wmem));
+       if (!ret) {
+               /* disable encryption */
+               wmem = 0x0;
+               wiimote_cmd_write(ext->wdata, 0xa400fb, &wmem, sizeof(wmem));
+       }
+
+       /* read extension ID */
+       ret = wiimote_cmd_read(ext->wdata, 0xa400fe, rmem, 2);
+       if (ret == 2) {
+               if (rmem[0] == 0 && rmem[1] == 0)
+                       type = WIIEXT_NUNCHUCK;
+               else if (rmem[0] == 0x01 && rmem[1] == 0x01)
+                       type = WIIEXT_CLASSIC;
+       }
+
+       wiimote_cmd_release(ext->wdata);
+
+       return type;
+}
+
+static void ext_enable(struct wiimote_ext *ext, bool motionp, __u8 ext_type)
+{
+       unsigned long flags;
+       __u8 wmem;
+       int ret;
+
+       if (motionp) {
+               if (wiimote_cmd_acquire(ext->wdata))
+                       return;
+
+               if (ext_type == WIIEXT_CLASSIC)
+                       wmem = 0x07;
+               else if (ext_type == WIIEXT_NUNCHUCK)
+                       wmem = 0x05;
+               else
+                       wmem = 0x04;
+
+               ret = wiimote_cmd_write(ext->wdata, 0xa600fe, &wmem, sizeof(wmem));
+               wiimote_cmd_release(ext->wdata);
+               if (ret)
+                       return;
+       }
+
+       spin_lock_irqsave(&ext->wdata->state.lock, flags);
+       ext->motionp = motionp;
+       ext->ext_type = ext_type;
+       wiiproto_req_drm(ext->wdata, WIIPROTO_REQ_NULL);
+       spin_unlock_irqrestore(&ext->wdata->state.lock, flags);
+}
+
+static void wiiext_worker(struct work_struct *work)
+{
+       struct wiimote_ext *ext = container_of(work, struct wiimote_ext,
+                                                                       worker);
+       bool motionp;
+       __u8 ext_type;
+
+       ext_disable(ext);
+       motionp = motionp_read(ext);
+       ext_type = ext_read(ext);
+       ext_enable(ext, motionp, ext_type);
+}
+
+/* schedule work only once, otherwise mark for reschedule */
+static void wiiext_schedule(struct wiimote_ext *ext)
+{
+       queue_work(system_nrt_wq, &ext->worker);
+}
+
+/*
+ * Reacts on extension port events
+ * Whenever the driver gets an event from the wiimote that an extension has been
+ * plugged or unplugged, this funtion shall be called. It checks what extensions
+ * are connected and initializes and activates them.
+ * This can be called in atomic context. The initialization is done in a
+ * separate worker thread. The state.lock spinlock must be held by the caller.
+ */
+void wiiext_event(struct wiimote_data *wdata, bool plugged)
+{
+       if (!wdata->ext)
+               return;
+
+       if (wdata->ext->plugged == plugged)
+               return;
+
+       wdata->ext->plugged = plugged;
+
+       if (!plugged)
+               wdata->ext->mp_plugged = false;
+
+       /*
+        * We need to call wiiext_schedule(wdata->ext) here, however, the
+        * extension initialization logic is not fully understood and so
+        * automatic initialization is not supported, yet.
+        */
+}
+
+/*
+ * Returns true if the current DRM mode should contain extension data and false
+ * if there is no interest in extension data.
+ * All supported extensions send 6 byte extension data so any DRM that contains
+ * extension bytes is fine.
+ * The caller must hold the state.lock spinlock.
+ */
+bool wiiext_active(struct wiimote_data *wdata)
+{
+       if (!wdata->ext)
+               return false;
+
+       return wdata->ext->motionp || wdata->ext->ext_type;
+}
+
+static void handler_motionp(struct wiimote_ext *ext, const __u8 *payload)
+{
+       __s32 x, y, z;
+       bool plugged;
+
+       /*        |   8    7    6    5    4    3 |  2  |  1  |
+        *   -----+------------------------------+-----+-----+
+        *    1   |               Yaw Speed <7:0>            |
+        *    2   |              Roll Speed <7:0>            |
+        *    3   |             Pitch Speed <7:0>            |
+        *   -----+------------------------------+-----+-----+
+        *    4   |       Yaw Speed <13:8>       | Yaw |Pitch|
+        *   -----+------------------------------+-----+-----+
+        *    5   |      Roll Speed <13:8>       |Roll | Ext |
+        *   -----+------------------------------+-----+-----+
+        *    6   |     Pitch Speed <13:8>       |  1  |  0  |
+        *   -----+------------------------------+-----+-----+
+        * The single bits Yaw, Roll, Pitch in the lower right corner specify
+        * whether the wiimote is rotating fast (0) or slow (1). Speed for slow
+        * roation is 440 deg/s and for fast rotation 2000 deg/s. To get a
+        * linear scale we multiply by 2000/440 = ~4.5454 which is 18 for fast
+        * and 9 for slow.
+        * If the wiimote is not rotating the sensor reports 2^13 = 8192.
+        * Ext specifies whether an extension is connected to the motionp.
+        */
+
+       x = payload[0];
+       y = payload[1];
+       z = payload[2];
+
+       x |= (((__u16)payload[3]) << 6) & 0xff00;
+       y |= (((__u16)payload[4]) << 6) & 0xff00;
+       z |= (((__u16)payload[5]) << 6) & 0xff00;
+
+       x -= 8192;
+       y -= 8192;
+       z -= 8192;
+
+       if (!(payload[3] & 0x02))
+               x *= 18;
+       else
+               x *= 9;
+       if (!(payload[4] & 0x02))
+               y *= 18;
+       else
+               y *= 9;
+       if (!(payload[3] & 0x01))
+               z *= 18;
+       else
+               z *= 9;
+
+       input_report_abs(ext->mp_input, ABS_RX, x);
+       input_report_abs(ext->mp_input, ABS_RY, y);
+       input_report_abs(ext->mp_input, ABS_RZ, z);
+       input_sync(ext->mp_input);
+
+       plugged = payload[5] & 0x01;
+       if (plugged != ext->mp_plugged)
+               ext->mp_plugged = plugged;
+}
+
+static void handler_nunchuck(struct wiimote_ext *ext, const __u8 *payload)
+{
+       __s16 x, y, z, bx, by;
+
+       /*   Byte |   8    7 |  6    5 |  4    3 |  2 |  1  |
+        *   -----+----------+---------+---------+----+-----+
+        *    1   |              Button X <7:0>             |
+        *    2   |              Button Y <7:0>             |
+        *   -----+----------+---------+---------+----+-----+
+        *    3   |               Speed X <9:2>             |
+        *    4   |               Speed Y <9:2>             |
+        *    5   |               Speed Z <9:2>             |
+        *   -----+----------+---------+---------+----+-----+
+        *    6   | Z <1:0>  | Y <1:0> | X <1:0> | BC | BZ  |
+        *   -----+----------+---------+---------+----+-----+
+        * Button X/Y is the analog stick. Speed X, Y and Z are the
+        * accelerometer data in the same format as the wiimote's accelerometer.
+        * The 6th byte contains the LSBs of the accelerometer data.
+        * BC and BZ are the C and Z buttons: 0 means pressed
+        *
+        * If reported interleaved with motionp, then the layout changes. The
+        * 5th and 6th byte changes to:
+        *   -----+-----------------------------------+-----+
+        *    5   |            Speed Z <9:3>          | EXT |
+        *   -----+--------+-----+-----+----+----+----+-----+
+        *    6   |Z <2:1> |Y <1>|X <1>| BC | BZ | 0  |  0  |
+        *   -----+--------+-----+-----+----+----+----+-----+
+        * All three accelerometer values lose their LSB. The other data is
+        * still available but slightly moved.
+        *
+        * Center data for button values is 128. Center value for accelerometer
+        * values it 512 / 0x200
+        */
+
+       bx = payload[0];
+       by = payload[1];
+       bx -= 128;
+       by -= 128;
+
+       x = payload[2] << 2;
+       y = payload[3] << 2;
+       z = payload[4] << 2;
+
+       if (ext->motionp) {
+               x |= (payload[5] >> 3) & 0x02;
+               y |= (payload[5] >> 4) & 0x02;
+               z &= ~0x4;
+               z |= (payload[5] >> 5) & 0x06;
+       } else {
+               x |= (payload[5] >> 2) & 0x03;
+               y |= (payload[5] >> 4) & 0x03;
+               z |= (payload[5] >> 6) & 0x03;
+       }
+
+       x -= 0x200;
+       y -= 0x200;
+       z -= 0x200;
+
+       input_report_abs(ext->input, ABS_HAT0X, bx);
+       input_report_abs(ext->input, ABS_HAT0Y, by);
+
+       input_report_abs(ext->input, ABS_RX, x);
+       input_report_abs(ext->input, ABS_RY, y);
+       input_report_abs(ext->input, ABS_RZ, z);
+
+       if (ext->motionp) {
+               input_report_key(ext->input,
+                       wiiext_keymap[WIIEXT_KEY_Z], !!(payload[5] & 0x04));
+               input_report_key(ext->input,
+                       wiiext_keymap[WIIEXT_KEY_C], !!(payload[5] & 0x08));
+       } else {
+               input_report_key(ext->input,
+                       wiiext_keymap[WIIEXT_KEY_Z], !!(payload[5] & 0x01));
+               input_report_key(ext->input,
+                       wiiext_keymap[WIIEXT_KEY_C], !!(payload[5] & 0x02));
+       }
+
+       input_sync(ext->input);
+}
+
+static void handler_classic(struct wiimote_ext *ext, const __u8 *payload)
+{
+       __s8 rx, ry, lx, ly, lt, rt;
+
+       /*   Byte |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
+        *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+        *    1   | RX <5:4>  |              LX <5:0>             |
+        *    2   | RX <3:2>  |              LY <5:0>             |
+        *   -----+-----+-----+-----+-----------------------------+
+        *    3   |RX<1>| LT <5:4>  |         RY <5:1>            |
+        *   -----+-----+-----------+-----------------------------+
+        *    4   |     LT <3:1>    |         RT <5:1>            |
+        *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+        *    5   | BDR | BDD | BLT | B-  | BH  | B+  | BRT |  1  |
+        *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+        *    6   | BZL | BB  | BY  | BA  | BX  | BZR | BDL | BDU |
+        *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+        * All buttons are 0 if pressed
+        * RX and RY are right analog stick
+        * LX and LY are left analog stick
+        * LT is left trigger, RT is right trigger
+        * BLT is 0 if left trigger is fully pressed
+        * BRT is 0 if right trigger is fully pressed
+        * BDR, BDD, BDL, BDU form the D-Pad with right, down, left, up buttons
+        * BZL is left Z button and BZR is right Z button
+        * B-, BH, B+ are +, HOME and - buttons
+        * BB, BY, BA, BX are A, B, X, Y buttons
+        * LSB of RX, RY, LT, and RT are not transmitted and always 0.
+        *
+        * With motionp enabled it changes slightly to this:
+        *   Byte |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
+        *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+        *    1   | RX <4:3>  |          LX <5:1>           | BDU |
+        *    2   | RX <2:1>  |          LY <5:1>           | BDL |
+        *   -----+-----+-----+-----+-----------------------+-----+
+        *    3   |RX<0>| LT <4:3>  |         RY <4:0>            |
+        *   -----+-----+-----------+-----------------------------+
+        *    4   |     LT <2:0>    |         RT <4:0>            |
+        *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+        *    5   | BDR | BDD | BLT | B-  | BH  | B+  | BRT | EXT |
+        *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+        *    6   | BZL | BB  | BY  | BA  | BX  | BZR |  0  |  0  |
+        *   -----+-----+-----+-----+-----+-----+-----+-----+-----+
+        * Only the LSBs of LX and LY are lost. BDU and BDL are moved, the rest
+        * is the same as before.
+        */
+
+       if (ext->motionp) {
+               lx = payload[0] & 0x3e;
+               ly = payload[0] & 0x3e;
+       } else {
+               lx = payload[0] & 0x3f;
+               ly = payload[0] & 0x3f;
+       }
+
+       rx = (payload[0] >> 3) & 0x14;
+       rx |= (payload[1] >> 5) & 0x06;
+       rx |= (payload[2] >> 7) & 0x01;
+       ry = payload[2] & 0x1f;
+
+       rt = payload[3] & 0x1f;
+       lt = (payload[2] >> 2) & 0x18;
+       lt |= (payload[3] >> 5) & 0x07;
+
+       rx <<= 1;
+       ry <<= 1;
+       rt <<= 1;
+       lt <<= 1;
+
+       input_report_abs(ext->input, ABS_HAT1X, lx - 0x20);
+       input_report_abs(ext->input, ABS_HAT1Y, ly - 0x20);
+       input_report_abs(ext->input, ABS_HAT2X, rx - 0x20);
+       input_report_abs(ext->input, ABS_HAT2Y, ry - 0x20);
+       input_report_abs(ext->input, ABS_HAT3X, rt - 0x20);
+       input_report_abs(ext->input, ABS_HAT3Y, lt - 0x20);
+
+       input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_RIGHT],
+                                                       !!(payload[4] & 0x80));
+       input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_DOWN],
+                                                       !!(payload[4] & 0x40));
+       input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LT],
+                                                       !!(payload[4] & 0x20));
+       input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_MINUS],
+                                                       !!(payload[4] & 0x10));
+       input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_HOME],
+                                                       !!(payload[4] & 0x08));
+       input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_PLUS],
+                                                       !!(payload[4] & 0x04));
+       input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_RT],
+                                                       !!(payload[4] & 0x02));
+       input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_ZL],
+                                                       !!(payload[5] & 0x80));
+       input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_B],
+                                                       !!(payload[5] & 0x40));
+       input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_Y],
+                                                       !!(payload[5] & 0x20));
+       input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_A],
+                                                       !!(payload[5] & 0x10));
+       input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_X],
+                                                       !!(payload[5] & 0x08));
+       input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_ZR],
+                                                       !!(payload[5] & 0x04));
+
+       if (ext->motionp) {
+               input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_UP],
+                                                       !!(payload[0] & 0x01));
+               input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LEFT],
+                                                       !!(payload[1] & 0x01));
+       } else {
+               input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_UP],
+                                                       !!(payload[5] & 0x01));
+               input_report_key(ext->input, wiiext_keymap[WIIEXT_KEY_LEFT],
+                                                       !!(payload[5] & 0x02));
+       }
+
+       input_sync(ext->input);
+}
+
+/* call this with state.lock spinlock held */
+void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload)
+{
+       struct wiimote_ext *ext = wdata->ext;
+
+       if (!ext)
+               return;
+
+       if (ext->motionp && (payload[5] & 0x02)) {
+               handler_motionp(ext, payload);
+       } else if (ext->ext_type == WIIEXT_NUNCHUCK) {
+               handler_nunchuck(ext, payload);
+       } else if (ext->ext_type == WIIEXT_CLASSIC) {
+               handler_classic(ext, payload);
+       }
+}
+
+static ssize_t wiiext_show(struct device *dev, struct device_attribute *attr,
+                                                               char *buf)
+{
+       struct wiimote_data *wdata = dev_to_wii(dev);
+       __u8 type = WIIEXT_NONE;
+       bool motionp = false;
+       unsigned long flags;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       if (wdata->ext) {
+               motionp = wdata->ext->motionp;
+               type = wdata->ext->ext_type;
+       }
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       if (type == WIIEXT_NUNCHUCK) {
+               if (motionp)
+                       return sprintf(buf, "motionp+nunchuck\n");
+               else
+                       return sprintf(buf, "nunchuck\n");
+       } else if (type == WIIEXT_CLASSIC) {
+               if (motionp)
+                       return sprintf(buf, "motionp+classic\n");
+               else
+                       return sprintf(buf, "classic\n");
+       } else {
+               if (motionp)
+                       return sprintf(buf, "motionp\n");
+               else
+                       return sprintf(buf, "none\n");
+       }
+}
+
+static DEVICE_ATTR(extension, S_IRUGO, wiiext_show, NULL);
+
+static int wiiext_input_open(struct input_dev *dev)
+{
+       struct wiimote_ext *ext = input_get_drvdata(dev);
+       int ret;
+
+       ret = hid_hw_open(ext->wdata->hdev);
+       if (ret)
+               return ret;
+
+       atomic_inc(&ext->opened);
+       wiiext_schedule(ext);
+
+       return 0;
+}
+
+static void wiiext_input_close(struct input_dev *dev)
+{
+       struct wiimote_ext *ext = input_get_drvdata(dev);
+
+       atomic_dec(&ext->opened);
+       wiiext_schedule(ext);
+       hid_hw_close(ext->wdata->hdev);
+}
+
+static int wiiext_mp_open(struct input_dev *dev)
+{
+       struct wiimote_ext *ext = input_get_drvdata(dev);
+       int ret;
+
+       ret = hid_hw_open(ext->wdata->hdev);
+       if (ret)
+               return ret;
+
+       atomic_inc(&ext->mp_opened);
+       wiiext_schedule(ext);
+
+       return 0;
+}
+
+static void wiiext_mp_close(struct input_dev *dev)
+{
+       struct wiimote_ext *ext = input_get_drvdata(dev);
+
+       atomic_dec(&ext->mp_opened);
+       wiiext_schedule(ext);
+       hid_hw_close(ext->wdata->hdev);
+}
+
+/* Initializes the extension driver of a wiimote */
+int wiiext_init(struct wiimote_data *wdata)
+{
+       struct wiimote_ext *ext;
+       unsigned long flags;
+       int ret, i;
+
+       ext = kzalloc(sizeof(*ext), GFP_KERNEL);
+       if (!ext)
+               return -ENOMEM;
+
+       ext->wdata = wdata;
+       INIT_WORK(&ext->worker, wiiext_worker);
+
+       ext->input = input_allocate_device();
+       if (!ext->input) {
+               ret = -ENOMEM;
+               goto err_input;
+       }
+
+       input_set_drvdata(ext->input, ext);
+       ext->input->open = wiiext_input_open;
+       ext->input->close = wiiext_input_close;
+       ext->input->dev.parent = &wdata->hdev->dev;
+       ext->input->id.bustype = wdata->hdev->bus;
+       ext->input->id.vendor = wdata->hdev->vendor;
+       ext->input->id.product = wdata->hdev->product;
+       ext->input->id.version = wdata->hdev->version;
+       ext->input->name = WIIMOTE_NAME " Extension";
+
+       set_bit(EV_KEY, ext->input->evbit);
+       for (i = 0; i < WIIEXT_KEY_COUNT; ++i)
+               set_bit(wiiext_keymap[i], ext->input->keybit);
+
+       set_bit(EV_ABS, ext->input->evbit);
+       set_bit(ABS_HAT0X, ext->input->absbit);
+       set_bit(ABS_HAT0Y, ext->input->absbit);
+       set_bit(ABS_HAT1X, ext->input->absbit);
+       set_bit(ABS_HAT1Y, ext->input->absbit);
+       set_bit(ABS_HAT2X, ext->input->absbit);
+       set_bit(ABS_HAT2Y, ext->input->absbit);
+       set_bit(ABS_HAT3X, ext->input->absbit);
+       set_bit(ABS_HAT3Y, ext->input->absbit);
+       input_set_abs_params(ext->input, ABS_HAT0X, -120, 120, 2, 4);
+       input_set_abs_params(ext->input, ABS_HAT0Y, -120, 120, 2, 4);
+       input_set_abs_params(ext->input, ABS_HAT1X, -30, 30, 1, 1);
+       input_set_abs_params(ext->input, ABS_HAT1Y, -30, 30, 1, 1);
+       input_set_abs_params(ext->input, ABS_HAT2X, -30, 30, 1, 1);
+       input_set_abs_params(ext->input, ABS_HAT2Y, -30, 30, 1, 1);
+       input_set_abs_params(ext->input, ABS_HAT3X, -30, 30, 1, 1);
+       input_set_abs_params(ext->input, ABS_HAT3Y, -30, 30, 1, 1);
+       set_bit(ABS_RX, ext->input->absbit);
+       set_bit(ABS_RY, ext->input->absbit);
+       set_bit(ABS_RZ, ext->input->absbit);
+       input_set_abs_params(ext->input, ABS_RX, -500, 500, 2, 4);
+       input_set_abs_params(ext->input, ABS_RY, -500, 500, 2, 4);
+       input_set_abs_params(ext->input, ABS_RZ, -500, 500, 2, 4);
+
+       ret = input_register_device(ext->input);
+       if (ret) {
+               input_free_device(ext->input);
+               goto err_input;
+       }
+
+       ext->mp_input = input_allocate_device();
+       if (!ext->mp_input) {
+               ret = -ENOMEM;
+               goto err_mp;
+       }
+
+       input_set_drvdata(ext->mp_input, ext);
+       ext->mp_input->open = wiiext_mp_open;
+       ext->mp_input->close = wiiext_mp_close;
+       ext->mp_input->dev.parent = &wdata->hdev->dev;
+       ext->mp_input->id.bustype = wdata->hdev->bus;
+       ext->mp_input->id.vendor = wdata->hdev->vendor;
+       ext->mp_input->id.product = wdata->hdev->product;
+       ext->mp_input->id.version = wdata->hdev->version;
+       ext->mp_input->name = WIIMOTE_NAME " Motion+";
+
+       set_bit(EV_ABS, ext->mp_input->evbit);
+       set_bit(ABS_RX, ext->mp_input->absbit);
+       set_bit(ABS_RY, ext->mp_input->absbit);
+       set_bit(ABS_RZ, ext->mp_input->absbit);
+       input_set_abs_params(ext->mp_input, ABS_RX, -160000, 160000, 4, 8);
+       input_set_abs_params(ext->mp_input, ABS_RY, -160000, 160000, 4, 8);
+       input_set_abs_params(ext->mp_input, ABS_RZ, -160000, 160000, 4, 8);
+
+       ret = input_register_device(ext->mp_input);
+       if (ret) {
+               input_free_device(ext->mp_input);
+               goto err_mp;
+       }
+
+       ret = device_create_file(&wdata->hdev->dev, &dev_attr_extension);
+       if (ret)
+               goto err_dev;
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wdata->ext = ext;
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       return 0;
+
+err_dev:
+       input_unregister_device(ext->mp_input);
+err_mp:
+       input_unregister_device(ext->input);
+err_input:
+       kfree(ext);
+       return ret;
+}
+
+/* Deinitializes the extension driver of a wiimote */
+void wiiext_deinit(struct wiimote_data *wdata)
+{
+       struct wiimote_ext *ext = wdata->ext;
+       unsigned long flags;
+
+       if (!ext)
+               return;
+
+       /*
+        * We first unset wdata->ext to avoid further input from the wiimote
+        * core. The worker thread does not access this pointer so it is not
+        * affected by this.
+        * We kill the worker after this so it does not get respawned during
+        * deinitialization.
+        */
+
+       spin_lock_irqsave(&wdata->state.lock, flags);
+       wdata->ext = NULL;
+       spin_unlock_irqrestore(&wdata->state.lock, flags);
+
+       device_remove_file(&wdata->hdev->dev, &dev_attr_extension);
+       input_unregister_device(ext->mp_input);
+       input_unregister_device(ext->input);
+
+       cancel_work_sync(&ext->worker);
+       kfree(ext);
+}
diff --git a/drivers/hid/hid-wiimote.c b/drivers/hid/hid-wiimote.c
deleted file mode 100644 (file)
index 76739c0..0000000
+++ /dev/null
@@ -1,1346 +0,0 @@
-/*
- * HID driver for Nintendo Wiimote devices
- * Copyright (c) 2011 David Herrmann
- */
-
-/*
- * 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/completion.h>
-#include <linux/device.h>
-#include <linux/hid.h>
-#include <linux/input.h>
-#include <linux/leds.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/power_supply.h>
-#include <linux/spinlock.h>
-#include "hid-ids.h"
-
-#define WIIMOTE_VERSION "0.2"
-#define WIIMOTE_NAME "Nintendo Wii Remote"
-#define WIIMOTE_BUFSIZE 32
-
-struct wiimote_buf {
-       __u8 data[HID_MAX_BUFFER_SIZE];
-       size_t size;
-};
-
-struct wiimote_state {
-       spinlock_t lock;
-       __u8 flags;
-       __u8 accel_split[2];
-
-       /* synchronous cmd requests */
-       struct mutex sync;
-       struct completion ready;
-       int cmd;
-       __u32 opt;
-
-       /* results of synchronous requests */
-       __u8 cmd_battery;
-       __u8 cmd_err;
-};
-
-struct wiimote_data {
-       struct hid_device *hdev;
-       struct input_dev *input;
-       struct led_classdev *leds[4];
-       struct input_dev *accel;
-       struct input_dev *ir;
-       struct power_supply battery;
-
-       spinlock_t qlock;
-       __u8 head;
-       __u8 tail;
-       struct wiimote_buf outq[WIIMOTE_BUFSIZE];
-       struct work_struct worker;
-
-       struct wiimote_state state;
-};
-
-#define WIIPROTO_FLAG_LED1             0x01
-#define WIIPROTO_FLAG_LED2             0x02
-#define WIIPROTO_FLAG_LED3             0x04
-#define WIIPROTO_FLAG_LED4             0x08
-#define WIIPROTO_FLAG_RUMBLE           0x10
-#define WIIPROTO_FLAG_ACCEL            0x20
-#define WIIPROTO_FLAG_IR_BASIC         0x40
-#define WIIPROTO_FLAG_IR_EXT           0x80
-#define WIIPROTO_FLAG_IR_FULL          0xc0 /* IR_BASIC | IR_EXT */
-#define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \
-                                       WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4)
-#define WIIPROTO_FLAGS_IR (WIIPROTO_FLAG_IR_BASIC | WIIPROTO_FLAG_IR_EXT | \
-                                                       WIIPROTO_FLAG_IR_FULL)
-
-/* return flag for led \num */
-#define WIIPROTO_FLAG_LED(num) (WIIPROTO_FLAG_LED1 << (num - 1))
-
-enum wiiproto_reqs {
-       WIIPROTO_REQ_NULL = 0x0,
-       WIIPROTO_REQ_RUMBLE = 0x10,
-       WIIPROTO_REQ_LED = 0x11,
-       WIIPROTO_REQ_DRM = 0x12,
-       WIIPROTO_REQ_IR1 = 0x13,
-       WIIPROTO_REQ_SREQ = 0x15,
-       WIIPROTO_REQ_WMEM = 0x16,
-       WIIPROTO_REQ_RMEM = 0x17,
-       WIIPROTO_REQ_IR2 = 0x1a,
-       WIIPROTO_REQ_STATUS = 0x20,
-       WIIPROTO_REQ_DATA = 0x21,
-       WIIPROTO_REQ_RETURN = 0x22,
-       WIIPROTO_REQ_DRM_K = 0x30,
-       WIIPROTO_REQ_DRM_KA = 0x31,
-       WIIPROTO_REQ_DRM_KE = 0x32,
-       WIIPROTO_REQ_DRM_KAI = 0x33,
-       WIIPROTO_REQ_DRM_KEE = 0x34,
-       WIIPROTO_REQ_DRM_KAE = 0x35,
-       WIIPROTO_REQ_DRM_KIE = 0x36,
-       WIIPROTO_REQ_DRM_KAIE = 0x37,
-       WIIPROTO_REQ_DRM_E = 0x3d,
-       WIIPROTO_REQ_DRM_SKAI1 = 0x3e,
-       WIIPROTO_REQ_DRM_SKAI2 = 0x3f,
-};
-
-enum wiiproto_keys {
-       WIIPROTO_KEY_LEFT,
-       WIIPROTO_KEY_RIGHT,
-       WIIPROTO_KEY_UP,
-       WIIPROTO_KEY_DOWN,
-       WIIPROTO_KEY_PLUS,
-       WIIPROTO_KEY_MINUS,
-       WIIPROTO_KEY_ONE,
-       WIIPROTO_KEY_TWO,
-       WIIPROTO_KEY_A,
-       WIIPROTO_KEY_B,
-       WIIPROTO_KEY_HOME,
-       WIIPROTO_KEY_COUNT
-};
-
-static __u16 wiiproto_keymap[] = {
-       KEY_LEFT,       /* WIIPROTO_KEY_LEFT */
-       KEY_RIGHT,      /* WIIPROTO_KEY_RIGHT */
-       KEY_UP,         /* WIIPROTO_KEY_UP */
-       KEY_DOWN,       /* WIIPROTO_KEY_DOWN */
-       KEY_NEXT,       /* WIIPROTO_KEY_PLUS */
-       KEY_PREVIOUS,   /* WIIPROTO_KEY_MINUS */
-       BTN_1,          /* WIIPROTO_KEY_ONE */
-       BTN_2,          /* WIIPROTO_KEY_TWO */
-       BTN_A,          /* WIIPROTO_KEY_A */
-       BTN_B,          /* WIIPROTO_KEY_B */
-       BTN_MODE,       /* WIIPROTO_KEY_HOME */
-};
-
-static enum power_supply_property wiimote_battery_props[] = {
-       POWER_SUPPLY_PROP_CAPACITY
-};
-
-/* requires the state.lock spinlock to be held */
-static inline bool wiimote_cmd_pending(struct wiimote_data *wdata, int cmd,
-                                                               __u32 opt)
-{
-       return wdata->state.cmd == cmd && wdata->state.opt == opt;
-}
-
-/* requires the state.lock spinlock to be held */
-static inline void wiimote_cmd_complete(struct wiimote_data *wdata)
-{
-       wdata->state.cmd = WIIPROTO_REQ_NULL;
-       complete(&wdata->state.ready);
-}
-
-static inline int wiimote_cmd_acquire(struct wiimote_data *wdata)
-{
-       return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0;
-}
-
-/* requires the state.lock spinlock to be held */
-static inline void wiimote_cmd_set(struct wiimote_data *wdata, int cmd,
-                                                               __u32 opt)
-{
-       INIT_COMPLETION(wdata->state.ready);
-       wdata->state.cmd = cmd;
-       wdata->state.opt = opt;
-}
-
-static inline void wiimote_cmd_release(struct wiimote_data *wdata)
-{
-       mutex_unlock(&wdata->state.sync);
-}
-
-static inline int wiimote_cmd_wait(struct wiimote_data *wdata)
-{
-       int ret;
-
-       ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ);
-       if (ret < 0)
-               return -ERESTARTSYS;
-       else if (ret == 0)
-               return -EIO;
-       else
-               return 0;
-}
-
-static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
-                                                               size_t count)
-{
-       __u8 *buf;
-       ssize_t ret;
-
-       if (!hdev->hid_output_raw_report)
-               return -ENODEV;
-
-       buf = kmemdup(buffer, count, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       ret = hdev->hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT);
-
-       kfree(buf);
-       return ret;
-}
-
-static void wiimote_worker(struct work_struct *work)
-{
-       struct wiimote_data *wdata = container_of(work, struct wiimote_data,
-                                                                       worker);
-       unsigned long flags;
-
-       spin_lock_irqsave(&wdata->qlock, flags);
-
-       while (wdata->head != wdata->tail) {
-               spin_unlock_irqrestore(&wdata->qlock, flags);
-               wiimote_hid_send(wdata->hdev, wdata->outq[wdata->tail].data,
-                                               wdata->outq[wdata->tail].size);
-               spin_lock_irqsave(&wdata->qlock, flags);
-
-               wdata->tail = (wdata->tail + 1) % WIIMOTE_BUFSIZE;
-       }
-
-       spin_unlock_irqrestore(&wdata->qlock, flags);
-}
-
-static void wiimote_queue(struct wiimote_data *wdata, const __u8 *buffer,
-                                                               size_t count)
-{
-       unsigned long flags;
-       __u8 newhead;
-
-       if (count > HID_MAX_BUFFER_SIZE) {
-               hid_warn(wdata->hdev, "Sending too large output report\n");
-               return;
-       }
-
-       /*
-        * Copy new request into our output queue and check whether the
-        * queue is full. If it is full, discard this request.
-        * If it is empty we need to start a new worker that will
-        * send out the buffer to the hid device.
-        * If the queue is not empty, then there must be a worker
-        * that is currently sending out our buffer and this worker
-        * will reschedule itself until the queue is empty.
-        */
-
-       spin_lock_irqsave(&wdata->qlock, flags);
-
-       memcpy(wdata->outq[wdata->head].data, buffer, count);
-       wdata->outq[wdata->head].size = count;
-       newhead = (wdata->head + 1) % WIIMOTE_BUFSIZE;
-
-       if (wdata->head == wdata->tail) {
-               wdata->head = newhead;
-               schedule_work(&wdata->worker);
-       } else if (newhead != wdata->tail) {
-               wdata->head = newhead;
-       } else {
-               hid_warn(wdata->hdev, "Output queue is full");
-       }
-
-       spin_unlock_irqrestore(&wdata->qlock, flags);
-}
-
-/*
- * This sets the rumble bit on the given output report if rumble is
- * currently enabled.
- * \cmd1 must point to the second byte in the output report => &cmd[1]
- * This must be called on nearly every output report before passing it
- * into the output queue!
- */
-static inline void wiiproto_keep_rumble(struct wiimote_data *wdata, __u8 *cmd1)
-{
-       if (wdata->state.flags & WIIPROTO_FLAG_RUMBLE)
-               *cmd1 |= 0x01;
-}
-
-static void wiiproto_req_rumble(struct wiimote_data *wdata, __u8 rumble)
-{
-       __u8 cmd[2];
-
-       rumble = !!rumble;
-       if (rumble == !!(wdata->state.flags & WIIPROTO_FLAG_RUMBLE))
-               return;
-
-       if (rumble)
-               wdata->state.flags |= WIIPROTO_FLAG_RUMBLE;
-       else
-               wdata->state.flags &= ~WIIPROTO_FLAG_RUMBLE;
-
-       cmd[0] = WIIPROTO_REQ_RUMBLE;
-       cmd[1] = 0;
-
-       wiiproto_keep_rumble(wdata, &cmd[1]);
-       wiimote_queue(wdata, cmd, sizeof(cmd));
-}
-
-static void wiiproto_req_leds(struct wiimote_data *wdata, int leds)
-{
-       __u8 cmd[2];
-
-       leds &= WIIPROTO_FLAGS_LEDS;
-       if ((wdata->state.flags & WIIPROTO_FLAGS_LEDS) == leds)
-               return;
-       wdata->state.flags = (wdata->state.flags & ~WIIPROTO_FLAGS_LEDS) | leds;
-
-       cmd[0] = WIIPROTO_REQ_LED;
-       cmd[1] = 0;
-
-       if (leds & WIIPROTO_FLAG_LED1)
-               cmd[1] |= 0x10;
-       if (leds & WIIPROTO_FLAG_LED2)
-               cmd[1] |= 0x20;
-       if (leds & WIIPROTO_FLAG_LED3)
-               cmd[1] |= 0x40;
-       if (leds & WIIPROTO_FLAG_LED4)
-               cmd[1] |= 0x80;
-
-       wiiproto_keep_rumble(wdata, &cmd[1]);
-       wiimote_queue(wdata, cmd, sizeof(cmd));
-}
-
-/*
- * Check what peripherals of the wiimote are currently
- * active and select a proper DRM that supports all of
- * the requested data inputs.
- */
-static __u8 select_drm(struct wiimote_data *wdata)
-{
-       __u8 ir = wdata->state.flags & WIIPROTO_FLAGS_IR;
-
-       if (ir == WIIPROTO_FLAG_IR_BASIC) {
-               if (wdata->state.flags & WIIPROTO_FLAG_ACCEL)
-                       return WIIPROTO_REQ_DRM_KAIE;
-               else
-                       return WIIPROTO_REQ_DRM_KIE;
-       } else if (ir == WIIPROTO_FLAG_IR_EXT) {
-               return WIIPROTO_REQ_DRM_KAI;
-       } else if (ir == WIIPROTO_FLAG_IR_FULL) {
-               return WIIPROTO_REQ_DRM_SKAI1;
-       } else {
-               if (wdata->state.flags & WIIPROTO_FLAG_ACCEL)
-                       return WIIPROTO_REQ_DRM_KA;
-               else
-                       return WIIPROTO_REQ_DRM_K;
-       }
-}
-
-static void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm)
-{
-       __u8 cmd[3];
-
-       if (drm == WIIPROTO_REQ_NULL)
-               drm = select_drm(wdata);
-
-       cmd[0] = WIIPROTO_REQ_DRM;
-       cmd[1] = 0;
-       cmd[2] = drm;
-
-       wiiproto_keep_rumble(wdata, &cmd[1]);
-       wiimote_queue(wdata, cmd, sizeof(cmd));
-}
-
-static void wiiproto_req_status(struct wiimote_data *wdata)
-{
-       __u8 cmd[2];
-
-       cmd[0] = WIIPROTO_REQ_SREQ;
-       cmd[1] = 0;
-
-       wiiproto_keep_rumble(wdata, &cmd[1]);
-       wiimote_queue(wdata, cmd, sizeof(cmd));
-}
-
-static void wiiproto_req_accel(struct wiimote_data *wdata, __u8 accel)
-{
-       accel = !!accel;
-       if (accel == !!(wdata->state.flags & WIIPROTO_FLAG_ACCEL))
-               return;
-
-       if (accel)
-               wdata->state.flags |= WIIPROTO_FLAG_ACCEL;
-       else
-               wdata->state.flags &= ~WIIPROTO_FLAG_ACCEL;
-
-       wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
-}
-
-static void wiiproto_req_ir1(struct wiimote_data *wdata, __u8 flags)
-{
-       __u8 cmd[2];
-
-       cmd[0] = WIIPROTO_REQ_IR1;
-       cmd[1] = flags;
-
-       wiiproto_keep_rumble(wdata, &cmd[1]);
-       wiimote_queue(wdata, cmd, sizeof(cmd));
-}
-
-static void wiiproto_req_ir2(struct wiimote_data *wdata, __u8 flags)
-{
-       __u8 cmd[2];
-
-       cmd[0] = WIIPROTO_REQ_IR2;
-       cmd[1] = flags;
-
-       wiiproto_keep_rumble(wdata, &cmd[1]);
-       wiimote_queue(wdata, cmd, sizeof(cmd));
-}
-
-#define wiiproto_req_wreg(wdata, os, buf, sz) \
-                       wiiproto_req_wmem((wdata), false, (os), (buf), (sz))
-
-#define wiiproto_req_weeprom(wdata, os, buf, sz) \
-                       wiiproto_req_wmem((wdata), true, (os), (buf), (sz))
-
-static void wiiproto_req_wmem(struct wiimote_data *wdata, bool eeprom,
-                               __u32 offset, const __u8 *buf, __u8 size)
-{
-       __u8 cmd[22];
-
-       if (size > 16 || size == 0) {
-               hid_warn(wdata->hdev, "Invalid length %d wmem request\n", size);
-               return;
-       }
-
-       memset(cmd, 0, sizeof(cmd));
-       cmd[0] = WIIPROTO_REQ_WMEM;
-       cmd[2] = (offset >> 16) & 0xff;
-       cmd[3] = (offset >> 8) & 0xff;
-       cmd[4] = offset & 0xff;
-       cmd[5] = size;
-       memcpy(&cmd[6], buf, size);
-
-       if (!eeprom)
-               cmd[1] |= 0x04;
-
-       wiiproto_keep_rumble(wdata, &cmd[1]);
-       wiimote_queue(wdata, cmd, sizeof(cmd));
-}
-
-/* requries the cmd-mutex to be held */
-static int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
-                                               const __u8 *wmem, __u8 size)
-{
-       unsigned long flags;
-       int ret;
-
-       spin_lock_irqsave(&wdata->state.lock, flags);
-       wiimote_cmd_set(wdata, WIIPROTO_REQ_WMEM, 0);
-       wiiproto_req_wreg(wdata, offset, wmem, size);
-       spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-       ret = wiimote_cmd_wait(wdata);
-       if (!ret && wdata->state.cmd_err)
-               ret = -EIO;
-
-       return ret;
-}
-
-static int wiimote_battery_get_property(struct power_supply *psy,
-                                               enum power_supply_property psp,
-                                               union power_supply_propval *val)
-{
-       struct wiimote_data *wdata = container_of(psy,
-                                               struct wiimote_data, battery);
-       int ret = 0, state;
-       unsigned long flags;
-
-       ret = wiimote_cmd_acquire(wdata);
-       if (ret)
-               return ret;
-
-       spin_lock_irqsave(&wdata->state.lock, flags);
-       wiimote_cmd_set(wdata, WIIPROTO_REQ_SREQ, 0);
-       wiiproto_req_status(wdata);
-       spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-       ret = wiimote_cmd_wait(wdata);
-       state = wdata->state.cmd_battery;
-       wiimote_cmd_release(wdata);
-
-       if (ret)
-               return ret;
-
-       switch (psp) {
-               case POWER_SUPPLY_PROP_CAPACITY:
-                       val->intval = state * 100 / 255;
-                       break;
-               default:
-                       ret = -EINVAL;
-                       break;
-       }
-
-       return ret;
-}
-
-static int wiimote_init_ir(struct wiimote_data *wdata, __u16 mode)
-{
-       int ret;
-       unsigned long flags;
-       __u8 format = 0;
-       static const __u8 data_enable[] = { 0x01 };
-       static const __u8 data_sens1[] = { 0x02, 0x00, 0x00, 0x71, 0x01,
-                                               0x00, 0xaa, 0x00, 0x64 };
-       static const __u8 data_sens2[] = { 0x63, 0x03 };
-       static const __u8 data_fin[] = { 0x08 };
-
-       spin_lock_irqsave(&wdata->state.lock, flags);
-
-       if (mode == (wdata->state.flags & WIIPROTO_FLAGS_IR)) {
-               spin_unlock_irqrestore(&wdata->state.lock, flags);
-               return 0;
-       }
-
-       if (mode == 0) {
-               wdata->state.flags &= ~WIIPROTO_FLAGS_IR;
-               wiiproto_req_ir1(wdata, 0);
-               wiiproto_req_ir2(wdata, 0);
-               wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
-               spin_unlock_irqrestore(&wdata->state.lock, flags);
-               return 0;
-       }
-
-       spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-       ret = wiimote_cmd_acquire(wdata);
-       if (ret)
-               return ret;
-
-       /* send PIXEL CLOCK ENABLE cmd first */
-       spin_lock_irqsave(&wdata->state.lock, flags);
-       wiimote_cmd_set(wdata, WIIPROTO_REQ_IR1, 0);
-       wiiproto_req_ir1(wdata, 0x06);
-       spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-       ret = wiimote_cmd_wait(wdata);
-       if (ret)
-               goto unlock;
-       if (wdata->state.cmd_err) {
-               ret = -EIO;
-               goto unlock;
-       }
-
-       /* enable IR LOGIC */
-       spin_lock_irqsave(&wdata->state.lock, flags);
-       wiimote_cmd_set(wdata, WIIPROTO_REQ_IR2, 0);
-       wiiproto_req_ir2(wdata, 0x06);
-       spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-       ret = wiimote_cmd_wait(wdata);
-       if (ret)
-               goto unlock;
-       if (wdata->state.cmd_err) {
-               ret = -EIO;
-               goto unlock;
-       }
-
-       /* enable IR cam but do not make it send data, yet */
-       ret = wiimote_cmd_write(wdata, 0xb00030, data_enable,
-                                                       sizeof(data_enable));
-       if (ret)
-               goto unlock;
-
-       /* write first sensitivity block */
-       ret = wiimote_cmd_write(wdata, 0xb00000, data_sens1,
-                                                       sizeof(data_sens1));
-       if (ret)
-               goto unlock;
-
-       /* write second sensitivity block */
-       ret = wiimote_cmd_write(wdata, 0xb0001a, data_sens2,
-                                                       sizeof(data_sens2));
-       if (ret)
-               goto unlock;
-
-       /* put IR cam into desired state */
-       switch (mode) {
-               case WIIPROTO_FLAG_IR_FULL:
-                       format = 5;
-                       break;
-               case WIIPROTO_FLAG_IR_EXT:
-                       format = 3;
-                       break;
-               case WIIPROTO_FLAG_IR_BASIC:
-                       format = 1;
-                       break;
-       }
-       ret = wiimote_cmd_write(wdata, 0xb00033, &format, sizeof(format));
-       if (ret)
-               goto unlock;
-
-       /* make IR cam send data */
-       ret = wiimote_cmd_write(wdata, 0xb00030, data_fin, sizeof(data_fin));
-       if (ret)
-               goto unlock;
-
-       /* request new DRM mode compatible to IR mode */
-       spin_lock_irqsave(&wdata->state.lock, flags);
-       wdata->state.flags &= ~WIIPROTO_FLAGS_IR;
-       wdata->state.flags |= mode & WIIPROTO_FLAGS_IR;
-       wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
-       spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-unlock:
-       wiimote_cmd_release(wdata);
-       return ret;
-}
-
-static enum led_brightness wiimote_leds_get(struct led_classdev *led_dev)
-{
-       struct wiimote_data *wdata;
-       struct device *dev = led_dev->dev->parent;
-       int i;
-       unsigned long flags;
-       bool value = false;
-
-       wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
-
-       for (i = 0; i < 4; ++i) {
-               if (wdata->leds[i] == led_dev) {
-                       spin_lock_irqsave(&wdata->state.lock, flags);
-                       value = wdata->state.flags & WIIPROTO_FLAG_LED(i + 1);
-                       spin_unlock_irqrestore(&wdata->state.lock, flags);
-                       break;
-               }
-       }
-
-       return value ? LED_FULL : LED_OFF;
-}
-
-static void wiimote_leds_set(struct led_classdev *led_dev,
-                                               enum led_brightness value)
-{
-       struct wiimote_data *wdata;
-       struct device *dev = led_dev->dev->parent;
-       int i;
-       unsigned long flags;
-       __u8 state, flag;
-
-       wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
-
-       for (i = 0; i < 4; ++i) {
-               if (wdata->leds[i] == led_dev) {
-                       flag = WIIPROTO_FLAG_LED(i + 1);
-                       spin_lock_irqsave(&wdata->state.lock, flags);
-                       state = wdata->state.flags;
-                       if (value == LED_OFF)
-                               wiiproto_req_leds(wdata, state & ~flag);
-                       else
-                               wiiproto_req_leds(wdata, state | flag);
-                       spin_unlock_irqrestore(&wdata->state.lock, flags);
-                       break;
-               }
-       }
-}
-
-static int wiimote_ff_play(struct input_dev *dev, void *data,
-                                                       struct ff_effect *eff)
-{
-       struct wiimote_data *wdata = input_get_drvdata(dev);
-       __u8 value;
-       unsigned long flags;
-
-       /*
-        * The wiimote supports only a single rumble motor so if any magnitude
-        * is set to non-zero then we start the rumble motor. If both are set to
-        * zero, we stop the rumble motor.
-        */
-
-       if (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude)
-               value = 1;
-       else
-               value = 0;
-
-       spin_lock_irqsave(&wdata->state.lock, flags);
-       wiiproto_req_rumble(wdata, value);
-       spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-       return 0;
-}
-
-static int wiimote_input_open(struct input_dev *dev)
-{
-       struct wiimote_data *wdata = input_get_drvdata(dev);
-
-       return hid_hw_open(wdata->hdev);
-}
-
-static void wiimote_input_close(struct input_dev *dev)
-{
-       struct wiimote_data *wdata = input_get_drvdata(dev);
-
-       hid_hw_close(wdata->hdev);
-}
-
-static int wiimote_accel_open(struct input_dev *dev)
-{
-       struct wiimote_data *wdata = input_get_drvdata(dev);
-       int ret;
-       unsigned long flags;
-
-       ret = hid_hw_open(wdata->hdev);
-       if (ret)
-               return ret;
-
-       spin_lock_irqsave(&wdata->state.lock, flags);
-       wiiproto_req_accel(wdata, true);
-       spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-       return 0;
-}
-
-static void wiimote_accel_close(struct input_dev *dev)
-{
-       struct wiimote_data *wdata = input_get_drvdata(dev);
-       unsigned long flags;
-
-       spin_lock_irqsave(&wdata->state.lock, flags);
-       wiiproto_req_accel(wdata, false);
-       spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-       hid_hw_close(wdata->hdev);
-}
-
-static int wiimote_ir_open(struct input_dev *dev)
-{
-       struct wiimote_data *wdata = input_get_drvdata(dev);
-       int ret;
-
-       ret = hid_hw_open(wdata->hdev);
-       if (ret)
-               return ret;
-
-       ret = wiimote_init_ir(wdata, WIIPROTO_FLAG_IR_BASIC);
-       if (ret) {
-               hid_hw_close(wdata->hdev);
-               return ret;
-       }
-
-       return 0;
-}
-
-static void wiimote_ir_close(struct input_dev *dev)
-{
-       struct wiimote_data *wdata = input_get_drvdata(dev);
-
-       wiimote_init_ir(wdata, 0);
-       hid_hw_close(wdata->hdev);
-}
-
-static void handler_keys(struct wiimote_data *wdata, const __u8 *payload)
-{
-       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_LEFT],
-                                                       !!(payload[0] & 0x01));
-       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_RIGHT],
-                                                       !!(payload[0] & 0x02));
-       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_DOWN],
-                                                       !!(payload[0] & 0x04));
-       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_UP],
-                                                       !!(payload[0] & 0x08));
-       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_PLUS],
-                                                       !!(payload[0] & 0x10));
-       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_TWO],
-                                                       !!(payload[1] & 0x01));
-       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_ONE],
-                                                       !!(payload[1] & 0x02));
-       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_B],
-                                                       !!(payload[1] & 0x04));
-       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_A],
-                                                       !!(payload[1] & 0x08));
-       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_MINUS],
-                                                       !!(payload[1] & 0x10));
-       input_report_key(wdata->input, wiiproto_keymap[WIIPROTO_KEY_HOME],
-                                                       !!(payload[1] & 0x80));
-       input_sync(wdata->input);
-}
-
-static void handler_accel(struct wiimote_data *wdata, const __u8 *payload)
-{
-       __u16 x, y, z;
-
-       if (!(wdata->state.flags & WIIPROTO_FLAG_ACCEL))
-               return;
-
-       /*
-        * payload is: BB BB XX YY ZZ
-        * Accelerometer data is encoded into 3 10bit values. XX, YY and ZZ
-        * contain the upper 8 bits of each value. The lower 2 bits are
-        * contained in the buttons data BB BB.
-        * Bits 6 and 7 of the first buttons byte BB is the lower 2 bits of the
-        * X accel value. Bit 5 of the second buttons byte is the 2nd bit of Y
-        * accel value and bit 6 is the second bit of the Z value.
-        * The first bit of Y and Z values is not available and always set to 0.
-        * 0x200 is returned on no movement.
-        */
-
-       x = payload[2] << 2;
-       y = payload[3] << 2;
-       z = payload[4] << 2;
-
-       x |= (payload[0] >> 5) & 0x3;
-       y |= (payload[1] >> 4) & 0x2;
-       z |= (payload[1] >> 5) & 0x2;
-
-       input_report_abs(wdata->accel, ABS_RX, x - 0x200);
-       input_report_abs(wdata->accel, ABS_RY, y - 0x200);
-       input_report_abs(wdata->accel, ABS_RZ, z - 0x200);
-       input_sync(wdata->accel);
-}
-
-#define ir_to_input0(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
-                                                       ABS_HAT0X, ABS_HAT0Y)
-#define ir_to_input1(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
-                                                       ABS_HAT1X, ABS_HAT1Y)
-#define ir_to_input2(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
-                                                       ABS_HAT2X, ABS_HAT2Y)
-#define ir_to_input3(wdata, ir, packed) __ir_to_input((wdata), (ir), (packed), \
-                                                       ABS_HAT3X, ABS_HAT3Y)
-
-static void __ir_to_input(struct wiimote_data *wdata, const __u8 *ir,
-                                               bool packed, __u8 xid, __u8 yid)
-{
-       __u16 x, y;
-
-       if (!(wdata->state.flags & WIIPROTO_FLAGS_IR))
-               return;
-
-       /*
-        * Basic IR data is encoded into 3 bytes. The first two bytes are the
-        * upper 8 bit of the X/Y data, the 3rd byte contains the lower 2 bits
-        * of both.
-        * If data is packed, then the 3rd byte is put first and slightly
-        * reordered. This allows to interleave packed and non-packed data to
-        * have two IR sets in 5 bytes instead of 6.
-        * The resulting 10bit X/Y values are passed to the ABS_HATXY input dev.
-        */
-
-       if (packed) {
-               x = ir[1] << 2;
-               y = ir[2] << 2;
-
-               x |= ir[0] & 0x3;
-               y |= (ir[0] >> 2) & 0x3;
-       } else {
-               x = ir[0] << 2;
-               y = ir[1] << 2;
-
-               x |= (ir[2] >> 4) & 0x3;
-               y |= (ir[2] >> 6) & 0x3;
-       }
-
-       input_report_abs(wdata->ir, xid, x);
-       input_report_abs(wdata->ir, yid, y);
-}
-
-static void handler_status(struct wiimote_data *wdata, const __u8 *payload)
-{
-       handler_keys(wdata, payload);
-
-       /* on status reports the drm is reset so we need to resend the drm */
-       wiiproto_req_drm(wdata, WIIPROTO_REQ_NULL);
-
-       if (wiimote_cmd_pending(wdata, WIIPROTO_REQ_SREQ, 0)) {
-               wdata->state.cmd_battery = payload[5];
-               wiimote_cmd_complete(wdata);
-       }
-}
-
-static void handler_data(struct wiimote_data *wdata, const __u8 *payload)
-{
-       handler_keys(wdata, payload);
-}
-
-static void handler_return(struct wiimote_data *wdata, const __u8 *payload)
-{
-       __u8 err = payload[3];
-       __u8 cmd = payload[2];
-
-       handler_keys(wdata, payload);
-
-       if (wiimote_cmd_pending(wdata, cmd, 0)) {
-               wdata->state.cmd_err = err;
-               wiimote_cmd_complete(wdata);
-       } else if (err) {
-               hid_warn(wdata->hdev, "Remote error %hhu on req %hhu\n", err,
-                                                                       cmd);
-       }
-}
-
-static void handler_drm_KA(struct wiimote_data *wdata, const __u8 *payload)
-{
-       handler_keys(wdata, payload);
-       handler_accel(wdata, payload);
-}
-
-static void handler_drm_KE(struct wiimote_data *wdata, const __u8 *payload)
-{
-       handler_keys(wdata, payload);
-}
-
-static void handler_drm_KAI(struct wiimote_data *wdata, const __u8 *payload)
-{
-       handler_keys(wdata, payload);
-       handler_accel(wdata, payload);
-       ir_to_input0(wdata, &payload[5], false);
-       ir_to_input1(wdata, &payload[8], false);
-       ir_to_input2(wdata, &payload[11], false);
-       ir_to_input3(wdata, &payload[14], false);
-       input_sync(wdata->ir);
-}
-
-static void handler_drm_KEE(struct wiimote_data *wdata, const __u8 *payload)
-{
-       handler_keys(wdata, payload);
-}
-
-static void handler_drm_KIE(struct wiimote_data *wdata, const __u8 *payload)
-{
-       handler_keys(wdata, payload);
-       ir_to_input0(wdata, &payload[2], false);
-       ir_to_input1(wdata, &payload[4], true);
-       ir_to_input2(wdata, &payload[7], false);
-       ir_to_input3(wdata, &payload[9], true);
-       input_sync(wdata->ir);
-}
-
-static void handler_drm_KAE(struct wiimote_data *wdata, const __u8 *payload)
-{
-       handler_keys(wdata, payload);
-       handler_accel(wdata, payload);
-}
-
-static void handler_drm_KAIE(struct wiimote_data *wdata, const __u8 *payload)
-{
-       handler_keys(wdata, payload);
-       handler_accel(wdata, payload);
-       ir_to_input0(wdata, &payload[5], false);
-       ir_to_input1(wdata, &payload[7], true);
-       ir_to_input2(wdata, &payload[10], false);
-       ir_to_input3(wdata, &payload[12], true);
-       input_sync(wdata->ir);
-}
-
-static void handler_drm_E(struct wiimote_data *wdata, const __u8 *payload)
-{
-}
-
-static void handler_drm_SKAI1(struct wiimote_data *wdata, const __u8 *payload)
-{
-       handler_keys(wdata, payload);
-
-       wdata->state.accel_split[0] = payload[2];
-       wdata->state.accel_split[1] = (payload[0] >> 1) & (0x10 | 0x20);
-       wdata->state.accel_split[1] |= (payload[1] << 1) & (0x40 | 0x80);
-
-       ir_to_input0(wdata, &payload[3], false);
-       ir_to_input1(wdata, &payload[12], false);
-       input_sync(wdata->ir);
-}
-
-static void handler_drm_SKAI2(struct wiimote_data *wdata, const __u8 *payload)
-{
-       __u8 buf[5];
-
-       handler_keys(wdata, payload);
-
-       wdata->state.accel_split[1] |= (payload[0] >> 5) & (0x01 | 0x02);
-       wdata->state.accel_split[1] |= (payload[1] >> 3) & (0x04 | 0x08);
-
-       buf[0] = 0;
-       buf[1] = 0;
-       buf[2] = wdata->state.accel_split[0];
-       buf[3] = payload[2];
-       buf[4] = wdata->state.accel_split[1];
-       handler_accel(wdata, buf);
-
-       ir_to_input2(wdata, &payload[3], false);
-       ir_to_input3(wdata, &payload[12], false);
-       input_sync(wdata->ir);
-}
-
-struct wiiproto_handler {
-       __u8 id;
-       size_t size;
-       void (*func)(struct wiimote_data *wdata, const __u8 *payload);
-};
-
-static struct wiiproto_handler handlers[] = {
-       { .id = WIIPROTO_REQ_STATUS, .size = 6, .func = handler_status },
-       { .id = WIIPROTO_REQ_DATA, .size = 21, .func = handler_data },
-       { .id = WIIPROTO_REQ_RETURN, .size = 4, .func = handler_return },
-       { .id = WIIPROTO_REQ_DRM_K, .size = 2, .func = handler_keys },
-       { .id = WIIPROTO_REQ_DRM_KA, .size = 5, .func = handler_drm_KA },
-       { .id = WIIPROTO_REQ_DRM_KE, .size = 10, .func = handler_drm_KE },
-       { .id = WIIPROTO_REQ_DRM_KAI, .size = 17, .func = handler_drm_KAI },
-       { .id = WIIPROTO_REQ_DRM_KEE, .size = 21, .func = handler_drm_KEE },
-       { .id = WIIPROTO_REQ_DRM_KAE, .size = 21, .func = handler_drm_KAE },
-       { .id = WIIPROTO_REQ_DRM_KIE, .size = 21, .func = handler_drm_KIE },
-       { .id = WIIPROTO_REQ_DRM_KAIE, .size = 21, .func = handler_drm_KAIE },
-       { .id = WIIPROTO_REQ_DRM_E, .size = 21, .func = handler_drm_E },
-       { .id = WIIPROTO_REQ_DRM_SKAI1, .size = 21, .func = handler_drm_SKAI1 },
-       { .id = WIIPROTO_REQ_DRM_SKAI2, .size = 21, .func = handler_drm_SKAI2 },
-       { .id = 0 }
-};
-
-static int wiimote_hid_event(struct hid_device *hdev, struct hid_report *report,
-                                                       u8 *raw_data, int size)
-{
-       struct wiimote_data *wdata = hid_get_drvdata(hdev);
-       struct wiiproto_handler *h;
-       int i;
-       unsigned long flags;
-       bool handled = false;
-
-       if (size < 1)
-               return -EINVAL;
-
-       spin_lock_irqsave(&wdata->state.lock, flags);
-
-       for (i = 0; handlers[i].id; ++i) {
-               h = &handlers[i];
-               if (h->id == raw_data[0] && h->size < size) {
-                       h->func(wdata, &raw_data[1]);
-                       handled = true;
-               }
-       }
-
-       if (!handled)
-               hid_warn(hdev, "Unhandled report %hhu size %d\n", raw_data[0],
-                                                                       size);
-
-       spin_unlock_irqrestore(&wdata->state.lock, flags);
-
-       return 0;
-}
-
-static void wiimote_leds_destroy(struct wiimote_data *wdata)
-{
-       int i;
-       struct led_classdev *led;
-
-       for (i = 0; i < 4; ++i) {
-               if (wdata->leds[i]) {
-                       led = wdata->leds[i];
-                       wdata->leds[i] = NULL;
-                       led_classdev_unregister(led);
-                       kfree(led);
-               }
-       }
-}
-
-static int wiimote_leds_create(struct wiimote_data *wdata)
-{
-       int i, ret;
-       struct device *dev = &wdata->hdev->dev;
-       size_t namesz = strlen(dev_name(dev)) + 9;
-       struct led_classdev *led;
-       char *name;
-
-       for (i = 0; i < 4; ++i) {
-               led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL);
-               if (!led) {
-                       ret = -ENOMEM;
-                       goto err;
-               }
-               name = (void*)&led[1];
-               snprintf(name, namesz, "%s:blue:p%d", dev_name(dev), i);
-               led->name = name;
-               led->brightness = 0;
-               led->max_brightness = 1;
-               led->brightness_get = wiimote_leds_get;
-               led->brightness_set = wiimote_leds_set;
-
-               ret = led_classdev_register(dev, led);
-               if (ret) {
-                       kfree(led);
-                       goto err;
-               }
-               wdata->leds[i] = led;
-       }
-
-       return 0;
-
-err:
-       wiimote_leds_destroy(wdata);
-       return ret;
-}
-
-static struct wiimote_data *wiimote_create(struct hid_device *hdev)
-{
-       struct wiimote_data *wdata;
-       int i;
-
-       wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
-       if (!wdata)
-               return NULL;
-
-       wdata->input = input_allocate_device();
-       if (!wdata->input)
-               goto err;
-
-       wdata->hdev = hdev;
-       hid_set_drvdata(hdev, wdata);
-
-       input_set_drvdata(wdata->input, wdata);
-       wdata->input->open = wiimote_input_open;
-       wdata->input->close = wiimote_input_close;
-       wdata->input->dev.parent = &wdata->hdev->dev;
-       wdata->input->id.bustype = wdata->hdev->bus;
-       wdata->input->id.vendor = wdata->hdev->vendor;
-       wdata->input->id.product = wdata->hdev->product;
-       wdata->input->id.version = wdata->hdev->version;
-       wdata->input->name = WIIMOTE_NAME;
-
-       set_bit(EV_KEY, wdata->input->evbit);
-       for (i = 0; i < WIIPROTO_KEY_COUNT; ++i)
-               set_bit(wiiproto_keymap[i], wdata->input->keybit);
-
-       set_bit(FF_RUMBLE, wdata->input->ffbit);
-       if (input_ff_create_memless(wdata->input, NULL, wiimote_ff_play))
-               goto err_input;
-
-       wdata->accel = input_allocate_device();
-       if (!wdata->accel)
-               goto err_input;
-
-       input_set_drvdata(wdata->accel, wdata);
-       wdata->accel->open = wiimote_accel_open;
-       wdata->accel->close = wiimote_accel_close;
-       wdata->accel->dev.parent = &wdata->hdev->dev;
-       wdata->accel->id.bustype = wdata->hdev->bus;
-       wdata->accel->id.vendor = wdata->hdev->vendor;
-       wdata->accel->id.product = wdata->hdev->product;
-       wdata->accel->id.version = wdata->hdev->version;
-       wdata->accel->name = WIIMOTE_NAME " Accelerometer";
-
-       set_bit(EV_ABS, wdata->accel->evbit);
-       set_bit(ABS_RX, wdata->accel->absbit);
-       set_bit(ABS_RY, wdata->accel->absbit);
-       set_bit(ABS_RZ, wdata->accel->absbit);
-       input_set_abs_params(wdata->accel, ABS_RX, -500, 500, 2, 4);
-       input_set_abs_params(wdata->accel, ABS_RY, -500, 500, 2, 4);
-       input_set_abs_params(wdata->accel, ABS_RZ, -500, 500, 2, 4);
-
-       wdata->ir = input_allocate_device();
-       if (!wdata->ir)
-               goto err_ir;
-
-       input_set_drvdata(wdata->ir, wdata);
-       wdata->ir->open = wiimote_ir_open;
-       wdata->ir->close = wiimote_ir_close;
-       wdata->ir->dev.parent = &wdata->hdev->dev;
-       wdata->ir->id.bustype = wdata->hdev->bus;
-       wdata->ir->id.vendor = wdata->hdev->vendor;
-       wdata->ir->id.product = wdata->hdev->product;
-       wdata->ir->id.version = wdata->hdev->version;
-       wdata->ir->name = WIIMOTE_NAME " IR";
-
-       set_bit(EV_ABS, wdata->ir->evbit);
-       set_bit(ABS_HAT0X, wdata->ir->absbit);
-       set_bit(ABS_HAT0Y, wdata->ir->absbit);
-       set_bit(ABS_HAT1X, wdata->ir->absbit);
-       set_bit(ABS_HAT1Y, wdata->ir->absbit);
-       set_bit(ABS_HAT2X, wdata->ir->absbit);
-       set_bit(ABS_HAT2Y, wdata->ir->absbit);
-       set_bit(ABS_HAT3X, wdata->ir->absbit);
-       set_bit(ABS_HAT3Y, wdata->ir->absbit);
-       input_set_abs_params(wdata->ir, ABS_HAT0X, 0, 1023, 2, 4);
-       input_set_abs_params(wdata->ir, ABS_HAT0Y, 0, 767, 2, 4);
-       input_set_abs_params(wdata->ir, ABS_HAT1X, 0, 1023, 2, 4);
-       input_set_abs_params(wdata->ir, ABS_HAT1Y, 0, 767, 2, 4);
-       input_set_abs_params(wdata->ir, ABS_HAT2X, 0, 1023, 2, 4);
-       input_set_abs_params(wdata->ir, ABS_HAT2Y, 0, 767, 2, 4);
-       input_set_abs_params(wdata->ir, ABS_HAT3X, 0, 1023, 2, 4);
-       input_set_abs_params(wdata->ir, ABS_HAT3Y, 0, 767, 2, 4);
-
-       spin_lock_init(&wdata->qlock);
-       INIT_WORK(&wdata->worker, wiimote_worker);
-
-       spin_lock_init(&wdata->state.lock);
-       init_completion(&wdata->state.ready);
-       mutex_init(&wdata->state.sync);
-
-       return wdata;
-
-err_ir:
-       input_free_device(wdata->accel);
-err_input:
-       input_free_device(wdata->input);
-err:
-       kfree(wdata);
-       return NULL;
-}
-
-static void wiimote_destroy(struct wiimote_data *wdata)
-{
-       wiimote_leds_destroy(wdata);
-
-       power_supply_unregister(&wdata->battery);
-       input_unregister_device(wdata->accel);
-       input_unregister_device(wdata->ir);
-       input_unregister_device(wdata->input);
-       cancel_work_sync(&wdata->worker);
-       hid_hw_stop(wdata->hdev);
-
-       kfree(wdata);
-}
-
-static int wiimote_hid_probe(struct hid_device *hdev,
-                               const struct hid_device_id *id)
-{
-       struct wiimote_data *wdata;
-       int ret;
-
-       wdata = wiimote_create(hdev);
-       if (!wdata) {
-               hid_err(hdev, "Can't alloc device\n");
-               return -ENOMEM;
-       }
-
-       ret = hid_parse(hdev);
-       if (ret) {
-               hid_err(hdev, "HID parse failed\n");
-               goto err;
-       }
-
-       ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
-       if (ret) {
-               hid_err(hdev, "HW start failed\n");
-               goto err;
-       }
-
-       ret = input_register_device(wdata->accel);
-       if (ret) {
-               hid_err(hdev, "Cannot register input device\n");
-               goto err_stop;
-       }
-
-       ret = input_register_device(wdata->ir);
-       if (ret) {
-               hid_err(hdev, "Cannot register input device\n");
-               goto err_ir;
-       }
-
-       ret = input_register_device(wdata->input);
-       if (ret) {
-               hid_err(hdev, "Cannot register input device\n");
-               goto err_input;
-       }
-
-       wdata->battery.properties = wiimote_battery_props;
-       wdata->battery.num_properties = ARRAY_SIZE(wiimote_battery_props);
-       wdata->battery.get_property = wiimote_battery_get_property;
-       wdata->battery.name = "wiimote_battery";
-       wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
-       wdata->battery.use_for_apm = 0;
-
-       ret = power_supply_register(&wdata->hdev->dev, &wdata->battery);
-       if (ret) {
-               hid_err(hdev, "Cannot register battery device\n");
-               goto err_battery;
-       }
-
-       ret = wiimote_leds_create(wdata);
-       if (ret)
-               goto err_free;
-
-       hid_info(hdev, "New device registered\n");
-
-       /* by default set led1 after device initialization */
-       spin_lock_irq(&wdata->state.lock);
-       wiiproto_req_leds(wdata, WIIPROTO_FLAG_LED1);
-       spin_unlock_irq(&wdata->state.lock);
-
-       return 0;
-
-err_free:
-       wiimote_destroy(wdata);
-       return ret;
-
-err_battery:
-       input_unregister_device(wdata->input);
-       wdata->input = NULL;
-err_input:
-       input_unregister_device(wdata->ir);
-       wdata->ir = NULL;
-err_ir:
-       input_unregister_device(wdata->accel);
-       wdata->accel = NULL;
-err_stop:
-       hid_hw_stop(hdev);
-err:
-       input_free_device(wdata->ir);
-       input_free_device(wdata->accel);
-       input_free_device(wdata->input);
-       kfree(wdata);
-       return ret;
-}
-
-static void wiimote_hid_remove(struct hid_device *hdev)
-{
-       struct wiimote_data *wdata = hid_get_drvdata(hdev);
-
-       hid_info(hdev, "Device removed\n");
-       wiimote_destroy(wdata);
-}
-
-static const struct hid_device_id wiimote_hid_devices[] = {
-       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,
-                               USB_DEVICE_ID_NINTENDO_WIIMOTE) },
-       { }
-};
-MODULE_DEVICE_TABLE(hid, wiimote_hid_devices);
-
-static struct hid_driver wiimote_hid_driver = {
-       .name = "wiimote",
-       .id_table = wiimote_hid_devices,
-       .probe = wiimote_hid_probe,
-       .remove = wiimote_hid_remove,
-       .raw_event = wiimote_hid_event,
-};
-
-static int __init wiimote_init(void)
-{
-       int ret;
-
-       ret = hid_register_driver(&wiimote_hid_driver);
-       if (ret)
-               pr_err("Can't register wiimote hid driver\n");
-
-       return ret;
-}
-
-static void __exit wiimote_exit(void)
-{
-       hid_unregister_driver(&wiimote_hid_driver);
-}
-
-module_init(wiimote_init);
-module_exit(wiimote_exit);
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
-MODULE_DESCRIPTION(WIIMOTE_NAME " Device Driver");
-MODULE_VERSION(WIIMOTE_VERSION);
diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h
new file mode 100644 (file)
index 0000000..c81dbeb
--- /dev/null
@@ -0,0 +1,208 @@
+#ifndef __HID_WIIMOTE_H
+#define __HID_WIIMOTE_H
+
+/*
+ * HID driver for Nintendo Wiimote devices
+ * Copyright (c) 2011 David Herrmann
+ */
+
+/*
+ * 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/completion.h>
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/power_supply.h>
+#include <linux/spinlock.h>
+
+#define WIIMOTE_NAME "Nintendo Wii Remote"
+#define WIIMOTE_BUFSIZE 32
+
+#define WIIPROTO_FLAG_LED1             0x01
+#define WIIPROTO_FLAG_LED2             0x02
+#define WIIPROTO_FLAG_LED3             0x04
+#define WIIPROTO_FLAG_LED4             0x08
+#define WIIPROTO_FLAG_RUMBLE           0x10
+#define WIIPROTO_FLAG_ACCEL            0x20
+#define WIIPROTO_FLAG_IR_BASIC         0x40
+#define WIIPROTO_FLAG_IR_EXT           0x80
+#define WIIPROTO_FLAG_IR_FULL          0xc0 /* IR_BASIC | IR_EXT */
+#define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \
+                                       WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4)
+#define WIIPROTO_FLAGS_IR (WIIPROTO_FLAG_IR_BASIC | WIIPROTO_FLAG_IR_EXT | \
+                                                       WIIPROTO_FLAG_IR_FULL)
+
+/* return flag for led \num */
+#define WIIPROTO_FLAG_LED(num) (WIIPROTO_FLAG_LED1 << (num - 1))
+
+struct wiimote_buf {
+       __u8 data[HID_MAX_BUFFER_SIZE];
+       size_t size;
+};
+
+struct wiimote_state {
+       spinlock_t lock;
+       __u8 flags;
+       __u8 accel_split[2];
+       __u8 drm;
+
+       /* synchronous cmd requests */
+       struct mutex sync;
+       struct completion ready;
+       int cmd;
+       __u32 opt;
+
+       /* results of synchronous requests */
+       __u8 cmd_battery;
+       __u8 cmd_err;
+       __u8 *cmd_read_buf;
+       __u8 cmd_read_size;
+};
+
+struct wiimote_data {
+       struct hid_device *hdev;
+       struct input_dev *input;
+       struct led_classdev *leds[4];
+       struct input_dev *accel;
+       struct input_dev *ir;
+       struct power_supply battery;
+       struct wiimote_ext *ext;
+       struct wiimote_debug *debug;
+
+       spinlock_t qlock;
+       __u8 head;
+       __u8 tail;
+       struct wiimote_buf outq[WIIMOTE_BUFSIZE];
+       struct work_struct worker;
+
+       struct wiimote_state state;
+};
+
+enum wiiproto_reqs {
+       WIIPROTO_REQ_NULL = 0x0,
+       WIIPROTO_REQ_RUMBLE = 0x10,
+       WIIPROTO_REQ_LED = 0x11,
+       WIIPROTO_REQ_DRM = 0x12,
+       WIIPROTO_REQ_IR1 = 0x13,
+       WIIPROTO_REQ_SREQ = 0x15,
+       WIIPROTO_REQ_WMEM = 0x16,
+       WIIPROTO_REQ_RMEM = 0x17,
+       WIIPROTO_REQ_IR2 = 0x1a,
+       WIIPROTO_REQ_STATUS = 0x20,
+       WIIPROTO_REQ_DATA = 0x21,
+       WIIPROTO_REQ_RETURN = 0x22,
+       WIIPROTO_REQ_DRM_K = 0x30,
+       WIIPROTO_REQ_DRM_KA = 0x31,
+       WIIPROTO_REQ_DRM_KE = 0x32,
+       WIIPROTO_REQ_DRM_KAI = 0x33,
+       WIIPROTO_REQ_DRM_KEE = 0x34,
+       WIIPROTO_REQ_DRM_KAE = 0x35,
+       WIIPROTO_REQ_DRM_KIE = 0x36,
+       WIIPROTO_REQ_DRM_KAIE = 0x37,
+       WIIPROTO_REQ_DRM_E = 0x3d,
+       WIIPROTO_REQ_DRM_SKAI1 = 0x3e,
+       WIIPROTO_REQ_DRM_SKAI2 = 0x3f,
+       WIIPROTO_REQ_MAX
+};
+
+#define dev_to_wii(pdev) hid_get_drvdata(container_of(pdev, struct hid_device, \
+                                                                       dev))
+
+extern void wiiproto_req_drm(struct wiimote_data *wdata, __u8 drm);
+extern int wiimote_cmd_write(struct wiimote_data *wdata, __u32 offset,
+                                               const __u8 *wmem, __u8 size);
+extern ssize_t wiimote_cmd_read(struct wiimote_data *wdata, __u32 offset,
+                                                       __u8 *rmem, __u8 size);
+
+#define wiiproto_req_rreg(wdata, os, sz) \
+                               wiiproto_req_rmem((wdata), false, (os), (sz))
+#define wiiproto_req_reeprom(wdata, os, sz) \
+                               wiiproto_req_rmem((wdata), true, (os), (sz))
+extern void wiiproto_req_rmem(struct wiimote_data *wdata, bool eeprom,
+                                               __u32 offset, __u16 size);
+
+#ifdef CONFIG_HID_WIIMOTE_EXT
+
+extern int wiiext_init(struct wiimote_data *wdata);
+extern void wiiext_deinit(struct wiimote_data *wdata);
+extern void wiiext_event(struct wiimote_data *wdata, bool plugged);
+extern bool wiiext_active(struct wiimote_data *wdata);
+extern void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload);
+
+#else
+
+static inline int wiiext_init(void *u) { return 0; }
+static inline void wiiext_deinit(void *u) { }
+static inline void wiiext_event(void *u, bool p) { }
+static inline bool wiiext_active(void *u) { return false; }
+static inline void wiiext_handle(void *u, const __u8 *p) { }
+
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+
+extern int wiidebug_init(struct wiimote_data *wdata);
+extern void wiidebug_deinit(struct wiimote_data *wdata);
+
+#else
+
+static inline int wiidebug_init(void *u) { return 0; }
+static inline void wiidebug_deinit(void *u) { }
+
+#endif
+
+/* requires the state.lock spinlock to be held */
+static inline bool wiimote_cmd_pending(struct wiimote_data *wdata, int cmd,
+                                                               __u32 opt)
+{
+       return wdata->state.cmd == cmd && wdata->state.opt == opt;
+}
+
+/* requires the state.lock spinlock to be held */
+static inline void wiimote_cmd_complete(struct wiimote_data *wdata)
+{
+       wdata->state.cmd = WIIPROTO_REQ_NULL;
+       complete(&wdata->state.ready);
+}
+
+static inline int wiimote_cmd_acquire(struct wiimote_data *wdata)
+{
+       return mutex_lock_interruptible(&wdata->state.sync) ? -ERESTARTSYS : 0;
+}
+
+/* requires the state.lock spinlock to be held */
+static inline void wiimote_cmd_set(struct wiimote_data *wdata, int cmd,
+                                                               __u32 opt)
+{
+       INIT_COMPLETION(wdata->state.ready);
+       wdata->state.cmd = cmd;
+       wdata->state.opt = opt;
+}
+
+static inline void wiimote_cmd_release(struct wiimote_data *wdata)
+{
+       mutex_unlock(&wdata->state.sync);
+}
+
+static inline int wiimote_cmd_wait(struct wiimote_data *wdata)
+{
+       int ret;
+
+       ret = wait_for_completion_interruptible_timeout(&wdata->state.ready, HZ);
+       if (ret < 0)
+               return -ERESTARTSYS;
+       else if (ret == 0)
+               return -EIO;
+       else
+               return 0;
+}
+
+#endif
index b403fcef0b8693fbeb3eb44c9d80fd8735757060..5bf91dbad59d4181bdf23fdeda7bd7327117933b 100644 (file)
@@ -197,16 +197,24 @@ static int usbhid_restart_out_queue(struct usbhid_device *usbhid)
 {
        struct hid_device *hid = usb_get_intfdata(usbhid->intf);
        int kicked;
+       int r;
 
        if (!hid)
                return 0;
 
        if ((kicked = (usbhid->outhead != usbhid->outtail))) {
                dbg("Kicking head %d tail %d", usbhid->outhead, usbhid->outtail);
+
+               r = usb_autopm_get_interface_async(usbhid->intf);
+               if (r < 0)
+                       return r;
+               /* Asynchronously flush queue. */
+               set_bit(HID_OUT_RUNNING, &usbhid->iofl);
                if (hid_submit_out(hid)) {
                        clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
-                       wake_up(&usbhid->wait);
+                       usb_autopm_put_interface_async(usbhid->intf);
                }
+               wake_up(&usbhid->wait);
        }
        return kicked;
 }
@@ -215,6 +223,7 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid)
 {
        struct hid_device *hid = usb_get_intfdata(usbhid->intf);
        int kicked;
+       int r;
 
        WARN_ON(hid == NULL);
        if (!hid)
@@ -222,10 +231,17 @@ static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid)
 
        if ((kicked = (usbhid->ctrlhead != usbhid->ctrltail))) {
                dbg("Kicking head %d tail %d", usbhid->ctrlhead, usbhid->ctrltail);
+
+               r = usb_autopm_get_interface_async(usbhid->intf);
+               if (r < 0)
+                       return r;
+               /* Asynchronously flush queue. */
+               set_bit(HID_CTRL_RUNNING, &usbhid->iofl);
                if (hid_submit_ctrl(hid)) {
                        clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
-                       wake_up(&usbhid->wait);
+                       usb_autopm_put_interface_async(usbhid->intf);
                }
+               wake_up(&usbhid->wait);
        }
        return kicked;
 }
@@ -304,30 +320,21 @@ static int hid_submit_out(struct hid_device *hid)
        report = usbhid->out[usbhid->outtail].report;
        raw_report = usbhid->out[usbhid->outtail].raw_report;
 
-       r = usb_autopm_get_interface_async(usbhid->intf);
-       if (r < 0)
-               return -1;
-
-       /*
-        * if the device hasn't been woken, we leave the output
-        * to resume()
-        */
-       if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) {
-               usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
-               usbhid->urbout->dev = hid_to_usb_dev(hid);
-               memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length);
-               kfree(raw_report);
+       usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) +
+                                                1 + (report->id > 0);
+       usbhid->urbout->dev = hid_to_usb_dev(hid);
+       memcpy(usbhid->outbuf, raw_report,
+              usbhid->urbout->transfer_buffer_length);
+       kfree(raw_report);
 
-               dbg_hid("submitting out urb\n");
+       dbg_hid("submitting out urb\n");
 
-               if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) {
-                       hid_err(hid, "usb_submit_urb(out) failed\n");
-                       usb_autopm_put_interface_async(usbhid->intf);
-                       return -1;
-               }
-               usbhid->last_out = jiffies;
+       r = usb_submit_urb(usbhid->urbout, GFP_ATOMIC);
+       if (r < 0) {
+               hid_err(hid, "usb_submit_urb(out) failed: %d\n", r);
+               return r;
        }
-
+       usbhid->last_out = jiffies;
        return 0;
 }
 
@@ -343,50 +350,48 @@ static int hid_submit_ctrl(struct hid_device *hid)
        raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report;
        dir = usbhid->ctrl[usbhid->ctrltail].dir;
 
-       r = usb_autopm_get_interface_async(usbhid->intf);
-       if (r < 0)
-               return -1;
-       if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) {
-               len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
-               if (dir == USB_DIR_OUT) {
-                       usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
-                       usbhid->urbctrl->transfer_buffer_length = len;
-                       memcpy(usbhid->ctrlbuf, raw_report, len);
-                       kfree(raw_report);
-               } else {
-                       int maxpacket, padlen;
-
-                       usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
-                       maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0);
-                       if (maxpacket > 0) {
-                               padlen = DIV_ROUND_UP(len, maxpacket);
-                               padlen *= maxpacket;
-                               if (padlen > usbhid->bufsize)
-                                       padlen = usbhid->bufsize;
-                       } else
-                               padlen = 0;
-                       usbhid->urbctrl->transfer_buffer_length = padlen;
-               }
-               usbhid->urbctrl->dev = hid_to_usb_dev(hid);
-
-               usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
-               usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT;
-               usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id);
-               usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
-               usbhid->cr->wLength = cpu_to_le16(len);
-
-               dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n",
-                       usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
-                       usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
-
-               if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) {
-                       usb_autopm_put_interface_async(usbhid->intf);
-                       hid_err(hid, "usb_submit_urb(ctrl) failed\n");
-                       return -1;
-               }
-               usbhid->last_ctrl = jiffies;
+       len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
+       if (dir == USB_DIR_OUT) {
+               usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
+               usbhid->urbctrl->transfer_buffer_length = len;
+               memcpy(usbhid->ctrlbuf, raw_report, len);
+               kfree(raw_report);
+       } else {
+               int maxpacket, padlen;
+
+               usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
+               maxpacket = usb_maxpacket(hid_to_usb_dev(hid),
+                                         usbhid->urbctrl->pipe, 0);
+               if (maxpacket > 0) {
+                       padlen = DIV_ROUND_UP(len, maxpacket);
+                       padlen *= maxpacket;
+                       if (padlen > usbhid->bufsize)
+                               padlen = usbhid->bufsize;
+               } else
+                       padlen = 0;
+               usbhid->urbctrl->transfer_buffer_length = padlen;
        }
-
+       usbhid->urbctrl->dev = hid_to_usb_dev(hid);
+
+       usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
+       usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT :
+                                                     HID_REQ_GET_REPORT;
+       usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) |
+                                        report->id);
+       usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
+       usbhid->cr->wLength = cpu_to_le16(len);
+
+       dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n",
+               usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" :
+                                                            "Get_Report",
+               usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
+
+       r = usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC);
+       if (r < 0) {
+               hid_err(hid, "usb_submit_urb(ctrl) failed: %d\n", r);
+               return r;
+       }
+       usbhid->last_ctrl = jiffies;
        return 0;
 }
 
@@ -423,11 +428,8 @@ static void hid_irq_out(struct urb *urb)
        else
                usbhid->outtail = (usbhid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1);
 
-       if (usbhid->outhead != usbhid->outtail) {
-               if (hid_submit_out(hid)) {
-                       clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
-                       wake_up(&usbhid->wait);
-               }
+       if (usbhid->outhead != usbhid->outtail && !hid_submit_out(hid)) {
+               /* Successfully submitted next urb in queue */
                spin_unlock_irqrestore(&usbhid->lock, flags);
                return;
        }
@@ -474,13 +476,9 @@ static void hid_ctrl(struct urb *urb)
        else
                usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
 
-       if (usbhid->ctrlhead != usbhid->ctrltail) {
-               if (hid_submit_ctrl(hid)) {
-                       clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
-                       wake_up(&usbhid->wait);
-               }
+       if (usbhid->ctrlhead != usbhid->ctrltail && !hid_submit_ctrl(hid)) {
+               /* Successfully submitted next urb in queue */
                spin_unlock(&usbhid->lock);
-               usb_autopm_put_interface_async(usbhid->intf);
                return;
        }
 
@@ -515,9 +513,23 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
                usbhid->out[usbhid->outhead].report = report;
                usbhid->outhead = head;
 
+               /* Try to awake from autosuspend... */
+               if (usb_autopm_get_interface_async(usbhid->intf) < 0)
+                       return;
+
+               /*
+                * But if still suspended, leave urb enqueued, don't submit.
+                * Submission will occur if/when resume() drains the queue.
+                */
+               if (test_bit(HID_REPORTED_IDLE, &usbhid->iofl))
+                       return;
+
                if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) {
-                       if (hid_submit_out(hid))
+                       if (hid_submit_out(hid)) {
                                clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
+                               usb_autopm_put_interface_async(usbhid->intf);
+                       }
+                       wake_up(&usbhid->wait);
                } else {
                        /*
                         * the queue is known to run
@@ -549,9 +561,23 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re
        usbhid->ctrl[usbhid->ctrlhead].dir = dir;
        usbhid->ctrlhead = head;
 
+       /* Try to awake from autosuspend... */
+       if (usb_autopm_get_interface_async(usbhid->intf) < 0)
+               return;
+
+       /*
+        * If already suspended, leave urb enqueued, but don't submit.
+        * Submission will occur if/when resume() drains the queue.
+        */
+       if (test_bit(HID_REPORTED_IDLE, &usbhid->iofl))
+               return;
+
        if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) {
-               if (hid_submit_ctrl(hid))
+               if (hid_submit_ctrl(hid)) {
                        clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
+                       usb_autopm_put_interface_async(usbhid->intf);
+               }
+               wake_up(&usbhid->wait);
        } else {
                /*
                 * the queue is known to run
@@ -576,6 +602,30 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
 }
 EXPORT_SYMBOL_GPL(usbhid_submit_report);
 
+/* Workqueue routine to send requests to change LEDs */
+static void hid_led(struct work_struct *work)
+{
+       struct usbhid_device *usbhid =
+               container_of(work, struct usbhid_device, led_work);
+       struct hid_device *hid = usbhid->hid;
+       struct hid_field *field;
+       unsigned long flags;
+
+       field = hidinput_get_led_field(hid);
+       if (!field) {
+               hid_warn(hid, "LED event field not found\n");
+               return;
+       }
+
+       spin_lock_irqsave(&usbhid->lock, flags);
+       if (!test_bit(HID_DISCONNECTED, &usbhid->iofl)) {
+               usbhid->ledcount = hidinput_count_leds(hid);
+               hid_dbg(usbhid->hid, "New ledcount = %u\n", usbhid->ledcount);
+               __usbhid_submit_report(hid, field->report, USB_DIR_OUT);
+       }
+       spin_unlock_irqrestore(&usbhid->lock, flags);
+}
+
 static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
 {
        struct hid_device *hid = input_get_drvdata(dev);
@@ -595,17 +645,15 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un
                return -1;
        }
 
+       spin_lock_irqsave(&usbhid->lock, flags);
        hid_set_field(field, offset, value);
-       if (value) {
-               spin_lock_irqsave(&usbhid->lock, flags);
-               usbhid->ledcount++;
-               spin_unlock_irqrestore(&usbhid->lock, flags);
-       } else {
-               spin_lock_irqsave(&usbhid->lock, flags);
-               usbhid->ledcount--;
-               spin_unlock_irqrestore(&usbhid->lock, flags);
-       }
-       usbhid_submit_report(hid, field->report, USB_DIR_OUT);
+       spin_unlock_irqrestore(&usbhid->lock, flags);
+
+       /*
+        * Defer performing requested LED action.
+        * This is more likely gather all LED changes into a single URB.
+        */
+       schedule_work(&usbhid->led_work);
 
        return 0;
 }
@@ -1100,7 +1148,7 @@ static void usbhid_stop(struct hid_device *hid)
                return;
 
        clear_bit(HID_STARTED, &usbhid->iofl);
-       spin_lock_irq(&usbhid->lock);   /* Sync with error handler */
+       spin_lock_irq(&usbhid->lock);   /* Sync with error and led handlers */
        set_bit(HID_DISCONNECTED, &usbhid->iofl);
        spin_unlock_irq(&usbhid->lock);
        usb_kill_urb(usbhid->urbin);
@@ -1234,6 +1282,8 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
        setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
        spin_lock_init(&usbhid->lock);
 
+       INIT_WORK(&usbhid->led_work, hid_led);
+
        ret = hid_add_device(hid);
        if (ret) {
                if (ret != -ENODEV)
@@ -1266,6 +1316,7 @@ static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid)
 {
        del_timer_sync(&usbhid->io_retry);
        cancel_work_sync(&usbhid->reset_work);
+       cancel_work_sync(&usbhid->led_work);
 }
 
 static void hid_cease_io(struct usbhid_device *usbhid)
@@ -1367,16 +1418,6 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
                        return -EIO;
        }
 
-       if (!ignoreled && PMSG_IS_AUTO(message)) {
-               spin_lock_irq(&usbhid->lock);
-               if (test_bit(HID_LED_ON, &usbhid->iofl)) {
-                       spin_unlock_irq(&usbhid->lock);
-                       usbhid_mark_busy(usbhid);
-                       return -EBUSY;
-               }
-               spin_unlock_irq(&usbhid->lock);
-       }
-
        hid_cancel_delayed_stuff(usbhid);
        hid_cease_io(usbhid);
 
index 5028d60a22a19b6b69cd1c942728f350d40744b7..c831af937481c66123965743f62aaf5f8a634b3c 100644 (file)
@@ -47,6 +47,7 @@ static const struct hid_blacklist {
 
        { USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL },
 
+       { USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_GREENASIA, USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
@@ -67,6 +68,9 @@ static const struct hid_blacklist {
        { 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_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
index 1673cac93d77bfa22aba124c0a17ce6cc5c05e56..cb8f703efde5a00747b77d1bebc313e17864ae86 100644 (file)
@@ -55,7 +55,6 @@ struct usb_interface *usbhid_find_interface(int minor);
 #define HID_STARTED            8
 #define HID_REPORTED_IDLE      9
 #define HID_KEYS_PRESSED       10
-#define HID_LED_ON             11
 
 /*
  * USB-specific HID struct, to be pointed to
@@ -97,6 +96,8 @@ struct usbhid_device {
        struct work_struct reset_work;                                  /* Task context for resets */
        wait_queue_head_t wait;                                         /* For sleeping */
        int ledcount;                                                   /* counting the number of active leds */
+
+       struct work_struct led_work;                                    /* Task context for setting LEDs */
 };
 
 #define        hid_to_usb_dev(hid_dev) \
index bc445d7e3bf5213a7f4fa9d4b31acbc8c140fef4..796086980f4a622dfe2857eec1d00ff33cdff787 100644 (file)
@@ -64,6 +64,32 @@ static const unsigned char usb_kbd_keycode[256] = {
        150,158,159,128,136,177,178,176,142,152,173,140
 };
 
+
+/**
+ * struct usb_kbd - state of each attached keyboard
+ * @dev:       input device associated with this keyboard
+ * @usbdev:    usb device associated with this keyboard
+ * @old:       data received in the past from the @irq URB representing which
+ *             keys were pressed. By comparing with the current list of keys
+ *             that are pressed, we are able to see key releases.
+ * @irq:       URB for receiving a list of keys that are pressed when a
+ *             new key is pressed or a key that was pressed is released.
+ * @led:       URB for sending LEDs (e.g. numlock, ...)
+ * @newleds:   data that will be sent with the @led URB representing which LEDs
+               should be on
+ * @name:      Name of the keyboard. @dev's name field points to this buffer
+ * @phys:      Physical path of the keyboard. @dev's phys field points to this
+ *             buffer
+ * @new:       Buffer for the @irq URB
+ * @cr:                Control request for @led URB
+ * @leds:      Buffer for the @led URB
+ * @new_dma:   DMA address for @irq URB
+ * @leds_dma:  DMA address for @led URB
+ * @leds_lock: spinlock that protects @leds, @newleds, and @led_urb_submitted
+ * @led_urb_submitted: indicates whether @led is in progress, i.e. it has been
+ *             submitted and its completion handler has not returned yet
+ *             without resubmitting @led
+ */
 struct usb_kbd {
        struct input_dev *dev;
        struct usb_device *usbdev;
@@ -78,6 +104,10 @@ struct usb_kbd {
        unsigned char *leds;
        dma_addr_t new_dma;
        dma_addr_t leds_dma;
+       
+       spinlock_t leds_lock;
+       bool led_urb_submitted;
+
 };
 
 static void usb_kbd_irq(struct urb *urb)
@@ -136,44 +166,66 @@ resubmit:
 static int usb_kbd_event(struct input_dev *dev, unsigned int type,
                         unsigned int code, int value)
 {
+       unsigned long flags;
        struct usb_kbd *kbd = input_get_drvdata(dev);
 
        if (type != EV_LED)
                return -1;
 
+       spin_lock_irqsave(&kbd->leds_lock, flags);
        kbd->newleds = (!!test_bit(LED_KANA,    dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
                       (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL,   dev->led) << 1) |
                       (!!test_bit(LED_NUML,    dev->led));
 
-       if (kbd->led->status == -EINPROGRESS)
+       if (kbd->led_urb_submitted){
+               spin_unlock_irqrestore(&kbd->leds_lock, flags);
                return 0;
+       }
 
-       if (*(kbd->leds) == kbd->newleds)
+       if (*(kbd->leds) == kbd->newleds){
+               spin_unlock_irqrestore(&kbd->leds_lock, flags);
                return 0;
+       }
 
        *(kbd->leds) = kbd->newleds;
+       
        kbd->led->dev = kbd->usbdev;
        if (usb_submit_urb(kbd->led, GFP_ATOMIC))
                pr_err("usb_submit_urb(leds) failed\n");
-
+       else
+               kbd->led_urb_submitted = true;
+       
+       spin_unlock_irqrestore(&kbd->leds_lock, flags);
+       
        return 0;
 }
 
 static void usb_kbd_led(struct urb *urb)
 {
+       unsigned long flags;
        struct usb_kbd *kbd = urb->context;
 
        if (urb->status)
                hid_warn(urb->dev, "led urb status %d received\n",
                         urb->status);
 
-       if (*(kbd->leds) == kbd->newleds)
+       spin_lock_irqsave(&kbd->leds_lock, flags);
+
+       if (*(kbd->leds) == kbd->newleds){
+               kbd->led_urb_submitted = false;
+               spin_unlock_irqrestore(&kbd->leds_lock, flags);
                return;
+       }
 
        *(kbd->leds) = kbd->newleds;
+       
        kbd->led->dev = kbd->usbdev;
-       if (usb_submit_urb(kbd->led, GFP_ATOMIC))
+       if (usb_submit_urb(kbd->led, GFP_ATOMIC)){
                hid_err(urb->dev, "usb_submit_urb(leds) failed\n");
+               kbd->led_urb_submitted = false;
+       }
+       spin_unlock_irqrestore(&kbd->leds_lock, flags);
+       
 }
 
 static int usb_kbd_open(struct input_dev *dev)
@@ -252,6 +304,7 @@ static int usb_kbd_probe(struct usb_interface *iface,
 
        kbd->usbdev = dev;
        kbd->dev = input_dev;
+       spin_lock_init(&kbd->leds_lock);
 
        if (dev->manufacturer)
                strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
@@ -334,6 +387,7 @@ static void usb_kbd_disconnect(struct usb_interface *intf)
        if (kbd) {
                usb_kill_urb(kbd->irq);
                input_unregister_device(kbd->dev);
+               usb_kill_urb(kbd->led);
                usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
                kfree(kbd);
        }
index 4af56fbc3c0691ccc243f05de359f838a9f8528f..12d03e7ad6366fea465687fb321d8384119f1377 100644 (file)
@@ -31,6 +31,8 @@ static const struct usb_device_id id_table[] = {
                        .driver_info = DELCOM_VISUAL_SIGNAL_INDICATOR },
        { USB_DEVICE(0x1d34, 0x0004),
                        .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
+       { USB_DEVICE(0x1d34, 0x000a),
+                       .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
        { },
 };
 MODULE_DEVICE_TABLE(usb, id_table);
index c235e4e8767c5468bdb3057d6513b386d99dc576..3a95da60fd3e5cda95617fffc824168437373d95 100644 (file)
@@ -72,6 +72,7 @@
 #include <linux/workqueue.h>
 #include <linux/input.h>
 #include <linux/semaphore.h>
+#include <linux/power_supply.h>
 
 /*
  * We parse each description item into this structure. Short items data
@@ -190,6 +191,7 @@ struct hid_item {
 #define HID_UP_UNDEFINED       0x00000000
 #define HID_UP_GENDESK         0x00010000
 #define HID_UP_SIMULATION      0x00020000
+#define HID_UP_GENDEVCTRLS     0x00060000
 #define HID_UP_KEYBOARD                0x00070000
 #define HID_UP_LED             0x00080000
 #define HID_UP_BUTTON          0x00090000
@@ -239,6 +241,8 @@ struct hid_item {
 #define HID_GD_RIGHT           0x00010092
 #define HID_GD_LEFT            0x00010093
 
+#define HID_DC_BATTERYSTRENGTH 0x00060020
+
 #define HID_DG_DIGITIZER       0x000d0001
 #define HID_DG_PEN             0x000d0002
 #define HID_DG_LIGHTPEN                0x000d0003
@@ -482,6 +486,19 @@ struct hid_device {                                                        /* device report descriptor */
        struct hid_driver *driver;
        struct hid_ll_driver *ll_driver;
 
+#ifdef CONFIG_HID_BATTERY_STRENGTH
+       /*
+        * Power supply information for HID devices which report
+        * battery strength. power_supply is registered iff
+        * battery.name is non-NULL.
+        */
+       struct power_supply battery;
+       __s32 battery_min;
+       __s32 battery_max;
+       __s32 battery_report_type;
+       __s32 battery_report_id;
+#endif
+
        unsigned int status;                                            /* see STAT flags above */
        unsigned claimed;                                               /* Claimed by hidinput, hiddev? */
        unsigned quirks;                                                /* Various quirks the device can pull on us */
@@ -712,6 +729,8 @@ extern void hidinput_disconnect(struct hid_device *);
 int hid_set_field(struct hid_field *, unsigned, __s32);
 int hid_input_report(struct hid_device *, int type, u8 *, int, int);
 int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field);
+struct hid_field *hidinput_get_led_field(struct hid_device *hid);
+unsigned int hidinput_count_leds(struct hid_device *hid);
 void hid_output_report(struct hid_report *report, __u8 *data);
 struct hid_device *hid_allocate_device(void);
 struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id);
@@ -719,6 +738,8 @@ int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);
 int hid_check_keys_pressed(struct hid_device *hid);
 int hid_connect(struct hid_device *hid, unsigned int connect_mask);
 void hid_disconnect(struct hid_device *hid);
+const struct hid_device_id *hid_match_id(struct hid_device *hdev,
+                                        const struct hid_device_id *id);
 
 /**
  * hid_map_usage - map usage input bits