Merge branches 'for-4.5/upstream-fixes', 'for-4.6/cmedia', 'for-4.6/i2c-hid', 'for...
authorJiri Kosina <jkosina@suse.cz>
Thu, 17 Mar 2016 12:51:54 +0000 (13:51 +0100)
committerJiri Kosina <jkosina@suse.cz>
Thu, 17 Mar 2016 12:51:54 +0000 (13:51 +0100)
18 files changed:
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-cmedia.c [new file with mode: 0644]
drivers/hid/hid-core.c
drivers/hid/hid-corsair.c
drivers/hid/hid-dr.c
drivers/hid/hid-ids.h
drivers/hid/hid-lg.c
drivers/hid/hid-logitech-hidpp.c
drivers/hid/hid-microsoft.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-penmount.c
drivers/hid/hid-rmi.c
drivers/hid/hid-sony.c
drivers/hid/hid-thingm.c
drivers/hid/usbhid/hid-quirks.c
drivers/hid/wacom_sys.c
drivers/hid/wacom_wac.c

index 513a16cc6e18536e1e38e23bf2068b99bb65f197..411722570035b0c2d3375991e82008c0d61a08fc 100644 (file)
@@ -196,6 +196,12 @@ config HID_PRODIKEYS
          multimedia keyboard, but will lack support for the musical keyboard
          and some additional multimedia keys.
 
+config HID_CMEDIA
+       tristate "CMedia CM6533 HID audio jack controls"
+       depends on HID
+       ---help---
+       Support for CMedia CM6533 HID audio jack controls.
+
 config HID_CP2112
        tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support"
        depends on USB_HID && I2C && GPIOLIB
index 00011fee08b98f80fb032244041a1e3e7c45cb44..be56ab6f75a87c063229cb7bd39b5028c7d11ed9 100644 (file)
@@ -29,6 +29,7 @@ obj-$(CONFIG_HID_BELKIN)      += hid-belkin.o
 obj-$(CONFIG_HID_BETOP_FF)     += hid-betopff.o
 obj-$(CONFIG_HID_CHERRY)       += hid-cherry.o
 obj-$(CONFIG_HID_CHICONY)      += hid-chicony.o
+obj-$(CONFIG_HID_CMEDIA)       += hid-cmedia.o
 obj-$(CONFIG_HID_CORSAIR)      += hid-corsair.o
 obj-$(CONFIG_HID_CP2112)       += hid-cp2112.o
 obj-$(CONFIG_HID_CYPRESS)      += hid-cypress.o
diff --git a/drivers/hid/hid-cmedia.c b/drivers/hid/hid-cmedia.c
new file mode 100644 (file)
index 0000000..7230f85
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * HID driver for CMedia CM6533 audio jack controls
+ *
+ * Copyright (C) 2015 Ben Chen <ben_chen@bizlinktech.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include "hid-ids.h"
+
+MODULE_AUTHOR("Ben Chen");
+MODULE_DESCRIPTION("CM6533 HID jack controls");
+MODULE_LICENSE("GPL");
+
+#define CM6533_JD_TYPE_COUNT      1
+#define CM6533_JD_RAWEV_LEN     16
+#define CM6533_JD_SFX_OFFSET     8
+
+/*
+*
+*CM6533 audio jack HID raw events:
+*
+*Plug in:
+*01000600 002083xx 080008c0 10000000
+*about 3 seconds later...
+*01000a00 002083xx 08000380 10000000
+*01000600 002083xx 08000380 10000000
+*
+*Plug out:
+*01000400 002083xx 080008c0 x0000000
+*/
+
+static const u8 ji_sfx[] = { 0x08, 0x00, 0x08, 0xc0 };
+static const u8 ji_in[]  = { 0x01, 0x00, 0x06, 0x00 };
+static const u8 ji_out[] = { 0x01, 0x00, 0x04, 0x00 };
+
+static int jack_switch_types[CM6533_JD_TYPE_COUNT] = {
+       SW_HEADPHONE_INSERT,
+};
+
+struct cmhid {
+       struct input_dev *input_dev;
+       struct hid_device *hid;
+       unsigned short switch_map[CM6533_JD_TYPE_COUNT];
+};
+
+static void hp_ev(struct hid_device *hid, struct cmhid *cm, int value)
+{
+       input_report_switch(cm->input_dev, SW_HEADPHONE_INSERT, value);
+       input_sync(cm->input_dev);
+}
+
+static int cmhid_raw_event(struct hid_device *hid, struct hid_report *report,
+        u8 *data, int len)
+{
+       struct cmhid *cm = hid_get_drvdata(hid);
+
+       if (len != CM6533_JD_RAWEV_LEN)
+               goto out;
+       if (memcmp(data+CM6533_JD_SFX_OFFSET, ji_sfx, sizeof(ji_sfx)))
+               goto out;
+
+       if (!memcmp(data, ji_out, sizeof(ji_out))) {
+               hp_ev(hid, cm, 0);
+               goto out;
+       }
+       if (!memcmp(data, ji_in, sizeof(ji_in))) {
+               hp_ev(hid, cm, 1);
+               goto out;
+       }
+
+out:
+       return 0;
+}
+
+static int cmhid_input_configured(struct hid_device *hid,
+               struct hid_input *hidinput)
+{
+       struct input_dev *input_dev = hidinput->input;
+       struct cmhid *cm = hid_get_drvdata(hid);
+       int i;
+
+       cm->input_dev = input_dev;
+       memcpy(cm->switch_map, jack_switch_types, sizeof(cm->switch_map));
+       input_dev->evbit[0] = BIT(EV_SW);
+       for (i = 0; i < CM6533_JD_TYPE_COUNT; i++)
+               input_set_capability(cm->input_dev,
+                               EV_SW, jack_switch_types[i]);
+       return 0;
+}
+
+static int cmhid_input_mapping(struct hid_device *hid,
+               struct hid_input *hi, struct hid_field *field,
+               struct hid_usage *usage, unsigned long **bit, int *max)
+{
+       return -1;
+}
+
+static int cmhid_probe(struct hid_device *hid, const struct hid_device_id *id)
+{
+       int ret;
+       struct cmhid *cm;
+
+       cm = kzalloc(sizeof(struct cmhid), GFP_KERNEL);
+       if (!cm) {
+               ret = -ENOMEM;
+               goto allocfail;
+       }
+
+       cm->hid = hid;
+
+       hid->quirks |= HID_QUIRK_HIDINPUT_FORCE;
+       hid_set_drvdata(hid, cm);
+
+       ret = hid_parse(hid);
+       if (ret) {
+               hid_err(hid, "parse failed\n");
+               goto fail;
+       }
+
+       ret = hid_hw_start(hid, HID_CONNECT_DEFAULT | HID_CONNECT_HIDDEV_FORCE);
+       if (ret) {
+               hid_err(hid, "hw start failed\n");
+               goto fail;
+       }
+
+       return 0;
+fail:
+       kfree(cm);
+allocfail:
+       return ret;
+}
+
+static void cmhid_remove(struct hid_device *hid)
+{
+       struct cmhid *cm = hid_get_drvdata(hid);
+
+       hid_hw_stop(hid);
+       kfree(cm);
+}
+
+static const struct hid_device_id cmhid_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM6533) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, cmhid_devices);
+
+static struct hid_driver cmhid_driver = {
+       .name = "cm6533_jd",
+       .id_table = cmhid_devices,
+       .raw_event = cmhid_raw_event,
+       .input_configured = cmhid_input_configured,
+       .probe = cmhid_probe,
+       .remove = cmhid_remove,
+       .input_mapping = cmhid_input_mapping,
+};
+module_hid_driver(cmhid_driver);
+
index 7e89288b15375a2e8016ea6b31f5904e95c32719..bdb8cc89cacc73ea6442c9cf84de4180ded70772 100644 (file)
@@ -1075,7 +1075,7 @@ static u32 s32ton(__s32 value, unsigned n)
  * Extract/implement a data field from/to a little endian report (bit array).
  *
  * Code sort-of follows HID spec:
- *     http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ *     http://www.usb.org/developers/hidpage/HID1_11.pdf
  *
  * While the USB HID spec allows unlimited length bit fields in "report
  * descriptors", most devices never use more than 16 bits.
@@ -1083,20 +1083,37 @@ static u32 s32ton(__s32 value, unsigned n)
  * Search linux-kernel and linux-usb-devel archives for "hid-core extract".
  */
 
-__u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
-                    unsigned offset, unsigned n)
-{
-       u64 x;
+static u32 __extract(u8 *report, unsigned offset, int n)
+{
+       unsigned int idx = offset / 8;
+       unsigned int bit_nr = 0;
+       unsigned int bit_shift = offset % 8;
+       int bits_to_copy = 8 - bit_shift;
+       u32 value = 0;
+       u32 mask = n < 32 ? (1U << n) - 1 : ~0U;
+
+       while (n > 0) {
+               value |= ((u32)report[idx] >> bit_shift) << bit_nr;
+               n -= bits_to_copy;
+               bit_nr += bits_to_copy;
+               bits_to_copy = 8;
+               bit_shift = 0;
+               idx++;
+       }
+
+       return value & mask;
+}
 
-       if (n > 32)
+u32 hid_field_extract(const struct hid_device *hid, u8 *report,
+                       unsigned offset, unsigned n)
+{
+       if (n > 32) {
                hid_warn(hid, "hid_field_extract() called with n (%d) > 32! (%s)\n",
                         n, current->comm);
+               n = 32;
+       }
 
-       report += offset >> 3;  /* adjust byte index */
-       offset &= 7;            /* now only need bit offset into one byte */
-       x = get_unaligned_le64(report);
-       x = (x >> offset) & ((1ULL << n) - 1);  /* extract bit field */
-       return (u32) x;
+       return __extract(report, offset, n);
 }
 EXPORT_SYMBOL_GPL(hid_field_extract);
 
@@ -1106,31 +1123,56 @@ EXPORT_SYMBOL_GPL(hid_field_extract);
  * The data mangled in the bit stream remains in little endian
  * order the whole time. It make more sense to talk about
  * endianness of register values by considering a register
- * a "cached" copy of the little endiad bit stream.
+ * a "cached" copy of the little endian bit stream.
  */
-static void implement(const struct hid_device *hid, __u8 *report,
-                     unsigned offset, unsigned n, __u32 value)
+
+static void __implement(u8 *report, unsigned offset, int n, u32 value)
+{
+       unsigned int idx = offset / 8;
+       unsigned int size = offset + n;
+       unsigned int bit_shift = offset % 8;
+       int bits_to_set = 8 - bit_shift;
+       u8 bit_mask = 0xff << bit_shift;
+
+       while (n - bits_to_set >= 0) {
+               report[idx] &= ~bit_mask;
+               report[idx] |= value << bit_shift;
+               value >>= bits_to_set;
+               n -= bits_to_set;
+               bits_to_set = 8;
+               bit_mask = 0xff;
+               bit_shift = 0;
+               idx++;
+       }
+
+       /* last nibble */
+       if (n) {
+               if (size % 8)
+                       bit_mask &= (1U << (size % 8)) - 1;
+               report[idx] &= ~bit_mask;
+               report[idx] |= (value << bit_shift) & bit_mask;
+       }
+}
+
+static void implement(const struct hid_device *hid, u8 *report,
+                     unsigned offset, unsigned n, u32 value)
 {
-       u64 x;
-       u64 m = (1ULL << n) - 1;
+       u64 m;
 
-       if (n > 32)
+       if (n > 32) {
                hid_warn(hid, "%s() called with n (%d) > 32! (%s)\n",
                         __func__, n, current->comm);
+               n = 32;
+       }
 
+       m = (1ULL << n) - 1;
        if (value > m)
                hid_warn(hid, "%s() called with too large value %d! (%s)\n",
                         __func__, value, current->comm);
        WARN_ON(value > m);
        value &= m;
 
-       report += offset >> 3;
-       offset &= 7;
-
-       x = get_unaligned_le64(report);
-       x &= ~(m << offset);
-       x |= ((u64)value) << offset;
-       put_unaligned_le64(x, report);
+       __implement(report, offset, n, value);
 }
 
 /*
@@ -1251,6 +1293,7 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
                /* Ignore report if ErrorRollOver */
                if (!(field->flags & HID_MAIN_ITEM_VARIABLE) &&
                    value[n] >= min && value[n] <= max &&
+                   value[n] - min < field->maxusage &&
                    field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1)
                        goto exit;
        }
@@ -1263,11 +1306,13 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
                }
 
                if (field->value[n] >= min && field->value[n] <= max
+                       && field->value[n] - min < field->maxusage
                        && field->usage[field->value[n] - min].hid
                        && search(value, field->value[n], count))
                                hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt);
 
                if (value[n] >= min && value[n] <= max
+                       && value[n] - min < field->maxusage
                        && field->usage[value[n] - min].hid
                        && search(field->value, value[n], count))
                                hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt);
@@ -1891,6 +1936,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) },
@@ -1919,6 +1965,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP) },
@@ -2003,6 +2050,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
        { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) },
@@ -2051,6 +2099,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM6533) },
        { }
 };
 
@@ -2615,9 +2664,10 @@ int hid_add_device(struct hid_device *hdev)
        /*
         * Scan generic devices for group information
         */
-       if (hid_ignore_special_drivers ||
-           (!hdev->group &&
-            !hid_match_id(hdev, hid_have_special_driver))) {
+       if (hid_ignore_special_drivers) {
+               hdev->group = HID_GROUP_GENERIC;
+       } else if (!hdev->group &&
+                  !hid_match_id(hdev, hid_have_special_driver)) {
                ret = hid_scan_report(hdev);
                if (ret)
                        hid_warn(hdev, "bad device descriptor (%d)\n", ret);
index 58551964ce8692d97d33ffa8ae02f76c500871cf..717704e9ae07bd97d64f38321d2d49f07de62b83 100644 (file)
@@ -595,6 +595,9 @@ static int corsair_input_mapping(struct hid_device *dev,
 {
        int gkey;
 
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD)
+               return 0;
+
        gkey = corsair_usage_to_gkey(usage->hid & HID_USAGE);
        if (gkey != 0) {
                hid_map_usage_clear(input, usage, bit, max, EV_KEY,
index 1d78ba3b799e6f020eee8f9e16c9cab5b8f61627..8fd4bf77f264940ec04631252062e06aafe148c8 100644 (file)
@@ -151,7 +151,7 @@ static inline int drff_init(struct hid_device *hid)
  * descriptor. In any case, it's a wonder it works on Windows.
  *
  *  Usage Page (Desktop),             ; Generic desktop controls (01h)
- *  Usage (Joystik),                  ; Joystik (04h, application collection)
+ *  Usage (Joystick),                 ; Joystick (04h, application collection)
  *  Collection (Application),
  *    Collection (Logical),
  *      Report Size (8),
@@ -207,7 +207,7 @@ static inline int drff_init(struct hid_device *hid)
 /* Fixed report descriptor for PID 0x011 joystick */
 static __u8 pid0011_rdesc_fixed[] = {
        0x05, 0x01,         /*  Usage Page (Desktop),           */
-       0x09, 0x04,         /*  Usage (Joystik),                */
+       0x09, 0x04,         /*  Usage (Joystick),               */
        0xA1, 0x01,         /*  Collection (Application),       */
        0xA1, 0x02,         /*      Collection (Logical),       */
        0x14,               /*          Logical Minimum (0),    */
index b6ff6e78ac54e281a8617212024147d63c891346..5c0e43ed5c53ca28e554b7d833b093f545b8b440 100644 (file)
@@ -61,6 +61,9 @@
 #define USB_VENDOR_ID_AIREN            0x1a2c
 #define USB_DEVICE_ID_AIREN_SLIMPLUS   0x0002
 
+#define USB_VENDOR_ID_AKAI             0x2011
+#define USB_DEVICE_ID_AKAI_MPKMINI2    0x0715
+
 #define USB_VENDOR_ID_ALCOR            0x058f
 #define USB_DEVICE_ID_ALCOR_USBRS232   0x9720
 
 
 #define USB_VENDOR_ID_CMEDIA           0x0d8c
 #define USB_DEVICE_ID_CM109            0x000e
+#define USB_DEVICE_ID_CM6533           0x0022
 
 #define USB_VENDOR_ID_CODEMERCS                0x07c0
 #define USB_DEVICE_ID_CODEMERCS_IOW_FIRST      0x1500
 #define USB_DEVICE_ID_MS_NE7K          0x071d
 #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K      0x0730
 #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500    0x076c
+#define USB_DEVICE_ID_MS_COMFORT_KEYBOARD 0x00e3
 #define USB_DEVICE_ID_MS_SURFACE_PRO_2   0x0799
 #define USB_DEVICE_ID_MS_TOUCH_COVER_2   0x07a7
 #define USB_DEVICE_ID_MS_TYPE_COVER_2    0x07a9
 #define USB_VENDOR_ID_QUANTA           0x0408
 #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH             0x3000
 #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001                0x3001
+#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3003                0x3003
 #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008                0x3008
 
 #define USB_VENDOR_ID_RAZER            0x1532
 
 #define USB_VENDOR_ID_SEMICO                   0x1a2c
 #define USB_DEVICE_ID_SEMICO_USB_KEYKOARD      0x0023
+#define USB_DEVICE_ID_SEMICO_USB_KEYKOARD2     0x0027
 
 #define USB_VENDOR_ID_SENNHEISER       0x1395
 #define USB_DEVICE_ID_SENNHEISER_BTD500USB     0x002c
 #define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER             0x0002
 #define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER    0x1000
 
+#define USB_VENDOR_ID_SINO_LITE                        0x1345
+#define USB_DEVICE_ID_SINO_LITE_CONTROLLER     0x3008
+
 #define USB_VENDOR_ID_SOUNDGRAPH       0x15c2
 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST    0x0034
 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST     0x0046
 #define USB_DEVICE_ID_RI_KA_WEBMAIL    0x1320  /* Webmail Notifier */
 
 #define USB_VENDOR_ID_MULTIPLE_1781    0x1781
-#define USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD    0x0a8d
+#define USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD    0x0a9d
 
 #define USB_VENDOR_ID_DRACAL_RAPHNET   0x289b
 #define USB_DEVICE_ID_RAPHNET_2NES2SNES        0x0002
index c690fae02cf823d16b6d985d944b6bda143b6282..feb2be71f77c199e9ab21fabd0e6ffcd267d2d9e 100644 (file)
@@ -61,7 +61,7 @@
  */
 static __u8 df_rdesc_fixed[] = {
 0x05, 0x01,         /*  Usage Page (Desktop),                   */
-0x09, 0x04,         /*  Usage (Joystik),                        */
+0x09, 0x04,         /*  Usage (Joystick),                       */
 0xA1, 0x01,         /*  Collection (Application),               */
 0xA1, 0x02,         /*      Collection (Logical),               */
 0x95, 0x01,         /*          Report Count (1),               */
@@ -127,7 +127,7 @@ static __u8 df_rdesc_fixed[] = {
 
 static __u8 dfp_rdesc_fixed[] = {
 0x05, 0x01,         /*  Usage Page (Desktop),                   */
-0x09, 0x04,         /*  Usage (Joystik),                        */
+0x09, 0x04,         /*  Usage (Joystick),                       */
 0xA1, 0x01,         /*  Collection (Application),               */
 0xA1, 0x02,         /*      Collection (Logical),               */
 0x95, 0x01,         /*          Report Count (1),               */
@@ -175,7 +175,7 @@ static __u8 dfp_rdesc_fixed[] = {
 
 static __u8 fv_rdesc_fixed[] = {
 0x05, 0x01,         /*  Usage Page (Desktop),                   */
-0x09, 0x04,         /*  Usage (Joystik),                        */
+0x09, 0x04,         /*  Usage (Joystick),                       */
 0xA1, 0x01,         /*  Collection (Application),               */
 0xA1, 0x02,         /*      Collection (Logical),               */
 0x95, 0x01,         /*          Report Count (1),               */
@@ -242,7 +242,7 @@ static __u8 fv_rdesc_fixed[] = {
 
 static __u8 momo_rdesc_fixed[] = {
 0x05, 0x01,         /*  Usage Page (Desktop),               */
-0x09, 0x04,         /*  Usage (Joystik),                    */
+0x09, 0x04,         /*  Usage (Joystick),                   */
 0xA1, 0x01,         /*  Collection (Application),           */
 0xA1, 0x02,         /*      Collection (Logical),           */
 0x95, 0x01,         /*          Report Count (1),           */
@@ -288,7 +288,7 @@ static __u8 momo_rdesc_fixed[] = {
 
 static __u8 momo2_rdesc_fixed[] = {
 0x05, 0x01,         /*  Usage Page (Desktop),               */
-0x09, 0x04,         /*  Usage (Joystik),                    */
+0x09, 0x04,         /*  Usage (Joystick),                   */
 0xA1, 0x01,         /*  Collection (Application),           */
 0xA1, 0x02,         /*      Collection (Logical),           */
 0x95, 0x01,         /*          Report Count (1),           */
index bd2ab476c65ef9f73a04a1f732dc7f75a3b49835..2e2515a4c070eac305a834c9229d0e1fa27f722f 100644 (file)
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/device.h>
+#include <linux/input.h>
+#include <linux/usb.h>
 #include <linux/hid.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/kfifo.h>
 #include <linux/input/mt.h>
+#include <linux/workqueue.h>
+#include <linux/atomic.h>
+#include <linux/fixp-arith.h>
 #include <asm/unaligned.h>
+#include "usbhid/usbhid.h"
 #include "hid-ids.h"
 
 MODULE_LICENSE("GPL");
@@ -773,6 +779,589 @@ static void hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev,
        }
 }
 
+/* -------------------------------------------------------------------------- */
+/* 0x8123: Force feedback support                                             */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_FF_GET_INFO              0x01
+#define HIDPP_FF_RESET_ALL             0x11
+#define HIDPP_FF_DOWNLOAD_EFFECT       0x21
+#define HIDPP_FF_SET_EFFECT_STATE      0x31
+#define HIDPP_FF_DESTROY_EFFECT                0x41
+#define HIDPP_FF_GET_APERTURE          0x51
+#define HIDPP_FF_SET_APERTURE          0x61
+#define HIDPP_FF_GET_GLOBAL_GAINS      0x71
+#define HIDPP_FF_SET_GLOBAL_GAINS      0x81
+
+#define HIDPP_FF_EFFECT_STATE_GET      0x00
+#define HIDPP_FF_EFFECT_STATE_STOP     0x01
+#define HIDPP_FF_EFFECT_STATE_PLAY     0x02
+#define HIDPP_FF_EFFECT_STATE_PAUSE    0x03
+
+#define HIDPP_FF_EFFECT_CONSTANT       0x00
+#define HIDPP_FF_EFFECT_PERIODIC_SINE          0x01
+#define HIDPP_FF_EFFECT_PERIODIC_SQUARE                0x02
+#define HIDPP_FF_EFFECT_PERIODIC_TRIANGLE      0x03
+#define HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHUP    0x04
+#define HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHDOWN  0x05
+#define HIDPP_FF_EFFECT_SPRING         0x06
+#define HIDPP_FF_EFFECT_DAMPER         0x07
+#define HIDPP_FF_EFFECT_FRICTION       0x08
+#define HIDPP_FF_EFFECT_INERTIA                0x09
+#define HIDPP_FF_EFFECT_RAMP           0x0A
+
+#define HIDPP_FF_EFFECT_AUTOSTART      0x80
+
+#define HIDPP_FF_EFFECTID_NONE         -1
+#define HIDPP_FF_EFFECTID_AUTOCENTER   -2
+
+#define HIDPP_FF_MAX_PARAMS    20
+#define HIDPP_FF_RESERVED_SLOTS        1
+
+struct hidpp_ff_private_data {
+       struct hidpp_device *hidpp;
+       u8 feature_index;
+       u8 version;
+       u16 gain;
+       s16 range;
+       u8 slot_autocenter;
+       u8 num_effects;
+       int *effect_ids;
+       struct workqueue_struct *wq;
+       atomic_t workqueue_size;
+};
+
+struct hidpp_ff_work_data {
+       struct work_struct work;
+       struct hidpp_ff_private_data *data;
+       int effect_id;
+       u8 command;
+       u8 params[HIDPP_FF_MAX_PARAMS];
+       u8 size;
+};
+
+static const signed short hiddpp_ff_effects[] = {
+       FF_CONSTANT,
+       FF_PERIODIC,
+       FF_SINE,
+       FF_SQUARE,
+       FF_SAW_UP,
+       FF_SAW_DOWN,
+       FF_TRIANGLE,
+       FF_SPRING,
+       FF_DAMPER,
+       FF_AUTOCENTER,
+       FF_GAIN,
+       -1
+};
+
+static const signed short hiddpp_ff_effects_v2[] = {
+       FF_RAMP,
+       FF_FRICTION,
+       FF_INERTIA,
+       -1
+};
+
+static const u8 HIDPP_FF_CONDITION_CMDS[] = {
+       HIDPP_FF_EFFECT_SPRING,
+       HIDPP_FF_EFFECT_FRICTION,
+       HIDPP_FF_EFFECT_DAMPER,
+       HIDPP_FF_EFFECT_INERTIA
+};
+
+static const char *HIDPP_FF_CONDITION_NAMES[] = {
+       "spring",
+       "friction",
+       "damper",
+       "inertia"
+};
+
+
+static u8 hidpp_ff_find_effect(struct hidpp_ff_private_data *data, int effect_id)
+{
+       int i;
+
+       for (i = 0; i < data->num_effects; i++)
+               if (data->effect_ids[i] == effect_id)
+                       return i+1;
+
+       return 0;
+}
+
+static void hidpp_ff_work_handler(struct work_struct *w)
+{
+       struct hidpp_ff_work_data *wd = container_of(w, struct hidpp_ff_work_data, work);
+       struct hidpp_ff_private_data *data = wd->data;
+       struct hidpp_report response;
+       u8 slot;
+       int ret;
+
+       /* add slot number if needed */
+       switch (wd->effect_id) {
+       case HIDPP_FF_EFFECTID_AUTOCENTER:
+               wd->params[0] = data->slot_autocenter;
+               break;
+       case HIDPP_FF_EFFECTID_NONE:
+               /* leave slot as zero */
+               break;
+       default:
+               /* find current slot for effect */
+               wd->params[0] = hidpp_ff_find_effect(data, wd->effect_id);
+               break;
+       }
+
+       /* send command and wait for reply */
+       ret = hidpp_send_fap_command_sync(data->hidpp, data->feature_index,
+               wd->command, wd->params, wd->size, &response);
+
+       if (ret) {
+               hid_err(data->hidpp->hid_dev, "Failed to send command to device!\n");
+               goto out;
+       }
+
+       /* parse return data */
+       switch (wd->command) {
+       case HIDPP_FF_DOWNLOAD_EFFECT:
+               slot = response.fap.params[0];
+               if (slot > 0 && slot <= data->num_effects) {
+                       if (wd->effect_id >= 0)
+                               /* regular effect uploaded */
+                               data->effect_ids[slot-1] = wd->effect_id;
+                       else if (wd->effect_id >= HIDPP_FF_EFFECTID_AUTOCENTER)
+                               /* autocenter spring uploaded */
+                               data->slot_autocenter = slot;
+               }
+               break;
+       case HIDPP_FF_DESTROY_EFFECT:
+               if (wd->effect_id >= 0)
+                       /* regular effect destroyed */
+                       data->effect_ids[wd->params[0]-1] = -1;
+               else if (wd->effect_id >= HIDPP_FF_EFFECTID_AUTOCENTER)
+                       /* autocenter spring destoyed */
+                       data->slot_autocenter = 0;
+               break;
+       case HIDPP_FF_SET_GLOBAL_GAINS:
+               data->gain = (wd->params[0] << 8) + wd->params[1];
+               break;
+       case HIDPP_FF_SET_APERTURE:
+               data->range = (wd->params[0] << 8) + wd->params[1];
+               break;
+       default:
+               /* no action needed */
+               break;
+       }
+
+out:
+       atomic_dec(&data->workqueue_size);
+       kfree(wd);
+}
+
+static int hidpp_ff_queue_work(struct hidpp_ff_private_data *data, int effect_id, u8 command, u8 *params, u8 size)
+{
+       struct hidpp_ff_work_data *wd = kzalloc(sizeof(*wd), GFP_KERNEL);
+       int s;
+
+       if (!wd)
+               return -ENOMEM;
+
+       INIT_WORK(&wd->work, hidpp_ff_work_handler);
+
+       wd->data = data;
+       wd->effect_id = effect_id;
+       wd->command = command;
+       wd->size = size;
+       memcpy(wd->params, params, size);
+
+       atomic_inc(&data->workqueue_size);
+       queue_work(data->wq, &wd->work);
+
+       /* warn about excessive queue size */
+       s = atomic_read(&data->workqueue_size);
+       if (s >= 20 && s % 20 == 0)
+               hid_warn(data->hidpp->hid_dev, "Force feedback command queue contains %d commands, causing substantial delays!", s);
+
+       return 0;
+}
+
+static int hidpp_ff_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
+{
+       struct hidpp_ff_private_data *data = dev->ff->private;
+       u8 params[20];
+       u8 size;
+       int force;
+
+       /* set common parameters */
+       params[2] = effect->replay.length >> 8;
+       params[3] = effect->replay.length & 255;
+       params[4] = effect->replay.delay >> 8;
+       params[5] = effect->replay.delay & 255;
+
+       switch (effect->type) {
+       case FF_CONSTANT:
+               force = (effect->u.constant.level * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
+               params[1] = HIDPP_FF_EFFECT_CONSTANT;
+               params[6] = force >> 8;
+               params[7] = force & 255;
+               params[8] = effect->u.constant.envelope.attack_level >> 7;
+               params[9] = effect->u.constant.envelope.attack_length >> 8;
+               params[10] = effect->u.constant.envelope.attack_length & 255;
+               params[11] = effect->u.constant.envelope.fade_level >> 7;
+               params[12] = effect->u.constant.envelope.fade_length >> 8;
+               params[13] = effect->u.constant.envelope.fade_length & 255;
+               size = 14;
+               dbg_hid("Uploading constant force level=%d in dir %d = %d\n",
+                               effect->u.constant.level,
+                               effect->direction, force);
+               dbg_hid("          envelope attack=(%d, %d ms) fade=(%d, %d ms)\n",
+                               effect->u.constant.envelope.attack_level,
+                               effect->u.constant.envelope.attack_length,
+                               effect->u.constant.envelope.fade_level,
+                               effect->u.constant.envelope.fade_length);
+               break;
+       case FF_PERIODIC:
+       {
+               switch (effect->u.periodic.waveform) {
+               case FF_SINE:
+                       params[1] = HIDPP_FF_EFFECT_PERIODIC_SINE;
+                       break;
+               case FF_SQUARE:
+                       params[1] = HIDPP_FF_EFFECT_PERIODIC_SQUARE;
+                       break;
+               case FF_SAW_UP:
+                       params[1] = HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHUP;
+                       break;
+               case FF_SAW_DOWN:
+                       params[1] = HIDPP_FF_EFFECT_PERIODIC_SAWTOOTHDOWN;
+                       break;
+               case FF_TRIANGLE:
+                       params[1] = HIDPP_FF_EFFECT_PERIODIC_TRIANGLE;
+                       break;
+               default:
+                       hid_err(data->hidpp->hid_dev, "Unexpected periodic waveform type %i!\n", effect->u.periodic.waveform);
+                       return -EINVAL;
+               }
+               force = (effect->u.periodic.magnitude * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
+               params[6] = effect->u.periodic.magnitude >> 8;
+               params[7] = effect->u.periodic.magnitude & 255;
+               params[8] = effect->u.periodic.offset >> 8;
+               params[9] = effect->u.periodic.offset & 255;
+               params[10] = effect->u.periodic.period >> 8;
+               params[11] = effect->u.periodic.period & 255;
+               params[12] = effect->u.periodic.phase >> 8;
+               params[13] = effect->u.periodic.phase & 255;
+               params[14] = effect->u.periodic.envelope.attack_level >> 7;
+               params[15] = effect->u.periodic.envelope.attack_length >> 8;
+               params[16] = effect->u.periodic.envelope.attack_length & 255;
+               params[17] = effect->u.periodic.envelope.fade_level >> 7;
+               params[18] = effect->u.periodic.envelope.fade_length >> 8;
+               params[19] = effect->u.periodic.envelope.fade_length & 255;
+               size = 20;
+               dbg_hid("Uploading periodic force mag=%d/dir=%d, offset=%d, period=%d ms, phase=%d\n",
+                               effect->u.periodic.magnitude, effect->direction,
+                               effect->u.periodic.offset,
+                               effect->u.periodic.period,
+                               effect->u.periodic.phase);
+               dbg_hid("          envelope attack=(%d, %d ms) fade=(%d, %d ms)\n",
+                               effect->u.periodic.envelope.attack_level,
+                               effect->u.periodic.envelope.attack_length,
+                               effect->u.periodic.envelope.fade_level,
+                               effect->u.periodic.envelope.fade_length);
+               break;
+       }
+       case FF_RAMP:
+               params[1] = HIDPP_FF_EFFECT_RAMP;
+               force = (effect->u.ramp.start_level * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
+               params[6] = force >> 8;
+               params[7] = force & 255;
+               force = (effect->u.ramp.end_level * fixp_sin16((effect->direction * 360) >> 16)) >> 15;
+               params[8] = force >> 8;
+               params[9] = force & 255;
+               params[10] = effect->u.ramp.envelope.attack_level >> 7;
+               params[11] = effect->u.ramp.envelope.attack_length >> 8;
+               params[12] = effect->u.ramp.envelope.attack_length & 255;
+               params[13] = effect->u.ramp.envelope.fade_level >> 7;
+               params[14] = effect->u.ramp.envelope.fade_length >> 8;
+               params[15] = effect->u.ramp.envelope.fade_length & 255;
+               size = 16;
+               dbg_hid("Uploading ramp force level=%d -> %d in dir %d = %d\n",
+                               effect->u.ramp.start_level,
+                               effect->u.ramp.end_level,
+                               effect->direction, force);
+               dbg_hid("          envelope attack=(%d, %d ms) fade=(%d, %d ms)\n",
+                               effect->u.ramp.envelope.attack_level,
+                               effect->u.ramp.envelope.attack_length,
+                               effect->u.ramp.envelope.fade_level,
+                               effect->u.ramp.envelope.fade_length);
+               break;
+       case FF_FRICTION:
+       case FF_INERTIA:
+       case FF_SPRING:
+       case FF_DAMPER:
+               params[1] = HIDPP_FF_CONDITION_CMDS[effect->type - FF_SPRING];
+               params[6] = effect->u.condition[0].left_saturation >> 9;
+               params[7] = (effect->u.condition[0].left_saturation >> 1) & 255;
+               params[8] = effect->u.condition[0].left_coeff >> 8;
+               params[9] = effect->u.condition[0].left_coeff & 255;
+               params[10] = effect->u.condition[0].deadband >> 9;
+               params[11] = (effect->u.condition[0].deadband >> 1) & 255;
+               params[12] = effect->u.condition[0].center >> 8;
+               params[13] = effect->u.condition[0].center & 255;
+               params[14] = effect->u.condition[0].right_coeff >> 8;
+               params[15] = effect->u.condition[0].right_coeff & 255;
+               params[16] = effect->u.condition[0].right_saturation >> 9;
+               params[17] = (effect->u.condition[0].right_saturation >> 1) & 255;
+               size = 18;
+               dbg_hid("Uploading %s force left coeff=%d, left sat=%d, right coeff=%d, right sat=%d\n",
+                               HIDPP_FF_CONDITION_NAMES[effect->type - FF_SPRING],
+                               effect->u.condition[0].left_coeff,
+                               effect->u.condition[0].left_saturation,
+                               effect->u.condition[0].right_coeff,
+                               effect->u.condition[0].right_saturation);
+               dbg_hid("          deadband=%d, center=%d\n",
+                               effect->u.condition[0].deadband,
+                               effect->u.condition[0].center);
+               break;
+       default:
+               hid_err(data->hidpp->hid_dev, "Unexpected force type %i!\n", effect->type);
+               return -EINVAL;
+       }
+
+       return hidpp_ff_queue_work(data, effect->id, HIDPP_FF_DOWNLOAD_EFFECT, params, size);
+}
+
+static int hidpp_ff_playback(struct input_dev *dev, int effect_id, int value)
+{
+       struct hidpp_ff_private_data *data = dev->ff->private;
+       u8 params[2];
+
+       params[1] = value ? HIDPP_FF_EFFECT_STATE_PLAY : HIDPP_FF_EFFECT_STATE_STOP;
+
+       dbg_hid("St%sing playback of effect %d.\n", value?"art":"opp", effect_id);
+
+       return hidpp_ff_queue_work(data, effect_id, HIDPP_FF_SET_EFFECT_STATE, params, ARRAY_SIZE(params));
+}
+
+static int hidpp_ff_erase_effect(struct input_dev *dev, int effect_id)
+{
+       struct hidpp_ff_private_data *data = dev->ff->private;
+       u8 slot = 0;
+
+       dbg_hid("Erasing effect %d.\n", effect_id);
+
+       return hidpp_ff_queue_work(data, effect_id, HIDPP_FF_DESTROY_EFFECT, &slot, 1);
+}
+
+static void hidpp_ff_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+       struct hidpp_ff_private_data *data = dev->ff->private;
+       u8 params[18];
+
+       dbg_hid("Setting autocenter to %d.\n", magnitude);
+
+       /* start a standard spring effect */
+       params[1] = HIDPP_FF_EFFECT_SPRING | HIDPP_FF_EFFECT_AUTOSTART;
+       /* zero delay and duration */
+       params[2] = params[3] = params[4] = params[5] = 0;
+       /* set coeff to 25% of saturation */
+       params[8] = params[14] = magnitude >> 11;
+       params[9] = params[15] = (magnitude >> 3) & 255;
+       params[6] = params[16] = magnitude >> 9;
+       params[7] = params[17] = (magnitude >> 1) & 255;
+       /* zero deadband and center */
+       params[10] = params[11] = params[12] = params[13] = 0;
+
+       hidpp_ff_queue_work(data, HIDPP_FF_EFFECTID_AUTOCENTER, HIDPP_FF_DOWNLOAD_EFFECT, params, ARRAY_SIZE(params));
+}
+
+static void hidpp_ff_set_gain(struct input_dev *dev, u16 gain)
+{
+       struct hidpp_ff_private_data *data = dev->ff->private;
+       u8 params[4];
+
+       dbg_hid("Setting gain to %d.\n", gain);
+
+       params[0] = gain >> 8;
+       params[1] = gain & 255;
+       params[2] = 0; /* no boost */
+       params[3] = 0;
+
+       hidpp_ff_queue_work(data, HIDPP_FF_EFFECTID_NONE, HIDPP_FF_SET_GLOBAL_GAINS, params, ARRAY_SIZE(params));
+}
+
+static ssize_t hidpp_ff_range_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct hid_device *hid = to_hid_device(dev);
+       struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+       struct input_dev *idev = hidinput->input;
+       struct hidpp_ff_private_data *data = idev->ff->private;
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", data->range);
+}
+
+static ssize_t hidpp_ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct hid_device *hid = to_hid_device(dev);
+       struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+       struct input_dev *idev = hidinput->input;
+       struct hidpp_ff_private_data *data = idev->ff->private;
+       u8 params[2];
+       int range = simple_strtoul(buf, NULL, 10);
+
+       range = clamp(range, 180, 900);
+
+       params[0] = range >> 8;
+       params[1] = range & 0x00FF;
+
+       hidpp_ff_queue_work(data, -1, HIDPP_FF_SET_APERTURE, params, ARRAY_SIZE(params));
+
+       return count;
+}
+
+static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, hidpp_ff_range_show, hidpp_ff_range_store);
+
+static void hidpp_ff_destroy(struct ff_device *ff)
+{
+       struct hidpp_ff_private_data *data = ff->private;
+
+       kfree(data->effect_ids);
+}
+
+static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index)
+{
+       struct hid_device *hid = hidpp->hid_dev;
+       struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+       struct input_dev *dev = hidinput->input;
+       const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor);
+       const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice);
+       struct ff_device *ff;
+       struct hidpp_report response;
+       struct hidpp_ff_private_data *data;
+       int error, j, num_slots;
+       u8 version;
+
+       if (!dev) {
+               hid_err(hid, "Struct input_dev not set!\n");
+               return -EINVAL;
+       }
+
+       /* Get firmware release */
+       version = bcdDevice & 255;
+
+       /* Set supported force feedback capabilities */
+       for (j = 0; hiddpp_ff_effects[j] >= 0; j++)
+               set_bit(hiddpp_ff_effects[j], dev->ffbit);
+       if (version > 1)
+               for (j = 0; hiddpp_ff_effects_v2[j] >= 0; j++)
+                       set_bit(hiddpp_ff_effects_v2[j], dev->ffbit);
+
+       /* Read number of slots available in device */
+       error = hidpp_send_fap_command_sync(hidpp, feature_index,
+               HIDPP_FF_GET_INFO, NULL, 0, &response);
+       if (error) {
+               if (error < 0)
+                       return error;
+               hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
+                       __func__, error);
+               return -EPROTO;
+       }
+
+       num_slots = response.fap.params[0] - HIDPP_FF_RESERVED_SLOTS;
+
+       error = input_ff_create(dev, num_slots);
+
+       if (error) {
+               hid_err(dev, "Failed to create FF device!\n");
+               return error;
+       }
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+       data->effect_ids = kcalloc(num_slots, sizeof(int), GFP_KERNEL);
+       if (!data->effect_ids) {
+               kfree(data);
+               return -ENOMEM;
+       }
+       data->hidpp = hidpp;
+       data->feature_index = feature_index;
+       data->version = version;
+       data->slot_autocenter = 0;
+       data->num_effects = num_slots;
+       for (j = 0; j < num_slots; j++)
+               data->effect_ids[j] = -1;
+
+       ff = dev->ff;
+       ff->private = data;
+
+       ff->upload = hidpp_ff_upload_effect;
+       ff->erase = hidpp_ff_erase_effect;
+       ff->playback = hidpp_ff_playback;
+       ff->set_gain = hidpp_ff_set_gain;
+       ff->set_autocenter = hidpp_ff_set_autocenter;
+       ff->destroy = hidpp_ff_destroy;
+
+
+       /* reset all forces */
+       error = hidpp_send_fap_command_sync(hidpp, feature_index,
+               HIDPP_FF_RESET_ALL, NULL, 0, &response);
+
+       /* Read current Range */
+       error = hidpp_send_fap_command_sync(hidpp, feature_index,
+               HIDPP_FF_GET_APERTURE, NULL, 0, &response);
+       if (error)
+               hid_warn(hidpp->hid_dev, "Failed to read range from device!\n");
+       data->range = error ? 900 : get_unaligned_be16(&response.fap.params[0]);
+
+       /* Create sysfs interface */
+       error = device_create_file(&(hidpp->hid_dev->dev), &dev_attr_range);
+       if (error)
+               hid_warn(hidpp->hid_dev, "Unable to create sysfs interface for \"range\", errno %d!\n", error);
+
+       /* Read the current gain values */
+       error = hidpp_send_fap_command_sync(hidpp, feature_index,
+               HIDPP_FF_GET_GLOBAL_GAINS, NULL, 0, &response);
+       if (error)
+               hid_warn(hidpp->hid_dev, "Failed to read gain values from device!\n");
+       data->gain = error ? 0xffff : get_unaligned_be16(&response.fap.params[0]);
+       /* ignore boost value at response.fap.params[2] */
+
+       /* init the hardware command queue */
+       data->wq = create_singlethread_workqueue("hidpp-ff-sendqueue");
+       atomic_set(&data->workqueue_size, 0);
+
+       /* initialize with zero autocenter to get wheel in usable state */
+       hidpp_ff_set_autocenter(dev, 0);
+
+       hid_info(hid, "Force feeback support loaded (firmware release %d).\n", version);
+
+       return 0;
+}
+
+static int hidpp_ff_deinit(struct hid_device *hid)
+{
+       struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+       struct input_dev *dev = hidinput->input;
+       struct hidpp_ff_private_data *data;
+
+       if (!dev) {
+               hid_err(hid, "Struct input_dev not found!\n");
+               return -EINVAL;
+       }
+
+       hid_info(hid, "Unloading HID++ force feedback.\n");
+       data = dev->ff->private;
+       if (!data) {
+               hid_err(hid, "Private data not found!\n");
+               return -EINVAL;
+       }
+
+       destroy_workqueue(data->wq);
+       device_remove_file(&hid->dev, &dev_attr_range);
+
+       return 0;
+}
+
+
 /* ************************************************************************** */
 /*                                                                            */
 /* Device Support                                                             */
@@ -1301,121 +1890,22 @@ static int k400_connect(struct hid_device *hdev, bool connected)
 
 #define HIDPP_PAGE_G920_FORCE_FEEDBACK                 0x8123
 
-/* Using session ID = 1 */
-#define CMD_G920_FORCE_GET_APERTURE                    0x51
-#define CMD_G920_FORCE_SET_APERTURE                    0x61
-
-struct g920_private_data {
-       u8 force_feature;
-       u16 range;
-};
-
-static ssize_t g920_range_show(struct device *dev, struct device_attribute *attr,
-                               char *buf)
-{
-       struct hid_device *hid = to_hid_device(dev);
-       struct hidpp_device *hidpp = hid_get_drvdata(hid);
-       struct g920_private_data *pdata;
-
-       pdata = hidpp->private_data;
-       if (!pdata) {
-               hid_err(hid, "Private driver data not found!\n");
-               return -EINVAL;
-       }
-
-       return scnprintf(buf, PAGE_SIZE, "%u\n", pdata->range);
-}
-
-static ssize_t g920_range_store(struct device *dev, struct device_attribute *attr,
-                                const char *buf, size_t count)
-{
-       struct hid_device *hid = to_hid_device(dev);
-       struct hidpp_device *hidpp = hid_get_drvdata(hid);
-       struct g920_private_data *pdata;
-       struct hidpp_report response;
-       u8 params[2];
-       int ret;
-       u16 range = simple_strtoul(buf, NULL, 10);
-
-       pdata = hidpp->private_data;
-       if (!pdata) {
-               hid_err(hid, "Private driver data not found!\n");
-               return -EINVAL;
-       }
-
-       if (range < 180)
-               range = 180;
-       else if (range > 900)
-               range = 900;
-
-       params[0] = range >> 8;
-       params[1] = range & 0x00FF;
-
-       ret = hidpp_send_fap_command_sync(hidpp, pdata->force_feature,
-               CMD_G920_FORCE_SET_APERTURE, params, 2, &response);
-       if (ret)
-               return ret;
-
-       pdata->range = range;
-       return count;
-}
-
-static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, g920_range_show, g920_range_store);
-
-static int g920_allocate(struct hid_device *hdev)
-{
-       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
-       struct g920_private_data *pdata;
-
-       pdata = devm_kzalloc(&hdev->dev, sizeof(struct g920_private_data),
-                       GFP_KERNEL);
-       if (!pdata)
-               return -ENOMEM;
-
-       hidpp->private_data = pdata;
-
-       return 0;
-}
-
 static int g920_get_config(struct hidpp_device *hidpp)
 {
-       struct g920_private_data *pdata = hidpp->private_data;
-       struct hidpp_report response;
        u8 feature_type;
        u8 feature_index;
        int ret;
 
-       pdata = hidpp->private_data;
-       if (!pdata) {
-               hid_err(hidpp->hid_dev, "Private driver data not found!\n");
-               return -EINVAL;
-       }
-
        /* Find feature and store for later use */
        ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_G920_FORCE_FEEDBACK,
                &feature_index, &feature_type);
        if (ret)
                return ret;
 
-       pdata->force_feature = feature_index;
-
-       /* Read current Range */
-       ret = hidpp_send_fap_command_sync(hidpp, feature_index,
-               CMD_G920_FORCE_GET_APERTURE, NULL, 0, &response);
-       if (ret > 0) {
-               hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
-                       __func__, ret);
-               return -EPROTO;
-       }
-       if (ret)
-               return ret;
-
-       pdata->range = get_unaligned_be16(&response.fap.params[0]);
-
-       /* Create sysfs interface */
-       ret = device_create_file(&(hidpp->hid_dev->dev), &dev_attr_range);
+       ret = hidpp_ff_init(hidpp, feature_index);
        if (ret)
-               hid_warn(hidpp->hid_dev, "Unable to create sysfs interface for \"range\", errno %d\n", ret);
+               hid_warn(hidpp->hid_dev, "Unable to initialize force feedback support, errno %d\n",
+                               ret);
 
        return 0;
 }
@@ -1739,10 +2229,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
                ret = k400_allocate(hdev);
                if (ret)
                        goto allocate_fail;
-       } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
-               ret = g920_allocate(hdev);
-               if (ret)
-                       goto allocate_fail;
        }
 
        INIT_WORK(&hidpp->work, delayed_work_cb);
@@ -1825,7 +2311,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
 hid_hw_open_failed:
        hid_device_io_stop(hdev);
        if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
-               device_remove_file(&hdev->dev, &dev_attr_range);
                hid_hw_close(hdev);
                hid_hw_stop(hdev);
        }
@@ -1843,7 +2328,7 @@ static void hidpp_remove(struct hid_device *hdev)
        struct hidpp_device *hidpp = hid_get_drvdata(hdev);
 
        if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
-               device_remove_file(&hdev->dev, &dev_attr_range);
+               hidpp_ff_deinit(hdev);
                hid_hw_close(hdev);
        }
        hid_hw_stop(hdev);
index 77a2cf3e4afec25eee5d5d313f63caaf9939653f..75cd3bc59c549a2be01c8092412dafd24abf8c61 100644 (file)
@@ -286,6 +286,8 @@ static const struct hid_device_id ms_devices[] = {
                .driver_data = MS_HIDINPUT },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER),
                .driver_data = MS_HIDINPUT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD),
+               .driver_data = MS_ERGONOMY},
 
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT),
                .driver_data = MS_PRESENTER },
index 296d4991560e45a9fed565c8a2d3cd463efb9ff1..25d3c4330bf69bbca32efb3883628f3af6a28875 100644 (file)
@@ -396,6 +396,11 @@ static void mt_feature_mapping(struct hid_device *hdev,
                        td->is_buttonpad = true;
 
                break;
+       case 0xff0000c5:
+               /* Retrieve the Win8 blob once to enable some devices */
+               if (usage->usage_index == 0)
+                       mt_get_feature(hdev, field->report);
+               break;
        }
 }
 
@@ -1133,6 +1138,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
                return ret;
 
        ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
+       if (ret)
+               dev_warn(&hdev->dev, "Cannot allocate sysfs group for %s\n",
+                               hdev->name);
 
        mt_set_maxcontacts(hdev);
        mt_set_input_mode(hdev);
@@ -1145,8 +1153,30 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
 }
 
 #ifdef CONFIG_PM
+static void mt_release_contacts(struct hid_device *hid)
+{
+       struct hid_input *hidinput;
+
+       list_for_each_entry(hidinput, &hid->inputs, list) {
+               struct input_dev *input_dev = hidinput->input;
+               struct input_mt *mt = input_dev->mt;
+               int i;
+
+               if (mt) {
+                       for (i = 0; i < mt->num_slots; i++) {
+                               input_mt_slot(input_dev, i);
+                               input_mt_report_slot_state(input_dev,
+                                                          MT_TOOL_FINGER,
+                                                          false);
+                       }
+                       input_sync(input_dev);
+               }
+       }
+}
+
 static int mt_reset_resume(struct hid_device *hdev)
 {
+       mt_release_contacts(hdev);
        mt_set_maxcontacts(hdev);
        mt_set_input_mode(hdev);
        return 0;
index c11dce85cd1877537487b50bc1e1eb3335ef8268..d90383f788e26d7cc328dd0ef5b80f656e38a6c7 100644 (file)
@@ -23,8 +23,12 @@ static int penmount_input_mapping(struct hid_device *hdev,
                struct hid_usage *usage, unsigned long **bit, int *max)
 {
        if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
-               hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
-               return 1;
+               if (((usage->hid - 1) & HID_USAGE) == 0) {
+                       hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
+                       return 1;
+               } else {
+                       return -1;
+               }
        }
 
        return 0;
index 67cd059a8f46cdf7dab8d73cb0edb7bd5aff1333..9cd2ca34a6be5583dbcd82a64feb018f66b20bf8 100644 (file)
@@ -594,6 +594,9 @@ static int rmi_suspend(struct hid_device *hdev, pm_message_t message)
        int ret;
        u8 buf[RMI_F11_CTRL_REG_COUNT];
 
+       if (!(data->device_flags & RMI_DEVICE))
+               return 0;
+
        ret = rmi_read_block(hdev, data->f11.control_base_addr, buf,
                                RMI_F11_CTRL_REG_COUNT);
        if (ret)
@@ -613,6 +616,9 @@ static int rmi_post_reset(struct hid_device *hdev)
        struct rmi_data *data = hid_get_drvdata(hdev);
        int ret;
 
+       if (!(data->device_flags & RMI_DEVICE))
+               return 0;
+
        ret = rmi_reset_attn_mode(hdev);
        if (ret) {
                hid_err(hdev, "can not set rmi mode\n");
@@ -640,6 +646,11 @@ static int rmi_post_reset(struct hid_device *hdev)
 
 static int rmi_post_resume(struct hid_device *hdev)
 {
+       struct rmi_data *data = hid_get_drvdata(hdev);
+
+       if (!(data->device_flags & RMI_DEVICE))
+               return 0;
+
        return rmi_reset_attn_mode(hdev);
 }
 #endif /* CONFIG_PM */
index 9b8db0e0ef1cdd6c3b2b050d87a50cb32e18ce1d..310436a54a3f308157a272d56d6624c3dd3c98c1 100644 (file)
@@ -50,6 +50,7 @@
 #define MOTION_CONTROLLER_BT      BIT(8)
 #define NAVIGATION_CONTROLLER_USB BIT(9)
 #define NAVIGATION_CONTROLLER_BT  BIT(10)
+#define SINO_LITE_CONTROLLER      BIT(11)
 
 #define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
 #define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
@@ -74,7 +75,7 @@
  * axis values.  Additionally, the controller only has 20 actual, physical axes
  * so there are several unused axes in between the used ones.
  */
-static __u8 sixaxis_rdesc[] = {
+static u8 sixaxis_rdesc[] = {
        0x05, 0x01,         /*  Usage Page (Desktop),               */
        0x09, 0x04,         /*  Usage (Joystick),                   */
        0xA1, 0x01,         /*  Collection (Application),           */
@@ -152,7 +153,7 @@ static __u8 sixaxis_rdesc[] = {
 };
 
 /* PS/3 Motion controller */
-static __u8 motion_rdesc[] = {
+static u8 motion_rdesc[] = {
        0x05, 0x01,         /*  Usage Page (Desktop),               */
        0x09, 0x04,         /*  Usage (Joystick),                   */
        0xA1, 0x01,         /*  Collection (Application),           */
@@ -249,9 +250,9 @@ static __u8 motion_rdesc[] = {
 };
 
 /* PS/3 Navigation controller */
-static __u8 navigation_rdesc[] = {
+static u8 navigation_rdesc[] = {
        0x05, 0x01,         /*  Usage Page (Desktop),               */
-       0x09, 0x04,         /*  Usage (Joystik),                    */
+       0x09, 0x04,         /*  Usage (Joystick),                   */
        0xA1, 0x01,         /*  Collection (Application),           */
        0xA1, 0x02,         /*      Collection (Logical),           */
        0x85, 0x01,         /*          Report ID (1),              */
@@ -809,7 +810,7 @@ static u8 dualshock4_bt_rdesc[] = {
        0xC0                /*  End Collection                      */
 };
 
-static __u8 ps3remote_rdesc[] = {
+static u8 ps3remote_rdesc[] = {
        0x05, 0x01,          /* GUsagePage Generic Desktop */
        0x09, 0x05,          /* LUsage 0x05 [Game Pad] */
        0xA1, 0x01,          /* MCollection Application (mouse, keyboard) */
@@ -817,14 +818,18 @@ static __u8 ps3remote_rdesc[] = {
         /* Use collection 1 for joypad buttons */
         0xA1, 0x02,         /* MCollection Logical (interrelated data) */
 
-         /* Ignore the 1st byte, maybe it is used for a controller
-          * number but it's not needed for correct operation */
+         /*
+          * Ignore the 1st byte, maybe it is used for a controller
+          * number but it's not needed for correct operation
+          */
          0x75, 0x08,        /* GReportSize 0x08 [8] */
          0x95, 0x01,        /* GReportCount 0x01 [1] */
          0x81, 0x01,        /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */
 
-         /* Bytes from 2nd to 4th are a bitmap for joypad buttons, for these
-          * buttons multiple keypresses are allowed */
+         /*
+          * Bytes from 2nd to 4th are a bitmap for joypad buttons, for these
+          * buttons multiple keypresses are allowed
+          */
          0x05, 0x09,        /* GUsagePage Button */
          0x19, 0x01,        /* LUsageMinimum 0x01 [Button 1 (primary/trigger)] */
          0x29, 0x18,        /* LUsageMaximum 0x18 [Button 24] */
@@ -849,8 +854,10 @@ static __u8 ps3remote_rdesc[] = {
          0x95, 0x01,        /* GReportCount 0x01 [1] */
          0x80,              /* MInput  */
 
-         /* Ignore bytes from 6th to 11th, 6th to 10th are always constant at
-          * 0xff and 11th is for press indication */
+         /*
+          * Ignore bytes from 6th to 11th, 6th to 10th are always constant at
+          * 0xff and 11th is for press indication
+          */
          0x75, 0x08,        /* GReportSize 0x08 [8] */
          0x95, 0x06,        /* GReportCount 0x06 [6] */
          0x81, 0x01,        /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */
@@ -929,7 +936,7 @@ static const unsigned int buzz_keymap[] = {
        /*
         * The controller has 4 remote buzzers, each with one LED and 5
         * buttons.
-        * 
+        *
         * We use the mapping chosen by the controller, which is:
         *
         * Key          Offset
@@ -943,15 +950,15 @@ static const unsigned int buzz_keymap[] = {
         * So, for example, the orange button on the third buzzer is mapped to
         * BTN_TRIGGER_HAPPY14
         */
-       1] = BTN_TRIGGER_HAPPY1,
-       2] = BTN_TRIGGER_HAPPY2,
-       3] = BTN_TRIGGER_HAPPY3,
-       4] = BTN_TRIGGER_HAPPY4,
-       5] = BTN_TRIGGER_HAPPY5,
-       6] = BTN_TRIGGER_HAPPY6,
-       7] = BTN_TRIGGER_HAPPY7,
-       8] = BTN_TRIGGER_HAPPY8,
-       9] = BTN_TRIGGER_HAPPY9,
+        [1] = BTN_TRIGGER_HAPPY1,
+        [2] = BTN_TRIGGER_HAPPY2,
+        [3] = BTN_TRIGGER_HAPPY3,
+        [4] = BTN_TRIGGER_HAPPY4,
+        [5] = BTN_TRIGGER_HAPPY5,
+        [6] = BTN_TRIGGER_HAPPY6,
+        [7] = BTN_TRIGGER_HAPPY7,
+        [8] = BTN_TRIGGER_HAPPY8,
+        [9] = BTN_TRIGGER_HAPPY9,
        [10] = BTN_TRIGGER_HAPPY10,
        [11] = BTN_TRIGGER_HAPPY11,
        [12] = BTN_TRIGGER_HAPPY12,
@@ -973,33 +980,33 @@ static enum power_supply_property sony_battery_props[] = {
 };
 
 struct sixaxis_led {
-       __u8 time_enabled; /* the total time the led is active (0xff means forever) */
-       __u8 duty_length;  /* how long a cycle is in deciseconds (0 means "really fast") */
-       __u8 enabled;
-       __u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */
-       __u8 duty_on;  /* % of duty_length the led is on (0xff mean 100%) */
+       u8 time_enabled; /* the total time the led is active (0xff means forever) */
+       u8 duty_length;  /* how long a cycle is in deciseconds (0 means "really fast") */
+       u8 enabled;
+       u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */
+       u8 duty_on;  /* % of duty_length the led is on (0xff mean 100%) */
 } __packed;
 
 struct sixaxis_rumble {
-       __u8 padding;
-       __u8 right_duration; /* Right motor duration (0xff means forever) */
-       __u8 right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
-       __u8 left_duration;    /* Left motor duration (0xff means forever) */
-       __u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
+       u8 padding;
+       u8 right_duration; /* Right motor duration (0xff means forever) */
+       u8 right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
+       u8 left_duration;    /* Left motor duration (0xff means forever) */
+       u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
 } __packed;
 
 struct sixaxis_output_report {
-       __u8 report_id;
+       u8 report_id;
        struct sixaxis_rumble rumble;
-       __u8 padding[4];
-       __u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
+       u8 padding[4];
+       u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
        struct sixaxis_led led[4];    /* LEDx at (4 - x) */
        struct sixaxis_led _reserved; /* LED5, not actually soldered */
 } __packed;
 
 union sixaxis_output_report_01 {
        struct sixaxis_output_report data;
-       __u8 buf[36];
+       u8 buf[36];
 };
 
 struct motion_output_report_02 {
@@ -1028,30 +1035,30 @@ struct sony_sc {
        struct led_classdev *leds[MAX_LEDS];
        unsigned long quirks;
        struct work_struct state_worker;
-       void(*send_output_report)(struct sony_sc*);
+       void (*send_output_report)(struct sony_sc *);
        struct power_supply *battery;
        struct power_supply_desc battery_desc;
        int device_id;
-       __u8 *output_report_dmabuf;
+       u8 *output_report_dmabuf;
 
 #ifdef CONFIG_SONY_FF
-       __u8 left;
-       __u8 right;
+       u8 left;
+       u8 right;
 #endif
 
-       __u8 mac_address[6];
-       __u8 worker_initialized;
-       __u8 cable_state;
-       __u8 battery_charging;
-       __u8 battery_capacity;
-       __u8 led_state[MAX_LEDS];
-       __u8 resume_led_state[MAX_LEDS];
-       __u8 led_delay_on[MAX_LEDS];
-       __u8 led_delay_off[MAX_LEDS];
-       __u8 led_count;
+       u8 mac_address[6];
+       u8 worker_initialized;
+       u8 cable_state;
+       u8 battery_charging;
+       u8 battery_capacity;
+       u8 led_state[MAX_LEDS];
+       u8 resume_led_state[MAX_LEDS];
+       u8 led_delay_on[MAX_LEDS];
+       u8 led_delay_off[MAX_LEDS];
+       u8 led_count;
 };
 
-static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc,
+static u8 *sixaxis_fixup(struct hid_device *hdev, u8 *rdesc,
                             unsigned int *rsize)
 {
        *rsize = sizeof(sixaxis_rdesc);
@@ -1072,7 +1079,7 @@ static u8 *navigation_fixup(struct hid_device *hdev, u8 *rdesc,
        return navigation_rdesc;
 }
 
-static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc,
+static u8 *ps3remote_fixup(struct hid_device *hdev, u8 *rdesc,
                             unsigned int *rsize)
 {
        *rsize = sizeof(ps3remote_rdesc);
@@ -1113,11 +1120,14 @@ static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi,
        return 1;
 }
 
-static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+static u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc,
                unsigned int *rsize)
 {
        struct sony_sc *sc = hid_get_drvdata(hdev);
 
+       if (sc->quirks & SINO_LITE_CONTROLLER)
+               return rdesc;
+
        /*
         * Some Sony RF receivers wrongly declare the mouse pointer as a
         * a constant non-data variable.
@@ -1164,12 +1174,12 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,
        return rdesc;
 }
 
-static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
+static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size)
 {
-       static const __u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 };
+       static const u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 };
        unsigned long flags;
        int offset;
-       __u8 cable_state, battery_capacity, battery_charging;
+       u8 cable_state, battery_capacity, battery_charging;
 
        /*
         * The sixaxis is charging if the battery value is 0xee
@@ -1184,7 +1194,7 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
                battery_charging = !(rd[offset] & 0x01);
                cable_state = 1;
        } else {
-               __u8 index = rd[offset] <= 5 ? rd[offset] : 5;
+               u8 index = rd[offset] <= 5 ? rd[offset] : 5;
                battery_capacity = sixaxis_battery_capacity[index];
                battery_charging = 0;
                cable_state = 0;
@@ -1197,14 +1207,14 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
        spin_unlock_irqrestore(&sc->lock, flags);
 }
 
-static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
+static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
 {
        struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
                                                struct hid_input, list);
        struct input_dev *input_dev = hidinput->input;
        unsigned long flags;
        int n, offset;
-       __u8 cable_state, battery_capacity, battery_charging;
+       u8 cable_state, battery_capacity, battery_charging;
 
        /*
         * Battery and touchpad data starts at byte 30 in the USB report and
@@ -1254,7 +1264,7 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
         * follows the data for the first.
         */
        for (n = 0; n < 2; n++) {
-               __u16 x, y;
+               u16 x, y;
 
                x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8);
                y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4);
@@ -1270,7 +1280,7 @@ static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size)
 }
 
 static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
-               __u8 *rd, int size)
+               u8 *rd, int size)
 {
        struct sony_sc *sc = hid_get_drvdata(hdev);
 
@@ -1394,7 +1404,7 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
 {
        const int buf_size =
                max(SIXAXIS_REPORT_0xF2_SIZE, SIXAXIS_REPORT_0xF5_SIZE);
-       __u8 *buf;
+       u8 *buf;
        int ret;
 
        buf = kmalloc(buf_size, GFP_KERNEL);
@@ -1420,8 +1430,10 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
        }
 
        ret = hid_hw_output_report(hdev, buf, 1);
-       if (ret < 0)
-               hid_err(hdev, "can't set operational mode: step 3\n");
+       if (ret < 0) {
+               hid_info(hdev, "can't set operational mode: step 3, ignoring\n");
+               ret = 0;
+       }
 
 out:
        kfree(buf);
@@ -1431,8 +1443,8 @@ out:
 
 static int sixaxis_set_operational_bt(struct hid_device *hdev)
 {
-       static const __u8 report[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
-       __u8 *buf;
+       static const u8 report[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 };
+       u8 *buf;
        int ret;
 
        buf = kmemdup(report, sizeof(report), GFP_KERNEL);
@@ -1453,7 +1465,7 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev)
  */
 static int dualshock4_set_operational_bt(struct hid_device *hdev)
 {
-       __u8 *buf;
+       u8 *buf;
        int ret;
 
        buf = kmalloc(DS4_REPORT_0x02_SIZE, GFP_KERNEL);
@@ -1470,7 +1482,7 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
 
 static void sixaxis_set_leds_from_id(struct sony_sc *sc)
 {
-       static const __u8 sixaxis_leds[10][4] = {
+       static const u8 sixaxis_leds[10][4] = {
                                { 0x01, 0x00, 0x00, 0x00 },
                                { 0x00, 0x01, 0x00, 0x00 },
                                { 0x00, 0x00, 0x01, 0x00 },
@@ -1497,7 +1509,7 @@ static void sixaxis_set_leds_from_id(struct sony_sc *sc)
 static void dualshock4_set_leds_from_id(struct sony_sc *sc)
 {
        /* The first 4 color/index entries match what the PS4 assigns */
-       static const __u8 color_code[7][3] = {
+       static const u8 color_code[7][3] = {
                        /* Blue   */    { 0x00, 0x00, 0x01 },
                        /* Red    */    { 0x01, 0x00, 0x00 },
                        /* Green  */    { 0x00, 0x01, 0x00 },
@@ -1525,7 +1537,7 @@ static void buzz_set_leds(struct sony_sc *sc)
                &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
        struct hid_report *report = list_entry(report_list->next,
                struct hid_report, list);
-       __s32 *value = report->field[0]->value;
+       s32 *value = report->field[0]->value;
 
        BUILD_BUG_ON(MAX_LEDS < 4);
 
@@ -1619,7 +1631,7 @@ static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on,
        struct hid_device *hdev = to_hid_device(dev);
        struct sony_sc *drv_data = hid_get_drvdata(hdev);
        int n;
-       __u8 new_on, new_off;
+       u8 new_on, new_off;
 
        if (!drv_data) {
                hid_err(hdev, "No device data\n");
@@ -1690,8 +1702,8 @@ static int sony_leds_init(struct sony_sc *sc)
        const char *name_fmt;
        static const char * const ds4_name_str[] = { "red", "green", "blue",
                                                  "global" };
-       __u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 };
-       __u8 use_hw_blink[MAX_LEDS] = { 0 };
+       u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 };
+       u8 use_hw_blink[MAX_LEDS] = { 0 };
 
        BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
 
@@ -1719,7 +1731,7 @@ static int sony_leds_init(struct sony_sc *sc)
                name_len = 0;
                name_fmt = "%s:%s";
        } else if (sc->quirks & NAVIGATION_CONTROLLER) {
-               static const __u8 navigation_leds[4] = {0x01, 0x00, 0x00, 0x00};
+               static const u8 navigation_leds[4] = {0x01, 0x00, 0x00, 0x00};
 
                memcpy(sc->led_state, navigation_leds, sizeof(navigation_leds));
                sc->led_count = 1;
@@ -1796,7 +1808,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc)
        static const union sixaxis_output_report_01 default_report = {
                .buf = {
                        0x01,
-                       0x00, 0xff, 0x00, 0xff, 0x00,
+                       0x01, 0xff, 0x00, 0xff, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00,
                        0xff, 0x27, 0x10, 0x00, 0x32,
                        0xff, 0x27, 0x10, 0x00, 0x32,
@@ -1842,7 +1854,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc)
                }
        }
 
-       hid_hw_raw_request(sc->hdev, report->report_id, (__u8 *)report,
+       hid_hw_raw_request(sc->hdev, report->report_id, (u8 *)report,
                        sizeof(struct sixaxis_output_report),
                        HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
 }
@@ -1850,7 +1862,7 @@ static void sixaxis_send_output_report(struct sony_sc *sc)
 static void dualshock4_send_output_report(struct sony_sc *sc)
 {
        struct hid_device *hdev = sc->hdev;
-       __u8 *buf = sc->output_report_dmabuf;
+       u8 *buf = sc->output_report_dmabuf;
        int offset;
 
        if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
@@ -1910,7 +1922,7 @@ static void motion_send_output_report(struct sony_sc *sc)
        report->rumble = max(sc->right, sc->left);
 #endif
 
-       hid_hw_output_report(hdev, (__u8 *)report, MOTION_REPORT_0x02_SIZE);
+       hid_hw_output_report(hdev, (u8 *)report, MOTION_REPORT_0x02_SIZE);
 }
 
 static inline void sony_send_output_report(struct sony_sc *sc)
@@ -1922,6 +1934,7 @@ static inline void sony_send_output_report(struct sony_sc *sc)
 static void sony_state_worker(struct work_struct *work)
 {
        struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
+
        sc->send_output_report(sc);
 }
 
@@ -2142,7 +2155,7 @@ static int sony_get_bt_devaddr(struct sony_sc *sc)
 
 static int sony_check_add(struct sony_sc *sc)
 {
-       __u8 *buf = NULL;
+       u8 *buf = NULL;
        int n, ret;
 
        if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) ||
@@ -2253,7 +2266,7 @@ static void sony_release_device_id(struct sony_sc *sc)
 }
 
 static inline void sony_init_output_report(struct sony_sc *sc,
-                               void(*send_output_report)(struct sony_sc*))
+                               void (*send_output_report)(struct sony_sc *))
 {
        sc->send_output_report = send_output_report;
 
@@ -2441,7 +2454,7 @@ static int sony_suspend(struct hid_device *hdev, pm_message_t message)
        /*
         * On suspend save the current LED state,
         * stop running force-feedback and blank the LEDS.
-         */
+        */
        if (SONY_LED_SUPPORT || SONY_FF_SUPPORT) {
                struct sony_sc *sc = hid_get_drvdata(hdev);
 
@@ -2501,8 +2514,10 @@ static const struct hid_device_id sony_devices[] = {
                .driver_data = VAIO_RDESC_CONSTANT },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE),
                .driver_data = VAIO_RDESC_CONSTANT },
-       /* Wired Buzz Controller. Reported as Sony Hub from its USB ID and as
-        * Logitech joystick from the device descriptor. */
+       /*
+        * Wired Buzz Controller. Reported as Sony Hub from its USB ID and as
+        * Logitech joystick from the device descriptor.
+        */
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER),
                .driver_data = BUZZ_CONTROLLER },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER),
@@ -2521,6 +2536,9 @@ static const struct hid_device_id sony_devices[] = {
                .driver_data = DUALSHOCK4_CONTROLLER_USB },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER),
                .driver_data = DUALSHOCK4_CONTROLLER_BT },
+       /* Nyko Core Controller for PS3 */
+       { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER),
+               .driver_data = SIXAXIS_CONTROLLER_USB | SINO_LITE_CONTROLLER },
        { }
 };
 MODULE_DEVICE_TABLE(hid, sony_devices);
index b95d3978c2723ffc9a49a82bf07b2c2a06da0dbf..847a497cd47245223185665839fb9e2452a7665a 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/leds.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
-#include <linux/workqueue.h>
 
 #include "hid-ids.h"
 
@@ -56,7 +55,6 @@ struct thingm_rgb {
        struct thingm_led red;
        struct thingm_led green;
        struct thingm_led blue;
-       struct work_struct work;
        u8 num;
 };
 
@@ -79,9 +77,13 @@ static int thingm_send(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
                        buf[0], buf[1], buf[2], buf[3], buf[4],
                        buf[5], buf[6], buf[7], buf[8]);
 
+       mutex_lock(&tdev->lock);
+
        ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
                        HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
 
+       mutex_unlock(&tdev->lock);
+
        return ret < 0 ? ret : 0;
 }
 
@@ -89,16 +91,31 @@ static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE])
 {
        int ret;
 
+       /*
+        * A read consists of two operations: sending the read command
+        * and the actual read from the device. Use the mutex to protect
+        * the full sequence of both operations.
+        */
+       mutex_lock(&tdev->lock);
+
+       ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
+                       HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+       if (ret < 0)
+               goto err;
+
        ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE,
                        HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
        if (ret < 0)
-               return ret;
+               goto err;
+
+       ret = 0;
 
        hid_dbg(tdev->hdev, "<- %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n",
                        buf[0], buf[1], buf[2], buf[3], buf[4],
                        buf[5], buf[6], buf[7], buf[8]);
-
-       return 0;
+err:
+       mutex_unlock(&tdev->lock);
+       return ret;
 }
 
 static int thingm_version(struct thingm_device *tdev)
@@ -106,10 +123,6 @@ static int thingm_version(struct thingm_device *tdev)
        u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 };
        int err;
 
-       err = thingm_send(tdev, buf);
-       if (err)
-               return err;
-
        err = thingm_recv(tdev, buf);
        if (err)
                return err;
@@ -131,25 +144,17 @@ static int thingm_write_color(struct thingm_rgb *rgb)
        return thingm_send(rgb->tdev, buf);
 }
 
-static void thingm_work(struct work_struct *work)
-{
-       struct thingm_rgb *rgb = container_of(work, struct thingm_rgb, work);
-
-       mutex_lock(&rgb->tdev->lock);
-
-       if (thingm_write_color(rgb))
-               hid_err(rgb->tdev->hdev, "failed to write color\n");
-
-       mutex_unlock(&rgb->tdev->lock);
-}
-
-static void thingm_led_set(struct led_classdev *ldev,
-               enum led_brightness brightness)
+static int thingm_led_set(struct led_classdev *ldev,
+                         enum led_brightness brightness)
 {
        struct thingm_led *led = container_of(ldev, struct thingm_led, ldev);
+       int ret;
+
+       ret = thingm_write_color(led->rgb);
+       if (ret)
+               hid_err(led->rgb->tdev->hdev, "failed to write color\n");
 
-       /* the ledclass has already stored the brightness value */
-       schedule_work(&led->rgb->work);
+       return ret;
 }
 
 static int thingm_init_rgb(struct thingm_rgb *rgb)
@@ -162,10 +167,11 @@ static int thingm_init_rgb(struct thingm_rgb *rgb)
                        "thingm%d:red:led%d", minor, rgb->num);
        rgb->red.ldev.name = rgb->red.name;
        rgb->red.ldev.max_brightness = 255;
-       rgb->red.ldev.brightness_set = thingm_led_set;
+       rgb->red.ldev.brightness_set_blocking = thingm_led_set;
        rgb->red.rgb = rgb;
 
-       err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->red.ldev);
+       err = devm_led_classdev_register(&rgb->tdev->hdev->dev,
+                                        &rgb->red.ldev);
        if (err)
                return err;
 
@@ -174,46 +180,27 @@ static int thingm_init_rgb(struct thingm_rgb *rgb)
                        "thingm%d:green:led%d", minor, rgb->num);
        rgb->green.ldev.name = rgb->green.name;
        rgb->green.ldev.max_brightness = 255;
-       rgb->green.ldev.brightness_set = thingm_led_set;
+       rgb->green.ldev.brightness_set_blocking = thingm_led_set;
        rgb->green.rgb = rgb;
 
-       err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->green.ldev);
+       err = devm_led_classdev_register(&rgb->tdev->hdev->dev,
+                                        &rgb->green.ldev);
        if (err)
-               goto unregister_red;
+               return err;
 
        /* Register the blue diode */
        snprintf(rgb->blue.name, sizeof(rgb->blue.name),
                        "thingm%d:blue:led%d", minor, rgb->num);
        rgb->blue.ldev.name = rgb->blue.name;
        rgb->blue.ldev.max_brightness = 255;
-       rgb->blue.ldev.brightness_set = thingm_led_set;
+       rgb->blue.ldev.brightness_set_blocking = thingm_led_set;
        rgb->blue.rgb = rgb;
 
-       err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->blue.ldev);
-       if (err)
-               goto unregister_green;
-
-       INIT_WORK(&rgb->work, thingm_work);
-
-       return 0;
-
-unregister_green:
-       led_classdev_unregister(&rgb->green.ldev);
-
-unregister_red:
-       led_classdev_unregister(&rgb->red.ldev);
-
+       err = devm_led_classdev_register(&rgb->tdev->hdev->dev,
+                                        &rgb->blue.ldev);
        return err;
 }
 
-static void thingm_remove_rgb(struct thingm_rgb *rgb)
-{
-       led_classdev_unregister(&rgb->red.ldev);
-       led_classdev_unregister(&rgb->green.ldev);
-       led_classdev_unregister(&rgb->blue.ldev);
-       flush_work(&rgb->work);
-}
-
 static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
        struct thingm_device *tdev;
@@ -229,17 +216,13 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
 
        err = hid_parse(hdev);
        if (err)
-               goto error;
-
-       err = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
-       if (err)
-               goto error;
+               return err;
 
        mutex_init(&tdev->lock);
 
        err = thingm_version(tdev);
        if (err)
-               goto stop;
+               return err;
 
        hid_dbg(hdev, "firmware version: %c.%c\n",
                        tdev->version.major, tdev->version.minor);
@@ -250,17 +233,18 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
 
        if (!tdev->fwinfo) {
                hid_err(hdev, "unsupported firmware %c\n", tdev->version.major);
-               err = -ENODEV;
-               goto stop;
+               return -ENODEV;
        }
 
        tdev->rgb = devm_kzalloc(&hdev->dev,
                        sizeof(struct thingm_rgb) * tdev->fwinfo->numrgb,
                        GFP_KERNEL);
-       if (!tdev->rgb) {
-               err = -ENOMEM;
-               goto stop;
-       }
+       if (!tdev->rgb)
+               return -ENOMEM;
+
+       err = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+       if (err)
+               return err;
 
        for (i = 0; i < tdev->fwinfo->numrgb; ++i) {
                struct thingm_rgb *rgb = tdev->rgb + i;
@@ -269,28 +253,12 @@ static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)
                rgb->num = tdev->fwinfo->first + i;
                err = thingm_init_rgb(rgb);
                if (err) {
-                       while (--i >= 0)
-                               thingm_remove_rgb(tdev->rgb + i);
-                       goto stop;
+                       hid_hw_stop(hdev);
+                       return err;
                }
        }
 
        return 0;
-stop:
-       hid_hw_stop(hdev);
-error:
-       return err;
-}
-
-static void thingm_remove(struct hid_device *hdev)
-{
-       struct thingm_device *tdev = hid_get_drvdata(hdev);
-       int i;
-
-       hid_hw_stop(hdev);
-
-       for (i = 0; i < tdev->fwinfo->numrgb; ++i)
-               thingm_remove_rgb(tdev->rgb + i);
 }
 
 static const struct hid_device_id thingm_table[] = {
@@ -302,7 +270,6 @@ MODULE_DEVICE_TABLE(hid, thingm_table);
 static struct hid_driver thingm_driver = {
        .name = "thingm",
        .probe = thingm_probe,
-       .remove = thingm_remove,
        .id_table = thingm_table,
 };
 
index 7dd0953cd70f222f037c72b7c217b76eff8d2942..ed2f68edc8f1c648e30b15178fc550cb9a071f88 100644 (file)
@@ -55,6 +55,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_TOUCHPACK, USB_DEVICE_ID_TOUCHPACK_RTS, HID_QUIRK_MULTI_INPUT },
 
        { USB_VENDOR_ID_AIREN, USB_DEVICE_ID_AIREN_SLIMPLUS, HID_QUIRK_NOGET },
+       { USB_VENDOR_ID_AKAI, USB_DEVICE_ID_AKAI_MPKMINI2, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
@@ -106,6 +107,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_MOUSE_4D22, HID_QUIRK_ALWAYS_POLL },
        { USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001, HID_QUIRK_NOGET },
+       { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3003, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_REALTEK, USB_DEVICE_ID_REALTEK_READER, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET },
@@ -140,6 +142,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD2, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS1, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS2, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS },
index 5cb21dd91094f712038f56fcfc03ea5a2fa3e1c7..68a560957871c5a497e4e3d0229ca03dc359d8da 100644 (file)
@@ -1357,6 +1357,9 @@ static void wacom_clean_inputs(struct wacom *wacom)
        wacom->wacom_wac.pen_input = NULL;
        wacom->wacom_wac.touch_input = NULL;
        wacom->wacom_wac.pad_input = NULL;
+       wacom->wacom_wac.pen_registered = false;
+       wacom->wacom_wac.touch_registered = false;
+       wacom->wacom_wac.pad_registered = false;
        wacom_destroy_leds(wacom);
 }
 
@@ -1494,123 +1497,6 @@ static void wacom_calculate_res(struct wacom_features *features)
                                                    features->unitExpo);
 }
 
-static void wacom_wireless_work(struct work_struct *work)
-{
-       struct wacom *wacom = container_of(work, struct wacom, work);
-       struct usb_device *usbdev = wacom->usbdev;
-       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
-       struct hid_device *hdev1, *hdev2;
-       struct wacom *wacom1, *wacom2;
-       struct wacom_wac *wacom_wac1, *wacom_wac2;
-       int error;
-
-       /*
-        * Regardless if this is a disconnect or a new tablet,
-        * remove any existing input and battery devices.
-        */
-
-       wacom_destroy_battery(wacom);
-
-       /* Stylus interface */
-       hdev1 = usb_get_intfdata(usbdev->config->interface[1]);
-       wacom1 = hid_get_drvdata(hdev1);
-       wacom_wac1 = &(wacom1->wacom_wac);
-       wacom_clean_inputs(wacom1);
-
-       /* Touch interface */
-       hdev2 = usb_get_intfdata(usbdev->config->interface[2]);
-       wacom2 = hid_get_drvdata(hdev2);
-       wacom_wac2 = &(wacom2->wacom_wac);
-       wacom_clean_inputs(wacom2);
-
-       if (wacom_wac->pid == 0) {
-               hid_info(wacom->hdev, "wireless tablet disconnected\n");
-               wacom_wac1->shared->type = 0;
-       } else {
-               const struct hid_device_id *id = wacom_ids;
-
-               hid_info(wacom->hdev, "wireless tablet connected with PID %x\n",
-                        wacom_wac->pid);
-
-               while (id->bus) {
-                       if (id->vendor == USB_VENDOR_ID_WACOM &&
-                           id->product == wacom_wac->pid)
-                               break;
-                       id++;
-               }
-
-               if (!id->bus) {
-                       hid_info(wacom->hdev, "ignoring unknown PID.\n");
-                       return;
-               }
-
-               /* Stylus interface */
-               wacom_wac1->features =
-                       *((struct wacom_features *)id->driver_data);
-               wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PEN;
-               wacom_set_default_phy(&wacom_wac1->features);
-               wacom_calculate_res(&wacom_wac1->features);
-               snprintf(wacom_wac1->pen_name, WACOM_NAME_MAX, "%s (WL) Pen",
-                        wacom_wac1->features.name);
-               if (wacom_wac1->features.type < BAMBOO_PEN ||
-                   wacom_wac1->features.type > BAMBOO_PT) {
-                       snprintf(wacom_wac1->pad_name, WACOM_NAME_MAX, "%s (WL) Pad",
-                                wacom_wac1->features.name);
-                       wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PAD;
-               }
-               wacom_wac1->shared->touch_max = wacom_wac1->features.touch_max;
-               wacom_wac1->shared->type = wacom_wac1->features.type;
-               wacom_wac1->pid = wacom_wac->pid;
-               error = wacom_allocate_inputs(wacom1) ||
-                       wacom_register_inputs(wacom1);
-               if (error)
-                       goto fail;
-
-               /* Touch interface */
-               if (wacom_wac1->features.touch_max ||
-                   (wacom_wac1->features.type >= INTUOSHT &&
-                   wacom_wac1->features.type <= BAMBOO_PT)) {
-                       wacom_wac2->features =
-                               *((struct wacom_features *)id->driver_data);
-                       wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
-                       wacom_set_default_phy(&wacom_wac2->features);
-                       wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096;
-                       wacom_calculate_res(&wacom_wac2->features);
-                       snprintf(wacom_wac2->touch_name, WACOM_NAME_MAX,
-                                "%s (WL) Finger",wacom_wac2->features.name);
-                       if (wacom_wac1->features.touch_max)
-                               wacom_wac2->features.device_type |= WACOM_DEVICETYPE_TOUCH;
-                       if (wacom_wac1->features.type >= INTUOSHT &&
-                           wacom_wac1->features.type <= BAMBOO_PT) {
-                               snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX,
-                                        "%s (WL) Pad",wacom_wac2->features.name);
-                               wacom_wac2->features.device_type |= WACOM_DEVICETYPE_PAD;
-                       }
-                       wacom_wac2->pid = wacom_wac->pid;
-                       error = wacom_allocate_inputs(wacom2) ||
-                               wacom_register_inputs(wacom2);
-                       if (error)
-                               goto fail;
-
-                       if ((wacom_wac1->features.type == INTUOSHT ||
-                           wacom_wac1->features.type == INTUOSHT2) &&
-                           wacom_wac1->features.touch_max)
-                               wacom_wac->shared->touch_input = wacom_wac2->touch_input;
-               }
-
-               error = wacom_initialize_battery(wacom);
-               if (error)
-                       goto fail;
-       }
-
-       return;
-
-fail:
-       wacom_clean_inputs(wacom1);
-       wacom_clean_inputs(wacom2);
-       return;
-}
-
 void wacom_battery_work(struct work_struct *work)
 {
        struct wacom *wacom = container_of(work, struct wacom, work);
@@ -1642,7 +1528,7 @@ static size_t wacom_compute_pktlen(struct hid_device *hdev)
        return size;
 }
 
-static void wacom_update_name(struct wacom *wacom)
+static void wacom_update_name(struct wacom *wacom, const char *suffix)
 {
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
        struct wacom_features *features = &wacom_wac->features;
@@ -1678,68 +1564,28 @@ static void wacom_update_name(struct wacom *wacom)
 
        /* Append the device type to the name */
        snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name),
-               "%s Pen", name);
+               "%s%s Pen", name, suffix);
        snprintf(wacom_wac->touch_name, sizeof(wacom_wac->touch_name),
-               "%s Finger", name);
+               "%s%s Finger", name, suffix);
        snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
-               "%s Pad", name);
+               "%s%s Pad", name, suffix);
 }
 
-static int wacom_probe(struct hid_device *hdev,
-               const struct hid_device_id *id)
+static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
 {
-       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
-       struct usb_device *dev = interface_to_usbdev(intf);
-       struct wacom *wacom;
-       struct wacom_wac *wacom_wac;
-       struct wacom_features *features;
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct wacom_features *features = &wacom_wac->features;
+       struct hid_device *hdev = wacom->hdev;
        int error;
        unsigned int connect_mask = HID_CONNECT_HIDRAW;
 
-       if (!id->driver_data)
-               return -EINVAL;
-
-       hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
-
-       /* hid-core sets this quirk for the boot interface */
-       hdev->quirks &= ~HID_QUIRK_NOGET;
-
-       wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
-       if (!wacom)
-               return -ENOMEM;
-
-       hid_set_drvdata(hdev, wacom);
-       wacom->hdev = hdev;
-
-       /* ask for the report descriptor to be loaded by HID */
-       error = hid_parse(hdev);
-       if (error) {
-               hid_err(hdev, "parse failed\n");
-               goto fail_parse;
-       }
-
-       wacom_wac = &wacom->wacom_wac;
-       wacom_wac->features = *((struct wacom_features *)id->driver_data);
-       features = &wacom_wac->features;
        features->pktlen = wacom_compute_pktlen(hdev);
-       if (features->pktlen > WACOM_PKGLEN_MAX) {
-               error = -EINVAL;
-               goto fail_pktlen;
-       }
-
-       if (features->check_for_hid_type && features->hid_type != hdev->type) {
-               error = -ENODEV;
-               goto fail_type;
-       }
-
-       wacom->usbdev = dev;
-       wacom->intf = intf;
-       mutex_init(&wacom->lock);
-       INIT_WORK(&wacom->work, wacom_wireless_work);
+       if (features->pktlen > WACOM_PKGLEN_MAX)
+               return -EINVAL;
 
        error = wacom_allocate_inputs(wacom);
        if (error)
-               goto fail_allocate_inputs;
+               return error;
 
        /*
         * Bamboo Pad has a generic hid handling for the Pen, and we switch it
@@ -1752,7 +1598,7 @@ static int wacom_probe(struct hid_device *hdev,
                } else if ((features->pktlen != WACOM_PKGLEN_BPAD_TOUCH) &&
                           (features->pktlen != WACOM_PKGLEN_BPAD_TOUCH_USB)) {
                        error = -ENODEV;
-                       goto fail_shared_data;
+                       goto fail_allocate_inputs;
                }
        }
 
@@ -1772,14 +1618,14 @@ static int wacom_probe(struct hid_device *hdev,
                         error ? "Ignoring" : "Assuming pen");
 
                if (error)
-                       goto fail_shared_data;
+                       goto fail_parsed;
 
                features->device_type |= WACOM_DEVICETYPE_PEN;
        }
 
        wacom_calculate_res(features);
 
-       wacom_update_name(wacom);
+       wacom_update_name(wacom, wireless ? " (WL)" : "");
 
        error = wacom_add_shared_data(hdev);
        if (error)
@@ -1796,14 +1642,6 @@ static int wacom_probe(struct hid_device *hdev,
        if (error)
                goto fail_register_inputs;
 
-       if (hdev->bus == BUS_BLUETOOTH) {
-               error = device_create_file(&hdev->dev, &dev_attr_speed);
-               if (error)
-                       hid_warn(hdev,
-                                "can't create sysfs speed attribute err: %d\n",
-                                error);
-       }
-
        if (features->type == HID_GENERIC)
                connect_mask |= HID_CONNECT_DRIVER;
 
@@ -1814,8 +1652,10 @@ static int wacom_probe(struct hid_device *hdev,
                goto fail_hw_start;
        }
 
-       /* Note that if query fails it is not a hard failure */
-       wacom_query_tablet_data(hdev, features);
+       if (!wireless) {
+               /* Note that if query fails it is not a hard failure */
+               wacom_query_tablet_data(hdev, features);
+       }
 
        /* touch only Bamboo doesn't support pen */
        if ((features->type == BAMBOO_TOUCH) &&
@@ -1844,18 +1684,166 @@ static int wacom_probe(struct hid_device *hdev,
        return 0;
 
 fail_hw_start:
-       if (hdev->bus == BUS_BLUETOOTH)
-               device_remove_file(&hdev->dev, &dev_attr_speed);
+       hid_hw_stop(hdev);
 fail_register_inputs:
        wacom_clean_inputs(wacom);
        wacom_destroy_battery(wacom);
 fail_battery:
        wacom_remove_shared_data(wacom);
 fail_shared_data:
-       wacom_clean_inputs(wacom);
+fail_parsed:
 fail_allocate_inputs:
+       wacom_clean_inputs(wacom);
+       return error;
+}
+
+static void wacom_wireless_work(struct work_struct *work)
+{
+       struct wacom *wacom = container_of(work, struct wacom, work);
+       struct usb_device *usbdev = wacom->usbdev;
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct hid_device *hdev1, *hdev2;
+       struct wacom *wacom1, *wacom2;
+       struct wacom_wac *wacom_wac1, *wacom_wac2;
+       int error;
+
+       /*
+        * Regardless if this is a disconnect or a new tablet,
+        * remove any existing input and battery devices.
+        */
+
+       wacom_destroy_battery(wacom);
+
+       /* Stylus interface */
+       hdev1 = usb_get_intfdata(usbdev->config->interface[1]);
+       wacom1 = hid_get_drvdata(hdev1);
+       wacom_wac1 = &(wacom1->wacom_wac);
+       wacom_clean_inputs(wacom1);
+
+       /* Touch interface */
+       hdev2 = usb_get_intfdata(usbdev->config->interface[2]);
+       wacom2 = hid_get_drvdata(hdev2);
+       wacom_wac2 = &(wacom2->wacom_wac);
+       wacom_clean_inputs(wacom2);
+
+       if (wacom_wac->pid == 0) {
+               hid_info(wacom->hdev, "wireless tablet disconnected\n");
+               wacom_wac1->shared->type = 0;
+       } else {
+               const struct hid_device_id *id = wacom_ids;
+
+               hid_info(wacom->hdev, "wireless tablet connected with PID %x\n",
+                        wacom_wac->pid);
+
+               while (id->bus) {
+                       if (id->vendor == USB_VENDOR_ID_WACOM &&
+                           id->product == wacom_wac->pid)
+                               break;
+                       id++;
+               }
+
+               if (!id->bus) {
+                       hid_info(wacom->hdev, "ignoring unknown PID.\n");
+                       return;
+               }
+
+               /* Stylus interface */
+               wacom_wac1->features =
+                       *((struct wacom_features *)id->driver_data);
+
+               wacom_wac1->pid = wacom_wac->pid;
+               hid_hw_stop(hdev1);
+               error = wacom_parse_and_register(wacom1, true);
+               if (error)
+                       goto fail;
+
+               /* Touch interface */
+               if (wacom_wac1->features.touch_max ||
+                   (wacom_wac1->features.type >= INTUOSHT &&
+                   wacom_wac1->features.type <= BAMBOO_PT)) {
+                       wacom_wac2->features =
+                               *((struct wacom_features *)id->driver_data);
+                       wacom_wac2->pid = wacom_wac->pid;
+                       hid_hw_stop(hdev2);
+                       error = wacom_parse_and_register(wacom2, true);
+                       if (error)
+                               goto fail;
+               }
+
+               error = wacom_initialize_battery(wacom);
+               if (error)
+                       goto fail;
+       }
+
+       return;
+
+fail:
+       wacom_clean_inputs(wacom1);
+       wacom_clean_inputs(wacom2);
+       return;
+}
+
+static int wacom_probe(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+       struct usb_device *dev = interface_to_usbdev(intf);
+       struct wacom *wacom;
+       struct wacom_wac *wacom_wac;
+       struct wacom_features *features;
+       int error;
+
+       if (!id->driver_data)
+               return -EINVAL;
+
+       hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
+
+       /* hid-core sets this quirk for the boot interface */
+       hdev->quirks &= ~HID_QUIRK_NOGET;
+
+       wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
+       if (!wacom)
+               return -ENOMEM;
+
+       hid_set_drvdata(hdev, wacom);
+       wacom->hdev = hdev;
+
+       wacom_wac = &wacom->wacom_wac;
+       wacom_wac->features = *((struct wacom_features *)id->driver_data);
+       features = &wacom_wac->features;
+
+       if (features->check_for_hid_type && features->hid_type != hdev->type) {
+               error = -ENODEV;
+               goto fail_type;
+       }
+
+       wacom->usbdev = dev;
+       wacom->intf = intf;
+       mutex_init(&wacom->lock);
+       INIT_WORK(&wacom->work, wacom_wireless_work);
+
+       /* ask for the report descriptor to be loaded by HID */
+       error = hid_parse(hdev);
+       if (error) {
+               hid_err(hdev, "parse failed\n");
+               goto fail_parse;
+       }
+
+       error = wacom_parse_and_register(wacom, false);
+       if (error)
+               goto fail_parse;
+
+       if (hdev->bus == BUS_BLUETOOTH) {
+               error = device_create_file(&hdev->dev, &dev_attr_speed);
+               if (error)
+                       hid_warn(hdev,
+                                "can't create sysfs speed attribute err: %d\n",
+                                error);
+       }
+
+       return 0;
+
 fail_type:
-fail_pktlen:
 fail_parse:
        kfree(wacom);
        hid_set_drvdata(hdev, NULL);
@@ -1865,6 +1853,11 @@ fail_parse:
 static void wacom_remove(struct hid_device *hdev)
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct wacom_features *features = &wacom_wac->features;
+
+       if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR)
+               hid_hw_close(hdev);
 
        hid_hw_stop(hdev);
 
index 99ef77fcfb8040d9bcb0021ebb13a901a21c741e..bd198bbd4df0841b5a3596c09ecac822ecda40e5 100644 (file)
@@ -575,16 +575,102 @@ static int wacom_intuos_pad(struct wacom_wac *wacom)
        return 1;
 }
 
+static int wacom_intuos_get_tool_type(int tool_id)
+{
+       int tool_type;
+
+       switch (tool_id) {
+       case 0x812: /* Inking pen */
+       case 0x801: /* Intuos3 Inking pen */
+       case 0x120802: /* Intuos4/5 Inking Pen */
+       case 0x012:
+               tool_type = BTN_TOOL_PENCIL;
+               break;
+
+       case 0x822: /* Pen */
+       case 0x842:
+       case 0x852:
+       case 0x823: /* Intuos3 Grip Pen */
+       case 0x813: /* Intuos3 Classic Pen */
+       case 0x885: /* Intuos3 Marker Pen */
+       case 0x802: /* Intuos4/5 13HD/24HD General Pen */
+       case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */
+       case 0x8e2: /* IntuosHT2 pen */
+       case 0x022:
+       case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */
+       case 0x140802: /* Intuos4/5 13HD/24HD Classic Pen */
+       case 0x160802: /* Cintiq 13HD Pro Pen */
+       case 0x180802: /* DTH2242 Pen */
+       case 0x100802: /* Intuos4/5 13HD/24HD General Pen */
+               tool_type = BTN_TOOL_PEN;
+               break;
+
+       case 0x832: /* Stroke pen */
+       case 0x032:
+               tool_type = BTN_TOOL_BRUSH;
+               break;
+
+       case 0x007: /* Mouse 4D and 2D */
+       case 0x09c:
+       case 0x094:
+       case 0x017: /* Intuos3 2D Mouse */
+       case 0x806: /* Intuos4 Mouse */
+               tool_type = BTN_TOOL_MOUSE;
+               break;
+
+       case 0x096: /* Lens cursor */
+       case 0x097: /* Intuos3 Lens cursor */
+       case 0x006: /* Intuos4 Lens cursor */
+               tool_type = BTN_TOOL_LENS;
+               break;
+
+       case 0x82a: /* Eraser */
+       case 0x85a:
+       case 0x91a:
+       case 0xd1a:
+       case 0x0fa:
+       case 0x82b: /* Intuos3 Grip Pen Eraser */
+       case 0x81b: /* Intuos3 Classic Pen Eraser */
+       case 0x91b: /* Intuos3 Airbrush Eraser */
+       case 0x80c: /* Intuos4/5 13HD/24HD Marker Pen Eraser */
+       case 0x80a: /* Intuos4/5 13HD/24HD General Pen Eraser */
+       case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
+       case 0x14080a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */
+       case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
+       case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */
+       case 0x16080a: /* Cintiq 13HD Pro Pen Eraser */
+       case 0x18080a: /* DTH2242 Eraser */
+       case 0x10080a: /* Intuos4/5 13HD/24HD General Pen Eraser */
+               tool_type = BTN_TOOL_RUBBER;
+               break;
+
+       case 0xd12:
+       case 0x912:
+       case 0x112:
+       case 0x913: /* Intuos3 Airbrush */
+       case 0x902: /* Intuos4/5 13HD/24HD Airbrush */
+       case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */
+               tool_type = BTN_TOOL_AIRBRUSH;
+               break;
+
+       default: /* Unknown tool */
+               tool_type = BTN_TOOL_PEN;
+               break;
+       }
+       return tool_type;
+}
+
 static int wacom_intuos_inout(struct wacom_wac *wacom)
 {
        struct wacom_features *features = &wacom->features;
        unsigned char *data = wacom->data;
        struct input_dev *input = wacom->pen_input;
-       int idx = 0;
+       int idx = (features->type == INTUOS) ? (data[1] & 0x01) : 0;
 
-       /* tool number */
-       if (features->type == INTUOS)
-               idx = data[1] & 0x01;
+       if (!(((data[1] & 0xfc) == 0xc0) ||  /* in prox */
+           ((data[1] & 0xfe) == 0x20) ||    /* in range */
+           ((data[1] & 0xfe) == 0x80)))     /* out prox */
+               return 0;
 
        /* Enter report */
        if ((data[1] & 0xfc) == 0xc0) {
@@ -596,116 +682,24 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
                wacom->id[idx] = (data[2] << 4) | (data[3] >> 4) |
                        ((data[7] & 0x0f) << 20) | ((data[8] & 0xf0) << 12);
 
-               switch (wacom->id[idx]) {
-               case 0x812: /* Inking pen */
-               case 0x801: /* Intuos3 Inking pen */
-               case 0x120802: /* Intuos4/5 Inking Pen */
-               case 0x012:
-                       wacom->tool[idx] = BTN_TOOL_PENCIL;
-                       break;
-
-               case 0x822: /* Pen */
-               case 0x842:
-               case 0x852:
-               case 0x823: /* Intuos3 Grip Pen */
-               case 0x813: /* Intuos3 Classic Pen */
-               case 0x885: /* Intuos3 Marker Pen */
-               case 0x802: /* Intuos4/5 13HD/24HD General Pen */
-               case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */
-               case 0x022:
-               case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */
-               case 0x140802: /* Intuos4/5 13HD/24HD Classic Pen */
-               case 0x160802: /* Cintiq 13HD Pro Pen */
-               case 0x180802: /* DTH2242 Pen */
-               case 0x100802: /* Intuos4/5 13HD/24HD General Pen */
-                       wacom->tool[idx] = BTN_TOOL_PEN;
-                       break;
-
-               case 0x832: /* Stroke pen */
-               case 0x032:
-                       wacom->tool[idx] = BTN_TOOL_BRUSH;
-                       break;
-
-               case 0x007: /* Mouse 4D and 2D */
-               case 0x09c:
-               case 0x094:
-               case 0x017: /* Intuos3 2D Mouse */
-               case 0x806: /* Intuos4 Mouse */
-                       wacom->tool[idx] = BTN_TOOL_MOUSE;
-                       break;
-
-               case 0x096: /* Lens cursor */
-               case 0x097: /* Intuos3 Lens cursor */
-               case 0x006: /* Intuos4 Lens cursor */
-                       wacom->tool[idx] = BTN_TOOL_LENS;
-                       break;
-
-               case 0x82a: /* Eraser */
-               case 0x85a:
-               case 0x91a:
-               case 0xd1a:
-               case 0x0fa:
-               case 0x82b: /* Intuos3 Grip Pen Eraser */
-               case 0x81b: /* Intuos3 Classic Pen Eraser */
-               case 0x91b: /* Intuos3 Airbrush Eraser */
-               case 0x80c: /* Intuos4/5 13HD/24HD Marker Pen Eraser */
-               case 0x80a: /* Intuos4/5 13HD/24HD General Pen Eraser */
-               case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
-               case 0x14080a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */
-               case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
-               case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */
-               case 0x16080a: /* Cintiq 13HD Pro Pen Eraser */
-               case 0x18080a: /* DTH2242 Eraser */
-               case 0x10080a: /* Intuos4/5 13HD/24HD General Pen Eraser */
-                       wacom->tool[idx] = BTN_TOOL_RUBBER;
-                       break;
-
-               case 0xd12:
-               case 0x912:
-               case 0x112:
-               case 0x913: /* Intuos3 Airbrush */
-               case 0x902: /* Intuos4/5 13HD/24HD Airbrush */
-               case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */
-                       wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
-                       break;
-
-               default: /* Unknown tool */
-                       wacom->tool[idx] = BTN_TOOL_PEN;
-                       break;
-               }
+               wacom->tool[idx] = wacom_intuos_get_tool_type(wacom->id[idx]);
+
                return 1;
        }
 
-       /*
-        * don't report events for invalid data
-        */
-       /* older I4 styli don't work with new Cintiqs */
-       if ((!((wacom->id[idx] >> 20) & 0x01) &&
-                       (features->type == WACOM_21UX2)) ||
-           /* Only large Intuos support Lense Cursor */
-           (wacom->tool[idx] == BTN_TOOL_LENS &&
-               (features->type == INTUOS3 ||
-                features->type == INTUOS3S ||
-                features->type == INTUOS4 ||
-                features->type == INTUOS4S ||
-                features->type == INTUOS5 ||
-                features->type == INTUOS5S ||
-                features->type == INTUOSPM ||
-                features->type == INTUOSPS)) ||
-          /* Cintiq doesn't send data when RDY bit isn't set */
-          (features->type == CINTIQ && !(data[1] & 0x40)))
-               return 1;
+       /* in Range */
+       if ((data[1] & 0xfe) == 0x20) {
+               if (features->type != INTUOSHT2)
+                       wacom->shared->stylus_in_proximity = true;
 
-       wacom->shared->stylus_in_proximity = true;
-       if (wacom->shared->touch_down)
+               /* in Range while exiting */
+               if (wacom->reporting_data) {
+                       input_report_key(input, BTN_TOUCH, 0);
+                       input_report_abs(input, ABS_PRESSURE, 0);
+                       input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max);
+                       return 2;
+               }
                return 1;
-
-       /* in Range while exiting */
-       if (((data[1] & 0xfe) == 0x20) && wacom->reporting_data) {
-               input_report_key(input, BTN_TOUCH, 0);
-               input_report_abs(input, ABS_PRESSURE, 0);
-               input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max);
-               return 2;
        }
 
        /* Exit report */
@@ -750,13 +744,6 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
                return 2;
        }
 
-       /* don't report other events if we don't know the ID */
-       if (!wacom->id[idx]) {
-               /* but reschedule a read of the current tool */
-               wacom_intuos_schedule_prox_event(wacom);
-               return 1;
-       }
-
        return 0;
 }
 
@@ -897,6 +884,36 @@ static int wacom_intuos_general(struct wacom_wac *wacom)
                data[0] != WACOM_REPORT_INTUOS_PEN)
                return 0;
 
+       if (wacom->shared->touch_down)
+               return 1;
+
+       /* don't report events if we don't know the tool ID */
+       if (!wacom->id[idx]) {
+               /* but reschedule a read of the current tool */
+               wacom_intuos_schedule_prox_event(wacom);
+               return 1;
+       }
+
+       /*
+        * don't report events for invalid data
+        */
+       /* older I4 styli don't work with new Cintiqs */
+       if ((!((wacom->id[idx] >> 20) & 0x01) &&
+                       (features->type == WACOM_21UX2)) ||
+           /* Only large Intuos support Lense Cursor */
+           (wacom->tool[idx] == BTN_TOOL_LENS &&
+               (features->type == INTUOS3 ||
+                features->type == INTUOS3S ||
+                features->type == INTUOS4 ||
+                features->type == INTUOS4S ||
+                features->type == INTUOS5 ||
+                features->type == INTUOS5S ||
+                features->type == INTUOSPM ||
+                features->type == INTUOSPS)) ||
+          /* Cintiq doesn't send data when RDY bit isn't set */
+          (features->type == CINTIQ && !(data[1] & 0x40)))
+               return 1;
+
        x = (be16_to_cpup((__be16 *)&data[2]) << 1) | ((data[9] >> 1) & 1);
        y = (be16_to_cpup((__be16 *)&data[4]) << 1) | (data[9] & 1);
        distance = data[9] >> 2;