Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 25 Oct 2018 13:23:07 +0000 (06:23 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 25 Oct 2018 13:23:07 +0000 (06:23 -0700)
Pull HID updates from Jiri Kosina:

 - rumble support for Xbox One S, from Andrey Smirnov

 - high-resolution support for Logitech mice, from Harry Cutts

 - support for recent devices requiring the HID parse to be able to cope
   with tag report sizes > 256

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (35 commits)
  HID: usbhid: Add quirk for Redragon/Dragonrise Seymur 2
  HID: wacom: Work around HID descriptor bug in DTK-2451 and DTH-2452
  HID: google: add dependency on Cros EC for Hammer
  HID: elan: fix spelling mistake "registred" -> "registered"
  HID: google: drop superfluous const before SIMPLE_DEV_PM_OPS()
  HID: google: add support tablet mode switch for Whiskers
  mfd: cros: add "base attached" MKBP switch definition
  Input: reserve 2 events code because of HID
  HID: magicmouse: add support for Apple Magic Trackpad 2
  HID: i2c-hid: override HID descriptors for certain devices
  HID: hid-bigbenff: driver for BigBen Interactive PS3OFMINIPAD gamepad
  HID: logitech: fix a used uninitialized GCC warning
  HID: intel-ish-hid: using list_head for ipc write queue
  HID: intel-ish-hid: use resource-managed api
  HID: intel_ish-hid: Enhance API to get ring buffer sizes
  HID: intel-ish-hid: use helper function to search client id
  HID: intel-ish-hid: ishtp: add helper function for client search
  HID: intel-ish-hid: use helper function to access client buffer
  HID: intel-ish-hid: ishtp: add helper functions for client buffer operation
  HID: intel-ish-hid: use helper function for private driver data set/get
  ...

33 files changed:
Documentation/input/event-codes.rst
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-bigbenff.c [new file with mode: 0644]
drivers/hid/hid-core.c
drivers/hid/hid-cougar.c
drivers/hid/hid-elan.c
drivers/hid/hid-google-hammer.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-logitech-hidpp.c
drivers/hid/hid-magicmouse.c
drivers/hid/hid-microsoft.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-quirks.c
drivers/hid/i2c-hid/Makefile
drivers/hid/i2c-hid/i2c-hid-core.c [new file with mode: 0644]
drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c [new file with mode: 0644]
drivers/hid/i2c-hid/i2c-hid.c [deleted file]
drivers/hid/i2c-hid/i2c-hid.h [new file with mode: 0644]
drivers/hid/intel-ish-hid/ipc/ipc.c
drivers/hid/intel-ish-hid/ipc/pci-ish.c
drivers/hid/intel-ish-hid/ishtp-hid-client.c
drivers/hid/intel-ish-hid/ishtp/bus.c
drivers/hid/intel-ish-hid/ishtp/bus.h
drivers/hid/intel-ish-hid/ishtp/client-buffers.c
drivers/hid/intel-ish-hid/ishtp/client.c
drivers/hid/intel-ish-hid/ishtp/client.h
drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h
drivers/hid/wacom_wac.c
include/linux/hid.h
include/linux/mfd/cros_ec_commands.h
include/uapi/linux/input-event-codes.h

index a8c0873beb952e620db9bb2f2df823624ac90650..cef220c176a405b8f6839d846e074d295bd2b81e 100644 (file)
@@ -190,7 +190,16 @@ A few EV_REL codes have special meanings:
 * REL_WHEEL, REL_HWHEEL:
 
   - These codes are used for vertical and horizontal scroll wheels,
-    respectively.
+    respectively. The value is the number of "notches" moved on the wheel, the
+    physical size of which varies by device. For high-resolution wheels (which
+    report multiple events for each notch of movement, or do not have notches)
+    this may be an approximation based on the high-resolution scroll events.
+
+* REL_WHEEL_HI_RES:
+
+  - If a vertical scroll wheel supports high-resolution scrolling, this code
+    will be emitted in addition to REL_WHEEL. The value is the (approximate)
+    distance travelled by the user's finger, in microns.
 
 EV_ABS
 ------
index 61e1953ff9219db2783d5c1b7b2af43a0d39d27a..18c846477ba2586f7ba6381aea515990d4c915a7 100644 (file)
@@ -182,6 +182,19 @@ config HID_BETOP_FF
        Currently the following devices are known to be supported:
         - BETOP 2185 PC & BFM MODE
 
+config HID_BIGBEN_FF
+       tristate "BigBen Interactive Kids' gamepad support"
+       depends on USB_HID
+       depends on NEW_LEDS
+       depends on LEDS_CLASS
+       select INPUT_FF_MEMLESS
+       default !EXPERT
+       help
+         Support for the "Kid-friendly Wired Controller" PS3OFMINIPAD
+         gamepad made by BigBen Interactive, originally sold as a PS3
+         accessory. This driver fixes input mapping and adds support for
+         force feedback effects and LEDs on the device.
+
 config HID_CHERRY
        tristate "Cherry Cymotion keyboard"
        depends on HID
@@ -351,7 +364,7 @@ config HOLTEK_FF
 
 config HID_GOOGLE_HAMMER
        tristate "Google Hammer Keyboard"
-       depends on USB_HID && LEDS_CLASS
+       depends on USB_HID && LEDS_CLASS && MFD_CROS_EC
        ---help---
        Say Y here if you have a Google Hammer device.
 
@@ -596,6 +609,7 @@ config HID_MICROSOFT
        tristate "Microsoft non-fully HID-compliant devices"
        depends on HID
        default !EXPERT
+       select INPUT_FF_MEMLESS
        ---help---
        Support for Microsoft devices that are not fully compliant with HID standard.
 
index bd7ac53b75c566f81f452c7f0fe33c8cfa6b7e83..896a51ce7ce01e57d4765b27b0d4017eed9a2180 100644 (file)
@@ -31,6 +31,7 @@ obj-$(CONFIG_HID_ASUS)                += hid-asus.o
 obj-$(CONFIG_HID_AUREAL)       += hid-aureal.o
 obj-$(CONFIG_HID_BELKIN)       += hid-belkin.o
 obj-$(CONFIG_HID_BETOP_FF)     += hid-betopff.o
+obj-$(CONFIG_HID_BIGBEN_FF)    += hid-bigbenff.o
 obj-$(CONFIG_HID_CHERRY)       += hid-cherry.o
 obj-$(CONFIG_HID_CHICONY)      += hid-chicony.o
 obj-$(CONFIG_HID_CMEDIA)       += hid-cmedia.o
diff --git a/drivers/hid/hid-bigbenff.c b/drivers/hid/hid-bigbenff.c
new file mode 100644 (file)
index 0000000..3f6abd1
--- /dev/null
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ *  LED & force feedback support for BigBen Interactive
+ *
+ *  0x146b:0x0902 "Bigben Interactive Bigben Game Pad"
+ *  "Kid-friendly Wired Controller" PS3OFMINIPAD SONY
+ *  sold for use with the PS3
+ *
+ *  Copyright (c) 2018 Hanno Zulla <kontakt@hanno.de>
+ */
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/leds.h>
+#include <linux/hid.h>
+
+#include "hid-ids.h"
+
+
+/*
+ * The original descriptor for 0x146b:0x0902
+ *
+ *   0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
+ *   0x09, 0x05,        // Usage (Game Pad)
+ *   0xA1, 0x01,        // Collection (Application)
+ *   0x15, 0x00,        //   Logical Minimum (0)
+ *   0x25, 0x01,        //   Logical Maximum (1)
+ *   0x35, 0x00,        //   Physical Minimum (0)
+ *   0x45, 0x01,        //   Physical Maximum (1)
+ *   0x75, 0x01,        //   Report Size (1)
+ *   0x95, 0x0D,        //   Report Count (13)
+ *   0x05, 0x09,        //   Usage Page (Button)
+ *   0x19, 0x01,        //   Usage Minimum (0x01)
+ *   0x29, 0x0D,        //   Usage Maximum (0x0D)
+ *   0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ *   0x95, 0x03,        //   Report Count (3)
+ *   0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ *   0x05, 0x01,        //   Usage Page (Generic Desktop Ctrls)
+ *   0x25, 0x07,        //   Logical Maximum (7)
+ *   0x46, 0x3B, 0x01,  //   Physical Maximum (315)
+ *   0x75, 0x04,        //   Report Size (4)
+ *   0x95, 0x01,        //   Report Count (1)
+ *   0x65, 0x14,        //   Unit (System: English Rotation, Length: Centimeter)
+ *   0x09, 0x39,        //   Usage (Hat switch)
+ *   0x81, 0x42,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
+ *   0x65, 0x00,        //   Unit (None)
+ *   0x95, 0x01,        //   Report Count (1)
+ *   0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ *   0x26, 0xFF, 0x00,  //   Logical Maximum (255)
+ *   0x46, 0xFF, 0x00,  //   Physical Maximum (255)
+ *   0x09, 0x30,        //   Usage (X)
+ *   0x09, 0x31,        //   Usage (Y)
+ *   0x09, 0x32,        //   Usage (Z)
+ *   0x09, 0x35,        //   Usage (Rz)
+ *   0x75, 0x08,        //   Report Size (8)
+ *   0x95, 0x04,        //   Report Count (4)
+ *   0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ *   0x06, 0x00, 0xFF,  //   Usage Page (Vendor Defined 0xFF00)
+ *   0x09, 0x20,        //   Usage (0x20)
+ *   0x09, 0x21,        //   Usage (0x21)
+ *   0x09, 0x22,        //   Usage (0x22)
+ *   0x09, 0x23,        //   Usage (0x23)
+ *   0x09, 0x24,        //   Usage (0x24)
+ *   0x09, 0x25,        //   Usage (0x25)
+ *   0x09, 0x26,        //   Usage (0x26)
+ *   0x09, 0x27,        //   Usage (0x27)
+ *   0x09, 0x28,        //   Usage (0x28)
+ *   0x09, 0x29,        //   Usage (0x29)
+ *   0x09, 0x2A,        //   Usage (0x2A)
+ *   0x09, 0x2B,        //   Usage (0x2B)
+ *   0x95, 0x0C,        //   Report Count (12)
+ *   0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ *   0x0A, 0x21, 0x26,  //   Usage (0x2621)
+ *   0x95, 0x08,        //   Report Count (8)
+ *   0xB1, 0x02,        //   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ *   0x0A, 0x21, 0x26,  //   Usage (0x2621)
+ *   0x91, 0x02,        //   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
+ *   0x26, 0xFF, 0x03,  //   Logical Maximum (1023)
+ *   0x46, 0xFF, 0x03,  //   Physical Maximum (1023)
+ *   0x09, 0x2C,        //   Usage (0x2C)
+ *   0x09, 0x2D,        //   Usage (0x2D)
+ *   0x09, 0x2E,        //   Usage (0x2E)
+ *   0x09, 0x2F,        //   Usage (0x2F)
+ *   0x75, 0x10,        //   Report Size (16)
+ *   0x95, 0x04,        //   Report Count (4)
+ *   0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
+ *   0xC0,              // End Collection
+ */
+
+#define PID0902_RDESC_ORIG_SIZE 137
+
+/*
+ * The fixed descriptor for 0x146b:0x0902
+ *
+ * - map buttons according to gamepad.rst
+ * - assign right stick from Z/Rz to Rx/Ry
+ * - map previously unused analog trigger data to Z/RZ
+ * - simplify feature and output descriptor
+ */
+static __u8 pid0902_rdesc_fixed[] = {
+       0x05, 0x01,        /* Usage Page (Generic Desktop Ctrls) */
+       0x09, 0x05,        /* Usage (Game Pad) */
+       0xA1, 0x01,        /* Collection (Application) */
+       0x15, 0x00,        /*   Logical Minimum (0) */
+       0x25, 0x01,        /*   Logical Maximum (1) */
+       0x35, 0x00,        /*   Physical Minimum (0) */
+       0x45, 0x01,        /*   Physical Maximum (1) */
+       0x75, 0x01,        /*   Report Size (1) */
+       0x95, 0x0D,        /*   Report Count (13) */
+       0x05, 0x09,        /*   Usage Page (Button) */
+       0x09, 0x05,        /*   Usage (BTN_WEST) */
+       0x09, 0x01,        /*   Usage (BTN_SOUTH) */
+       0x09, 0x02,        /*   Usage (BTN_EAST) */
+       0x09, 0x04,        /*   Usage (BTN_NORTH) */
+       0x09, 0x07,        /*   Usage (BTN_TL) */
+       0x09, 0x08,        /*   Usage (BTN_TR) */
+       0x09, 0x09,        /*   Usage (BTN_TL2) */
+       0x09, 0x0A,        /*   Usage (BTN_TR2) */
+       0x09, 0x0B,        /*   Usage (BTN_SELECT) */
+       0x09, 0x0C,        /*   Usage (BTN_START) */
+       0x09, 0x0E,        /*   Usage (BTN_THUMBL) */
+       0x09, 0x0F,        /*   Usage (BTN_THUMBR) */
+       0x09, 0x0D,        /*   Usage (BTN_MODE) */
+       0x81, 0x02,        /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
+       0x75, 0x01,        /*   Report Size (1) */
+       0x95, 0x03,        /*   Report Count (3) */
+       0x81, 0x01,        /*   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
+       0x05, 0x01,        /*   Usage Page (Generic Desktop Ctrls) */
+       0x25, 0x07,        /*   Logical Maximum (7) */
+       0x46, 0x3B, 0x01,  /*   Physical Maximum (315) */
+       0x75, 0x04,        /*   Report Size (4) */
+       0x95, 0x01,        /*   Report Count (1) */
+       0x65, 0x14,        /*   Unit (System: English Rotation, Length: Centimeter) */
+       0x09, 0x39,        /*   Usage (Hat switch) */
+       0x81, 0x42,        /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State) */
+       0x65, 0x00,        /*   Unit (None) */
+       0x95, 0x01,        /*   Report Count (1) */
+       0x81, 0x01,        /*   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
+       0x26, 0xFF, 0x00,  /*   Logical Maximum (255) */
+       0x46, 0xFF, 0x00,  /*   Physical Maximum (255) */
+       0x09, 0x30,        /*   Usage (X) */
+       0x09, 0x31,        /*   Usage (Y) */
+       0x09, 0x33,        /*   Usage (Rx) */
+       0x09, 0x34,        /*   Usage (Ry) */
+       0x75, 0x08,        /*   Report Size (8) */
+       0x95, 0x04,        /*   Report Count (4) */
+       0x81, 0x02,        /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
+       0x95, 0x0A,        /*   Report Count (10) */
+       0x81, 0x01,        /*   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
+       0x05, 0x01,        /*   Usage Page (Generic Desktop Ctrls) */
+       0x26, 0xFF, 0x00,  /*   Logical Maximum (255) */
+       0x46, 0xFF, 0x00,  /*   Physical Maximum (255) */
+       0x09, 0x32,        /*   Usage (Z) */
+       0x09, 0x35,        /*   Usage (Rz) */
+       0x95, 0x02,        /*   Report Count (2) */
+       0x81, 0x02,        /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
+       0x95, 0x08,        /*   Report Count (8) */
+       0x81, 0x01,        /*   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
+       0x06, 0x00, 0xFF,  /*   Usage Page (Vendor Defined 0xFF00) */
+       0xB1, 0x02,        /*   Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */
+       0x0A, 0x21, 0x26,  /*   Usage (0x2621) */
+       0x95, 0x08,        /*   Report Count (8) */
+       0x91, 0x02,        /*   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */
+       0x0A, 0x21, 0x26,  /*   Usage (0x2621) */
+       0x95, 0x08,        /*   Report Count (8) */
+       0x81, 0x02,        /*   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
+       0xC0,              /* End Collection */
+};
+
+#define NUM_LEDS 4
+
+struct bigben_device {
+       struct hid_device *hid;
+       struct hid_report *report;
+       u8 led_state;         /* LED1 = 1 .. LED4 = 8 */
+       u8 right_motor_on;    /* right motor off/on 0/1 */
+       u8 left_motor_force;  /* left motor force 0-255 */
+       struct led_classdev *leds[NUM_LEDS];
+       bool work_led;
+       bool work_ff;
+       struct work_struct worker;
+};
+
+
+static void bigben_worker(struct work_struct *work)
+{
+       struct bigben_device *bigben = container_of(work,
+               struct bigben_device, worker);
+       struct hid_field *report_field = bigben->report->field[0];
+
+       if (bigben->work_led) {
+               bigben->work_led = false;
+               report_field->value[0] = 0x01; /* 1 = led message */
+               report_field->value[1] = 0x08; /* reserved value, always 8 */
+               report_field->value[2] = bigben->led_state;
+               report_field->value[3] = 0x00; /* padding */
+               report_field->value[4] = 0x00; /* padding */
+               report_field->value[5] = 0x00; /* padding */
+               report_field->value[6] = 0x00; /* padding */
+               report_field->value[7] = 0x00; /* padding */
+               hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT);
+       }
+
+       if (bigben->work_ff) {
+               bigben->work_ff = false;
+               report_field->value[0] = 0x02; /* 2 = rumble effect message */
+               report_field->value[1] = 0x08; /* reserved value, always 8 */
+               report_field->value[2] = bigben->right_motor_on;
+               report_field->value[3] = bigben->left_motor_force;
+               report_field->value[4] = 0xff; /* duration 0-254 (255 = nonstop) */
+               report_field->value[5] = 0x00; /* padding */
+               report_field->value[6] = 0x00; /* padding */
+               report_field->value[7] = 0x00; /* padding */
+               hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT);
+       }
+}
+
+static int hid_bigben_play_effect(struct input_dev *dev, void *data,
+                        struct ff_effect *effect)
+{
+       struct bigben_device *bigben = data;
+       u8 right_motor_on;
+       u8 left_motor_force;
+
+       if (effect->type != FF_RUMBLE)
+               return 0;
+
+       right_motor_on   = effect->u.rumble.weak_magnitude ? 1 : 0;
+       left_motor_force = effect->u.rumble.strong_magnitude / 256;
+
+       if (right_motor_on != bigben->right_motor_on ||
+                       left_motor_force != bigben->left_motor_force) {
+               bigben->right_motor_on   = right_motor_on;
+               bigben->left_motor_force = left_motor_force;
+               bigben->work_ff = true;
+               schedule_work(&bigben->worker);
+       }
+
+       return 0;
+}
+
+static void bigben_set_led(struct led_classdev *led,
+       enum led_brightness value)
+{
+       struct device *dev = led->dev->parent;
+       struct hid_device *hid = to_hid_device(dev);
+       struct bigben_device *bigben = hid_get_drvdata(hid);
+       int n;
+       bool work;
+
+       if (!bigben) {
+               hid_err(hid, "no device data\n");
+               return;
+       }
+
+       for (n = 0; n < NUM_LEDS; n++) {
+               if (led == bigben->leds[n]) {
+                       if (value == LED_OFF) {
+                               work = (bigben->led_state & BIT(n));
+                               bigben->led_state &= ~BIT(n);
+                       } else {
+                               work = !(bigben->led_state & BIT(n));
+                               bigben->led_state |= BIT(n);
+                       }
+
+                       if (work) {
+                               bigben->work_led = true;
+                               schedule_work(&bigben->worker);
+                       }
+                       return;
+               }
+       }
+}
+
+static enum led_brightness bigben_get_led(struct led_classdev *led)
+{
+       struct device *dev = led->dev->parent;
+       struct hid_device *hid = to_hid_device(dev);
+       struct bigben_device *bigben = hid_get_drvdata(hid);
+       int n;
+
+       if (!bigben) {
+               hid_err(hid, "no device data\n");
+               return LED_OFF;
+       }
+
+       for (n = 0; n < NUM_LEDS; n++) {
+               if (led == bigben->leds[n])
+                       return (bigben->led_state & BIT(n)) ? LED_ON : LED_OFF;
+       }
+
+       return LED_OFF;
+}
+
+static void bigben_remove(struct hid_device *hid)
+{
+       struct bigben_device *bigben = hid_get_drvdata(hid);
+
+       cancel_work_sync(&bigben->worker);
+       hid_hw_close(hid);
+       hid_hw_stop(hid);
+}
+
+static int bigben_probe(struct hid_device *hid,
+       const struct hid_device_id *id)
+{
+       struct bigben_device *bigben;
+       struct hid_input *hidinput;
+       struct list_head *report_list;
+       struct led_classdev *led;
+       char *name;
+       size_t name_sz;
+       int n, error;
+
+       bigben = devm_kzalloc(&hid->dev, sizeof(*bigben), GFP_KERNEL);
+       if (!bigben)
+               return -ENOMEM;
+       hid_set_drvdata(hid, bigben);
+       bigben->hid = hid;
+
+       error = hid_parse(hid);
+       if (error) {
+               hid_err(hid, "parse failed\n");
+               return error;
+       }
+
+       error = hid_hw_start(hid, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+       if (error) {
+               hid_err(hid, "hw start failed\n");
+               return error;
+       }
+
+       report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+       bigben->report = list_entry(report_list->next,
+               struct hid_report, list);
+
+       hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
+       set_bit(FF_RUMBLE, hidinput->input->ffbit);
+
+       INIT_WORK(&bigben->worker, bigben_worker);
+
+       error = input_ff_create_memless(hidinput->input, bigben,
+               hid_bigben_play_effect);
+       if (error)
+               return error;
+
+       name_sz = strlen(dev_name(&hid->dev)) + strlen(":red:bigben#") + 1;
+
+       for (n = 0; n < NUM_LEDS; n++) {
+               led = devm_kzalloc(
+                       &hid->dev,
+                       sizeof(struct led_classdev) + name_sz,
+                       GFP_KERNEL
+               );
+               if (!led)
+                       return -ENOMEM;
+               name = (void *)(&led[1]);
+               snprintf(name, name_sz,
+                       "%s:red:bigben%d",
+                       dev_name(&hid->dev), n + 1
+               );
+               led->name = name;
+               led->brightness = (n == 0) ? LED_ON : LED_OFF;
+               led->max_brightness = 1;
+               led->brightness_get = bigben_get_led;
+               led->brightness_set = bigben_set_led;
+               bigben->leds[n] = led;
+               error = devm_led_classdev_register(&hid->dev, led);
+               if (error)
+                       return error;
+       }
+
+       /* initial state: LED1 is on, no rumble effect */
+       bigben->led_state = BIT(0);
+       bigben->right_motor_on = 0;
+       bigben->left_motor_force = 0;
+       bigben->work_led = true;
+       bigben->work_ff = true;
+       schedule_work(&bigben->worker);
+
+       hid_info(hid, "LED and force feedback support for BigBen gamepad\n");
+
+       return 0;
+}
+
+static __u8 *bigben_report_fixup(struct hid_device *hid, __u8 *rdesc,
+       unsigned int *rsize)
+{
+       if (*rsize == PID0902_RDESC_ORIG_SIZE) {
+               rdesc = pid0902_rdesc_fixed;
+               *rsize = sizeof(pid0902_rdesc_fixed);
+       } else
+               hid_warn(hid, "unexpected rdesc, please submit for review\n");
+       return rdesc;
+}
+
+static const struct hid_device_id bigben_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_BIGBEN, USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, bigben_devices);
+
+static struct hid_driver bigben_driver = {
+       .name = "bigben",
+       .id_table = bigben_devices,
+       .probe = bigben_probe,
+       .report_fixup = bigben_report_fixup,
+       .remove = bigben_remove,
+};
+module_hid_driver(bigben_driver);
+
+MODULE_LICENSE("GPL");
index 44564f61e9cc3c85250e2e6d1e5ff47ed95dcd4d..5bec9244c45b54aa943ae5363cecdc9c5d7f38f5 100644 (file)
@@ -406,7 +406,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
 
        case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
                parser->global.report_size = item_udata(item);
-               if (parser->global.report_size > 128) {
+               if (parser->global.report_size > 256) {
                        hid_err(parser->device, "invalid report_size %d\n",
                                        parser->global.report_size);
                        return -1;
index ad2e87de7dc53ee2f2cf93e3e59a171b47d995b6..3f0916b64c60e9bd8fcf29b22386f2a6c7cd7649 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/hid.h>
 #include <linux/module.h>
+#include <linux/printk.h>
 
 #include "hid-ids.h"
 
@@ -15,11 +16,9 @@ MODULE_DESCRIPTION("Cougar 500k Gaming Keyboard");
 MODULE_LICENSE("GPL");
 MODULE_INFO(key_mappings, "G1-G6 are mapped to F13-F18");
 
-static int cougar_g6_is_space = 1;
-module_param_named(g6_is_space, cougar_g6_is_space, int, 0600);
+static bool g6_is_space = true;
 MODULE_PARM_DESC(g6_is_space,
-       "If set, G6 programmable key sends SPACE instead of F18 (0=off, 1=on) (default=1)");
-
+       "If true, G6 programmable key sends SPACE instead of F18 (default=true)");
 
 #define COUGAR_VENDOR_USAGE    0xff00ff00
 
@@ -82,20 +81,23 @@ struct cougar {
 static LIST_HEAD(cougar_udev_list);
 static DEFINE_MUTEX(cougar_udev_list_lock);
 
-static void cougar_fix_g6_mapping(struct hid_device *hdev)
+/**
+ * cougar_fix_g6_mapping - configure the mapping for key G6/Spacebar
+ */
+static void cougar_fix_g6_mapping(void)
 {
        int i;
 
        for (i = 0; cougar_mapping[i][0]; i++) {
                if (cougar_mapping[i][0] == COUGAR_KEY_G6) {
                        cougar_mapping[i][1] =
-                               cougar_g6_is_space ? KEY_SPACE : KEY_F18;
-                       hid_info(hdev, "G6 mapped to %s\n",
-                                cougar_g6_is_space ? "space" : "F18");
+                               g6_is_space ? KEY_SPACE : KEY_F18;
+                       pr_info("cougar: G6 mapped to %s\n",
+                               g6_is_space ? "space" : "F18");
                        return;
                }
        }
-       hid_warn(hdev, "no mapping defined for G6/spacebar");
+       pr_warn("cougar: no mappings defined for G6/spacebar");
 }
 
 /*
@@ -154,7 +156,8 @@ static void cougar_remove_shared_data(void *resource)
  * Bind the device group's shared data to this cougar struct.
  * If no shared data exists for this group, create and initialize it.
  */
-static int cougar_bind_shared_data(struct hid_device *hdev, struct cougar *cougar)
+static int cougar_bind_shared_data(struct hid_device *hdev,
+                                  struct cougar *cougar)
 {
        struct cougar_shared *shared;
        int error = 0;
@@ -228,7 +231,6 @@ static int cougar_probe(struct hid_device *hdev,
         * to it.
         */
        if (hdev->collection->usage == HID_GD_KEYBOARD) {
-               cougar_fix_g6_mapping(hdev);
                list_for_each_entry_safe(hidinput, next, &hdev->inputs, list) {
                        if (hidinput->registered && hidinput->input != NULL) {
                                cougar->shared->input = hidinput->input;
@@ -237,6 +239,8 @@ static int cougar_probe(struct hid_device *hdev,
                        }
                }
        } else if (hdev->collection->usage == COUGAR_VENDOR_USAGE) {
+               /* Preinit the mapping table */
+               cougar_fix_g6_mapping();
                error = hid_hw_open(hdev);
                if (error)
                        goto fail_stop_and_cleanup;
@@ -257,26 +261,32 @@ static int cougar_raw_event(struct hid_device *hdev, struct hid_report *report,
                            u8 *data, int size)
 {
        struct cougar *cougar;
+       struct cougar_shared *shared;
        unsigned char code, action;
        int i;
 
        cougar = hid_get_drvdata(hdev);
-       if (!cougar->special_intf || !cougar->shared ||
-           !cougar->shared->input || !cougar->shared->enabled)
+       shared = cougar->shared;
+       if (!cougar->special_intf || !shared)
                return 0;
 
+       if (!shared->enabled || !shared->input)
+               return -EPERM;
+
        code = data[COUGAR_FIELD_CODE];
        action = data[COUGAR_FIELD_ACTION];
        for (i = 0; cougar_mapping[i][0]; i++) {
                if (code == cougar_mapping[i][0]) {
-                       input_event(cougar->shared->input, EV_KEY,
+                       input_event(shared->input, EV_KEY,
                                    cougar_mapping[i][1], action);
-                       input_sync(cougar->shared->input);
-                       return 0;
+                       input_sync(shared->input);
+                       return -EPERM;
                }
        }
-       hid_warn(hdev, "unmapped special key code %x: ignoring\n", code);
-       return 0;
+       /* Avoid warnings on the same unmapped key twice */
+       if (action != 0)
+               hid_warn(hdev, "unmapped special key code %0x: ignoring\n", code);
+       return -EPERM;
 }
 
 static void cougar_remove(struct hid_device *hdev)
@@ -293,6 +303,26 @@ static void cougar_remove(struct hid_device *hdev)
        hid_hw_stop(hdev);
 }
 
+static int cougar_param_set_g6_is_space(const char *val,
+                                       const struct kernel_param *kp)
+{
+       int ret;
+
+       ret = param_set_bool(val, kp);
+       if (ret)
+               return ret;
+
+       cougar_fix_g6_mapping();
+
+       return 0;
+}
+
+static const struct kernel_param_ops cougar_g6_is_space_ops = {
+       .set    = cougar_param_set_g6_is_space,
+       .get    = param_get_bool,
+};
+module_param_cb(g6_is_space, &cougar_g6_is_space_ops, &g6_is_space, 0644);
+
 static struct hid_device_id cougar_id_table[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR,
                         USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD) },
index 07e26c3567eb979666be9c7966b67d6a5dbf56d0..0bfd6d1b44c143fce37aedf6977a529adfabfacf 100644 (file)
@@ -497,7 +497,7 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id)
                return 0;
 
        if (!drvdata->input) {
-               hid_err(hdev, "Input device is not registred\n");
+               hid_err(hdev, "Input device is not registered\n");
                ret = -ENAVAIL;
                goto err;
        }
index 6bf4da7ad63a51f3b9aa6713552c96be6042bba2..ee5e0bdcf078f7a05682ba45381d0f421dfde882 100644 (file)
  * any later version.
  */
 
+#include <linux/acpi.h>
 #include <linux/hid.h>
 #include <linux/leds.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
 #include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_wakeup.h>
+#include <asm/unaligned.h>
 
 #include "hid-ids.h"
 
-#define MAX_BRIGHTNESS 100
+/*
+ * C(hrome)B(ase)A(ttached)S(witch) - switch exported by Chrome EC and reporting
+ * state of the "Whiskers" base - attached or detached. Whiskers USB device also
+ * reports position of the keyboard - folded or not. Combining base state and
+ * position allows us to generate proper "Tablet mode" events.
+ */
+struct cbas_ec {
+       struct device *dev;     /* The platform device (EC) */
+       struct input_dev *input;
+       bool base_present;
+       struct notifier_block notifier;
+};
 
-/* HID usage for keyboard backlight (Alphanumeric display brightness) */
-#define HID_AD_BRIGHTNESS 0x00140046
+static struct cbas_ec cbas_ec;
+static DEFINE_SPINLOCK(cbas_ec_lock);
+static DEFINE_MUTEX(cbas_ec_reglock);
+
+static bool cbas_parse_base_state(const void *data)
+{
+       u32 switches = get_unaligned_le32(data);
+
+       return !!(switches & BIT(EC_MKBP_BASE_ATTACHED));
+}
+
+static int cbas_ec_query_base(struct cros_ec_device *ec_dev, bool get_state,
+                                 bool *state)
+{
+       struct ec_params_mkbp_info *params;
+       struct cros_ec_command *msg;
+       int ret;
+
+       msg = kzalloc(sizeof(*msg) + max(sizeof(u32), sizeof(*params)),
+                     GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       msg->command = EC_CMD_MKBP_INFO;
+       msg->version = 1;
+       msg->outsize = sizeof(*params);
+       msg->insize = sizeof(u32);
+       params = (struct ec_params_mkbp_info *)msg->data;
+       params->info_type = get_state ?
+               EC_MKBP_INFO_CURRENT : EC_MKBP_INFO_SUPPORTED;
+       params->event_type = EC_MKBP_EVENT_SWITCH;
+
+       ret = cros_ec_cmd_xfer_status(ec_dev, msg);
+       if (ret >= 0) {
+               if (ret != sizeof(u32)) {
+                       dev_warn(ec_dev->dev, "wrong result size: %d != %zu\n",
+                                ret, sizeof(u32));
+                       ret = -EPROTO;
+               } else {
+                       *state = cbas_parse_base_state(msg->data);
+                       ret = 0;
+               }
+       }
+
+       kfree(msg);
+
+       return ret;
+}
+
+static int cbas_ec_notify(struct notifier_block *nb,
+                             unsigned long queued_during_suspend,
+                             void *_notify)
+{
+       struct cros_ec_device *ec = _notify;
+       unsigned long flags;
+       bool base_present;
+
+       if (ec->event_data.event_type == EC_MKBP_EVENT_SWITCH) {
+               base_present = cbas_parse_base_state(
+                                       &ec->event_data.data.switches);
+               dev_dbg(cbas_ec.dev,
+                       "%s: base: %d\n", __func__, base_present);
+
+               if (device_may_wakeup(cbas_ec.dev) ||
+                   !queued_during_suspend) {
+
+                       pm_wakeup_event(cbas_ec.dev, 0);
+
+                       spin_lock_irqsave(&cbas_ec_lock, flags);
+
+                       /*
+                        * While input layer dedupes the events, we do not want
+                        * to disrupt the state reported by the base by
+                        * overriding it with state reported by the LID. Only
+                        * report changes, as we assume that on attach the base
+                        * is not folded.
+                        */
+                       if (base_present != cbas_ec.base_present) {
+                               input_report_switch(cbas_ec.input,
+                                                   SW_TABLET_MODE,
+                                                   !base_present);
+                               input_sync(cbas_ec.input);
+                               cbas_ec.base_present = base_present;
+                       }
+
+                       spin_unlock_irqrestore(&cbas_ec_lock, flags);
+               }
+       }
+
+       return NOTIFY_OK;
+}
+
+static __maybe_unused int cbas_ec_resume(struct device *dev)
+{
+       struct cros_ec_device *ec = dev_get_drvdata(dev->parent);
+       bool base_present;
+       int error;
+
+       error = cbas_ec_query_base(ec, true, &base_present);
+       if (error) {
+               dev_warn(dev, "failed to fetch base state on resume: %d\n",
+                        error);
+       } else {
+               spin_lock_irq(&cbas_ec_lock);
+
+               cbas_ec.base_present = base_present;
+
+               /*
+                * Only report if base is disconnected. If base is connected,
+                * it will resend its state on resume, and we'll update it
+                * in hammer_event().
+                */
+               if (!cbas_ec.base_present) {
+                       input_report_switch(cbas_ec.input, SW_TABLET_MODE, 1);
+                       input_sync(cbas_ec.input);
+               }
+
+               spin_unlock_irq(&cbas_ec_lock);
+       }
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(cbas_ec_pm_ops, NULL, cbas_ec_resume);
+
+static void cbas_ec_set_input(struct input_dev *input)
+{
+       /* Take the lock so hammer_event() does not race with us here */
+       spin_lock_irq(&cbas_ec_lock);
+       cbas_ec.input = input;
+       spin_unlock_irq(&cbas_ec_lock);
+}
+
+static int __cbas_ec_probe(struct platform_device *pdev)
+{
+       struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
+       struct input_dev *input;
+       bool base_supported;
+       int error;
+
+       error = cbas_ec_query_base(ec, false, &base_supported);
+       if (error)
+               return error;
+
+       if (!base_supported)
+               return -ENXIO;
+
+       input = devm_input_allocate_device(&pdev->dev);
+       if (!input)
+               return -ENOMEM;
+
+       input->name = "Whiskers Tablet Mode Switch";
+       input->id.bustype = BUS_HOST;
+
+       input_set_capability(input, EV_SW, SW_TABLET_MODE);
+
+       error = input_register_device(input);
+       if (error) {
+               dev_err(&pdev->dev, "cannot register input device: %d\n",
+                       error);
+               return error;
+       }
+
+       /* Seed the state */
+       error = cbas_ec_query_base(ec, true, &cbas_ec.base_present);
+       if (error) {
+               dev_err(&pdev->dev, "cannot query base state: %d\n", error);
+               return error;
+       }
+
+       input_report_switch(input, SW_TABLET_MODE, !cbas_ec.base_present);
+
+       cbas_ec_set_input(input);
+
+       cbas_ec.dev = &pdev->dev;
+       cbas_ec.notifier.notifier_call = cbas_ec_notify;
+       error = blocking_notifier_chain_register(&ec->event_notifier,
+                                                &cbas_ec.notifier);
+       if (error) {
+               dev_err(&pdev->dev, "cannot register notifier: %d\n", error);
+               cbas_ec_set_input(NULL);
+               return error;
+       }
+
+       device_init_wakeup(&pdev->dev, true);
+       return 0;
+}
+
+static int cbas_ec_probe(struct platform_device *pdev)
+{
+       int retval;
+
+       mutex_lock(&cbas_ec_reglock);
+
+       if (cbas_ec.input) {
+               retval = -EBUSY;
+               goto out;
+       }
+
+       retval = __cbas_ec_probe(pdev);
+
+out:
+       mutex_unlock(&cbas_ec_reglock);
+       return retval;
+}
+
+static int cbas_ec_remove(struct platform_device *pdev)
+{
+       struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
+
+       mutex_lock(&cbas_ec_reglock);
+
+       blocking_notifier_chain_unregister(&ec->event_notifier,
+                                          &cbas_ec.notifier);
+       cbas_ec_set_input(NULL);
+
+       mutex_unlock(&cbas_ec_reglock);
+       return 0;
+}
+
+static const struct acpi_device_id cbas_ec_acpi_ids[] = {
+       { "GOOG000B", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, cbas_ec_acpi_ids);
+
+static struct platform_driver cbas_ec_driver = {
+       .probe = cbas_ec_probe,
+       .remove = cbas_ec_remove,
+       .driver = {
+               .name = "cbas_ec",
+               .acpi_match_table = ACPI_PTR(cbas_ec_acpi_ids),
+               .pm = &cbas_ec_pm_ops,
+       },
+};
+
+#define MAX_BRIGHTNESS 100
 
 struct hammer_kbd_leds {
        struct led_classdev cdev;
@@ -90,33 +342,130 @@ static int hammer_register_leds(struct hid_device *hdev)
        return devm_led_classdev_register(&hdev->dev, &kbd_backlight->cdev);
 }
 
-static int hammer_input_configured(struct hid_device *hdev,
-                                  struct hid_input *hi)
+#define HID_UP_GOOGLEVENDOR    0xffd10000
+#define HID_VD_KBD_FOLDED      0x00000019
+#define WHISKERS_KBD_FOLDED    (HID_UP_GOOGLEVENDOR | HID_VD_KBD_FOLDED)
+
+/* HID usage for keyboard backlight (Alphanumeric display brightness) */
+#define HID_AD_BRIGHTNESS      0x00140046
+
+static int hammer_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+                               struct hid_field *field,
+                               struct hid_usage *usage,
+                               unsigned long **bit, int *max)
 {
-       struct list_head *report_list =
-               &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
+       if (hdev->product == USB_DEVICE_ID_GOOGLE_WHISKERS &&
+           usage->hid == WHISKERS_KBD_FOLDED) {
+               /*
+                * We do not want to have this usage mapped as it will get
+                * mixed in with "base attached" signal and delivered over
+                * separate input device for tablet switch mode.
+                */
+               return -1;
+       }
+
+       return 0;
+}
+
+static int hammer_event(struct hid_device *hid, struct hid_field *field,
+                       struct hid_usage *usage, __s32 value)
+{
+       unsigned long flags;
+
+       if (hid->product == USB_DEVICE_ID_GOOGLE_WHISKERS &&
+           usage->hid == WHISKERS_KBD_FOLDED) {
+               spin_lock_irqsave(&cbas_ec_lock, flags);
+
+               hid_dbg(hid, "%s: base: %d, folded: %d\n", __func__,
+                       cbas_ec.base_present, value);
+
+               /*
+                * We should not get event if base is detached, but in case
+                * we happen to service HID and EC notifications out of order
+                * let's still check the "base present" flag.
+                */
+               if (cbas_ec.input && cbas_ec.base_present) {
+                       input_report_switch(cbas_ec.input,
+                                           SW_TABLET_MODE, value);
+                       input_sync(cbas_ec.input);
+               }
+
+               spin_unlock_irqrestore(&cbas_ec_lock, flags);
+               return 1; /* We handled this event */
+       }
+
+       return 0;
+}
+
+static bool hammer_is_keyboard_interface(struct hid_device *hdev)
+{
+       struct hid_report_enum *re = &hdev->report_enum[HID_INPUT_REPORT];
        struct hid_report *report;
 
-       if (list_empty(report_list))
-               return 0;
+       list_for_each_entry(report, &re->report_list, list)
+               if (report->application == HID_GD_KEYBOARD)
+                       return true;
 
-       report = list_first_entry(report_list, struct hid_report, list);
+       return false;
+}
+
+static bool hammer_has_backlight_control(struct hid_device *hdev)
+{
+       struct hid_report_enum *re = &hdev->report_enum[HID_OUTPUT_REPORT];
+       struct hid_report *report;
+       int i, j;
 
-       if (report->maxfield == 1 &&
-           report->field[0]->application == HID_GD_KEYBOARD &&
-           report->field[0]->maxusage == 1 &&
-           report->field[0]->usage[0].hid == HID_AD_BRIGHTNESS) {
-               int err = hammer_register_leds(hdev);
+       list_for_each_entry(report, &re->report_list, list) {
+               if (report->application != HID_GD_KEYBOARD)
+                       continue;
 
-               if (err)
+               for (i = 0; i < report->maxfield; i++) {
+                       struct hid_field *field = report->field[i];
+
+                       for (j = 0; j < field->maxusage; j++)
+                               if (field->usage[j].hid == HID_AD_BRIGHTNESS)
+                                       return true;
+               }
+       }
+
+       return false;
+}
+
+static int hammer_probe(struct hid_device *hdev,
+                       const struct hid_device_id *id)
+{
+       int error;
+
+       /*
+        * We always want to poll for, and handle tablet mode events from
+        * Whiskers, even when nobody has opened the input device. This also
+        * prevents the hid core from dropping early tablet mode events from
+        * the device.
+        */
+       if (hdev->product == USB_DEVICE_ID_GOOGLE_WHISKERS &&
+                       hammer_is_keyboard_interface(hdev))
+               hdev->quirks |= HID_QUIRK_ALWAYS_POLL;
+
+       error = hid_parse(hdev);
+       if (error)
+               return error;
+
+       error = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+       if (error)
+               return error;
+
+       if (hammer_has_backlight_control(hdev)) {
+               error = hammer_register_leds(hdev);
+               if (error)
                        hid_warn(hdev,
                                "Failed to register keyboard backlight: %d\n",
-                               err);
+                               error);
        }
 
        return 0;
 }
 
+
 static const struct hid_device_id hammer_devices[] = {
        { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
                     USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_HAMMER) },
@@ -133,8 +482,34 @@ MODULE_DEVICE_TABLE(hid, hammer_devices);
 static struct hid_driver hammer_driver = {
        .name = "hammer",
        .id_table = hammer_devices,
-       .input_configured = hammer_input_configured,
+       .probe = hammer_probe,
+       .input_mapping = hammer_input_mapping,
+       .event = hammer_event,
 };
-module_hid_driver(hammer_driver);
+
+static int __init hammer_init(void)
+{
+       int error;
+
+       error = platform_driver_register(&cbas_ec_driver);
+       if (error)
+               return error;
+
+       error = hid_register_driver(&hammer_driver);
+       if (error) {
+               platform_driver_unregister(&cbas_ec_driver);
+               return error;
+       }
+
+       return 0;
+}
+module_init(hammer_init);
+
+static void __exit hammer_exit(void)
+{
+       hid_unregister_driver(&hammer_driver);
+       platform_driver_unregister(&cbas_ec_driver);
+}
+module_exit(hammer_exit);
 
 MODULE_LICENSE("GPL");
index bc49909aba8e664b6675fcac13921128c661dce9..f63489c882bb64f98f4c5bf9c84cbeb9df192c71 100644 (file)
@@ -92,6 +92,7 @@
 #define USB_DEVICE_ID_APPLE_MIGHTYMOUSE        0x0304
 #define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d
 #define USB_DEVICE_ID_APPLE_MAGICTRACKPAD      0x030e
+#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD2     0x0265
 #define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI      0x020e
 #define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO       0x020f
 #define USB_DEVICE_ID_APPLE_GEYSER_ANSI        0x0214
 #define USB_VENDOR_ID_BETOP_2185V2PC   0x8380
 #define USB_VENDOR_ID_BETOP_2185V2BFM  0x20bc
 
+#define USB_VENDOR_ID_BIGBEN   0x146b
+#define USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD      0x0902
+
 #define USB_VENDOR_ID_BTC              0x046e
 #define USB_DEVICE_ID_BTC_EMPREX_REMOTE        0x5578
 #define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2      0x5577
 #define USB_DEVICE_ID_DMI_ENC          0x5fab
 
 #define USB_VENDOR_ID_DRAGONRISE               0x0079
+#define USB_DEVICE_ID_REDRAGON_SEYMUR2         0x0006
 #define USB_DEVICE_ID_DRAGONRISE_WIIU          0x1800
 #define USB_DEVICE_ID_DRAGONRISE_PS3           0x1801
 #define USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR    0x1803
 #define USB_DEVICE_ID_MS_TOUCH_COVER_2   0x07a7
 #define USB_DEVICE_ID_MS_TYPE_COVER_2    0x07a9
 #define USB_DEVICE_ID_MS_POWER_COVER     0x07da
+#define USB_DEVICE_ID_MS_XBOX_ONE_S_CONTROLLER 0x02fd
 
 #define USB_VENDOR_ID_MOJO             0x8282
 #define USB_DEVICE_ID_RETRO_ADAPTER    0x3201
index a481eaf39e887bad41d89bf251c0dc4a8f2a2096..567c3bf64515d3599946c1d74aa79d52d9a61620 100644 (file)
@@ -758,6 +758,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
                break;
 
        case HID_UP_DIGITIZER:
+               if ((field->application & 0xff) == 0x01) /* Digitizer */
+                       __set_bit(INPUT_PROP_POINTER, input->propbit);
+               else if ((field->application & 0xff) == 0x02) /* Pen */
+                       __set_bit(INPUT_PROP_DIRECT, input->propbit);
+
                switch (usage->hid & 0xff) {
                case 0x00: /* Undefined */
                        goto ignore;
@@ -1516,6 +1521,7 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid,
        struct hid_input *hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL);
        struct input_dev *input_dev = input_allocate_device();
        const char *suffix = NULL;
+       size_t suffix_len, name_len;
 
        if (!hidinput || !input_dev)
                goto fail;
@@ -1559,10 +1565,15 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid,
        }
 
        if (suffix) {
-               hidinput->name = kasprintf(GFP_KERNEL, "%s %s",
-                                          hid->name, suffix);
-               if (!hidinput->name)
-                       goto fail;
+               name_len = strlen(hid->name);
+               suffix_len = strlen(suffix);
+               if ((name_len < suffix_len) ||
+                   strcmp(hid->name + name_len - suffix_len, suffix)) {
+                       hidinput->name = kasprintf(GFP_KERNEL, "%s %s",
+                                                  hid->name, suffix);
+                       if (!hidinput->name)
+                               goto fail;
+               }
        }
 
        input_set_drvdata(input_dev, hid);
@@ -1827,3 +1838,48 @@ void hidinput_disconnect(struct hid_device *hid)
 }
 EXPORT_SYMBOL_GPL(hidinput_disconnect);
 
+/**
+ * hid_scroll_counter_handle_scroll() - Send high- and low-resolution scroll
+ *                                      events given a high-resolution wheel
+ *                                      movement.
+ * @counter: a hid_scroll_counter struct describing the wheel.
+ * @hi_res_value: the movement of the wheel, in the mouse's high-resolution
+ *                units.
+ *
+ * Given a high-resolution movement, this function converts the movement into
+ * microns and emits high-resolution scroll events for the input device. It also
+ * uses the multiplier from &struct hid_scroll_counter to emit low-resolution
+ * scroll events when appropriate for backwards-compatibility with userspace
+ * input libraries.
+ */
+void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
+                                     int hi_res_value)
+{
+       int low_res_scroll_amount;
+       /* Some wheels will rest 7/8ths of a notch from the previous notch
+        * after slow movement, so we want the threshold for low-res events to
+        * be in the middle of the notches (e.g. after 4/8ths) as opposed to on
+        * the notches themselves (8/8ths).
+        */
+       int threshold = counter->resolution_multiplier / 2;
+
+       input_report_rel(counter->dev, REL_WHEEL_HI_RES,
+                        hi_res_value * counter->microns_per_hi_res_unit);
+
+       counter->remainder += hi_res_value;
+       if (abs(counter->remainder) >= threshold) {
+               /* Add (or subtract) 1 because we want to trigger when the wheel
+                * is half-way to the next notch (i.e. scroll 1 notch after a
+                * 1/2 notch movement, 2 notches after a 1 1/2 notch movement,
+                * etc.).
+                */
+               low_res_scroll_amount =
+                       counter->remainder / counter->resolution_multiplier
+                       + (hi_res_value > 0 ? 1 : -1);
+               input_report_rel(counter->dev, REL_WHEEL,
+                                low_res_scroll_amount);
+               counter->remainder -=
+                       low_res_scroll_amount * counter->resolution_multiplier;
+       }
+}
+EXPORT_SYMBOL_GPL(hid_scroll_counter_handle_scroll);
index 19cc980eebce6a3019c44d55dcbef0002e1cda10..f01280898b24bd7fea8770417137586823578550 100644 (file)
@@ -64,6 +64,14 @@ MODULE_PARM_DESC(disable_tap_to_click,
 #define HIDPP_QUIRK_NO_HIDINPUT                        BIT(23)
 #define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS       BIT(24)
 #define HIDPP_QUIRK_UNIFYING                   BIT(25)
+#define HIDPP_QUIRK_HI_RES_SCROLL_1P0          BIT(26)
+#define HIDPP_QUIRK_HI_RES_SCROLL_X2120                BIT(27)
+#define HIDPP_QUIRK_HI_RES_SCROLL_X2121                BIT(28)
+
+/* Convenience constant to check for any high-res support. */
+#define HIDPP_QUIRK_HI_RES_SCROLL      (HIDPP_QUIRK_HI_RES_SCROLL_1P0 | \
+                                        HIDPP_QUIRK_HI_RES_SCROLL_X2120 | \
+                                        HIDPP_QUIRK_HI_RES_SCROLL_X2121)
 
 #define HIDPP_QUIRK_DELAYED_INIT               HIDPP_QUIRK_NO_HIDINPUT
 
@@ -149,6 +157,7 @@ struct hidpp_device {
        unsigned long capabilities;
 
        struct hidpp_battery battery;
+       struct hid_scroll_counter vertical_wheel_counter;
 };
 
 /* HID++ 1.0 error codes */
@@ -400,32 +409,53 @@ static void hidpp_prefix_name(char **name, int name_length)
 #define HIDPP_SET_LONG_REGISTER                                0x82
 #define HIDPP_GET_LONG_REGISTER                                0x83
 
-#define HIDPP_REG_GENERAL                              0x00
-
-static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
+/**
+ * hidpp10_set_register_bit() - Sets a single bit in a HID++ 1.0 register.
+ * @hidpp_dev: the device to set the register on.
+ * @register_address: the address of the register to modify.
+ * @byte: the byte of the register to modify. Should be less than 3.
+ * Return: 0 if successful, otherwise a negative error code.
+ */
+static int hidpp10_set_register_bit(struct hidpp_device *hidpp_dev,
+       u8 register_address, u8 byte, u8 bit)
 {
        struct hidpp_report response;
        int ret;
        u8 params[3] = { 0 };
 
        ret = hidpp_send_rap_command_sync(hidpp_dev,
-                                       REPORT_ID_HIDPP_SHORT,
-                                       HIDPP_GET_REGISTER,
-                                       HIDPP_REG_GENERAL,
-                                       NULL, 0, &response);
+                                         REPORT_ID_HIDPP_SHORT,
+                                         HIDPP_GET_REGISTER,
+                                         register_address,
+                                         NULL, 0, &response);
        if (ret)
                return ret;
 
        memcpy(params, response.rap.params, 3);
 
-       /* Set the battery bit */
-       params[0] |= BIT(4);
+       params[byte] |= BIT(bit);
 
        return hidpp_send_rap_command_sync(hidpp_dev,
-                                       REPORT_ID_HIDPP_SHORT,
-                                       HIDPP_SET_REGISTER,
-                                       HIDPP_REG_GENERAL,
-                                       params, 3, &response);
+                                          REPORT_ID_HIDPP_SHORT,
+                                          HIDPP_SET_REGISTER,
+                                          register_address,
+                                          params, 3, &response);
+}
+
+
+#define HIDPP_REG_GENERAL                              0x00
+
+static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
+{
+       return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_GENERAL, 0, 4);
+}
+
+#define HIDPP_REG_FEATURES                             0x01
+
+/* On HID++ 1.0 devices, high-res scroll was called "scrolling acceleration". */
+static int hidpp10_enable_scrolling_acceleration(struct hidpp_device *hidpp_dev)
+{
+       return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_FEATURES, 0, 6);
 }
 
 #define HIDPP_REG_BATTERY_STATUS                       0x07
@@ -1136,6 +1166,100 @@ static int hidpp_battery_get_property(struct power_supply *psy,
        return ret;
 }
 
+/* -------------------------------------------------------------------------- */
+/* 0x2120: Hi-resolution scrolling                                            */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_HI_RESOLUTION_SCROLLING                     0x2120
+
+#define CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE 0x10
+
+static int hidpp_hrs_set_highres_scrolling_mode(struct hidpp_device *hidpp,
+       bool enabled, u8 *multiplier)
+{
+       u8 feature_index;
+       u8 feature_type;
+       int ret;
+       u8 params[1];
+       struct hidpp_report response;
+
+       ret = hidpp_root_get_feature(hidpp,
+                                    HIDPP_PAGE_HI_RESOLUTION_SCROLLING,
+                                    &feature_index,
+                                    &feature_type);
+       if (ret)
+               return ret;
+
+       params[0] = enabled ? BIT(0) : 0;
+       ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+                                         CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE,
+                                         params, sizeof(params), &response);
+       if (ret)
+               return ret;
+       *multiplier = response.fap.params[1];
+       return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+/* 0x2121: HiRes Wheel                                                        */
+/* -------------------------------------------------------------------------- */
+
+#define HIDPP_PAGE_HIRES_WHEEL         0x2121
+
+#define CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY   0x00
+#define CMD_HIRES_WHEEL_SET_WHEEL_MODE         0x20
+
+static int hidpp_hrw_get_wheel_capability(struct hidpp_device *hidpp,
+       u8 *multiplier)
+{
+       u8 feature_index;
+       u8 feature_type;
+       int ret;
+       struct hidpp_report response;
+
+       ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL,
+                                    &feature_index, &feature_type);
+       if (ret)
+               goto return_default;
+
+       ret = hidpp_send_fap_command_sync(hidpp, feature_index,
+                                         CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY,
+                                         NULL, 0, &response);
+       if (ret)
+               goto return_default;
+
+       *multiplier = response.fap.params[0];
+       return 0;
+return_default:
+       hid_warn(hidpp->hid_dev,
+                "Couldn't get wheel multiplier (error %d), assuming %d.\n",
+                ret, *multiplier);
+       return ret;
+}
+
+static int hidpp_hrw_set_wheel_mode(struct hidpp_device *hidpp, bool invert,
+       bool high_resolution, bool use_hidpp)
+{
+       u8 feature_index;
+       u8 feature_type;
+       int ret;
+       u8 params[1];
+       struct hidpp_report response;
+
+       ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL,
+                                    &feature_index, &feature_type);
+       if (ret)
+               return ret;
+
+       params[0] = (invert          ? BIT(2) : 0) |
+                   (high_resolution ? BIT(1) : 0) |
+                   (use_hidpp       ? BIT(0) : 0);
+
+       return hidpp_send_fap_command_sync(hidpp, feature_index,
+                                          CMD_HIRES_WHEEL_SET_WHEEL_MODE,
+                                          params, sizeof(params), &response);
+}
+
 /* -------------------------------------------------------------------------- */
 /* 0x4301: Solar Keyboard                                                     */
 /* -------------------------------------------------------------------------- */
@@ -2399,7 +2523,8 @@ static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)
                input_report_rel(mydata->input, REL_Y, v);
 
                v = hid_snto32(data[6], 8);
-               input_report_rel(mydata->input, REL_WHEEL, v);
+               hid_scroll_counter_handle_scroll(
+                               &hidpp->vertical_wheel_counter, v);
 
                input_sync(mydata->input);
        }
@@ -2527,6 +2652,72 @@ static int g920_get_config(struct hidpp_device *hidpp)
        return 0;
 }
 
+/* -------------------------------------------------------------------------- */
+/* High-resolution scroll wheels                                              */
+/* -------------------------------------------------------------------------- */
+
+/**
+ * struct hi_res_scroll_info - Stores info on a device's high-res scroll wheel.
+ * @product_id: the HID product ID of the device being described.
+ * @microns_per_hi_res_unit: the distance moved by the user's finger for each
+ *                         high-resolution unit reported by the device, in
+ *                         256ths of a millimetre.
+ */
+struct hi_res_scroll_info {
+       __u32 product_id;
+       int microns_per_hi_res_unit;
+};
+
+static struct hi_res_scroll_info hi_res_scroll_devices[] = {
+       { /* Anywhere MX */
+         .product_id = 0x1017, .microns_per_hi_res_unit = 445 },
+       { /* Performance MX */
+         .product_id = 0x101a, .microns_per_hi_res_unit = 406 },
+       { /* M560 */
+         .product_id = 0x402d, .microns_per_hi_res_unit = 435 },
+       { /* MX Master 2S */
+         .product_id = 0x4069, .microns_per_hi_res_unit = 406 },
+};
+
+static int hi_res_scroll_look_up_microns(__u32 product_id)
+{
+       int i;
+       int num_devices = sizeof(hi_res_scroll_devices)
+                         / sizeof(hi_res_scroll_devices[0]);
+       for (i = 0; i < num_devices; i++) {
+               if (hi_res_scroll_devices[i].product_id == product_id)
+                       return hi_res_scroll_devices[i].microns_per_hi_res_unit;
+       }
+       /* We don't have a value for this device, so use a sensible default. */
+       return 406;
+}
+
+static int hi_res_scroll_enable(struct hidpp_device *hidpp)
+{
+       int ret;
+       u8 multiplier = 8;
+
+       if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2121) {
+               ret = hidpp_hrw_set_wheel_mode(hidpp, false, true, false);
+               hidpp_hrw_get_wheel_capability(hidpp, &multiplier);
+       } else if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2120) {
+               ret = hidpp_hrs_set_highres_scrolling_mode(hidpp, true,
+                                                          &multiplier);
+       } else /* if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_1P0) */
+               ret = hidpp10_enable_scrolling_acceleration(hidpp);
+
+       if (ret)
+               return ret;
+
+       hidpp->vertical_wheel_counter.resolution_multiplier = multiplier;
+       hidpp->vertical_wheel_counter.microns_per_hi_res_unit =
+               hi_res_scroll_look_up_microns(hidpp->hid_dev->product);
+       hid_info(hidpp->hid_dev, "multiplier = %d, microns = %d\n",
+                multiplier,
+                hidpp->vertical_wheel_counter.microns_per_hi_res_unit);
+       return 0;
+}
+
 /* -------------------------------------------------------------------------- */
 /* Generic HID++ devices                                                      */
 /* -------------------------------------------------------------------------- */
@@ -2572,6 +2763,11 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,
                wtp_populate_input(hidpp, input, origin_is_hid_core);
        else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
                m560_populate_input(hidpp, input, origin_is_hid_core);
+
+       if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) {
+               input_set_capability(input, EV_REL, REL_WHEEL_HI_RES);
+               hidpp->vertical_wheel_counter.dev = input;
+       }
 }
 
 static int hidpp_input_configured(struct hid_device *hdev,
@@ -2690,6 +2886,27 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
        return 0;
 }
 
+static int hidpp_event(struct hid_device *hdev, struct hid_field *field,
+       struct hid_usage *usage, __s32 value)
+{
+       /* This function will only be called for scroll events, due to the
+        * restriction imposed in hidpp_usages.
+        */
+       struct hidpp_device *hidpp = hid_get_drvdata(hdev);
+       struct hid_scroll_counter *counter = &hidpp->vertical_wheel_counter;
+       /* A scroll event may occur before the multiplier has been retrieved or
+        * the input device set, or high-res scroll enabling may fail. In such
+        * cases we must return early (falling back to default behaviour) to
+        * avoid a crash in hid_scroll_counter_handle_scroll.
+        */
+       if (!(hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) || value == 0
+           || counter->dev == NULL || counter->resolution_multiplier == 0)
+               return 0;
+
+       hid_scroll_counter_handle_scroll(counter, value);
+       return 1;
+}
+
 static int hidpp_initialize_battery(struct hidpp_device *hidpp)
 {
        static atomic_t battery_no = ATOMIC_INIT(0);
@@ -2901,6 +3118,9 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
        if (hidpp->battery.ps)
                power_supply_changed(hidpp->battery.ps);
 
+       if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL)
+               hi_res_scroll_enable(hidpp);
+
        if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input)
                /* if the input nodes are already created, we can stop now */
                return;
@@ -3086,35 +3306,63 @@ static void hidpp_remove(struct hid_device *hdev)
        mutex_destroy(&hidpp->send_mutex);
 }
 
+#define LDJ_DEVICE(product) \
+       HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, \
+                  USB_VENDOR_ID_LOGITECH, (product))
+
 static const struct hid_device_id hidpp_devices[] = {
        { /* wireless touchpad */
-         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
-               USB_VENDOR_ID_LOGITECH, 0x4011),
+         LDJ_DEVICE(0x4011),
          .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT |
                         HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS },
        { /* wireless touchpad T650 */
-         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
-               USB_VENDOR_ID_LOGITECH, 0x4101),
+         LDJ_DEVICE(0x4101),
          .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT },
        { /* wireless touchpad T651 */
          HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
                USB_DEVICE_ID_LOGITECH_T651),
          .driver_data = HIDPP_QUIRK_CLASS_WTP },
+       { /* Mouse Logitech Anywhere MX */
+         LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
+       { /* Mouse Logitech Cube */
+         LDJ_DEVICE(0x4010), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
+       { /* Mouse Logitech M335 */
+         LDJ_DEVICE(0x4050), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+       { /* Mouse Logitech M515 */
+         LDJ_DEVICE(0x4007), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
        { /* Mouse logitech M560 */
-         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
-               USB_VENDOR_ID_LOGITECH, 0x402d),
-         .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 },
+         LDJ_DEVICE(0x402d),
+         .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560
+               | HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
+       { /* Mouse Logitech M705 (firmware RQM17) */
+         LDJ_DEVICE(0x101b), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
+       { /* Mouse Logitech M705 (firmware RQM67) */
+         LDJ_DEVICE(0x406d), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+       { /* Mouse Logitech M720 */
+         LDJ_DEVICE(0x405e), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+       { /* Mouse Logitech MX Anywhere 2 */
+         LDJ_DEVICE(0x404a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+       { LDJ_DEVICE(0xb013), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+       { LDJ_DEVICE(0xb018), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+       { LDJ_DEVICE(0xb01f), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+       { /* Mouse Logitech MX Anywhere 2S */
+         LDJ_DEVICE(0x406a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+       { /* Mouse Logitech MX Master */
+         LDJ_DEVICE(0x4041), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+       { LDJ_DEVICE(0x4060), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+       { LDJ_DEVICE(0x4071), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+       { /* Mouse Logitech MX Master 2S */
+         LDJ_DEVICE(0x4069), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+       { /* Mouse Logitech Performance MX */
+         LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
        { /* Keyboard logitech K400 */
-         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
-               USB_VENDOR_ID_LOGITECH, 0x4024),
+         LDJ_DEVICE(0x4024),
          .driver_data = HIDPP_QUIRK_CLASS_K400 },
        { /* Solar Keyboard Logitech K750 */
-         HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
-               USB_VENDOR_ID_LOGITECH, 0x4002),
+         LDJ_DEVICE(0x4002),
          .driver_data = HIDPP_QUIRK_CLASS_K750 },
 
-       { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
-               USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
+       { LDJ_DEVICE(HID_ANY_ID) },
 
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
                .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
@@ -3123,12 +3371,19 @@ static const struct hid_device_id hidpp_devices[] = {
 
 MODULE_DEVICE_TABLE(hid, hidpp_devices);
 
+static const struct hid_usage_id hidpp_usages[] = {
+       { HID_GD_WHEEL, EV_REL, REL_WHEEL },
+       { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
+};
+
 static struct hid_driver hidpp_driver = {
        .name = "logitech-hidpp-device",
        .id_table = hidpp_devices,
        .probe = hidpp_probe,
        .remove = hidpp_remove,
        .raw_event = hidpp_raw_event,
+       .usage_table = hidpp_usages,
+       .event = hidpp_event,
        .input_configured = hidpp_input_configured,
        .input_mapping = hidpp_input_mapping,
        .input_mapped = hidpp_input_mapped,
index b454c43861578602e33d066a655eb5205e969533..1d5ea678d268768fe73f3bbf4fa6b589dbc6fcbe 100644 (file)
@@ -54,6 +54,8 @@ module_param(report_undeciphered, bool, 0644);
 MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event");
 
 #define TRACKPAD_REPORT_ID 0x28
+#define TRACKPAD2_USB_REPORT_ID 0x02
+#define TRACKPAD2_BT_REPORT_ID 0x31
 #define MOUSE_REPORT_ID    0x29
 #define DOUBLE_REPORT_ID   0xf7
 /* These definitions are not precise, but they're close enough.  (Bits
@@ -91,6 +93,17 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
 #define TRACKPAD_RES_Y \
        ((TRACKPAD_MAX_Y - TRACKPAD_MIN_Y) / (TRACKPAD_DIMENSION_Y / 100))
 
+#define TRACKPAD2_DIMENSION_X (float)16000
+#define TRACKPAD2_MIN_X -3678
+#define TRACKPAD2_MAX_X 3934
+#define TRACKPAD2_RES_X \
+       ((TRACKPAD2_MAX_X - TRACKPAD2_MIN_X) / (TRACKPAD2_DIMENSION_X / 100))
+#define TRACKPAD2_DIMENSION_Y (float)11490
+#define TRACKPAD2_MIN_Y -2478
+#define TRACKPAD2_MAX_Y 2587
+#define TRACKPAD2_RES_Y \
+       ((TRACKPAD2_MAX_Y - TRACKPAD2_MIN_Y) / (TRACKPAD2_DIMENSION_Y / 100))
+
 /**
  * struct magicmouse_sc - Tracks Magic Mouse-specific data.
  * @input: Input device through which we report events.
@@ -183,6 +196,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
 {
        struct input_dev *input = msc->input;
        int id, x, y, size, orientation, touch_major, touch_minor, state, down;
+       int pressure = 0;
 
        if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
                id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf;
@@ -194,6 +208,17 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
                touch_minor = tdata[4];
                state = tdata[7] & TOUCH_STATE_MASK;
                down = state != TOUCH_STATE_NONE;
+       } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
+               id = tdata[8] & 0xf;
+               x = (tdata[1] << 27 | tdata[0] << 19) >> 19;
+               y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19);
+               size = tdata[6];
+               orientation = (tdata[8] >> 5) - 4;
+               touch_major = tdata[4];
+               touch_minor = tdata[5];
+               pressure = tdata[7];
+               state = tdata[3] & 0xC0;
+               down = state == 0x80;
        } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
                id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf;
                x = (tdata[1] << 27 | tdata[0] << 19) >> 19;
@@ -215,7 +240,8 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
        /* If requested, emulate a scroll wheel by detecting small
         * vertical touch motions.
         */
-       if (emulate_scroll_wheel) {
+       if (emulate_scroll_wheel && (input->id.product !=
+                       USB_DEVICE_ID_APPLE_MAGICTRACKPAD2)) {
                unsigned long now = jiffies;
                int step_x = msc->touches[id].scroll_x - x;
                int step_y = msc->touches[id].scroll_y - y;
@@ -269,10 +295,14 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
                input_report_abs(input, ABS_MT_POSITION_X, x);
                input_report_abs(input, ABS_MT_POSITION_Y, y);
 
+               if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2)
+                       input_report_abs(input, ABS_MT_PRESSURE, pressure);
+
                if (report_undeciphered) {
                        if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
                                input_event(input, EV_MSC, MSC_RAW, tdata[7]);
-                       else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+                       else if (input->id.product !=
+                                       USB_DEVICE_ID_APPLE_MAGICTRACKPAD2)
                                input_event(input, EV_MSC, MSC_RAW, tdata[8]);
                }
        }
@@ -287,6 +317,7 @@ static int magicmouse_raw_event(struct hid_device *hdev,
 
        switch (data[0]) {
        case TRACKPAD_REPORT_ID:
+       case TRACKPAD2_BT_REPORT_ID:
                /* Expect four bytes of prefix, and N*9 bytes of touch data. */
                if (size < 4 || ((size - 4) % 9) != 0)
                        return 0;
@@ -308,6 +339,22 @@ static int magicmouse_raw_event(struct hid_device *hdev,
                 * ts = data[1] >> 6 | data[2] << 2 | data[3] << 10;
                 */
                break;
+       case TRACKPAD2_USB_REPORT_ID:
+               /* Expect twelve bytes of prefix and N*9 bytes of touch data. */
+               if (size < 12 || ((size - 12) % 9) != 0)
+                       return 0;
+               npoints = (size - 12) / 9;
+               if (npoints > 15) {
+                       hid_warn(hdev, "invalid size value (%d) for TRACKPAD2_USB_REPORT_ID\n",
+                                       size);
+                       return 0;
+               }
+               msc->ntouches = 0;
+               for (ii = 0; ii < npoints; ii++)
+                       magicmouse_emit_touch(msc, ii, data + ii * 9 + 12);
+
+               clicks = data[1];
+               break;
        case MOUSE_REPORT_ID:
                /* Expect six bytes of prefix, and N*8 bytes of touch data. */
                if (size < 6 || ((size - 6) % 8) != 0)
@@ -352,6 +399,9 @@ static int magicmouse_raw_event(struct hid_device *hdev,
                magicmouse_emit_buttons(msc, clicks & 3);
                input_report_rel(input, REL_X, x);
                input_report_rel(input, REL_Y, y);
+       } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
+               input_mt_sync_frame(input);
+               input_report_key(input, BTN_MOUSE, clicks & 1);
        } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
                input_report_key(input, BTN_MOUSE, clicks & 1);
                input_mt_report_pointer_emulation(input, true);
@@ -364,6 +414,7 @@ static int magicmouse_raw_event(struct hid_device *hdev,
 static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev)
 {
        int error;
+       int mt_flags = 0;
 
        __set_bit(EV_KEY, input->evbit);
 
@@ -380,6 +431,22 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
                        __set_bit(REL_WHEEL, input->relbit);
                        __set_bit(REL_HWHEEL, input->relbit);
                }
+       } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
+               /* setting the device name to ensure the same driver settings
+                * get loaded, whether connected through bluetooth or USB
+                */
+               input->name = "Apple Inc. Magic Trackpad 2";
+
+               __clear_bit(EV_MSC, input->evbit);
+               __clear_bit(BTN_0, input->keybit);
+               __clear_bit(BTN_RIGHT, input->keybit);
+               __clear_bit(BTN_MIDDLE, input->keybit);
+               __set_bit(BTN_MOUSE, input->keybit);
+               __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+               __set_bit(BTN_TOOL_FINGER, input->keybit);
+
+               mt_flags = INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
+                               INPUT_MT_TRACK;
        } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
                /* input->keybit is initialized with incorrect button info
                 * for Magic Trackpad. There really is only one physical
@@ -402,14 +469,13 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
 
        __set_bit(EV_ABS, input->evbit);
 
-       error = input_mt_init_slots(input, 16, 0);
+       error = input_mt_init_slots(input, 16, mt_flags);
        if (error)
                return error;
        input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2,
                             4, 0);
        input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255 << 2,
                             4, 0);
-       input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
 
        /* Note: Touch Y position from the device is inverted relative
         * to how pointer motion is reported (and relative to how USB
@@ -418,6 +484,7 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
         * inverse of the reported Y.
         */
        if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
+               input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
                input_set_abs_params(input, ABS_MT_POSITION_X,
                                     MOUSE_MIN_X, MOUSE_MAX_X, 4, 0);
                input_set_abs_params(input, ABS_MT_POSITION_Y,
@@ -427,7 +494,25 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
                                  MOUSE_RES_X);
                input_abs_set_res(input, ABS_MT_POSITION_Y,
                                  MOUSE_RES_Y);
+       } else if (input->id.product ==  USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
+               input_set_abs_params(input, ABS_MT_PRESSURE, 0, 253, 0, 0);
+               input_set_abs_params(input, ABS_PRESSURE, 0, 253, 0, 0);
+               input_set_abs_params(input, ABS_MT_ORIENTATION, -3, 4, 0, 0);
+               input_set_abs_params(input, ABS_X, TRACKPAD2_MIN_X,
+                                    TRACKPAD2_MAX_X, 0, 0);
+               input_set_abs_params(input, ABS_Y, TRACKPAD2_MIN_Y,
+                                    TRACKPAD2_MAX_Y, 0, 0);
+               input_set_abs_params(input, ABS_MT_POSITION_X,
+                                    TRACKPAD2_MIN_X, TRACKPAD2_MAX_X, 0, 0);
+               input_set_abs_params(input, ABS_MT_POSITION_Y,
+                                    TRACKPAD2_MIN_Y, TRACKPAD2_MAX_Y, 0, 0);
+
+               input_abs_set_res(input, ABS_X, TRACKPAD2_RES_X);
+               input_abs_set_res(input, ABS_Y, TRACKPAD2_RES_Y);
+               input_abs_set_res(input, ABS_MT_POSITION_X, TRACKPAD2_RES_X);
+               input_abs_set_res(input, ABS_MT_POSITION_Y, TRACKPAD2_RES_Y);
        } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+               input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
                input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X,
                                     TRACKPAD_MAX_X, 4, 0);
                input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y,
@@ -447,7 +532,8 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
 
        input_set_events_per_packet(input, 60);
 
-       if (report_undeciphered) {
+       if (report_undeciphered &&
+           input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
                __set_bit(EV_MSC, input->evbit);
                __set_bit(MSC_RAW, input->mscbit);
        }
@@ -465,7 +551,8 @@ static int magicmouse_input_mapping(struct hid_device *hdev,
                msc->input = hi->input;
 
        /* Magic Trackpad does not give relative data after switching to MT */
-       if (hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD &&
+       if ((hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD ||
+            hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) &&
            field->flags & HID_MAIN_ITEM_RELATIVE)
                return -1;
 
@@ -494,11 +581,20 @@ static int magicmouse_input_configured(struct hid_device *hdev,
 static int magicmouse_probe(struct hid_device *hdev,
        const struct hid_device_id *id)
 {
-       const u8 feature[] = { 0xd7, 0x01 };
+       const u8 *feature;
+       const u8 feature_mt[] = { 0xD7, 0x01 };
+       const u8 feature_mt_trackpad2_usb[] = { 0x02, 0x01 };
+       const u8 feature_mt_trackpad2_bt[] = { 0xF1, 0x02, 0x01 };
        u8 *buf;
        struct magicmouse_sc *msc;
        struct hid_report *report;
        int ret;
+       int feature_size;
+
+       if (id->vendor == USB_VENDOR_ID_APPLE &&
+           id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 &&
+           hdev->type != HID_TYPE_USBMOUSE)
+               return 0;
 
        msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL);
        if (msc == NULL) {
@@ -532,7 +628,14 @@ static int magicmouse_probe(struct hid_device *hdev,
        if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
                report = hid_register_report(hdev, HID_INPUT_REPORT,
                        MOUSE_REPORT_ID, 0);
-       else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
+       else if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
+               if (id->vendor == BT_VENDOR_ID_APPLE)
+                       report = hid_register_report(hdev, HID_INPUT_REPORT,
+                               TRACKPAD2_BT_REPORT_ID, 0);
+               else /* USB_VENDOR_ID_APPLE */
+                       report = hid_register_report(hdev, HID_INPUT_REPORT,
+                               TRACKPAD2_USB_REPORT_ID, 0);
+       } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
                report = hid_register_report(hdev, HID_INPUT_REPORT,
                        TRACKPAD_REPORT_ID, 0);
                report = hid_register_report(hdev, HID_INPUT_REPORT,
@@ -546,7 +649,20 @@ static int magicmouse_probe(struct hid_device *hdev,
        }
        report->size = 6;
 
-       buf = kmemdup(feature, sizeof(feature), GFP_KERNEL);
+       if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
+               if (id->vendor == BT_VENDOR_ID_APPLE) {
+                       feature_size = sizeof(feature_mt_trackpad2_bt);
+                       feature = feature_mt_trackpad2_bt;
+               } else { /* USB_VENDOR_ID_APPLE */
+                       feature_size = sizeof(feature_mt_trackpad2_usb);
+                       feature = feature_mt_trackpad2_usb;
+               }
+       } else {
+               feature_size = sizeof(feature_mt);
+               feature = feature_mt;
+       }
+
+       buf = kmemdup(feature, feature_size, GFP_KERNEL);
        if (!buf) {
                ret = -ENOMEM;
                goto err_stop_hw;
@@ -560,10 +676,10 @@ static int magicmouse_probe(struct hid_device *hdev,
         * but there seems to be no other way of switching the mode.
         * Thus the super-ugly hacky success check below.
         */
-       ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(feature),
+       ret = hid_hw_raw_request(hdev, buf[0], buf, feature_size,
                                HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
        kfree(buf);
-       if (ret != -EIO && ret != sizeof(feature)) {
+       if (ret != -EIO && ret != feature_size) {
                hid_err(hdev, "unable to request touch data (%d)\n", ret);
                goto err_stop_hw;
        }
@@ -579,6 +695,10 @@ static const struct hid_device_id magic_mice[] = {
                USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
                USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 },
+       { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
+               USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE,
+               USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 },
        { }
 };
 MODULE_DEVICE_TABLE(hid, magic_mice);
index 72d983626afd0fb4a2b2a68084da61ae47828f70..330cb073cb66786437562d8d19e4e7f04851b6bd 100644 (file)
 #define MS_NOGET               BIT(4)
 #define MS_DUPLICATE_USAGES    BIT(5)
 #define MS_SURFACE_DIAL                BIT(6)
+#define MS_QUIRK_FF            BIT(7)
+
+struct ms_data {
+       unsigned long quirks;
+       struct hid_device *hdev;
+       struct work_struct ff_worker;
+       __u8 strong;
+       __u8 weak;
+       void *output_report_dmabuf;
+};
+
+#define XB1S_FF_REPORT         3
+#define ENABLE_WEAK            BIT(0)
+#define ENABLE_STRONG          BIT(1)
+
+enum {
+       MAGNITUDE_STRONG = 2,
+       MAGNITUDE_WEAK,
+       MAGNITUDE_NUM
+};
+
+struct xb1s_ff_report {
+       __u8    report_id;
+       __u8    enable;
+       __u8    magnitude[MAGNITUDE_NUM];
+       __u8    duration_10ms;
+       __u8    start_delay_10ms;
+       __u8    loop_count;
+} __packed;
 
 static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                unsigned int *rsize)
 {
-       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+       struct ms_data *ms = hid_get_drvdata(hdev);
+       unsigned long quirks = ms->quirks;
 
        /*
         * Microsoft Wireless Desktop Receiver (Model 1028) has
@@ -159,7 +189,8 @@ static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                struct hid_field *field, struct hid_usage *usage,
                unsigned long **bit, int *max)
 {
-       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+       struct ms_data *ms = hid_get_drvdata(hdev);
+       unsigned long quirks = ms->quirks;
 
        if (quirks & MS_ERGONOMY) {
                int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max);
@@ -185,7 +216,8 @@ static int ms_input_mapped(struct hid_device *hdev, struct hid_input *hi,
                struct hid_field *field, struct hid_usage *usage,
                unsigned long **bit, int *max)
 {
-       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+       struct ms_data *ms = hid_get_drvdata(hdev);
+       unsigned long quirks = ms->quirks;
 
        if (quirks & MS_DUPLICATE_USAGES)
                clear_bit(usage->code, *bit);
@@ -196,7 +228,8 @@ static int ms_input_mapped(struct hid_device *hdev, struct hid_input *hi,
 static int ms_event(struct hid_device *hdev, struct hid_field *field,
                struct hid_usage *usage, __s32 value)
 {
-       unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
+       struct ms_data *ms = hid_get_drvdata(hdev);
+       unsigned long quirks = ms->quirks;
        struct input_dev *input;
 
        if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
@@ -251,12 +284,97 @@ static int ms_event(struct hid_device *hdev, struct hid_field *field,
        return 0;
 }
 
+static void ms_ff_worker(struct work_struct *work)
+{
+       struct ms_data *ms = container_of(work, struct ms_data, ff_worker);
+       struct hid_device *hdev = ms->hdev;
+       struct xb1s_ff_report *r = ms->output_report_dmabuf;
+       int ret;
+
+       memset(r, 0, sizeof(*r));
+
+       r->report_id = XB1S_FF_REPORT;
+       r->enable = ENABLE_WEAK | ENABLE_STRONG;
+       /*
+        * Specifying maximum duration and maximum loop count should
+        * cover maximum duration of a single effect, which is 65536
+        * ms
+        */
+       r->duration_10ms = U8_MAX;
+       r->loop_count = U8_MAX;
+       r->magnitude[MAGNITUDE_STRONG] = ms->strong; /* left actuator */
+       r->magnitude[MAGNITUDE_WEAK] = ms->weak;     /* right actuator */
+
+       ret = hid_hw_output_report(hdev, (__u8 *)r, sizeof(*r));
+       if (ret)
+               hid_warn(hdev, "failed to send FF report\n");
+}
+
+static int ms_play_effect(struct input_dev *dev, void *data,
+                         struct ff_effect *effect)
+{
+       struct hid_device *hid = input_get_drvdata(dev);
+       struct ms_data *ms = hid_get_drvdata(hid);
+
+       if (effect->type != FF_RUMBLE)
+               return 0;
+
+       /*
+        * Magnitude is 0..100 so scale the 16-bit input here
+        */
+       ms->strong = ((u32) effect->u.rumble.strong_magnitude * 100) / U16_MAX;
+       ms->weak = ((u32) effect->u.rumble.weak_magnitude * 100) / U16_MAX;
+
+       schedule_work(&ms->ff_worker);
+       return 0;
+}
+
+static int ms_init_ff(struct hid_device *hdev)
+{
+       struct hid_input *hidinput = list_entry(hdev->inputs.next,
+                                               struct hid_input, list);
+       struct input_dev *input_dev = hidinput->input;
+       struct ms_data *ms = hid_get_drvdata(hdev);
+
+       if (!(ms->quirks & MS_QUIRK_FF))
+               return 0;
+
+       ms->hdev = hdev;
+       INIT_WORK(&ms->ff_worker, ms_ff_worker);
+
+       ms->output_report_dmabuf = devm_kzalloc(&hdev->dev,
+                                               sizeof(struct xb1s_ff_report),
+                                               GFP_KERNEL);
+       if (ms->output_report_dmabuf == NULL)
+               return -ENOMEM;
+
+       input_set_capability(input_dev, EV_FF, FF_RUMBLE);
+       return input_ff_create_memless(input_dev, NULL, ms_play_effect);
+}
+
+static void ms_remove_ff(struct hid_device *hdev)
+{
+       struct ms_data *ms = hid_get_drvdata(hdev);
+
+       if (!(ms->quirks & MS_QUIRK_FF))
+               return;
+
+       cancel_work_sync(&ms->ff_worker);
+}
+
 static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
        unsigned long quirks = id->driver_data;
+       struct ms_data *ms;
        int ret;
 
-       hid_set_drvdata(hdev, (void *)quirks);
+       ms = devm_kzalloc(&hdev->dev, sizeof(*ms), GFP_KERNEL);
+       if (ms == NULL)
+               return -ENOMEM;
+
+       ms->quirks = quirks;
+
+       hid_set_drvdata(hdev, ms);
 
        if (quirks & MS_NOGET)
                hdev->quirks |= HID_QUIRK_NOGET;
@@ -277,11 +395,21 @@ static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id)
                goto err_free;
        }
 
+       ret = ms_init_ff(hdev);
+       if (ret)
+               hid_err(hdev, "could not initialize ff, continuing anyway");
+
        return 0;
 err_free:
        return ret;
 }
 
+static void ms_remove(struct hid_device *hdev)
+{
+       hid_hw_stop(hdev);
+       ms_remove_ff(hdev);
+}
+
 static const struct hid_device_id ms_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV),
                .driver_data = MS_HIDINPUT },
@@ -318,6 +446,8 @@ static const struct hid_device_id ms_devices[] = {
                .driver_data = MS_PRESENTER },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, 0x091B),
                .driver_data = MS_SURFACE_DIAL },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_XBOX_ONE_S_CONTROLLER),
+               .driver_data = MS_QUIRK_FF },
        { }
 };
 MODULE_DEVICE_TABLE(hid, ms_devices);
@@ -330,6 +460,7 @@ static struct hid_driver ms_driver = {
        .input_mapped = ms_input_mapped,
        .event = ms_event,
        .probe = ms_probe,
+       .remove = ms_remove,
 };
 module_hid_driver(ms_driver);
 
index da954f3f4da7fcf5071522eec3a6b5b62dd46eed..f7c6de2b6730b90d0c189d686fe5ea3bb88bde67 100644 (file)
@@ -1319,6 +1319,13 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                return mt_touch_input_mapping(hdev, hi, field, usage, bit, max,
                                              application);
 
+       /*
+        * some egalax touchscreens have "application == DG_TOUCHSCREEN"
+        * for the stylus. Overwrite the hid_input application
+        */
+       if (field->physical == HID_DG_STYLUS)
+               hi->application = HID_DG_STYLUS;
+
        /* let hid-core decide for the others */
        return 0;
 }
@@ -1507,14 +1514,12 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
        struct mt_device *td = hid_get_drvdata(hdev);
        char *name;
        const char *suffix = NULL;
-       unsigned int application = 0;
        struct mt_report_data *rdata;
        struct mt_application *mt_application = NULL;
        struct hid_report *report;
        int ret;
 
        list_for_each_entry(report, &hi->reports, hidinput_list) {
-               application = report->application;
                rdata = mt_find_report_data(td, report);
                if (!rdata) {
                        hid_err(hdev, "failed to allocate data for report\n");
@@ -1529,46 +1534,33 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
                        if (ret)
                                return ret;
                }
-
-               /*
-                * some egalax touchscreens have "application == DG_TOUCHSCREEN"
-                * for the stylus. Check this first, and then rely on
-                * the application field.
-                */
-               if (report->field[0]->physical == HID_DG_STYLUS) {
-                       suffix = "Pen";
-                       /* force BTN_STYLUS to allow tablet matching in udev */
-                       __set_bit(BTN_STYLUS, hi->input->keybit);
-               }
        }
 
-       if (!suffix) {
-               switch (application) {
-               case HID_GD_KEYBOARD:
-               case HID_GD_KEYPAD:
-               case HID_GD_MOUSE:
-               case HID_DG_TOUCHPAD:
-               case HID_GD_SYSTEM_CONTROL:
-               case HID_CP_CONSUMER_CONTROL:
-               case HID_GD_WIRELESS_RADIO_CTLS:
-               case HID_GD_SYSTEM_MULTIAXIS:
-                       /* already handled by hid core */
-                       break;
-               case HID_DG_TOUCHSCREEN:
-                       /* we do not set suffix = "Touchscreen" */
-                       hi->input->name = hdev->name;
-                       break;
-               case HID_DG_STYLUS:
-                       /* force BTN_STYLUS to allow tablet matching in udev */
-                       __set_bit(BTN_STYLUS, hi->input->keybit);
-                       break;
-               case HID_VD_ASUS_CUSTOM_MEDIA_KEYS:
-                       suffix = "Custom Media Keys";
-                       break;
-               default:
-                       suffix = "UNKNOWN";
-                       break;
-               }
+       switch (hi->application) {
+       case HID_GD_KEYBOARD:
+       case HID_GD_KEYPAD:
+       case HID_GD_MOUSE:
+       case HID_DG_TOUCHPAD:
+       case HID_GD_SYSTEM_CONTROL:
+       case HID_CP_CONSUMER_CONTROL:
+       case HID_GD_WIRELESS_RADIO_CTLS:
+       case HID_GD_SYSTEM_MULTIAXIS:
+               /* already handled by hid core */
+               break;
+       case HID_DG_TOUCHSCREEN:
+               /* we do not set suffix = "Touchscreen" */
+               hi->input->name = hdev->name;
+               break;
+       case HID_DG_STYLUS:
+               /* force BTN_STYLUS to allow tablet matching in udev */
+               __set_bit(BTN_STYLUS, hi->input->keybit);
+               break;
+       case HID_VD_ASUS_CUSTOM_MEDIA_KEYS:
+               suffix = "Custom Media Keys";
+               break;
+       default:
+               suffix = "UNKNOWN";
+               break;
        }
 
        if (suffix) {
index 249d49b6b16c76c11ee1e8f9752cf755b6747b64..52c3b01917e7236821b804a35711b1e034da4788 100644 (file)
@@ -70,6 +70,7 @@ static const struct hid_device_id hid_quirks[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC), HID_QUIRK_NOGET },
        { HID_USB_DEVICE(USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_2NES2SNES), HID_QUIRK_MULTI_INPUT },
        { HID_USB_DEVICE(USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_4NES4SNES), HID_QUIRK_MULTI_INPUT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_REDRAGON_SEYMUR2), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE },
        { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR), HID_QUIRK_MULTI_INPUT },
        { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE1), HID_QUIRK_MULTI_INPUT },
        { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3), HID_QUIRK_MULTI_INPUT },
index 832d8f9aaba27fed7926177383f96ec1edaf9666..099e1ce2f2347592f8cf00cff8f620cc4d170301 100644 (file)
@@ -3,3 +3,6 @@
 #
 
 obj-$(CONFIG_I2C_HID)                          += i2c-hid.o
+
+i2c-hid-objs                                   =  i2c-hid-core.o
+i2c-hid-$(CONFIG_DMI)                          += i2c-hid-dmi-quirks.o
diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
new file mode 100644 (file)
index 0000000..4aab96c
--- /dev/null
@@ -0,0 +1,1329 @@
+/*
+ * HID over I2C protocol implementation
+ *
+ * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
+ * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
+ * Copyright (c) 2012 Red Hat, Inc
+ *
+ * This code is partly based on "USB HID support for Linux":
+ *
+ *  Copyright (c) 1999 Andreas Gal
+ *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ *  Copyright (c) 2007-2008 Oliver Neukum
+ *  Copyright (c) 2006-2010 Jiri Kosina
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/hid.h>
+#include <linux/mutex.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/platform_data/i2c-hid.h>
+
+#include "../hid-ids.h"
+#include "i2c-hid.h"
+
+/* quirks to control the device */
+#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV       BIT(0)
+#define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET       BIT(1)
+#define I2C_HID_QUIRK_NO_RUNTIME_PM            BIT(2)
+
+/* flags */
+#define I2C_HID_STARTED                0
+#define I2C_HID_RESET_PENDING  1
+#define I2C_HID_READ_PENDING   2
+
+#define I2C_HID_PWR_ON         0x00
+#define I2C_HID_PWR_SLEEP      0x01
+
+/* debug option */
+static bool debug;
+module_param(debug, bool, 0444);
+MODULE_PARM_DESC(debug, "print a lot of debug information");
+
+#define i2c_hid_dbg(ihid, fmt, arg...)                                   \
+do {                                                                     \
+       if (debug)                                                        \
+               dev_printk(KERN_DEBUG, &(ihid)->client->dev, fmt, ##arg); \
+} while (0)
+
+struct i2c_hid_desc {
+       __le16 wHIDDescLength;
+       __le16 bcdVersion;
+       __le16 wReportDescLength;
+       __le16 wReportDescRegister;
+       __le16 wInputRegister;
+       __le16 wMaxInputLength;
+       __le16 wOutputRegister;
+       __le16 wMaxOutputLength;
+       __le16 wCommandRegister;
+       __le16 wDataRegister;
+       __le16 wVendorID;
+       __le16 wProductID;
+       __le16 wVersionID;
+       __le32 reserved;
+} __packed;
+
+struct i2c_hid_cmd {
+       unsigned int registerIndex;
+       __u8 opcode;
+       unsigned int length;
+       bool wait;
+};
+
+union command {
+       u8 data[0];
+       struct cmd {
+               __le16 reg;
+               __u8 reportTypeID;
+               __u8 opcode;
+       } __packed c;
+};
+
+#define I2C_HID_CMD(opcode_) \
+       .opcode = opcode_, .length = 4, \
+       .registerIndex = offsetof(struct i2c_hid_desc, wCommandRegister)
+
+/* fetch HID descriptor */
+static const struct i2c_hid_cmd hid_descr_cmd = { .length = 2 };
+/* fetch report descriptors */
+static const struct i2c_hid_cmd hid_report_descr_cmd = {
+               .registerIndex = offsetof(struct i2c_hid_desc,
+                       wReportDescRegister),
+               .opcode = 0x00,
+               .length = 2 };
+/* commands */
+static const struct i2c_hid_cmd hid_reset_cmd =                { I2C_HID_CMD(0x01),
+                                                         .wait = true };
+static const struct i2c_hid_cmd hid_get_report_cmd =   { I2C_HID_CMD(0x02) };
+static const struct i2c_hid_cmd hid_set_report_cmd =   { I2C_HID_CMD(0x03) };
+static const struct i2c_hid_cmd hid_set_power_cmd =    { I2C_HID_CMD(0x08) };
+static const struct i2c_hid_cmd hid_no_cmd =           { .length = 0 };
+
+/*
+ * These definitions are not used here, but are defined by the spec.
+ * Keeping them here for documentation purposes.
+ *
+ * static const struct i2c_hid_cmd hid_get_idle_cmd = { I2C_HID_CMD(0x04) };
+ * static const struct i2c_hid_cmd hid_set_idle_cmd = { I2C_HID_CMD(0x05) };
+ * static const struct i2c_hid_cmd hid_get_protocol_cmd = { I2C_HID_CMD(0x06) };
+ * static const struct i2c_hid_cmd hid_set_protocol_cmd = { I2C_HID_CMD(0x07) };
+ */
+
+/* The main device structure */
+struct i2c_hid {
+       struct i2c_client       *client;        /* i2c client */
+       struct hid_device       *hid;   /* pointer to corresponding HID dev */
+       union {
+               __u8 hdesc_buffer[sizeof(struct i2c_hid_desc)];
+               struct i2c_hid_desc hdesc;      /* the HID Descriptor */
+       };
+       __le16                  wHIDDescRegister; /* location of the i2c
+                                                  * register of the HID
+                                                  * descriptor. */
+       unsigned int            bufsize;        /* i2c buffer size */
+       u8                      *inbuf;         /* Input buffer */
+       u8                      *rawbuf;        /* Raw Input buffer */
+       u8                      *cmdbuf;        /* Command buffer */
+       u8                      *argsbuf;       /* Command arguments buffer */
+
+       unsigned long           flags;          /* device flags */
+       unsigned long           quirks;         /* Various quirks */
+
+       wait_queue_head_t       wait;           /* For waiting the interrupt */
+
+       struct i2c_hid_platform_data pdata;
+
+       bool                    irq_wake_enabled;
+       struct mutex            reset_lock;
+};
+
+static const struct i2c_hid_quirks {
+       __u16 idVendor;
+       __u16 idProduct;
+       __u32 quirks;
+} i2c_hid_quirks[] = {
+       { USB_VENDOR_ID_WEIDA, USB_DEVICE_ID_WEIDA_8752,
+               I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
+       { USB_VENDOR_ID_WEIDA, USB_DEVICE_ID_WEIDA_8755,
+               I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
+       { I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288,
+               I2C_HID_QUIRK_NO_IRQ_AFTER_RESET |
+               I2C_HID_QUIRK_NO_RUNTIME_PM },
+       { 0, 0 }
+};
+
+/*
+ * i2c_hid_lookup_quirk: return any quirks associated with a I2C HID device
+ * @idVendor: the 16-bit vendor ID
+ * @idProduct: the 16-bit product ID
+ *
+ * Returns: a u32 quirks value.
+ */
+static u32 i2c_hid_lookup_quirk(const u16 idVendor, const u16 idProduct)
+{
+       u32 quirks = 0;
+       int n;
+
+       for (n = 0; i2c_hid_quirks[n].idVendor; n++)
+               if (i2c_hid_quirks[n].idVendor == idVendor &&
+                   (i2c_hid_quirks[n].idProduct == (__u16)HID_ANY_ID ||
+                    i2c_hid_quirks[n].idProduct == idProduct))
+                       quirks = i2c_hid_quirks[n].quirks;
+
+       return quirks;
+}
+
+static int __i2c_hid_command(struct i2c_client *client,
+               const struct i2c_hid_cmd *command, u8 reportID,
+               u8 reportType, u8 *args, int args_len,
+               unsigned char *buf_recv, int data_len)
+{
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+       union command *cmd = (union command *)ihid->cmdbuf;
+       int ret;
+       struct i2c_msg msg[2];
+       int msg_num = 1;
+
+       int length = command->length;
+       bool wait = command->wait;
+       unsigned int registerIndex = command->registerIndex;
+
+       /* special case for hid_descr_cmd */
+       if (command == &hid_descr_cmd) {
+               cmd->c.reg = ihid->wHIDDescRegister;
+       } else {
+               cmd->data[0] = ihid->hdesc_buffer[registerIndex];
+               cmd->data[1] = ihid->hdesc_buffer[registerIndex + 1];
+       }
+
+       if (length > 2) {
+               cmd->c.opcode = command->opcode;
+               cmd->c.reportTypeID = reportID | reportType << 4;
+       }
+
+       memcpy(cmd->data + length, args, args_len);
+       length += args_len;
+
+       i2c_hid_dbg(ihid, "%s: cmd=%*ph\n", __func__, length, cmd->data);
+
+       msg[0].addr = client->addr;
+       msg[0].flags = client->flags & I2C_M_TEN;
+       msg[0].len = length;
+       msg[0].buf = cmd->data;
+       if (data_len > 0) {
+               msg[1].addr = client->addr;
+               msg[1].flags = client->flags & I2C_M_TEN;
+               msg[1].flags |= I2C_M_RD;
+               msg[1].len = data_len;
+               msg[1].buf = buf_recv;
+               msg_num = 2;
+               set_bit(I2C_HID_READ_PENDING, &ihid->flags);
+       }
+
+       if (wait)
+               set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
+
+       ret = i2c_transfer(client->adapter, msg, msg_num);
+
+       if (data_len > 0)
+               clear_bit(I2C_HID_READ_PENDING, &ihid->flags);
+
+       if (ret != msg_num)
+               return ret < 0 ? ret : -EIO;
+
+       ret = 0;
+
+       if (wait && (ihid->quirks & I2C_HID_QUIRK_NO_IRQ_AFTER_RESET)) {
+               msleep(100);
+       } else if (wait) {
+               i2c_hid_dbg(ihid, "%s: waiting...\n", __func__);
+               if (!wait_event_timeout(ihid->wait,
+                               !test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
+                               msecs_to_jiffies(5000)))
+                       ret = -ENODATA;
+               i2c_hid_dbg(ihid, "%s: finished.\n", __func__);
+       }
+
+       return ret;
+}
+
+static int i2c_hid_command(struct i2c_client *client,
+               const struct i2c_hid_cmd *command,
+               unsigned char *buf_recv, int data_len)
+{
+       return __i2c_hid_command(client, command, 0, 0, NULL, 0,
+                               buf_recv, data_len);
+}
+
+static int i2c_hid_get_report(struct i2c_client *client, u8 reportType,
+               u8 reportID, unsigned char *buf_recv, int data_len)
+{
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+       u8 args[3];
+       int ret;
+       int args_len = 0;
+       u16 readRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
+
+       i2c_hid_dbg(ihid, "%s\n", __func__);
+
+       if (reportID >= 0x0F) {
+               args[args_len++] = reportID;
+               reportID = 0x0F;
+       }
+
+       args[args_len++] = readRegister & 0xFF;
+       args[args_len++] = readRegister >> 8;
+
+       ret = __i2c_hid_command(client, &hid_get_report_cmd, reportID,
+               reportType, args, args_len, buf_recv, data_len);
+       if (ret) {
+               dev_err(&client->dev,
+                       "failed to retrieve report from device.\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+/**
+ * i2c_hid_set_or_send_report: forward an incoming report to the device
+ * @client: the i2c_client of the device
+ * @reportType: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT
+ * @reportID: the report ID
+ * @buf: the actual data to transfer, without the report ID
+ * @len: size of buf
+ * @use_data: true: use SET_REPORT HID command, false: send plain OUTPUT report
+ */
+static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType,
+               u8 reportID, unsigned char *buf, size_t data_len, bool use_data)
+{
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+       u8 *args = ihid->argsbuf;
+       const struct i2c_hid_cmd *hidcmd;
+       int ret;
+       u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
+       u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister);
+       u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength);
+       u16 size;
+       int args_len;
+       int index = 0;
+
+       i2c_hid_dbg(ihid, "%s\n", __func__);
+
+       if (data_len > ihid->bufsize)
+               return -EINVAL;
+
+       size =          2                       /* size */ +
+                       (reportID ? 1 : 0)      /* reportID */ +
+                       data_len                /* buf */;
+       args_len =      (reportID >= 0x0F ? 1 : 0) /* optional third byte */ +
+                       2                       /* dataRegister */ +
+                       size                    /* args */;
+
+       if (!use_data && maxOutputLength == 0)
+               return -ENOSYS;
+
+       if (reportID >= 0x0F) {
+               args[index++] = reportID;
+               reportID = 0x0F;
+       }
+
+       /*
+        * use the data register for feature reports or if the device does not
+        * support the output register
+        */
+       if (use_data) {
+               args[index++] = dataRegister & 0xFF;
+               args[index++] = dataRegister >> 8;
+               hidcmd = &hid_set_report_cmd;
+       } else {
+               args[index++] = outputRegister & 0xFF;
+               args[index++] = outputRegister >> 8;
+               hidcmd = &hid_no_cmd;
+       }
+
+       args[index++] = size & 0xFF;
+       args[index++] = size >> 8;
+
+       if (reportID)
+               args[index++] = reportID;
+
+       memcpy(&args[index], buf, data_len);
+
+       ret = __i2c_hid_command(client, hidcmd, reportID,
+               reportType, args, args_len, NULL, 0);
+       if (ret) {
+               dev_err(&client->dev, "failed to set a report to device.\n");
+               return ret;
+       }
+
+       return data_len;
+}
+
+static int i2c_hid_set_power(struct i2c_client *client, int power_state)
+{
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+       int ret;
+
+       i2c_hid_dbg(ihid, "%s\n", __func__);
+
+       /*
+        * Some devices require to send a command to wakeup before power on.
+        * The call will get a return value (EREMOTEIO) but device will be
+        * triggered and activated. After that, it goes like a normal device.
+        */
+       if (power_state == I2C_HID_PWR_ON &&
+           ihid->quirks & I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV) {
+               ret = i2c_hid_command(client, &hid_set_power_cmd, NULL, 0);
+
+               /* Device was already activated */
+               if (!ret)
+                       goto set_pwr_exit;
+       }
+
+       ret = __i2c_hid_command(client, &hid_set_power_cmd, power_state,
+               0, NULL, 0, NULL, 0);
+
+       if (ret)
+               dev_err(&client->dev, "failed to change power setting.\n");
+
+set_pwr_exit:
+       return ret;
+}
+
+static int i2c_hid_hwreset(struct i2c_client *client)
+{
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+       int ret;
+
+       i2c_hid_dbg(ihid, "%s\n", __func__);
+
+       /*
+        * This prevents sending feature reports while the device is
+        * being reset. Otherwise we may lose the reset complete
+        * interrupt.
+        */
+       mutex_lock(&ihid->reset_lock);
+
+       ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
+       if (ret)
+               goto out_unlock;
+
+       /*
+        * The HID over I2C specification states that if a DEVICE needs time
+        * after the PWR_ON request, it should utilise CLOCK stretching.
+        * However, it has been observered that the Windows driver provides a
+        * 1ms sleep between the PWR_ON and RESET requests and that some devices
+        * rely on this.
+        */
+       usleep_range(1000, 5000);
+
+       i2c_hid_dbg(ihid, "resetting...\n");
+
+       ret = i2c_hid_command(client, &hid_reset_cmd, NULL, 0);
+       if (ret) {
+               dev_err(&client->dev, "failed to reset device.\n");
+               i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+       }
+
+out_unlock:
+       mutex_unlock(&ihid->reset_lock);
+       return ret;
+}
+
+static void i2c_hid_get_input(struct i2c_hid *ihid)
+{
+       int ret;
+       u32 ret_size;
+       int size = le16_to_cpu(ihid->hdesc.wMaxInputLength);
+
+       if (size > ihid->bufsize)
+               size = ihid->bufsize;
+
+       ret = i2c_master_recv(ihid->client, ihid->inbuf, size);
+       if (ret != size) {
+               if (ret < 0)
+                       return;
+
+               dev_err(&ihid->client->dev, "%s: got %d data instead of %d\n",
+                       __func__, ret, size);
+               return;
+       }
+
+       ret_size = ihid->inbuf[0] | ihid->inbuf[1] << 8;
+
+       if (!ret_size) {
+               /* host or device initiated RESET completed */
+               if (test_and_clear_bit(I2C_HID_RESET_PENDING, &ihid->flags))
+                       wake_up(&ihid->wait);
+               return;
+       }
+
+       if ((ret_size > size) || (ret_size < 2)) {
+               dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n",
+                       __func__, size, ret_size);
+               return;
+       }
+
+       i2c_hid_dbg(ihid, "input: %*ph\n", ret_size, ihid->inbuf);
+
+       if (test_bit(I2C_HID_STARTED, &ihid->flags))
+               hid_input_report(ihid->hid, HID_INPUT_REPORT, ihid->inbuf + 2,
+                               ret_size - 2, 1);
+
+       return;
+}
+
+static irqreturn_t i2c_hid_irq(int irq, void *dev_id)
+{
+       struct i2c_hid *ihid = dev_id;
+
+       if (test_bit(I2C_HID_READ_PENDING, &ihid->flags))
+               return IRQ_HANDLED;
+
+       i2c_hid_get_input(ihid);
+
+       return IRQ_HANDLED;
+}
+
+static int i2c_hid_get_report_length(struct hid_report *report)
+{
+       return ((report->size - 1) >> 3) + 1 +
+               report->device->report_enum[report->type].numbered + 2;
+}
+
+/*
+ * Traverse the supplied list of reports and find the longest
+ */
+static void i2c_hid_find_max_report(struct hid_device *hid, unsigned int type,
+               unsigned int *max)
+{
+       struct hid_report *report;
+       unsigned int size;
+
+       /* We should not rely on wMaxInputLength, as some devices may set it to
+        * a wrong length. */
+       list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
+               size = i2c_hid_get_report_length(report);
+               if (*max < size)
+                       *max = size;
+       }
+}
+
+static void i2c_hid_free_buffers(struct i2c_hid *ihid)
+{
+       kfree(ihid->inbuf);
+       kfree(ihid->rawbuf);
+       kfree(ihid->argsbuf);
+       kfree(ihid->cmdbuf);
+       ihid->inbuf = NULL;
+       ihid->rawbuf = NULL;
+       ihid->cmdbuf = NULL;
+       ihid->argsbuf = NULL;
+       ihid->bufsize = 0;
+}
+
+static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size)
+{
+       /* the worst case is computed from the set_report command with a
+        * reportID > 15 and the maximum report length */
+       int args_len = sizeof(__u8) + /* ReportID */
+                      sizeof(__u8) + /* optional ReportID byte */
+                      sizeof(__u16) + /* data register */
+                      sizeof(__u16) + /* size of the report */
+                      report_size; /* report */
+
+       ihid->inbuf = kzalloc(report_size, GFP_KERNEL);
+       ihid->rawbuf = kzalloc(report_size, GFP_KERNEL);
+       ihid->argsbuf = kzalloc(args_len, GFP_KERNEL);
+       ihid->cmdbuf = kzalloc(sizeof(union command) + args_len, GFP_KERNEL);
+
+       if (!ihid->inbuf || !ihid->rawbuf || !ihid->argsbuf || !ihid->cmdbuf) {
+               i2c_hid_free_buffers(ihid);
+               return -ENOMEM;
+       }
+
+       ihid->bufsize = report_size;
+
+       return 0;
+}
+
+static int i2c_hid_get_raw_report(struct hid_device *hid,
+               unsigned char report_number, __u8 *buf, size_t count,
+               unsigned char report_type)
+{
+       struct i2c_client *client = hid->driver_data;
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+       size_t ret_count, ask_count;
+       int ret;
+
+       if (report_type == HID_OUTPUT_REPORT)
+               return -EINVAL;
+
+       /* +2 bytes to include the size of the reply in the query buffer */
+       ask_count = min(count + 2, (size_t)ihid->bufsize);
+
+       ret = i2c_hid_get_report(client,
+                       report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
+                       report_number, ihid->rawbuf, ask_count);
+
+       if (ret < 0)
+               return ret;
+
+       ret_count = ihid->rawbuf[0] | (ihid->rawbuf[1] << 8);
+
+       if (ret_count <= 2)
+               return 0;
+
+       ret_count = min(ret_count, ask_count);
+
+       /* The query buffer contains the size, dropping it in the reply */
+       count = min(count, ret_count - 2);
+       memcpy(buf, ihid->rawbuf + 2, count);
+
+       return count;
+}
+
+static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
+               size_t count, unsigned char report_type, bool use_data)
+{
+       struct i2c_client *client = hid->driver_data;
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+       int report_id = buf[0];
+       int ret;
+
+       if (report_type == HID_INPUT_REPORT)
+               return -EINVAL;
+
+       mutex_lock(&ihid->reset_lock);
+
+       if (report_id) {
+               buf++;
+               count--;
+       }
+
+       ret = i2c_hid_set_or_send_report(client,
+                               report_type == HID_FEATURE_REPORT ? 0x03 : 0x02,
+                               report_id, buf, count, use_data);
+
+       if (report_id && ret >= 0)
+               ret++; /* add report_id to the number of transfered bytes */
+
+       mutex_unlock(&ihid->reset_lock);
+
+       return ret;
+}
+
+static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf,
+               size_t count)
+{
+       return i2c_hid_output_raw_report(hid, buf, count, HID_OUTPUT_REPORT,
+                       false);
+}
+
+static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
+                              __u8 *buf, size_t len, unsigned char rtype,
+                              int reqtype)
+{
+       switch (reqtype) {
+       case HID_REQ_GET_REPORT:
+               return i2c_hid_get_raw_report(hid, reportnum, buf, len, rtype);
+       case HID_REQ_SET_REPORT:
+               if (buf[0] != reportnum)
+                       return -EINVAL;
+               return i2c_hid_output_raw_report(hid, buf, len, rtype, true);
+       default:
+               return -EIO;
+       }
+}
+
+static int i2c_hid_parse(struct hid_device *hid)
+{
+       struct i2c_client *client = hid->driver_data;
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+       struct i2c_hid_desc *hdesc = &ihid->hdesc;
+       unsigned int rsize;
+       char *rdesc;
+       int ret;
+       int tries = 3;
+       char *use_override;
+
+       i2c_hid_dbg(ihid, "entering %s\n", __func__);
+
+       rsize = le16_to_cpu(hdesc->wReportDescLength);
+       if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
+               dbg_hid("weird size of report descriptor (%u)\n", rsize);
+               return -EINVAL;
+       }
+
+       do {
+               ret = i2c_hid_hwreset(client);
+               if (ret)
+                       msleep(1000);
+       } while (tries-- > 0 && ret);
+
+       if (ret)
+               return ret;
+
+       use_override = i2c_hid_get_dmi_hid_report_desc_override(client->name,
+                                                               &rsize);
+
+       if (use_override) {
+               rdesc = use_override;
+               i2c_hid_dbg(ihid, "Using a HID report descriptor override\n");
+       } else {
+               rdesc = kzalloc(rsize, GFP_KERNEL);
+
+               if (!rdesc) {
+                       dbg_hid("couldn't allocate rdesc memory\n");
+                       return -ENOMEM;
+               }
+
+               i2c_hid_dbg(ihid, "asking HID report descriptor\n");
+
+               ret = i2c_hid_command(client, &hid_report_descr_cmd,
+                                     rdesc, rsize);
+               if (ret) {
+                       hid_err(hid, "reading report descriptor failed\n");
+                       kfree(rdesc);
+                       return -EIO;
+               }
+       }
+
+       i2c_hid_dbg(ihid, "Report Descriptor: %*ph\n", rsize, rdesc);
+
+       ret = hid_parse_report(hid, rdesc, rsize);
+       if (!use_override)
+               kfree(rdesc);
+
+       if (ret) {
+               dbg_hid("parsing report descriptor failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int i2c_hid_start(struct hid_device *hid)
+{
+       struct i2c_client *client = hid->driver_data;
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+       int ret;
+       unsigned int bufsize = HID_MIN_BUFFER_SIZE;
+
+       i2c_hid_find_max_report(hid, HID_INPUT_REPORT, &bufsize);
+       i2c_hid_find_max_report(hid, HID_OUTPUT_REPORT, &bufsize);
+       i2c_hid_find_max_report(hid, HID_FEATURE_REPORT, &bufsize);
+
+       if (bufsize > ihid->bufsize) {
+               disable_irq(client->irq);
+               i2c_hid_free_buffers(ihid);
+
+               ret = i2c_hid_alloc_buffers(ihid, bufsize);
+               enable_irq(client->irq);
+
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void i2c_hid_stop(struct hid_device *hid)
+{
+       hid->claimed = 0;
+}
+
+static int i2c_hid_open(struct hid_device *hid)
+{
+       struct i2c_client *client = hid->driver_data;
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+       int ret = 0;
+
+       ret = pm_runtime_get_sync(&client->dev);
+       if (ret < 0)
+               return ret;
+
+       set_bit(I2C_HID_STARTED, &ihid->flags);
+       return 0;
+}
+
+static void i2c_hid_close(struct hid_device *hid)
+{
+       struct i2c_client *client = hid->driver_data;
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+
+       clear_bit(I2C_HID_STARTED, &ihid->flags);
+
+       /* Save some power */
+       pm_runtime_put(&client->dev);
+}
+
+static int i2c_hid_power(struct hid_device *hid, int lvl)
+{
+       struct i2c_client *client = hid->driver_data;
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+
+       i2c_hid_dbg(ihid, "%s lvl:%d\n", __func__, lvl);
+
+       switch (lvl) {
+       case PM_HINT_FULLON:
+               pm_runtime_get_sync(&client->dev);
+               break;
+       case PM_HINT_NORMAL:
+               pm_runtime_put(&client->dev);
+               break;
+       }
+       return 0;
+}
+
+struct hid_ll_driver i2c_hid_ll_driver = {
+       .parse = i2c_hid_parse,
+       .start = i2c_hid_start,
+       .stop = i2c_hid_stop,
+       .open = i2c_hid_open,
+       .close = i2c_hid_close,
+       .power = i2c_hid_power,
+       .output_report = i2c_hid_output_report,
+       .raw_request = i2c_hid_raw_request,
+};
+EXPORT_SYMBOL_GPL(i2c_hid_ll_driver);
+
+static int i2c_hid_init_irq(struct i2c_client *client)
+{
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+       unsigned long irqflags = 0;
+       int ret;
+
+       dev_dbg(&client->dev, "Requesting IRQ: %d\n", client->irq);
+
+       if (!irq_get_trigger_type(client->irq))
+               irqflags = IRQF_TRIGGER_LOW;
+
+       ret = request_threaded_irq(client->irq, NULL, i2c_hid_irq,
+                                  irqflags | IRQF_ONESHOT, client->name, ihid);
+       if (ret < 0) {
+               dev_warn(&client->dev,
+                       "Could not register for %s interrupt, irq = %d,"
+                       " ret = %d\n",
+                       client->name, client->irq, ret);
+
+               return ret;
+       }
+
+       return 0;
+}
+
+static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
+{
+       struct i2c_client *client = ihid->client;
+       struct i2c_hid_desc *hdesc = &ihid->hdesc;
+       unsigned int dsize;
+       int ret;
+
+       /* i2c hid fetch using a fixed descriptor size (30 bytes) */
+       if (i2c_hid_get_dmi_i2c_hid_desc_override(client->name)) {
+               i2c_hid_dbg(ihid, "Using a HID descriptor override\n");
+               ihid->hdesc =
+                       *i2c_hid_get_dmi_i2c_hid_desc_override(client->name);
+       } else {
+               i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
+               ret = i2c_hid_command(client, &hid_descr_cmd,
+                                     ihid->hdesc_buffer,
+                                     sizeof(struct i2c_hid_desc));
+               if (ret) {
+                       dev_err(&client->dev, "hid_descr_cmd failed\n");
+                       return -ENODEV;
+               }
+       }
+
+       /* Validate the length of HID descriptor, the 4 first bytes:
+        * bytes 0-1 -> length
+        * bytes 2-3 -> bcdVersion (has to be 1.00) */
+       /* check bcdVersion == 1.0 */
+       if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
+               dev_err(&client->dev,
+                       "unexpected HID descriptor bcdVersion (0x%04hx)\n",
+                       le16_to_cpu(hdesc->bcdVersion));
+               return -ENODEV;
+       }
+
+       /* Descriptor length should be 30 bytes as per the specification */
+       dsize = le16_to_cpu(hdesc->wHIDDescLength);
+       if (dsize != sizeof(struct i2c_hid_desc)) {
+               dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
+                       dsize);
+               return -ENODEV;
+       }
+       i2c_hid_dbg(ihid, "HID Descriptor: %*ph\n", dsize, ihid->hdesc_buffer);
+       return 0;
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id i2c_hid_acpi_blacklist[] = {
+       /*
+        * The CHPN0001 ACPI device, which is used to describe the Chipone
+        * ICN8505 controller, has a _CID of PNP0C50 but is not HID compatible.
+        */
+       {"CHPN0001", 0 },
+       { },
+};
+
+static int i2c_hid_acpi_pdata(struct i2c_client *client,
+               struct i2c_hid_platform_data *pdata)
+{
+       static guid_t i2c_hid_guid =
+               GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
+                         0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
+       union acpi_object *obj;
+       struct acpi_device *adev;
+       acpi_handle handle;
+
+       handle = ACPI_HANDLE(&client->dev);
+       if (!handle || acpi_bus_get_device(handle, &adev)) {
+               dev_err(&client->dev, "Error could not get ACPI device\n");
+               return -ENODEV;
+       }
+
+       if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0)
+               return -ENODEV;
+
+       obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, 1, 1, NULL,
+                                     ACPI_TYPE_INTEGER);
+       if (!obj) {
+               dev_err(&client->dev, "Error _DSM call to get HID descriptor address failed\n");
+               return -ENODEV;
+       }
+
+       pdata->hid_descriptor_address = obj->integer.value;
+       ACPI_FREE(obj);
+
+       return 0;
+}
+
+static void i2c_hid_acpi_fix_up_power(struct device *dev)
+{
+       struct acpi_device *adev;
+
+       adev = ACPI_COMPANION(dev);
+       if (adev)
+               acpi_device_fix_up_power(adev);
+}
+
+static const struct acpi_device_id i2c_hid_acpi_match[] = {
+       {"ACPI0C50", 0 },
+       {"PNP0C50", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, i2c_hid_acpi_match);
+#else
+static inline int i2c_hid_acpi_pdata(struct i2c_client *client,
+               struct i2c_hid_platform_data *pdata)
+{
+       return -ENODEV;
+}
+
+static inline void i2c_hid_acpi_fix_up_power(struct device *dev) {}
+#endif
+
+#ifdef CONFIG_OF
+static int i2c_hid_of_probe(struct i2c_client *client,
+               struct i2c_hid_platform_data *pdata)
+{
+       struct device *dev = &client->dev;
+       u32 val;
+       int ret;
+
+       ret = of_property_read_u32(dev->of_node, "hid-descr-addr", &val);
+       if (ret) {
+               dev_err(&client->dev, "HID register address not provided\n");
+               return -ENODEV;
+       }
+       if (val >> 16) {
+               dev_err(&client->dev, "Bad HID register address: 0x%08x\n",
+                       val);
+               return -EINVAL;
+       }
+       pdata->hid_descriptor_address = val;
+
+       return 0;
+}
+
+static const struct of_device_id i2c_hid_of_match[] = {
+       { .compatible = "hid-over-i2c" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, i2c_hid_of_match);
+#else
+static inline int i2c_hid_of_probe(struct i2c_client *client,
+               struct i2c_hid_platform_data *pdata)
+{
+       return -ENODEV;
+}
+#endif
+
+static void i2c_hid_fwnode_probe(struct i2c_client *client,
+                                struct i2c_hid_platform_data *pdata)
+{
+       u32 val;
+
+       if (!device_property_read_u32(&client->dev, "post-power-on-delay-ms",
+                                     &val))
+               pdata->post_power_delay_ms = val;
+}
+
+static int i2c_hid_probe(struct i2c_client *client,
+                        const struct i2c_device_id *dev_id)
+{
+       int ret;
+       struct i2c_hid *ihid;
+       struct hid_device *hid;
+       __u16 hidRegister;
+       struct i2c_hid_platform_data *platform_data = client->dev.platform_data;
+
+       dbg_hid("HID probe called for i2c 0x%02x\n", client->addr);
+
+       if (!client->irq) {
+               dev_err(&client->dev,
+                       "HID over i2c has not been provided an Int IRQ\n");
+               return -EINVAL;
+       }
+
+       if (client->irq < 0) {
+               if (client->irq != -EPROBE_DEFER)
+                       dev_err(&client->dev,
+                               "HID over i2c doesn't have a valid IRQ\n");
+               return client->irq;
+       }
+
+       ihid = devm_kzalloc(&client->dev, sizeof(*ihid), GFP_KERNEL);
+       if (!ihid)
+               return -ENOMEM;
+
+       if (client->dev.of_node) {
+               ret = i2c_hid_of_probe(client, &ihid->pdata);
+               if (ret)
+                       return ret;
+       } else if (!platform_data) {
+               ret = i2c_hid_acpi_pdata(client, &ihid->pdata);
+               if (ret)
+                       return ret;
+       } else {
+               ihid->pdata = *platform_data;
+       }
+
+       /* Parse platform agnostic common properties from ACPI / device tree */
+       i2c_hid_fwnode_probe(client, &ihid->pdata);
+
+       ihid->pdata.supplies[0].supply = "vdd";
+       ihid->pdata.supplies[1].supply = "vddl";
+
+       ret = devm_regulator_bulk_get(&client->dev,
+                                     ARRAY_SIZE(ihid->pdata.supplies),
+                                     ihid->pdata.supplies);
+       if (ret)
+               return ret;
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies),
+                                   ihid->pdata.supplies);
+       if (ret < 0)
+               return ret;
+
+       if (ihid->pdata.post_power_delay_ms)
+               msleep(ihid->pdata.post_power_delay_ms);
+
+       i2c_set_clientdata(client, ihid);
+
+       ihid->client = client;
+
+       hidRegister = ihid->pdata.hid_descriptor_address;
+       ihid->wHIDDescRegister = cpu_to_le16(hidRegister);
+
+       init_waitqueue_head(&ihid->wait);
+       mutex_init(&ihid->reset_lock);
+
+       /* we need to allocate the command buffer without knowing the maximum
+        * size of the reports. Let's use HID_MIN_BUFFER_SIZE, then we do the
+        * real computation later. */
+       ret = i2c_hid_alloc_buffers(ihid, HID_MIN_BUFFER_SIZE);
+       if (ret < 0)
+               goto err_regulator;
+
+       i2c_hid_acpi_fix_up_power(&client->dev);
+
+       pm_runtime_get_noresume(&client->dev);
+       pm_runtime_set_active(&client->dev);
+       pm_runtime_enable(&client->dev);
+       device_enable_async_suspend(&client->dev);
+
+       /* Make sure there is something at this address */
+       ret = i2c_smbus_read_byte(client);
+       if (ret < 0) {
+               dev_dbg(&client->dev, "nothing at this address: %d\n", ret);
+               ret = -ENXIO;
+               goto err_pm;
+       }
+
+       ret = i2c_hid_fetch_hid_descriptor(ihid);
+       if (ret < 0)
+               goto err_pm;
+
+       ret = i2c_hid_init_irq(client);
+       if (ret < 0)
+               goto err_pm;
+
+       hid = hid_allocate_device();
+       if (IS_ERR(hid)) {
+               ret = PTR_ERR(hid);
+               goto err_irq;
+       }
+
+       ihid->hid = hid;
+
+       hid->driver_data = client;
+       hid->ll_driver = &i2c_hid_ll_driver;
+       hid->dev.parent = &client->dev;
+       hid->bus = BUS_I2C;
+       hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
+       hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
+       hid->product = le16_to_cpu(ihid->hdesc.wProductID);
+
+       snprintf(hid->name, sizeof(hid->name), "%s %04hX:%04hX",
+                client->name, hid->vendor, hid->product);
+       strlcpy(hid->phys, dev_name(&client->dev), sizeof(hid->phys));
+
+       ihid->quirks = i2c_hid_lookup_quirk(hid->vendor, hid->product);
+
+       ret = hid_add_device(hid);
+       if (ret) {
+               if (ret != -ENODEV)
+                       hid_err(client, "can't add hid device: %d\n", ret);
+               goto err_mem_free;
+       }
+
+       if (!(ihid->quirks & I2C_HID_QUIRK_NO_RUNTIME_PM))
+               pm_runtime_put(&client->dev);
+
+       return 0;
+
+err_mem_free:
+       hid_destroy_device(hid);
+
+err_irq:
+       free_irq(client->irq, ihid);
+
+err_pm:
+       pm_runtime_put_noidle(&client->dev);
+       pm_runtime_disable(&client->dev);
+
+err_regulator:
+       regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
+                              ihid->pdata.supplies);
+       i2c_hid_free_buffers(ihid);
+       return ret;
+}
+
+static int i2c_hid_remove(struct i2c_client *client)
+{
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+       struct hid_device *hid;
+
+       if (!(ihid->quirks & I2C_HID_QUIRK_NO_RUNTIME_PM))
+               pm_runtime_get_sync(&client->dev);
+       pm_runtime_disable(&client->dev);
+       pm_runtime_set_suspended(&client->dev);
+       pm_runtime_put_noidle(&client->dev);
+
+       hid = ihid->hid;
+       hid_destroy_device(hid);
+
+       free_irq(client->irq, ihid);
+
+       if (ihid->bufsize)
+               i2c_hid_free_buffers(ihid);
+
+       regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
+                              ihid->pdata.supplies);
+
+       return 0;
+}
+
+static void i2c_hid_shutdown(struct i2c_client *client)
+{
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+
+       i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+       free_irq(client->irq, ihid);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int i2c_hid_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+       struct hid_device *hid = ihid->hid;
+       int ret;
+       int wake_status;
+
+       if (hid->driver && hid->driver->suspend) {
+               /*
+                * Wake up the device so that IO issues in
+                * HID driver's suspend code can succeed.
+                */
+               ret = pm_runtime_resume(dev);
+               if (ret < 0)
+                       return ret;
+
+               ret = hid->driver->suspend(hid, PMSG_SUSPEND);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (!pm_runtime_suspended(dev)) {
+               /* Save some power */
+               i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+
+               disable_irq(client->irq);
+       }
+
+       if (device_may_wakeup(&client->dev)) {
+               wake_status = enable_irq_wake(client->irq);
+               if (!wake_status)
+                       ihid->irq_wake_enabled = true;
+               else
+                       hid_warn(hid, "Failed to enable irq wake: %d\n",
+                               wake_status);
+       } else {
+               regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
+                                      ihid->pdata.supplies);
+       }
+
+       return 0;
+}
+
+static int i2c_hid_resume(struct device *dev)
+{
+       int ret;
+       struct i2c_client *client = to_i2c_client(dev);
+       struct i2c_hid *ihid = i2c_get_clientdata(client);
+       struct hid_device *hid = ihid->hid;
+       int wake_status;
+
+       if (!device_may_wakeup(&client->dev)) {
+               ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies),
+                                           ihid->pdata.supplies);
+               if (ret)
+                       hid_warn(hid, "Failed to enable supplies: %d\n", ret);
+
+               if (ihid->pdata.post_power_delay_ms)
+                       msleep(ihid->pdata.post_power_delay_ms);
+       } else if (ihid->irq_wake_enabled) {
+               wake_status = disable_irq_wake(client->irq);
+               if (!wake_status)
+                       ihid->irq_wake_enabled = false;
+               else
+                       hid_warn(hid, "Failed to disable irq wake: %d\n",
+                               wake_status);
+       }
+
+       /* We'll resume to full power */
+       pm_runtime_disable(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+
+       enable_irq(client->irq);
+
+       /* Instead of resetting device, simply powers the device on. This
+        * solves "incomplete reports" on Raydium devices 2386:3118 and
+        * 2386:4B33 and fixes various SIS touchscreens no longer sending
+        * data after a suspend/resume.
+        */
+       ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
+       if (ret)
+               return ret;
+
+       if (hid->driver && hid->driver->reset_resume) {
+               ret = hid->driver->reset_resume(hid);
+               return ret;
+       }
+
+       return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int i2c_hid_runtime_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+
+       i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+       disable_irq(client->irq);
+       return 0;
+}
+
+static int i2c_hid_runtime_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+
+       enable_irq(client->irq);
+       i2c_hid_set_power(client, I2C_HID_PWR_ON);
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops i2c_hid_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume)
+       SET_RUNTIME_PM_OPS(i2c_hid_runtime_suspend, i2c_hid_runtime_resume,
+                          NULL)
+};
+
+static const struct i2c_device_id i2c_hid_id_table[] = {
+       { "hid", 0 },
+       { "hid-over-i2c", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, i2c_hid_id_table);
+
+
+static struct i2c_driver i2c_hid_driver = {
+       .driver = {
+               .name   = "i2c_hid",
+               .pm     = &i2c_hid_pm,
+               .acpi_match_table = ACPI_PTR(i2c_hid_acpi_match),
+               .of_match_table = of_match_ptr(i2c_hid_of_match),
+       },
+
+       .probe          = i2c_hid_probe,
+       .remove         = i2c_hid_remove,
+       .shutdown       = i2c_hid_shutdown,
+       .id_table       = i2c_hid_id_table,
+};
+
+module_i2c_driver(i2c_hid_driver);
+
+MODULE_DESCRIPTION("HID over I2C core driver");
+MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
new file mode 100644 (file)
index 0000000..1d645c9
--- /dev/null
@@ -0,0 +1,376 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Quirks for I2C-HID devices that do not supply proper descriptors
+ *
+ * Copyright (c) 2018 Julian Sax <jsbc@gmx.de>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/dmi.h>
+#include <linux/mod_devicetable.h>
+
+#include "i2c-hid.h"
+
+
+struct i2c_hid_desc_override {
+       union {
+               struct i2c_hid_desc *i2c_hid_desc;
+               uint8_t             *i2c_hid_desc_buffer;
+       };
+       uint8_t              *hid_report_desc;
+       unsigned int          hid_report_desc_size;
+       uint8_t              *i2c_name;
+};
+
+
+/*
+ * descriptors for the SIPODEV SP1064 touchpad
+ *
+ * This device does not supply any descriptors and on windows a filter
+ * driver operates between the i2c-hid layer and the device and injects
+ * these descriptors when the device is prompted. The descriptors were
+ * extracted by listening to the i2c-hid traffic that occurs between the
+ * windows filter driver and the windows i2c-hid driver.
+ */
+
+static const struct i2c_hid_desc_override sipodev_desc = {
+       .i2c_hid_desc_buffer = (uint8_t [])
+       {0x1e, 0x00,                  /* Length of descriptor                 */
+        0x00, 0x01,                  /* Version of descriptor                */
+        0xdb, 0x01,                  /* Length of report descriptor          */
+        0x21, 0x00,                  /* Location of report descriptor        */
+        0x24, 0x00,                  /* Location of input report             */
+        0x1b, 0x00,                  /* Max input report length              */
+        0x25, 0x00,                  /* Location of output report            */
+        0x11, 0x00,                  /* Max output report length             */
+        0x22, 0x00,                  /* Location of command register         */
+        0x23, 0x00,                  /* Location of data register            */
+        0x11, 0x09,                  /* Vendor ID                            */
+        0x88, 0x52,                  /* Product ID                           */
+        0x06, 0x00,                  /* Version ID                           */
+        0x00, 0x00, 0x00, 0x00       /* Reserved                             */
+       },
+
+       .hid_report_desc = (uint8_t [])
+       {0x05, 0x01,                  /* Usage Page (Desktop),                */
+        0x09, 0x02,                  /* Usage (Mouse),                       */
+        0xA1, 0x01,                  /* Collection (Application),            */
+        0x85, 0x01,                  /*     Report ID (1),                   */
+        0x09, 0x01,                  /*     Usage (Pointer),                 */
+        0xA1, 0x00,                  /*     Collection (Physical),           */
+        0x05, 0x09,                  /*         Usage Page (Button),         */
+        0x19, 0x01,                  /*         Usage Minimum (01h),         */
+        0x29, 0x02,                  /*         Usage Maximum (02h),         */
+        0x25, 0x01,                  /*         Logical Maximum (1),         */
+        0x75, 0x01,                  /*         Report Size (1),             */
+        0x95, 0x02,                  /*         Report Count (2),            */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0x95, 0x06,                  /*         Report Count (6),            */
+        0x81, 0x01,                  /*         Input (Constant),            */
+        0x05, 0x01,                  /*         Usage Page (Desktop),        */
+        0x09, 0x30,                  /*         Usage (X),                   */
+        0x09, 0x31,                  /*         Usage (Y),                   */
+        0x15, 0x81,                  /*         Logical Minimum (-127),      */
+        0x25, 0x7F,                  /*         Logical Maximum (127),       */
+        0x75, 0x08,                  /*         Report Size (8),             */
+        0x95, 0x02,                  /*         Report Count (2),            */
+        0x81, 0x06,                  /*         Input (Variable, Relative),  */
+        0xC0,                        /*     End Collection,                  */
+        0xC0,                        /* End Collection,                      */
+        0x05, 0x0D,                  /* Usage Page (Digitizer),              */
+        0x09, 0x05,                  /* Usage (Touchpad),                    */
+        0xA1, 0x01,                  /* Collection (Application),            */
+        0x85, 0x04,                  /*     Report ID (4),                   */
+        0x05, 0x0D,                  /*     Usage Page (Digitizer),          */
+        0x09, 0x22,                  /*     Usage (Finger),                  */
+        0xA1, 0x02,                  /*     Collection (Logical),            */
+        0x15, 0x00,                  /*         Logical Minimum (0),         */
+        0x25, 0x01,                  /*         Logical Maximum (1),         */
+        0x09, 0x47,                  /*         Usage (Touch Valid),         */
+        0x09, 0x42,                  /*         Usage (Tip Switch),          */
+        0x95, 0x02,                  /*         Report Count (2),            */
+        0x75, 0x01,                  /*         Report Size (1),             */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0x95, 0x01,                  /*         Report Count (1),            */
+        0x75, 0x03,                  /*         Report Size (3),             */
+        0x25, 0x05,                  /*         Logical Maximum (5),         */
+        0x09, 0x51,                  /*         Usage (Contact Identifier),  */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0x75, 0x01,                  /*         Report Size (1),             */
+        0x95, 0x03,                  /*         Report Count (3),            */
+        0x81, 0x03,                  /*         Input (Constant, Variable),  */
+        0x05, 0x01,                  /*         Usage Page (Desktop),        */
+        0x26, 0x44, 0x0A,            /*         Logical Maximum (2628),      */
+        0x75, 0x10,                  /*         Report Size (16),            */
+        0x55, 0x0E,                  /*         Unit Exponent (14),          */
+        0x65, 0x11,                  /*         Unit (Centimeter),           */
+        0x09, 0x30,                  /*         Usage (X),                   */
+        0x46, 0x1A, 0x04,            /*         Physical Maximum (1050),     */
+        0x95, 0x01,                  /*         Report Count (1),            */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0x46, 0xBC, 0x02,            /*         Physical Maximum (700),      */
+        0x26, 0x34, 0x05,            /*         Logical Maximum (1332),      */
+        0x09, 0x31,                  /*         Usage (Y),                   */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0xC0,                        /*     End Collection,                  */
+        0x05, 0x0D,                  /*     Usage Page (Digitizer),          */
+        0x09, 0x22,                  /*     Usage (Finger),                  */
+        0xA1, 0x02,                  /*     Collection (Logical),            */
+        0x25, 0x01,                  /*         Logical Maximum (1),         */
+        0x09, 0x47,                  /*         Usage (Touch Valid),         */
+        0x09, 0x42,                  /*         Usage (Tip Switch),          */
+        0x95, 0x02,                  /*         Report Count (2),            */
+        0x75, 0x01,                  /*         Report Size (1),             */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0x95, 0x01,                  /*         Report Count (1),            */
+        0x75, 0x03,                  /*         Report Size (3),             */
+        0x25, 0x05,                  /*         Logical Maximum (5),         */
+        0x09, 0x51,                  /*         Usage (Contact Identifier),  */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0x75, 0x01,                  /*         Report Size (1),             */
+        0x95, 0x03,                  /*         Report Count (3),            */
+        0x81, 0x03,                  /*         Input (Constant, Variable),  */
+        0x05, 0x01,                  /*         Usage Page (Desktop),        */
+        0x26, 0x44, 0x0A,            /*         Logical Maximum (2628),      */
+        0x75, 0x10,                  /*         Report Size (16),            */
+        0x09, 0x30,                  /*         Usage (X),                   */
+        0x46, 0x1A, 0x04,            /*         Physical Maximum (1050),     */
+        0x95, 0x01,                  /*         Report Count (1),            */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0x46, 0xBC, 0x02,            /*         Physical Maximum (700),      */
+        0x26, 0x34, 0x05,            /*         Logical Maximum (1332),      */
+        0x09, 0x31,                  /*         Usage (Y),                   */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0xC0,                        /*     End Collection,                  */
+        0x05, 0x0D,                  /*     Usage Page (Digitizer),          */
+        0x09, 0x22,                  /*     Usage (Finger),                  */
+        0xA1, 0x02,                  /*     Collection (Logical),            */
+        0x25, 0x01,                  /*         Logical Maximum (1),         */
+        0x09, 0x47,                  /*         Usage (Touch Valid),         */
+        0x09, 0x42,                  /*         Usage (Tip Switch),          */
+        0x95, 0x02,                  /*         Report Count (2),            */
+        0x75, 0x01,                  /*         Report Size (1),             */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0x95, 0x01,                  /*         Report Count (1),            */
+        0x75, 0x03,                  /*         Report Size (3),             */
+        0x25, 0x05,                  /*         Logical Maximum (5),         */
+        0x09, 0x51,                  /*         Usage (Contact Identifier),  */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0x75, 0x01,                  /*         Report Size (1),             */
+        0x95, 0x03,                  /*         Report Count (3),            */
+        0x81, 0x03,                  /*         Input (Constant, Variable),  */
+        0x05, 0x01,                  /*         Usage Page (Desktop),        */
+        0x26, 0x44, 0x0A,            /*         Logical Maximum (2628),      */
+        0x75, 0x10,                  /*         Report Size (16),            */
+        0x09, 0x30,                  /*         Usage (X),                   */
+        0x46, 0x1A, 0x04,            /*         Physical Maximum (1050),     */
+        0x95, 0x01,                  /*         Report Count (1),            */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0x46, 0xBC, 0x02,            /*         Physical Maximum (700),      */
+        0x26, 0x34, 0x05,            /*         Logical Maximum (1332),      */
+        0x09, 0x31,                  /*         Usage (Y),                   */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0xC0,                        /*     End Collection,                  */
+        0x05, 0x0D,                  /*     Usage Page (Digitizer),          */
+        0x09, 0x22,                  /*     Usage (Finger),                  */
+        0xA1, 0x02,                  /*     Collection (Logical),            */
+        0x25, 0x01,                  /*         Logical Maximum (1),         */
+        0x09, 0x47,                  /*         Usage (Touch Valid),         */
+        0x09, 0x42,                  /*         Usage (Tip Switch),          */
+        0x95, 0x02,                  /*         Report Count (2),            */
+        0x75, 0x01,                  /*         Report Size (1),             */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0x95, 0x01,                  /*         Report Count (1),            */
+        0x75, 0x03,                  /*         Report Size (3),             */
+        0x25, 0x05,                  /*         Logical Maximum (5),         */
+        0x09, 0x51,                  /*         Usage (Contact Identifier),  */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0x75, 0x01,                  /*         Report Size (1),             */
+        0x95, 0x03,                  /*         Report Count (3),            */
+        0x81, 0x03,                  /*         Input (Constant, Variable),  */
+        0x05, 0x01,                  /*         Usage Page (Desktop),        */
+        0x26, 0x44, 0x0A,            /*         Logical Maximum (2628),      */
+        0x75, 0x10,                  /*         Report Size (16),            */
+        0x09, 0x30,                  /*         Usage (X),                   */
+        0x46, 0x1A, 0x04,            /*         Physical Maximum (1050),     */
+        0x95, 0x01,                  /*         Report Count (1),            */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0x46, 0xBC, 0x02,            /*         Physical Maximum (700),      */
+        0x26, 0x34, 0x05,            /*         Logical Maximum (1332),      */
+        0x09, 0x31,                  /*         Usage (Y),                   */
+        0x81, 0x02,                  /*         Input (Variable),            */
+        0xC0,                        /*     End Collection,                  */
+        0x05, 0x0D,                  /*     Usage Page (Digitizer),          */
+        0x55, 0x0C,                  /*     Unit Exponent (12),              */
+        0x66, 0x01, 0x10,            /*     Unit (Seconds),                  */
+        0x47, 0xFF, 0xFF, 0x00, 0x00,/*     Physical Maximum (65535),        */
+        0x27, 0xFF, 0xFF, 0x00, 0x00,/*     Logical Maximum (65535),         */
+        0x75, 0x10,                  /*     Report Size (16),                */
+        0x95, 0x01,                  /*     Report Count (1),                */
+        0x09, 0x56,                  /*     Usage (Scan Time),               */
+        0x81, 0x02,                  /*     Input (Variable),                */
+        0x09, 0x54,                  /*     Usage (Contact Count),           */
+        0x25, 0x7F,                  /*     Logical Maximum (127),           */
+        0x75, 0x08,                  /*     Report Size (8),                 */
+        0x81, 0x02,                  /*     Input (Variable),                */
+        0x05, 0x09,                  /*     Usage Page (Button),             */
+        0x09, 0x01,                  /*     Usage (01h),                     */
+        0x25, 0x01,                  /*     Logical Maximum (1),             */
+        0x75, 0x01,                  /*     Report Size (1),                 */
+        0x95, 0x01,                  /*     Report Count (1),                */
+        0x81, 0x02,                  /*     Input (Variable),                */
+        0x95, 0x07,                  /*     Report Count (7),                */
+        0x81, 0x03,                  /*     Input (Constant, Variable),      */
+        0x05, 0x0D,                  /*     Usage Page (Digitizer),          */
+        0x85, 0x02,                  /*     Report ID (2),                   */
+        0x09, 0x55,                  /*     Usage (Contact Count Maximum),   */
+        0x09, 0x59,                  /*     Usage (59h),                     */
+        0x75, 0x04,                  /*     Report Size (4),                 */
+        0x95, 0x02,                  /*     Report Count (2),                */
+        0x25, 0x0F,                  /*     Logical Maximum (15),            */
+        0xB1, 0x02,                  /*     Feature (Variable),              */
+        0x05, 0x0D,                  /*     Usage Page (Digitizer),          */
+        0x85, 0x07,                  /*     Report ID (7),                   */
+        0x09, 0x60,                  /*     Usage (60h),                     */
+        0x75, 0x01,                  /*     Report Size (1),                 */
+        0x95, 0x01,                  /*     Report Count (1),                */
+        0x25, 0x01,                  /*     Logical Maximum (1),             */
+        0xB1, 0x02,                  /*     Feature (Variable),              */
+        0x95, 0x07,                  /*     Report Count (7),                */
+        0xB1, 0x03,                  /*     Feature (Constant, Variable),    */
+        0x85, 0x06,                  /*     Report ID (6),                   */
+        0x06, 0x00, 0xFF,            /*     Usage Page (FF00h),              */
+        0x09, 0xC5,                  /*     Usage (C5h),                     */
+        0x26, 0xFF, 0x00,            /*     Logical Maximum (255),           */
+        0x75, 0x08,                  /*     Report Size (8),                 */
+        0x96, 0x00, 0x01,            /*     Report Count (256),              */
+        0xB1, 0x02,                  /*     Feature (Variable),              */
+        0xC0,                        /* End Collection,                      */
+        0x06, 0x00, 0xFF,            /* Usage Page (FF00h),                  */
+        0x09, 0x01,                  /* Usage (01h),                         */
+        0xA1, 0x01,                  /* Collection (Application),            */
+        0x85, 0x0D,                  /*     Report ID (13),                  */
+        0x26, 0xFF, 0x00,            /*     Logical Maximum (255),           */
+        0x19, 0x01,                  /*     Usage Minimum (01h),             */
+        0x29, 0x02,                  /*     Usage Maximum (02h),             */
+        0x75, 0x08,                  /*     Report Size (8),                 */
+        0x95, 0x02,                  /*     Report Count (2),                */
+        0xB1, 0x02,                  /*     Feature (Variable),              */
+        0xC0,                        /* End Collection,                      */
+        0x05, 0x0D,                  /* Usage Page (Digitizer),              */
+        0x09, 0x0E,                  /* Usage (Configuration),               */
+        0xA1, 0x01,                  /* Collection (Application),            */
+        0x85, 0x03,                  /*     Report ID (3),                   */
+        0x09, 0x22,                  /*     Usage (Finger),                  */
+        0xA1, 0x02,                  /*     Collection (Logical),            */
+        0x09, 0x52,                  /*         Usage (Device Mode),         */
+        0x25, 0x0A,                  /*         Logical Maximum (10),        */
+        0x95, 0x01,                  /*         Report Count (1),            */
+        0xB1, 0x02,                  /*         Feature (Variable),          */
+        0xC0,                        /*     End Collection,                  */
+        0x09, 0x22,                  /*     Usage (Finger),                  */
+        0xA1, 0x00,                  /*     Collection (Physical),           */
+        0x85, 0x05,                  /*         Report ID (5),               */
+        0x09, 0x57,                  /*         Usage (57h),                 */
+        0x09, 0x58,                  /*         Usage (58h),                 */
+        0x75, 0x01,                  /*         Report Size (1),             */
+        0x95, 0x02,                  /*         Report Count (2),            */
+        0x25, 0x01,                  /*         Logical Maximum (1),         */
+        0xB1, 0x02,                  /*         Feature (Variable),          */
+        0x95, 0x06,                  /*         Report Count (6),            */
+        0xB1, 0x03,                  /*         Feature (Constant, Variable),*/
+        0xC0,                        /*     End Collection,                  */
+        0xC0                         /* End Collection                       */
+       },
+       .hid_report_desc_size = 475,
+       .i2c_name = "SYNA3602:00"
+};
+
+
+static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {
+       {
+               .ident = "Teclast F6 Pro",
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TECLAST"),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "F6 Pro"),
+               },
+               .driver_data = (void *)&sipodev_desc
+       },
+       {
+               .ident = "Teclast F7",
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TECLAST"),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "F7"),
+               },
+               .driver_data = (void *)&sipodev_desc
+       },
+       {
+               .ident = "Trekstor Primebook C13",
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Primebook C13"),
+               },
+               .driver_data = (void *)&sipodev_desc
+       },
+       {
+               .ident = "Trekstor Primebook C11",
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Primebook C11"),
+               },
+               .driver_data = (void *)&sipodev_desc
+       },
+       {
+               .ident = "Direkt-Tek DTLAPY116-2",
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Direkt-Tek"),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "DTLAPY116-2"),
+               },
+               .driver_data = (void *)&sipodev_desc
+       },
+       {
+               .ident = "Mediacom Flexbook Edge 11",
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MEDIACOM"),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "FlexBook edge11 - M-FBE11"),
+               },
+               .driver_data = (void *)&sipodev_desc
+       }
+};
+
+
+struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name)
+{
+       struct i2c_hid_desc_override *override;
+       const struct dmi_system_id *system_id;
+
+       system_id = dmi_first_match(i2c_hid_dmi_desc_override_table);
+       if (!system_id)
+               return NULL;
+
+       override = system_id->driver_data;
+       if (strcmp(override->i2c_name, i2c_name))
+               return NULL;
+
+       return override->i2c_hid_desc;
+}
+
+char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
+                                              unsigned int *size)
+{
+       struct i2c_hid_desc_override *override;
+       const struct dmi_system_id *system_id;
+
+       system_id = dmi_first_match(i2c_hid_dmi_desc_override_table);
+       if (!system_id)
+               return NULL;
+
+       override = system_id->driver_data;
+       if (strcmp(override->i2c_name, i2c_name))
+               return NULL;
+
+       *size = override->hid_report_desc_size;
+       return override->hid_report_desc;
+}
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
deleted file mode 100644 (file)
index 4e3592e..0000000
+++ /dev/null
@@ -1,1309 +0,0 @@
-/*
- * HID over I2C protocol implementation
- *
- * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
- * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
- * Copyright (c) 2012 Red Hat, Inc
- *
- * This code is partly based on "USB HID support for Linux":
- *
- *  Copyright (c) 1999 Andreas Gal
- *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
- *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- *  Copyright (c) 2007-2008 Oliver Neukum
- *  Copyright (c) 2006-2010 Jiri Kosina
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file COPYING in the main directory of this archive for
- * more details.
- */
-
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/input.h>
-#include <linux/irq.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/pm.h>
-#include <linux/pm_runtime.h>
-#include <linux/device.h>
-#include <linux/wait.h>
-#include <linux/err.h>
-#include <linux/string.h>
-#include <linux/list.h>
-#include <linux/jiffies.h>
-#include <linux/kernel.h>
-#include <linux/hid.h>
-#include <linux/mutex.h>
-#include <linux/acpi.h>
-#include <linux/of.h>
-#include <linux/regulator/consumer.h>
-
-#include <linux/platform_data/i2c-hid.h>
-
-#include "../hid-ids.h"
-
-/* quirks to control the device */
-#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV       BIT(0)
-#define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET       BIT(1)
-#define I2C_HID_QUIRK_NO_RUNTIME_PM            BIT(2)
-
-/* flags */
-#define I2C_HID_STARTED                0
-#define I2C_HID_RESET_PENDING  1
-#define I2C_HID_READ_PENDING   2
-
-#define I2C_HID_PWR_ON         0x00
-#define I2C_HID_PWR_SLEEP      0x01
-
-/* debug option */
-static bool debug;
-module_param(debug, bool, 0444);
-MODULE_PARM_DESC(debug, "print a lot of debug information");
-
-#define i2c_hid_dbg(ihid, fmt, arg...)                                   \
-do {                                                                     \
-       if (debug)                                                        \
-               dev_printk(KERN_DEBUG, &(ihid)->client->dev, fmt, ##arg); \
-} while (0)
-
-struct i2c_hid_desc {
-       __le16 wHIDDescLength;
-       __le16 bcdVersion;
-       __le16 wReportDescLength;
-       __le16 wReportDescRegister;
-       __le16 wInputRegister;
-       __le16 wMaxInputLength;
-       __le16 wOutputRegister;
-       __le16 wMaxOutputLength;
-       __le16 wCommandRegister;
-       __le16 wDataRegister;
-       __le16 wVendorID;
-       __le16 wProductID;
-       __le16 wVersionID;
-       __le32 reserved;
-} __packed;
-
-struct i2c_hid_cmd {
-       unsigned int registerIndex;
-       __u8 opcode;
-       unsigned int length;
-       bool wait;
-};
-
-union command {
-       u8 data[0];
-       struct cmd {
-               __le16 reg;
-               __u8 reportTypeID;
-               __u8 opcode;
-       } __packed c;
-};
-
-#define I2C_HID_CMD(opcode_) \
-       .opcode = opcode_, .length = 4, \
-       .registerIndex = offsetof(struct i2c_hid_desc, wCommandRegister)
-
-/* fetch HID descriptor */
-static const struct i2c_hid_cmd hid_descr_cmd = { .length = 2 };
-/* fetch report descriptors */
-static const struct i2c_hid_cmd hid_report_descr_cmd = {
-               .registerIndex = offsetof(struct i2c_hid_desc,
-                       wReportDescRegister),
-               .opcode = 0x00,
-               .length = 2 };
-/* commands */
-static const struct i2c_hid_cmd hid_reset_cmd =                { I2C_HID_CMD(0x01),
-                                                         .wait = true };
-static const struct i2c_hid_cmd hid_get_report_cmd =   { I2C_HID_CMD(0x02) };
-static const struct i2c_hid_cmd hid_set_report_cmd =   { I2C_HID_CMD(0x03) };
-static const struct i2c_hid_cmd hid_set_power_cmd =    { I2C_HID_CMD(0x08) };
-static const struct i2c_hid_cmd hid_no_cmd =           { .length = 0 };
-
-/*
- * These definitions are not used here, but are defined by the spec.
- * Keeping them here for documentation purposes.
- *
- * static const struct i2c_hid_cmd hid_get_idle_cmd = { I2C_HID_CMD(0x04) };
- * static const struct i2c_hid_cmd hid_set_idle_cmd = { I2C_HID_CMD(0x05) };
- * static const struct i2c_hid_cmd hid_get_protocol_cmd = { I2C_HID_CMD(0x06) };
- * static const struct i2c_hid_cmd hid_set_protocol_cmd = { I2C_HID_CMD(0x07) };
- */
-
-/* The main device structure */
-struct i2c_hid {
-       struct i2c_client       *client;        /* i2c client */
-       struct hid_device       *hid;   /* pointer to corresponding HID dev */
-       union {
-               __u8 hdesc_buffer[sizeof(struct i2c_hid_desc)];
-               struct i2c_hid_desc hdesc;      /* the HID Descriptor */
-       };
-       __le16                  wHIDDescRegister; /* location of the i2c
-                                                  * register of the HID
-                                                  * descriptor. */
-       unsigned int            bufsize;        /* i2c buffer size */
-       u8                      *inbuf;         /* Input buffer */
-       u8                      *rawbuf;        /* Raw Input buffer */
-       u8                      *cmdbuf;        /* Command buffer */
-       u8                      *argsbuf;       /* Command arguments buffer */
-
-       unsigned long           flags;          /* device flags */
-       unsigned long           quirks;         /* Various quirks */
-
-       wait_queue_head_t       wait;           /* For waiting the interrupt */
-
-       struct i2c_hid_platform_data pdata;
-
-       bool                    irq_wake_enabled;
-       struct mutex            reset_lock;
-};
-
-static const struct i2c_hid_quirks {
-       __u16 idVendor;
-       __u16 idProduct;
-       __u32 quirks;
-} i2c_hid_quirks[] = {
-       { USB_VENDOR_ID_WEIDA, USB_DEVICE_ID_WEIDA_8752,
-               I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
-       { USB_VENDOR_ID_WEIDA, USB_DEVICE_ID_WEIDA_8755,
-               I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
-       { I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288,
-               I2C_HID_QUIRK_NO_IRQ_AFTER_RESET |
-               I2C_HID_QUIRK_NO_RUNTIME_PM },
-       { 0, 0 }
-};
-
-/*
- * i2c_hid_lookup_quirk: return any quirks associated with a I2C HID device
- * @idVendor: the 16-bit vendor ID
- * @idProduct: the 16-bit product ID
- *
- * Returns: a u32 quirks value.
- */
-static u32 i2c_hid_lookup_quirk(const u16 idVendor, const u16 idProduct)
-{
-       u32 quirks = 0;
-       int n;
-
-       for (n = 0; i2c_hid_quirks[n].idVendor; n++)
-               if (i2c_hid_quirks[n].idVendor == idVendor &&
-                   (i2c_hid_quirks[n].idProduct == (__u16)HID_ANY_ID ||
-                    i2c_hid_quirks[n].idProduct == idProduct))
-                       quirks = i2c_hid_quirks[n].quirks;
-
-       return quirks;
-}
-
-static int __i2c_hid_command(struct i2c_client *client,
-               const struct i2c_hid_cmd *command, u8 reportID,
-               u8 reportType, u8 *args, int args_len,
-               unsigned char *buf_recv, int data_len)
-{
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-       union command *cmd = (union command *)ihid->cmdbuf;
-       int ret;
-       struct i2c_msg msg[2];
-       int msg_num = 1;
-
-       int length = command->length;
-       bool wait = command->wait;
-       unsigned int registerIndex = command->registerIndex;
-
-       /* special case for hid_descr_cmd */
-       if (command == &hid_descr_cmd) {
-               cmd->c.reg = ihid->wHIDDescRegister;
-       } else {
-               cmd->data[0] = ihid->hdesc_buffer[registerIndex];
-               cmd->data[1] = ihid->hdesc_buffer[registerIndex + 1];
-       }
-
-       if (length > 2) {
-               cmd->c.opcode = command->opcode;
-               cmd->c.reportTypeID = reportID | reportType << 4;
-       }
-
-       memcpy(cmd->data + length, args, args_len);
-       length += args_len;
-
-       i2c_hid_dbg(ihid, "%s: cmd=%*ph\n", __func__, length, cmd->data);
-
-       msg[0].addr = client->addr;
-       msg[0].flags = client->flags & I2C_M_TEN;
-       msg[0].len = length;
-       msg[0].buf = cmd->data;
-       if (data_len > 0) {
-               msg[1].addr = client->addr;
-               msg[1].flags = client->flags & I2C_M_TEN;
-               msg[1].flags |= I2C_M_RD;
-               msg[1].len = data_len;
-               msg[1].buf = buf_recv;
-               msg_num = 2;
-               set_bit(I2C_HID_READ_PENDING, &ihid->flags);
-       }
-
-       if (wait)
-               set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
-
-       ret = i2c_transfer(client->adapter, msg, msg_num);
-
-       if (data_len > 0)
-               clear_bit(I2C_HID_READ_PENDING, &ihid->flags);
-
-       if (ret != msg_num)
-               return ret < 0 ? ret : -EIO;
-
-       ret = 0;
-
-       if (wait && (ihid->quirks & I2C_HID_QUIRK_NO_IRQ_AFTER_RESET)) {
-               msleep(100);
-       } else if (wait) {
-               i2c_hid_dbg(ihid, "%s: waiting...\n", __func__);
-               if (!wait_event_timeout(ihid->wait,
-                               !test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
-                               msecs_to_jiffies(5000)))
-                       ret = -ENODATA;
-               i2c_hid_dbg(ihid, "%s: finished.\n", __func__);
-       }
-
-       return ret;
-}
-
-static int i2c_hid_command(struct i2c_client *client,
-               const struct i2c_hid_cmd *command,
-               unsigned char *buf_recv, int data_len)
-{
-       return __i2c_hid_command(client, command, 0, 0, NULL, 0,
-                               buf_recv, data_len);
-}
-
-static int i2c_hid_get_report(struct i2c_client *client, u8 reportType,
-               u8 reportID, unsigned char *buf_recv, int data_len)
-{
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-       u8 args[3];
-       int ret;
-       int args_len = 0;
-       u16 readRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
-
-       i2c_hid_dbg(ihid, "%s\n", __func__);
-
-       if (reportID >= 0x0F) {
-               args[args_len++] = reportID;
-               reportID = 0x0F;
-       }
-
-       args[args_len++] = readRegister & 0xFF;
-       args[args_len++] = readRegister >> 8;
-
-       ret = __i2c_hid_command(client, &hid_get_report_cmd, reportID,
-               reportType, args, args_len, buf_recv, data_len);
-       if (ret) {
-               dev_err(&client->dev,
-                       "failed to retrieve report from device.\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-/**
- * i2c_hid_set_or_send_report: forward an incoming report to the device
- * @client: the i2c_client of the device
- * @reportType: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT
- * @reportID: the report ID
- * @buf: the actual data to transfer, without the report ID
- * @len: size of buf
- * @use_data: true: use SET_REPORT HID command, false: send plain OUTPUT report
- */
-static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType,
-               u8 reportID, unsigned char *buf, size_t data_len, bool use_data)
-{
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-       u8 *args = ihid->argsbuf;
-       const struct i2c_hid_cmd *hidcmd;
-       int ret;
-       u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
-       u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister);
-       u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength);
-       u16 size;
-       int args_len;
-       int index = 0;
-
-       i2c_hid_dbg(ihid, "%s\n", __func__);
-
-       if (data_len > ihid->bufsize)
-               return -EINVAL;
-
-       size =          2                       /* size */ +
-                       (reportID ? 1 : 0)      /* reportID */ +
-                       data_len                /* buf */;
-       args_len =      (reportID >= 0x0F ? 1 : 0) /* optional third byte */ +
-                       2                       /* dataRegister */ +
-                       size                    /* args */;
-
-       if (!use_data && maxOutputLength == 0)
-               return -ENOSYS;
-
-       if (reportID >= 0x0F) {
-               args[index++] = reportID;
-               reportID = 0x0F;
-       }
-
-       /*
-        * use the data register for feature reports or if the device does not
-        * support the output register
-        */
-       if (use_data) {
-               args[index++] = dataRegister & 0xFF;
-               args[index++] = dataRegister >> 8;
-               hidcmd = &hid_set_report_cmd;
-       } else {
-               args[index++] = outputRegister & 0xFF;
-               args[index++] = outputRegister >> 8;
-               hidcmd = &hid_no_cmd;
-       }
-
-       args[index++] = size & 0xFF;
-       args[index++] = size >> 8;
-
-       if (reportID)
-               args[index++] = reportID;
-
-       memcpy(&args[index], buf, data_len);
-
-       ret = __i2c_hid_command(client, hidcmd, reportID,
-               reportType, args, args_len, NULL, 0);
-       if (ret) {
-               dev_err(&client->dev, "failed to set a report to device.\n");
-               return ret;
-       }
-
-       return data_len;
-}
-
-static int i2c_hid_set_power(struct i2c_client *client, int power_state)
-{
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-       int ret;
-
-       i2c_hid_dbg(ihid, "%s\n", __func__);
-
-       /*
-        * Some devices require to send a command to wakeup before power on.
-        * The call will get a return value (EREMOTEIO) but device will be
-        * triggered and activated. After that, it goes like a normal device.
-        */
-       if (power_state == I2C_HID_PWR_ON &&
-           ihid->quirks & I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV) {
-               ret = i2c_hid_command(client, &hid_set_power_cmd, NULL, 0);
-
-               /* Device was already activated */
-               if (!ret)
-                       goto set_pwr_exit;
-       }
-
-       ret = __i2c_hid_command(client, &hid_set_power_cmd, power_state,
-               0, NULL, 0, NULL, 0);
-
-       if (ret)
-               dev_err(&client->dev, "failed to change power setting.\n");
-
-set_pwr_exit:
-       return ret;
-}
-
-static int i2c_hid_hwreset(struct i2c_client *client)
-{
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-       int ret;
-
-       i2c_hid_dbg(ihid, "%s\n", __func__);
-
-       /*
-        * This prevents sending feature reports while the device is
-        * being reset. Otherwise we may lose the reset complete
-        * interrupt.
-        */
-       mutex_lock(&ihid->reset_lock);
-
-       ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
-       if (ret)
-               goto out_unlock;
-
-       /*
-        * The HID over I2C specification states that if a DEVICE needs time
-        * after the PWR_ON request, it should utilise CLOCK stretching.
-        * However, it has been observered that the Windows driver provides a
-        * 1ms sleep between the PWR_ON and RESET requests and that some devices
-        * rely on this.
-        */
-       usleep_range(1000, 5000);
-
-       i2c_hid_dbg(ihid, "resetting...\n");
-
-       ret = i2c_hid_command(client, &hid_reset_cmd, NULL, 0);
-       if (ret) {
-               dev_err(&client->dev, "failed to reset device.\n");
-               i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
-       }
-
-out_unlock:
-       mutex_unlock(&ihid->reset_lock);
-       return ret;
-}
-
-static void i2c_hid_get_input(struct i2c_hid *ihid)
-{
-       int ret;
-       u32 ret_size;
-       int size = le16_to_cpu(ihid->hdesc.wMaxInputLength);
-
-       if (size > ihid->bufsize)
-               size = ihid->bufsize;
-
-       ret = i2c_master_recv(ihid->client, ihid->inbuf, size);
-       if (ret != size) {
-               if (ret < 0)
-                       return;
-
-               dev_err(&ihid->client->dev, "%s: got %d data instead of %d\n",
-                       __func__, ret, size);
-               return;
-       }
-
-       ret_size = ihid->inbuf[0] | ihid->inbuf[1] << 8;
-
-       if (!ret_size) {
-               /* host or device initiated RESET completed */
-               if (test_and_clear_bit(I2C_HID_RESET_PENDING, &ihid->flags))
-                       wake_up(&ihid->wait);
-               return;
-       }
-
-       if ((ret_size > size) || (ret_size < 2)) {
-               dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n",
-                       __func__, size, ret_size);
-               return;
-       }
-
-       i2c_hid_dbg(ihid, "input: %*ph\n", ret_size, ihid->inbuf);
-
-       if (test_bit(I2C_HID_STARTED, &ihid->flags))
-               hid_input_report(ihid->hid, HID_INPUT_REPORT, ihid->inbuf + 2,
-                               ret_size - 2, 1);
-
-       return;
-}
-
-static irqreturn_t i2c_hid_irq(int irq, void *dev_id)
-{
-       struct i2c_hid *ihid = dev_id;
-
-       if (test_bit(I2C_HID_READ_PENDING, &ihid->flags))
-               return IRQ_HANDLED;
-
-       i2c_hid_get_input(ihid);
-
-       return IRQ_HANDLED;
-}
-
-static int i2c_hid_get_report_length(struct hid_report *report)
-{
-       return ((report->size - 1) >> 3) + 1 +
-               report->device->report_enum[report->type].numbered + 2;
-}
-
-/*
- * Traverse the supplied list of reports and find the longest
- */
-static void i2c_hid_find_max_report(struct hid_device *hid, unsigned int type,
-               unsigned int *max)
-{
-       struct hid_report *report;
-       unsigned int size;
-
-       /* We should not rely on wMaxInputLength, as some devices may set it to
-        * a wrong length. */
-       list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
-               size = i2c_hid_get_report_length(report);
-               if (*max < size)
-                       *max = size;
-       }
-}
-
-static void i2c_hid_free_buffers(struct i2c_hid *ihid)
-{
-       kfree(ihid->inbuf);
-       kfree(ihid->rawbuf);
-       kfree(ihid->argsbuf);
-       kfree(ihid->cmdbuf);
-       ihid->inbuf = NULL;
-       ihid->rawbuf = NULL;
-       ihid->cmdbuf = NULL;
-       ihid->argsbuf = NULL;
-       ihid->bufsize = 0;
-}
-
-static int i2c_hid_alloc_buffers(struct i2c_hid *ihid, size_t report_size)
-{
-       /* the worst case is computed from the set_report command with a
-        * reportID > 15 and the maximum report length */
-       int args_len = sizeof(__u8) + /* ReportID */
-                      sizeof(__u8) + /* optional ReportID byte */
-                      sizeof(__u16) + /* data register */
-                      sizeof(__u16) + /* size of the report */
-                      report_size; /* report */
-
-       ihid->inbuf = kzalloc(report_size, GFP_KERNEL);
-       ihid->rawbuf = kzalloc(report_size, GFP_KERNEL);
-       ihid->argsbuf = kzalloc(args_len, GFP_KERNEL);
-       ihid->cmdbuf = kzalloc(sizeof(union command) + args_len, GFP_KERNEL);
-
-       if (!ihid->inbuf || !ihid->rawbuf || !ihid->argsbuf || !ihid->cmdbuf) {
-               i2c_hid_free_buffers(ihid);
-               return -ENOMEM;
-       }
-
-       ihid->bufsize = report_size;
-
-       return 0;
-}
-
-static int i2c_hid_get_raw_report(struct hid_device *hid,
-               unsigned char report_number, __u8 *buf, size_t count,
-               unsigned char report_type)
-{
-       struct i2c_client *client = hid->driver_data;
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-       size_t ret_count, ask_count;
-       int ret;
-
-       if (report_type == HID_OUTPUT_REPORT)
-               return -EINVAL;
-
-       /* +2 bytes to include the size of the reply in the query buffer */
-       ask_count = min(count + 2, (size_t)ihid->bufsize);
-
-       ret = i2c_hid_get_report(client,
-                       report_type == HID_FEATURE_REPORT ? 0x03 : 0x01,
-                       report_number, ihid->rawbuf, ask_count);
-
-       if (ret < 0)
-               return ret;
-
-       ret_count = ihid->rawbuf[0] | (ihid->rawbuf[1] << 8);
-
-       if (ret_count <= 2)
-               return 0;
-
-       ret_count = min(ret_count, ask_count);
-
-       /* The query buffer contains the size, dropping it in the reply */
-       count = min(count, ret_count - 2);
-       memcpy(buf, ihid->rawbuf + 2, count);
-
-       return count;
-}
-
-static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
-               size_t count, unsigned char report_type, bool use_data)
-{
-       struct i2c_client *client = hid->driver_data;
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-       int report_id = buf[0];
-       int ret;
-
-       if (report_type == HID_INPUT_REPORT)
-               return -EINVAL;
-
-       mutex_lock(&ihid->reset_lock);
-
-       if (report_id) {
-               buf++;
-               count--;
-       }
-
-       ret = i2c_hid_set_or_send_report(client,
-                               report_type == HID_FEATURE_REPORT ? 0x03 : 0x02,
-                               report_id, buf, count, use_data);
-
-       if (report_id && ret >= 0)
-               ret++; /* add report_id to the number of transfered bytes */
-
-       mutex_unlock(&ihid->reset_lock);
-
-       return ret;
-}
-
-static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf,
-               size_t count)
-{
-       return i2c_hid_output_raw_report(hid, buf, count, HID_OUTPUT_REPORT,
-                       false);
-}
-
-static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
-                              __u8 *buf, size_t len, unsigned char rtype,
-                              int reqtype)
-{
-       switch (reqtype) {
-       case HID_REQ_GET_REPORT:
-               return i2c_hid_get_raw_report(hid, reportnum, buf, len, rtype);
-       case HID_REQ_SET_REPORT:
-               if (buf[0] != reportnum)
-                       return -EINVAL;
-               return i2c_hid_output_raw_report(hid, buf, len, rtype, true);
-       default:
-               return -EIO;
-       }
-}
-
-static int i2c_hid_parse(struct hid_device *hid)
-{
-       struct i2c_client *client = hid->driver_data;
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-       struct i2c_hid_desc *hdesc = &ihid->hdesc;
-       unsigned int rsize;
-       char *rdesc;
-       int ret;
-       int tries = 3;
-
-       i2c_hid_dbg(ihid, "entering %s\n", __func__);
-
-       rsize = le16_to_cpu(hdesc->wReportDescLength);
-       if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
-               dbg_hid("weird size of report descriptor (%u)\n", rsize);
-               return -EINVAL;
-       }
-
-       do {
-               ret = i2c_hid_hwreset(client);
-               if (ret)
-                       msleep(1000);
-       } while (tries-- > 0 && ret);
-
-       if (ret)
-               return ret;
-
-       rdesc = kzalloc(rsize, GFP_KERNEL);
-
-       if (!rdesc) {
-               dbg_hid("couldn't allocate rdesc memory\n");
-               return -ENOMEM;
-       }
-
-       i2c_hid_dbg(ihid, "asking HID report descriptor\n");
-
-       ret = i2c_hid_command(client, &hid_report_descr_cmd, rdesc, rsize);
-       if (ret) {
-               hid_err(hid, "reading report descriptor failed\n");
-               kfree(rdesc);
-               return -EIO;
-       }
-
-       i2c_hid_dbg(ihid, "Report Descriptor: %*ph\n", rsize, rdesc);
-
-       ret = hid_parse_report(hid, rdesc, rsize);
-       kfree(rdesc);
-       if (ret) {
-               dbg_hid("parsing report descriptor failed\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static int i2c_hid_start(struct hid_device *hid)
-{
-       struct i2c_client *client = hid->driver_data;
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-       int ret;
-       unsigned int bufsize = HID_MIN_BUFFER_SIZE;
-
-       i2c_hid_find_max_report(hid, HID_INPUT_REPORT, &bufsize);
-       i2c_hid_find_max_report(hid, HID_OUTPUT_REPORT, &bufsize);
-       i2c_hid_find_max_report(hid, HID_FEATURE_REPORT, &bufsize);
-
-       if (bufsize > ihid->bufsize) {
-               disable_irq(client->irq);
-               i2c_hid_free_buffers(ihid);
-
-               ret = i2c_hid_alloc_buffers(ihid, bufsize);
-               enable_irq(client->irq);
-
-               if (ret)
-                       return ret;
-       }
-
-       return 0;
-}
-
-static void i2c_hid_stop(struct hid_device *hid)
-{
-       hid->claimed = 0;
-}
-
-static int i2c_hid_open(struct hid_device *hid)
-{
-       struct i2c_client *client = hid->driver_data;
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-       int ret = 0;
-
-       ret = pm_runtime_get_sync(&client->dev);
-       if (ret < 0)
-               return ret;
-
-       set_bit(I2C_HID_STARTED, &ihid->flags);
-       return 0;
-}
-
-static void i2c_hid_close(struct hid_device *hid)
-{
-       struct i2c_client *client = hid->driver_data;
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-
-       clear_bit(I2C_HID_STARTED, &ihid->flags);
-
-       /* Save some power */
-       pm_runtime_put(&client->dev);
-}
-
-static int i2c_hid_power(struct hid_device *hid, int lvl)
-{
-       struct i2c_client *client = hid->driver_data;
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-
-       i2c_hid_dbg(ihid, "%s lvl:%d\n", __func__, lvl);
-
-       switch (lvl) {
-       case PM_HINT_FULLON:
-               pm_runtime_get_sync(&client->dev);
-               break;
-       case PM_HINT_NORMAL:
-               pm_runtime_put(&client->dev);
-               break;
-       }
-       return 0;
-}
-
-struct hid_ll_driver i2c_hid_ll_driver = {
-       .parse = i2c_hid_parse,
-       .start = i2c_hid_start,
-       .stop = i2c_hid_stop,
-       .open = i2c_hid_open,
-       .close = i2c_hid_close,
-       .power = i2c_hid_power,
-       .output_report = i2c_hid_output_report,
-       .raw_request = i2c_hid_raw_request,
-};
-EXPORT_SYMBOL_GPL(i2c_hid_ll_driver);
-
-static int i2c_hid_init_irq(struct i2c_client *client)
-{
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-       unsigned long irqflags = 0;
-       int ret;
-
-       dev_dbg(&client->dev, "Requesting IRQ: %d\n", client->irq);
-
-       if (!irq_get_trigger_type(client->irq))
-               irqflags = IRQF_TRIGGER_LOW;
-
-       ret = request_threaded_irq(client->irq, NULL, i2c_hid_irq,
-                                  irqflags | IRQF_ONESHOT, client->name, ihid);
-       if (ret < 0) {
-               dev_warn(&client->dev,
-                       "Could not register for %s interrupt, irq = %d,"
-                       " ret = %d\n",
-                       client->name, client->irq, ret);
-
-               return ret;
-       }
-
-       return 0;
-}
-
-static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
-{
-       struct i2c_client *client = ihid->client;
-       struct i2c_hid_desc *hdesc = &ihid->hdesc;
-       unsigned int dsize;
-       int ret;
-
-       /* i2c hid fetch using a fixed descriptor size (30 bytes) */
-       i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
-       ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer,
-                               sizeof(struct i2c_hid_desc));
-       if (ret) {
-               dev_err(&client->dev, "hid_descr_cmd failed\n");
-               return -ENODEV;
-       }
-
-       /* Validate the length of HID descriptor, the 4 first bytes:
-        * bytes 0-1 -> length
-        * bytes 2-3 -> bcdVersion (has to be 1.00) */
-       /* check bcdVersion == 1.0 */
-       if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {
-               dev_err(&client->dev,
-                       "unexpected HID descriptor bcdVersion (0x%04hx)\n",
-                       le16_to_cpu(hdesc->bcdVersion));
-               return -ENODEV;
-       }
-
-       /* Descriptor length should be 30 bytes as per the specification */
-       dsize = le16_to_cpu(hdesc->wHIDDescLength);
-       if (dsize != sizeof(struct i2c_hid_desc)) {
-               dev_err(&client->dev, "weird size of HID descriptor (%u)\n",
-                       dsize);
-               return -ENODEV;
-       }
-       i2c_hid_dbg(ihid, "HID Descriptor: %*ph\n", dsize, ihid->hdesc_buffer);
-       return 0;
-}
-
-#ifdef CONFIG_ACPI
-static const struct acpi_device_id i2c_hid_acpi_blacklist[] = {
-       /*
-        * The CHPN0001 ACPI device, which is used to describe the Chipone
-        * ICN8505 controller, has a _CID of PNP0C50 but is not HID compatible.
-        */
-       {"CHPN0001", 0 },
-       { },
-};
-
-static int i2c_hid_acpi_pdata(struct i2c_client *client,
-               struct i2c_hid_platform_data *pdata)
-{
-       static guid_t i2c_hid_guid =
-               GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
-                         0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
-       union acpi_object *obj;
-       struct acpi_device *adev;
-       acpi_handle handle;
-
-       handle = ACPI_HANDLE(&client->dev);
-       if (!handle || acpi_bus_get_device(handle, &adev)) {
-               dev_err(&client->dev, "Error could not get ACPI device\n");
-               return -ENODEV;
-       }
-
-       if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0)
-               return -ENODEV;
-
-       obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, 1, 1, NULL,
-                                     ACPI_TYPE_INTEGER);
-       if (!obj) {
-               dev_err(&client->dev, "Error _DSM call to get HID descriptor address failed\n");
-               return -ENODEV;
-       }
-
-       pdata->hid_descriptor_address = obj->integer.value;
-       ACPI_FREE(obj);
-
-       return 0;
-}
-
-static void i2c_hid_acpi_fix_up_power(struct device *dev)
-{
-       struct acpi_device *adev;
-
-       adev = ACPI_COMPANION(dev);
-       if (adev)
-               acpi_device_fix_up_power(adev);
-}
-
-static const struct acpi_device_id i2c_hid_acpi_match[] = {
-       {"ACPI0C50", 0 },
-       {"PNP0C50", 0 },
-       { },
-};
-MODULE_DEVICE_TABLE(acpi, i2c_hid_acpi_match);
-#else
-static inline int i2c_hid_acpi_pdata(struct i2c_client *client,
-               struct i2c_hid_platform_data *pdata)
-{
-       return -ENODEV;
-}
-
-static inline void i2c_hid_acpi_fix_up_power(struct device *dev) {}
-#endif
-
-#ifdef CONFIG_OF
-static int i2c_hid_of_probe(struct i2c_client *client,
-               struct i2c_hid_platform_data *pdata)
-{
-       struct device *dev = &client->dev;
-       u32 val;
-       int ret;
-
-       ret = of_property_read_u32(dev->of_node, "hid-descr-addr", &val);
-       if (ret) {
-               dev_err(&client->dev, "HID register address not provided\n");
-               return -ENODEV;
-       }
-       if (val >> 16) {
-               dev_err(&client->dev, "Bad HID register address: 0x%08x\n",
-                       val);
-               return -EINVAL;
-       }
-       pdata->hid_descriptor_address = val;
-
-       return 0;
-}
-
-static const struct of_device_id i2c_hid_of_match[] = {
-       { .compatible = "hid-over-i2c" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, i2c_hid_of_match);
-#else
-static inline int i2c_hid_of_probe(struct i2c_client *client,
-               struct i2c_hid_platform_data *pdata)
-{
-       return -ENODEV;
-}
-#endif
-
-static void i2c_hid_fwnode_probe(struct i2c_client *client,
-                                struct i2c_hid_platform_data *pdata)
-{
-       u32 val;
-
-       if (!device_property_read_u32(&client->dev, "post-power-on-delay-ms",
-                                     &val))
-               pdata->post_power_delay_ms = val;
-}
-
-static int i2c_hid_probe(struct i2c_client *client,
-                        const struct i2c_device_id *dev_id)
-{
-       int ret;
-       struct i2c_hid *ihid;
-       struct hid_device *hid;
-       __u16 hidRegister;
-       struct i2c_hid_platform_data *platform_data = client->dev.platform_data;
-
-       dbg_hid("HID probe called for i2c 0x%02x\n", client->addr);
-
-       if (!client->irq) {
-               dev_err(&client->dev,
-                       "HID over i2c has not been provided an Int IRQ\n");
-               return -EINVAL;
-       }
-
-       if (client->irq < 0) {
-               if (client->irq != -EPROBE_DEFER)
-                       dev_err(&client->dev,
-                               "HID over i2c doesn't have a valid IRQ\n");
-               return client->irq;
-       }
-
-       ihid = devm_kzalloc(&client->dev, sizeof(*ihid), GFP_KERNEL);
-       if (!ihid)
-               return -ENOMEM;
-
-       if (client->dev.of_node) {
-               ret = i2c_hid_of_probe(client, &ihid->pdata);
-               if (ret)
-                       return ret;
-       } else if (!platform_data) {
-               ret = i2c_hid_acpi_pdata(client, &ihid->pdata);
-               if (ret)
-                       return ret;
-       } else {
-               ihid->pdata = *platform_data;
-       }
-
-       /* Parse platform agnostic common properties from ACPI / device tree */
-       i2c_hid_fwnode_probe(client, &ihid->pdata);
-
-       ihid->pdata.supplies[0].supply = "vdd";
-       ihid->pdata.supplies[1].supply = "vddl";
-
-       ret = devm_regulator_bulk_get(&client->dev,
-                                     ARRAY_SIZE(ihid->pdata.supplies),
-                                     ihid->pdata.supplies);
-       if (ret)
-               return ret;
-
-       ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies),
-                                   ihid->pdata.supplies);
-       if (ret < 0)
-               return ret;
-
-       if (ihid->pdata.post_power_delay_ms)
-               msleep(ihid->pdata.post_power_delay_ms);
-
-       i2c_set_clientdata(client, ihid);
-
-       ihid->client = client;
-
-       hidRegister = ihid->pdata.hid_descriptor_address;
-       ihid->wHIDDescRegister = cpu_to_le16(hidRegister);
-
-       init_waitqueue_head(&ihid->wait);
-       mutex_init(&ihid->reset_lock);
-
-       /* we need to allocate the command buffer without knowing the maximum
-        * size of the reports. Let's use HID_MIN_BUFFER_SIZE, then we do the
-        * real computation later. */
-       ret = i2c_hid_alloc_buffers(ihid, HID_MIN_BUFFER_SIZE);
-       if (ret < 0)
-               goto err_regulator;
-
-       i2c_hid_acpi_fix_up_power(&client->dev);
-
-       pm_runtime_get_noresume(&client->dev);
-       pm_runtime_set_active(&client->dev);
-       pm_runtime_enable(&client->dev);
-       device_enable_async_suspend(&client->dev);
-
-       /* Make sure there is something at this address */
-       ret = i2c_smbus_read_byte(client);
-       if (ret < 0) {
-               dev_dbg(&client->dev, "nothing at this address: %d\n", ret);
-               ret = -ENXIO;
-               goto err_pm;
-       }
-
-       ret = i2c_hid_fetch_hid_descriptor(ihid);
-       if (ret < 0)
-               goto err_pm;
-
-       ret = i2c_hid_init_irq(client);
-       if (ret < 0)
-               goto err_pm;
-
-       hid = hid_allocate_device();
-       if (IS_ERR(hid)) {
-               ret = PTR_ERR(hid);
-               goto err_irq;
-       }
-
-       ihid->hid = hid;
-
-       hid->driver_data = client;
-       hid->ll_driver = &i2c_hid_ll_driver;
-       hid->dev.parent = &client->dev;
-       hid->bus = BUS_I2C;
-       hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
-       hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
-       hid->product = le16_to_cpu(ihid->hdesc.wProductID);
-
-       snprintf(hid->name, sizeof(hid->name), "%s %04hX:%04hX",
-                client->name, hid->vendor, hid->product);
-       strlcpy(hid->phys, dev_name(&client->dev), sizeof(hid->phys));
-
-       ihid->quirks = i2c_hid_lookup_quirk(hid->vendor, hid->product);
-
-       ret = hid_add_device(hid);
-       if (ret) {
-               if (ret != -ENODEV)
-                       hid_err(client, "can't add hid device: %d\n", ret);
-               goto err_mem_free;
-       }
-
-       if (!(ihid->quirks & I2C_HID_QUIRK_NO_RUNTIME_PM))
-               pm_runtime_put(&client->dev);
-
-       return 0;
-
-err_mem_free:
-       hid_destroy_device(hid);
-
-err_irq:
-       free_irq(client->irq, ihid);
-
-err_pm:
-       pm_runtime_put_noidle(&client->dev);
-       pm_runtime_disable(&client->dev);
-
-err_regulator:
-       regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
-                              ihid->pdata.supplies);
-       i2c_hid_free_buffers(ihid);
-       return ret;
-}
-
-static int i2c_hid_remove(struct i2c_client *client)
-{
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-       struct hid_device *hid;
-
-       if (!(ihid->quirks & I2C_HID_QUIRK_NO_RUNTIME_PM))
-               pm_runtime_get_sync(&client->dev);
-       pm_runtime_disable(&client->dev);
-       pm_runtime_set_suspended(&client->dev);
-       pm_runtime_put_noidle(&client->dev);
-
-       hid = ihid->hid;
-       hid_destroy_device(hid);
-
-       free_irq(client->irq, ihid);
-
-       if (ihid->bufsize)
-               i2c_hid_free_buffers(ihid);
-
-       regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
-                              ihid->pdata.supplies);
-
-       return 0;
-}
-
-static void i2c_hid_shutdown(struct i2c_client *client)
-{
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-
-       i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
-       free_irq(client->irq, ihid);
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int i2c_hid_suspend(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-       struct hid_device *hid = ihid->hid;
-       int ret;
-       int wake_status;
-
-       if (hid->driver && hid->driver->suspend) {
-               /*
-                * Wake up the device so that IO issues in
-                * HID driver's suspend code can succeed.
-                */
-               ret = pm_runtime_resume(dev);
-               if (ret < 0)
-                       return ret;
-
-               ret = hid->driver->suspend(hid, PMSG_SUSPEND);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (!pm_runtime_suspended(dev)) {
-               /* Save some power */
-               i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
-
-               disable_irq(client->irq);
-       }
-
-       if (device_may_wakeup(&client->dev)) {
-               wake_status = enable_irq_wake(client->irq);
-               if (!wake_status)
-                       ihid->irq_wake_enabled = true;
-               else
-                       hid_warn(hid, "Failed to enable irq wake: %d\n",
-                               wake_status);
-       } else {
-               regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
-                                      ihid->pdata.supplies);
-       }
-
-       return 0;
-}
-
-static int i2c_hid_resume(struct device *dev)
-{
-       int ret;
-       struct i2c_client *client = to_i2c_client(dev);
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
-       struct hid_device *hid = ihid->hid;
-       int wake_status;
-
-       if (!device_may_wakeup(&client->dev)) {
-               ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies),
-                                           ihid->pdata.supplies);
-               if (ret)
-                       hid_warn(hid, "Failed to enable supplies: %d\n", ret);
-
-               if (ihid->pdata.post_power_delay_ms)
-                       msleep(ihid->pdata.post_power_delay_ms);
-       } else if (ihid->irq_wake_enabled) {
-               wake_status = disable_irq_wake(client->irq);
-               if (!wake_status)
-                       ihid->irq_wake_enabled = false;
-               else
-                       hid_warn(hid, "Failed to disable irq wake: %d\n",
-                               wake_status);
-       }
-
-       /* We'll resume to full power */
-       pm_runtime_disable(dev);
-       pm_runtime_set_active(dev);
-       pm_runtime_enable(dev);
-
-       enable_irq(client->irq);
-
-       /* Instead of resetting device, simply powers the device on. This
-        * solves "incomplete reports" on Raydium devices 2386:3118 and
-        * 2386:4B33 and fixes various SIS touchscreens no longer sending
-        * data after a suspend/resume.
-        */
-       ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
-       if (ret)
-               return ret;
-
-       if (hid->driver && hid->driver->reset_resume) {
-               ret = hid->driver->reset_resume(hid);
-               return ret;
-       }
-
-       return 0;
-}
-#endif
-
-#ifdef CONFIG_PM
-static int i2c_hid_runtime_suspend(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-
-       i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
-       disable_irq(client->irq);
-       return 0;
-}
-
-static int i2c_hid_runtime_resume(struct device *dev)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-
-       enable_irq(client->irq);
-       i2c_hid_set_power(client, I2C_HID_PWR_ON);
-       return 0;
-}
-#endif
-
-static const struct dev_pm_ops i2c_hid_pm = {
-       SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume)
-       SET_RUNTIME_PM_OPS(i2c_hid_runtime_suspend, i2c_hid_runtime_resume,
-                          NULL)
-};
-
-static const struct i2c_device_id i2c_hid_id_table[] = {
-       { "hid", 0 },
-       { "hid-over-i2c", 0 },
-       { },
-};
-MODULE_DEVICE_TABLE(i2c, i2c_hid_id_table);
-
-
-static struct i2c_driver i2c_hid_driver = {
-       .driver = {
-               .name   = "i2c_hid",
-               .pm     = &i2c_hid_pm,
-               .acpi_match_table = ACPI_PTR(i2c_hid_acpi_match),
-               .of_match_table = of_match_ptr(i2c_hid_of_match),
-       },
-
-       .probe          = i2c_hid_probe,
-       .remove         = i2c_hid_remove,
-       .shutdown       = i2c_hid_shutdown,
-       .id_table       = i2c_hid_id_table,
-};
-
-module_i2c_driver(i2c_hid_driver);
-
-MODULE_DESCRIPTION("HID over I2C core driver");
-MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/hid/i2c-hid/i2c-hid.h b/drivers/hid/i2c-hid/i2c-hid.h
new file mode 100644 (file)
index 0000000..a8c19ae
--- /dev/null
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef I2C_HID_H
+#define I2C_HID_H
+
+
+#ifdef CONFIG_DMI
+struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name);
+char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
+                                              unsigned int *size);
+#else
+static inline struct i2c_hid_desc
+                  *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name)
+{ return NULL; }
+static inline char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
+                                                            unsigned int *size)
+{ return NULL; }
+#endif
+
+#endif
index bfbca7ec54ce4a9c97228368a8264c693d3e38da..742191bb24c69e3bd301d33a14ecf38bff87f84d 100644 (file)
@@ -280,14 +280,14 @@ static int write_ipc_from_queue(struct ishtp_device *dev)
         * if tx send list is empty - return 0;
         * may happen, as RX_COMPLETE handler doesn't check list emptiness.
         */
-       if (list_empty(&dev->wr_processing_list_head.link)) {
+       if (list_empty(&dev->wr_processing_list)) {
                spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
                out_ipc_locked = 0;
                return  0;
        }
 
-       ipc_link = list_entry(dev->wr_processing_list_head.link.next,
-                             struct wr_msg_ctl_info, link);
+       ipc_link = list_first_entry(&dev->wr_processing_list,
+                                   struct wr_msg_ctl_info, link);
        /* first 4 bytes of the data is the doorbell value (IPC header) */
        length = ipc_link->length - sizeof(uint32_t);
        doorbell_val = *(uint32_t *)ipc_link->inline_data;
@@ -338,7 +338,7 @@ static int write_ipc_from_queue(struct ishtp_device *dev)
        ipc_send_compl = ipc_link->ipc_send_compl;
        ipc_send_compl_prm = ipc_link->ipc_send_compl_prm;
        list_del_init(&ipc_link->link);
-       list_add_tail(&ipc_link->link, &dev->wr_free_list_head.link);
+       list_add(&ipc_link->link, &dev->wr_free_list);
        spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
 
        /*
@@ -372,18 +372,18 @@ static int write_ipc_to_queue(struct ishtp_device *dev,
        unsigned char *msg, int length)
 {
        struct wr_msg_ctl_info *ipc_link;
-       unsigned long   flags;
+       unsigned long flags;
 
        if (length > IPC_FULL_MSG_SIZE)
                return -EMSGSIZE;
 
        spin_lock_irqsave(&dev->wr_processing_spinlock, flags);
-       if (list_empty(&dev->wr_free_list_head.link)) {
+       if (list_empty(&dev->wr_free_list)) {
                spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
                return -ENOMEM;
        }
-       ipc_link = list_entry(dev->wr_free_list_head.link.next,
-               struct wr_msg_ctl_info, link);
+       ipc_link = list_first_entry(&dev->wr_free_list,
+                                   struct wr_msg_ctl_info, link);
        list_del_init(&ipc_link->link);
 
        ipc_link->ipc_send_compl = ipc_send_compl;
@@ -391,7 +391,7 @@ static int write_ipc_to_queue(struct ishtp_device *dev,
        ipc_link->length = length;
        memcpy(ipc_link->inline_data, msg, length);
 
-       list_add_tail(&ipc_link->link, &dev->wr_processing_list_head.link);
+       list_add_tail(&ipc_link->link, &dev->wr_processing_list);
        spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
 
        write_ipc_from_queue(dev);
@@ -487,17 +487,13 @@ static int ish_fw_reset_handler(struct ishtp_device *dev)
 {
        uint32_t        reset_id;
        unsigned long   flags;
-       struct wr_msg_ctl_info *processing, *next;
 
        /* Read reset ID */
        reset_id = ish_reg_read(dev, IPC_REG_ISH2HOST_MSG) & 0xFFFF;
 
        /* Clear IPC output queue */
        spin_lock_irqsave(&dev->wr_processing_spinlock, flags);
-       list_for_each_entry_safe(processing, next,
-                       &dev->wr_processing_list_head.link, link) {
-               list_move_tail(&processing->link, &dev->wr_free_list_head.link);
-       }
+       list_splice_init(&dev->wr_processing_list, &dev->wr_free_list);
        spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
 
        /* ISHTP notification in IPC_RESET */
@@ -921,9 +917,9 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
        spin_lock_init(&dev->out_ipc_spinlock);
 
        /* Init IPC processing and free lists */
-       INIT_LIST_HEAD(&dev->wr_processing_list_head.link);
-       INIT_LIST_HEAD(&dev->wr_free_list_head.link);
-       for (i = 0; i < IPC_TX_FIFO_SIZE; ++i) {
+       INIT_LIST_HEAD(&dev->wr_processing_list);
+       INIT_LIST_HEAD(&dev->wr_free_list);
+       for (i = 0; i < IPC_TX_FIFO_SIZE; i++) {
                struct wr_msg_ctl_info  *tx_buf;
 
                tx_buf = devm_kzalloc(&pdev->dev,
@@ -939,7 +935,7 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
                                i);
                        break;
                }
-               list_add_tail(&tx_buf->link, &dev->wr_free_list_head.link);
+               list_add_tail(&tx_buf->link, &dev->wr_free_list);
        }
 
        dev->ops = &ish_hw_ops;
index 256b3016116cecca6ece2f8ae2d94422cd875251..8793cc49f8554c2b331323c86071b5371e4ce635 100644 (file)
@@ -115,18 +115,19 @@ static const struct pci_device_id ish_invalid_pci_ids[] = {
  */
 static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
-       struct ishtp_device *dev;
+       int ret;
        struct ish_hw *hw;
-       int     ret;
+       struct ishtp_device *ishtp;
+       struct device *dev = &pdev->dev;
 
        /* Check for invalid platforms for ISH support */
        if (pci_dev_present(ish_invalid_pci_ids))
                return -ENODEV;
 
        /* enable pci dev */
-       ret = pci_enable_device(pdev);
+       ret = pcim_enable_device(pdev);
        if (ret) {
-               dev_err(&pdev->dev, "ISH: Failed to enable PCI device\n");
+               dev_err(dev, "ISH: Failed to enable PCI device\n");
                return ret;
        }
 
@@ -134,65 +135,44 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        pci_set_master(pdev);
 
        /* pci request regions for ISH driver */
-       ret = pci_request_regions(pdev, KBUILD_MODNAME);
+       ret = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME);
        if (ret) {
-               dev_err(&pdev->dev, "ISH: Failed to get PCI regions\n");
-               goto disable_device;
+               dev_err(dev, "ISH: Failed to get PCI regions\n");
+               return ret;
        }
 
        /* allocates and initializes the ISH dev structure */
-       dev = ish_dev_init(pdev);
-       if (!dev) {
+       ishtp = ish_dev_init(pdev);
+       if (!ishtp) {
                ret = -ENOMEM;
-               goto release_regions;
+               return ret;
        }
-       hw = to_ish_hw(dev);
-       dev->print_log = ish_event_tracer;
+       hw = to_ish_hw(ishtp);
+       ishtp->print_log = ish_event_tracer;
 
        /* mapping IO device memory */
-       hw->mem_addr = pci_iomap(pdev, 0, 0);
-       if (!hw->mem_addr) {
-               dev_err(&pdev->dev, "ISH: mapping I/O range failure\n");
-               ret = -ENOMEM;
-               goto free_device;
-       }
-
-       dev->pdev = pdev;
-
+       hw->mem_addr = pcim_iomap_table(pdev)[0];
+       ishtp->pdev = pdev;
        pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
 
        /* request and enable interrupt */
-       ret = request_irq(pdev->irq, ish_irq_handler, IRQF_SHARED,
-                         KBUILD_MODNAME, dev);
+       ret = devm_request_irq(dev, pdev->irq, ish_irq_handler,
+                              IRQF_SHARED, KBUILD_MODNAME, ishtp);
        if (ret) {
-               dev_err(&pdev->dev, "ISH: request IRQ failure (%d)\n",
-                       pdev->irq);
-               goto free_device;
+               dev_err(dev, "ISH: request IRQ %d failed\n", pdev->irq);
+               return ret;
        }
 
-       dev_set_drvdata(dev->devc, dev);
+       dev_set_drvdata(ishtp->devc, ishtp);
 
-       init_waitqueue_head(&dev->suspend_wait);
-       init_waitqueue_head(&dev->resume_wait);
+       init_waitqueue_head(&ishtp->suspend_wait);
+       init_waitqueue_head(&ishtp->resume_wait);
 
-       ret = ish_init(dev);
+       ret = ish_init(ishtp);
        if (ret)
-               goto free_irq;
+               return ret;
 
        return 0;
-
-free_irq:
-       free_irq(pdev->irq, dev);
-free_device:
-       pci_iounmap(pdev, hw->mem_addr);
-release_regions:
-       pci_release_regions(pdev);
-disable_device:
-       pci_clear_master(pdev);
-       pci_disable_device(pdev);
-       dev_err(&pdev->dev, "ISH: PCI driver initialization failed.\n");
-
-       return ret;
 }
 
 /**
@@ -204,16 +184,9 @@ disable_device:
 static void ish_remove(struct pci_dev *pdev)
 {
        struct ishtp_device *ishtp_dev = pci_get_drvdata(pdev);
-       struct ish_hw *hw = to_ish_hw(ishtp_dev);
 
        ishtp_bus_remove_all_clients(ishtp_dev, false);
        ish_device_disable(ishtp_dev);
-
-       free_irq(pdev->irq, ishtp_dev);
-       pci_iounmap(pdev, hw->mem_addr);
-       pci_release_regions(pdev);
-       pci_clear_master(pdev);
-       pci_disable_device(pdev);
 }
 
 static struct device __maybe_unused *ish_resume_device;
index 2d28cffc14046c94277b1960fa81aaede7cbd637..e64243bc9c96f3d0ca897d6334d07b7f16af3834 100644 (file)
@@ -320,23 +320,14 @@ do_get_report:
  */
 static void ish_cl_event_cb(struct ishtp_cl_device *device)
 {
-       struct ishtp_cl *hid_ishtp_cl = device->driver_data;
+       struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(device);
        struct ishtp_cl_rb *rb_in_proc;
        size_t r_length;
-       unsigned long flags;
 
        if (!hid_ishtp_cl)
                return;
 
-       spin_lock_irqsave(&hid_ishtp_cl->in_process_spinlock, flags);
-       while (!list_empty(&hid_ishtp_cl->in_process_list.list)) {
-               rb_in_proc = list_entry(
-                       hid_ishtp_cl->in_process_list.list.next,
-                       struct ishtp_cl_rb, list);
-               list_del_init(&rb_in_proc->list);
-               spin_unlock_irqrestore(&hid_ishtp_cl->in_process_spinlock,
-                       flags);
-
+       while ((rb_in_proc = ishtp_cl_rx_get_rb(hid_ishtp_cl)) != NULL) {
                if (!rb_in_proc->buffer.data)
                        return;
 
@@ -346,9 +337,7 @@ static void ish_cl_event_cb(struct ishtp_cl_device *device)
                process_recv(hid_ishtp_cl, rb_in_proc->buffer.data, r_length);
 
                ishtp_cl_io_rb_recycle(rb_in_proc);
-               spin_lock_irqsave(&hid_ishtp_cl->in_process_spinlock, flags);
        }
-       spin_unlock_irqrestore(&hid_ishtp_cl->in_process_spinlock, flags);
 }
 
 /**
@@ -637,8 +626,8 @@ static int ishtp_get_report_descriptor(struct ishtp_cl *hid_ishtp_cl,
 static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset)
 {
        struct ishtp_device *dev;
-       unsigned long flags;
        struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
+       struct ishtp_fw_client *fw_client;
        int i;
        int rv;
 
@@ -660,16 +649,14 @@ static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset)
        hid_ishtp_cl->rx_ring_size = HID_CL_RX_RING_SIZE;
        hid_ishtp_cl->tx_ring_size = HID_CL_TX_RING_SIZE;
 
-       spin_lock_irqsave(&dev->fw_clients_lock, flags);
-       i = ishtp_fw_cl_by_uuid(dev, &hid_ishtp_guid);
-       if (i < 0) {
-               spin_unlock_irqrestore(&dev->fw_clients_lock, flags);
+       fw_client = ishtp_fw_cl_get_client(dev, &hid_ishtp_guid);
+       if (!fw_client) {
                dev_err(&client_data->cl_device->dev,
                        "ish client uuid not found\n");
-               return i;
+               return -ENOENT;
        }
-       hid_ishtp_cl->fw_client_id = dev->fw_clients[i].client_id;
-       spin_unlock_irqrestore(&dev->fw_clients_lock, flags);
+
+       hid_ishtp_cl->fw_client_id = fw_client->client_id;
        hid_ishtp_cl->state = ISHTP_CL_CONNECTING;
 
        rv = ishtp_cl_connect(hid_ishtp_cl);
@@ -765,7 +752,7 @@ static void hid_ishtp_cl_reset_handler(struct work_struct *work)
        if (!hid_ishtp_cl)
                return;
 
-       cl_device->driver_data = hid_ishtp_cl;
+       ishtp_set_drvdata(cl_device, hid_ishtp_cl);
        hid_ishtp_cl->client_data = client_data;
        client_data->hid_ishtp_cl = hid_ishtp_cl;
 
@@ -814,7 +801,7 @@ static int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
        if (!hid_ishtp_cl)
                return -ENOMEM;
 
-       cl_device->driver_data = hid_ishtp_cl;
+       ishtp_set_drvdata(cl_device, hid_ishtp_cl);
        hid_ishtp_cl->client_data = client_data;
        client_data->hid_ishtp_cl = hid_ishtp_cl;
        client_data->cl_device = cl_device;
@@ -844,7 +831,7 @@ static int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
  */
 static int hid_ishtp_cl_remove(struct ishtp_cl_device *cl_device)
 {
-       struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data;
+       struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
        struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
 
        hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
@@ -874,7 +861,7 @@ static int hid_ishtp_cl_remove(struct ishtp_cl_device *cl_device)
  */
 static int hid_ishtp_cl_reset(struct ishtp_cl_device *cl_device)
 {
-       struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data;
+       struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
        struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
 
        hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
@@ -898,7 +885,7 @@ static int hid_ishtp_cl_reset(struct ishtp_cl_device *cl_device)
 static int hid_ishtp_cl_suspend(struct device *device)
 {
        struct ishtp_cl_device *cl_device = to_ishtp_cl_device(device);
-       struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data;
+       struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
        struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
 
        hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
@@ -919,7 +906,7 @@ static int hid_ishtp_cl_suspend(struct device *device)
 static int hid_ishtp_cl_resume(struct device *device)
 {
        struct ishtp_cl_device *cl_device = to_ishtp_cl_device(device);
-       struct ishtp_cl *hid_ishtp_cl = cl_device->driver_data;
+       struct ishtp_cl *hid_ishtp_cl = ishtp_get_drvdata(cl_device);
        struct ishtp_cl_data *client_data = hid_ishtp_cl->client_data;
 
        hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
index 2623a567ffba5ae51e90653e47bea42127ea9b02..728dc6d4561aae18e0f38b1732977974cdf865df 100644 (file)
@@ -148,6 +148,31 @@ int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const uuid_le *uuid)
 }
 EXPORT_SYMBOL(ishtp_fw_cl_by_uuid);
 
+/**
+ * ishtp_fw_cl_get_client() - return client information to client
+ * @dev: the ishtp device structure
+ * @uuid: uuid of the client to search
+ *
+ * Search firmware client using UUID and reture related client information.
+ *
+ * Return: pointer of client information on success, NULL on failure.
+ */
+struct ishtp_fw_client *ishtp_fw_cl_get_client(struct ishtp_device *dev,
+                                               const uuid_le *uuid)
+{
+       int i;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->fw_clients_lock, flags);
+       i = ishtp_fw_cl_by_uuid(dev, uuid);
+       spin_unlock_irqrestore(&dev->fw_clients_lock, flags);
+       if (i < 0 || dev->fw_clients[i].props.fixed_address)
+               return NULL;
+
+       return &dev->fw_clients[i];
+}
+EXPORT_SYMBOL(ishtp_fw_cl_get_client);
+
 /**
  * ishtp_fw_cl_by_id() - return index to fw_clients for client_id
  * @dev: the ishtp device structure
@@ -563,6 +588,33 @@ void ishtp_put_device(struct ishtp_cl_device *cl_device)
 }
 EXPORT_SYMBOL(ishtp_put_device);
 
+/**
+ * ishtp_set_drvdata() - set client driver data
+ * @cl_device: client device instance
+ * @data:      driver data need to be set
+ *
+ * Set client driver data to cl_device->driver_data.
+ */
+void ishtp_set_drvdata(struct ishtp_cl_device *cl_device, void *data)
+{
+       cl_device->driver_data = data;
+}
+EXPORT_SYMBOL(ishtp_set_drvdata);
+
+/**
+ * ishtp_get_drvdata() - get client driver data
+ * @cl_device: client device instance
+ *
+ * Get client driver data from cl_device->driver_data.
+ *
+ * Return: pointer of driver data
+ */
+void *ishtp_get_drvdata(struct ishtp_cl_device *cl_device)
+{
+       return cl_device->driver_data;
+}
+EXPORT_SYMBOL(ishtp_get_drvdata);
+
 /**
  * ishtp_bus_new_client() - Create a new client
  * @dev:       ISHTP device instance
index a1ffae7f26ad2cfdb94cb9b75ce2a5f3054f0d0b..b8a5bcc825369e05d3f5743ac59bb8bd24cbfb93 100644 (file)
@@ -101,6 +101,9 @@ void        ishtp_reset_compl_handler(struct ishtp_device *dev);
 void   ishtp_put_device(struct ishtp_cl_device *);
 void   ishtp_get_device(struct ishtp_cl_device *);
 
+void   ishtp_set_drvdata(struct ishtp_cl_device *cl_device, void *data);
+void   *ishtp_get_drvdata(struct ishtp_cl_device *cl_device);
+
 int    __ishtp_cl_driver_register(struct ishtp_cl_driver *driver,
                                   struct module *owner);
 #define ishtp_cl_driver_register(driver)               \
@@ -110,5 +113,7 @@ void        ishtp_cl_driver_unregister(struct ishtp_cl_driver *driver);
 int    ishtp_register_event_cb(struct ishtp_cl_device *device,
                                void (*read_cb)(struct ishtp_cl_device *));
 int    ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const uuid_le *cuuid);
+struct ishtp_fw_client *ishtp_fw_cl_get_client(struct ishtp_device *dev,
+                                               const uuid_le *uuid);
 
 #endif /* _LINUX_ISHTP_CL_BUS_H */
index b9b917d2d50db3fedaa17ce8f6fcb3a05e2f38c3..248651c35497e6cf6ca619d6d5e8fef3814a672a 100644 (file)
@@ -69,6 +69,8 @@ int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl)
        int     j;
        unsigned long   flags;
 
+       cl->tx_ring_free_size = 0;
+
        /* Allocate pool to free Tx bufs */
        for (j = 0; j < cl->tx_ring_size; ++j) {
                struct ishtp_cl_tx_ring *tx_buf;
@@ -85,6 +87,7 @@ int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl)
 
                spin_lock_irqsave(&cl->tx_free_list_spinlock, flags);
                list_add_tail(&tx_buf->list, &cl->tx_free_list.list);
+               ++cl->tx_ring_free_size;
                spin_unlock_irqrestore(&cl->tx_free_list_spinlock, flags);
        }
        return  0;
@@ -144,6 +147,7 @@ void ishtp_cl_free_tx_ring(struct ishtp_cl *cl)
                tx_buf = list_entry(cl->tx_free_list.list.next,
                                    struct ishtp_cl_tx_ring, list);
                list_del(&tx_buf->list);
+               --cl->tx_ring_free_size;
                kfree(tx_buf->send_buf.data);
                kfree(tx_buf);
        }
@@ -255,3 +259,48 @@ int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb)
        return  rets;
 }
 EXPORT_SYMBOL(ishtp_cl_io_rb_recycle);
+
+/**
+ * ishtp_cl_tx_empty() -test whether client device tx buffer is empty
+ * @cl: Pointer to client device instance
+ *
+ * Look client device tx buffer list, and check whether this list is empty
+ *
+ * Return: true if client tx buffer list is empty else false
+ */
+bool ishtp_cl_tx_empty(struct ishtp_cl *cl)
+{
+       int tx_list_empty;
+       unsigned long tx_flags;
+
+       spin_lock_irqsave(&cl->tx_list_spinlock, tx_flags);
+       tx_list_empty = list_empty(&cl->tx_list.list);
+       spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags);
+
+       return !!tx_list_empty;
+}
+EXPORT_SYMBOL(ishtp_cl_tx_empty);
+
+/**
+ * ishtp_cl_rx_get_rb() -Get a rb from client device rx buffer list
+ * @cl: Pointer to client device instance
+ *
+ * Check client device in-processing buffer list and get a rb from it.
+ *
+ * Return: rb pointer if buffer list isn't empty else NULL
+ */
+struct ishtp_cl_rb *ishtp_cl_rx_get_rb(struct ishtp_cl *cl)
+{
+       unsigned long rx_flags;
+       struct ishtp_cl_rb *rb;
+
+       spin_lock_irqsave(&cl->in_process_spinlock, rx_flags);
+       rb = list_first_entry_or_null(&cl->in_process_list.list,
+                               struct ishtp_cl_rb, list);
+       if (rb)
+               list_del_init(&rb->list);
+       spin_unlock_irqrestore(&cl->in_process_spinlock, rx_flags);
+
+       return rb;
+}
+EXPORT_SYMBOL(ishtp_cl_rx_get_rb);
index 007443ef5fca44afea6df14267f12cbe24978990..faeccdb1475bd3a9f4621e61891e9dee911646f2 100644 (file)
 #include "hbm.h"
 #include "client.h"
 
+int ishtp_cl_get_tx_free_buffer_size(struct ishtp_cl *cl)
+{
+       unsigned long tx_free_flags;
+       int size;
+
+       spin_lock_irqsave(&cl->tx_free_list_spinlock, tx_free_flags);
+       size = cl->tx_ring_free_size * cl->device->fw_client->props.max_msg_length;
+       spin_unlock_irqrestore(&cl->tx_free_list_spinlock, tx_free_flags);
+
+       return size;
+}
+EXPORT_SYMBOL(ishtp_cl_get_tx_free_buffer_size);
+
+int ishtp_cl_get_tx_free_rings(struct ishtp_cl *cl)
+{
+       return cl->tx_ring_free_size;
+}
+EXPORT_SYMBOL(ishtp_cl_get_tx_free_rings);
+
 /**
  * ishtp_read_list_flush() - Flush read queue
  * @cl: ishtp client instance
@@ -90,6 +109,7 @@ static void ishtp_cl_init(struct ishtp_cl *cl, struct ishtp_device *dev)
 
        cl->rx_ring_size = CL_DEF_RX_RING_SIZE;
        cl->tx_ring_size = CL_DEF_TX_RING_SIZE;
+       cl->tx_ring_free_size = cl->tx_ring_size;
 
        /* dma */
        cl->last_tx_path = CL_TX_PATH_IPC;
@@ -577,6 +597,8 @@ int ishtp_cl_send(struct ishtp_cl *cl, uint8_t *buf, size_t length)
         * max ISHTP message size per client
         */
        list_del_init(&cl_msg->list);
+       --cl->tx_ring_free_size;
+
        spin_unlock_irqrestore(&cl->tx_free_list_spinlock, tx_free_flags);
        memcpy(cl_msg->send_buf.data, buf, length);
        cl_msg->send_buf.size = length;
@@ -685,6 +707,7 @@ static void ipc_tx_callback(void *prm)
                ishtp_write_message(dev, &ishtp_hdr, pmsg);
                spin_lock_irqsave(&cl->tx_free_list_spinlock, tx_free_flags);
                list_add_tail(&cl_msg->list, &cl->tx_free_list.list);
+               ++cl->tx_ring_free_size;
                spin_unlock_irqrestore(&cl->tx_free_list_spinlock,
                        tx_free_flags);
        } else {
@@ -778,6 +801,7 @@ static void ishtp_cl_send_msg_dma(struct ishtp_device *dev,
        ishtp_write_message(dev, &hdr, (unsigned char *)&dma_xfer);
        spin_lock_irqsave(&cl->tx_free_list_spinlock, tx_free_flags);
        list_add_tail(&cl_msg->list, &cl->tx_free_list.list);
+       ++cl->tx_ring_free_size;
        spin_unlock_irqrestore(&cl->tx_free_list_spinlock, tx_free_flags);
        ++cl->send_msg_cnt_dma;
 }
index 79eade547f5db5663a2c8d7dc0d5b3fc0c96690a..042f4c4853b129acfc48898decdd07c9b33a0396 100644 (file)
@@ -84,6 +84,7 @@ struct ishtp_cl {
        /* Client Tx buffers list */
        unsigned int    tx_ring_size;
        struct ishtp_cl_tx_ring tx_list, tx_free_list;
+       int             tx_ring_free_size;
        spinlock_t      tx_list_spinlock;
        spinlock_t      tx_free_list_spinlock;
        size_t  tx_offs;        /* Offset in buffer at head of 'tx_list' */
@@ -137,6 +138,8 @@ int ishtp_cl_alloc_rx_ring(struct ishtp_cl *cl);
 int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl);
 void ishtp_cl_free_rx_ring(struct ishtp_cl *cl);
 void ishtp_cl_free_tx_ring(struct ishtp_cl *cl);
+int ishtp_cl_get_tx_free_buffer_size(struct ishtp_cl *cl);
+int ishtp_cl_get_tx_free_rings(struct ishtp_cl *cl);
 
 /* DMA I/F functions */
 void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg,
@@ -178,5 +181,7 @@ int ishtp_cl_flush_queues(struct ishtp_cl *cl);
 
 /* exported functions from ISHTP client buffer management scope */
 int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb);
+bool ishtp_cl_tx_empty(struct ishtp_cl *cl);
+struct ishtp_cl_rb *ishtp_cl_rx_get_rb(struct ishtp_cl *cl);
 
 #endif /* _ISHTP_CLIENT_H_ */
index 6a6d927b78b0e254d5f5248ed11a46852d969680..e7c6bfefaf9eb76c4ceed68e12f952481bccba16 100644 (file)
@@ -207,7 +207,7 @@ struct ishtp_device {
        struct work_struct bh_hbm_work;
 
        /* IPC write queue */
-       struct wr_msg_ctl_info wr_processing_list_head, wr_free_list_head;
+       struct list_head wr_processing_list, wr_free_list;
        /* For both processing list  and free list */
        spinlock_t wr_processing_spinlock;
 
index e0a06be5ef5c0898d645464e807fd762c52e041f..5dd3a8245f0fd5015b02c933b778a3336ed59d19 100644 (file)
@@ -3335,6 +3335,7 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
 
 void wacom_setup_device_quirks(struct wacom *wacom)
 {
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
        struct wacom_features *features = &wacom->wacom_wac.features;
 
        /* The pen and pad share the same interface on most devices */
@@ -3464,6 +3465,24 @@ void wacom_setup_device_quirks(struct wacom *wacom)
 
        if (features->type == REMOTE)
                features->device_type |= WACOM_DEVICETYPE_WL_MONITOR;
+
+       /* HID descriptor for DTK-2451 / DTH-2452 claims to report lots
+        * of things it shouldn't. Lets fix up the damage...
+        */
+       if (wacom->hdev->product == 0x382 || wacom->hdev->product == 0x37d) {
+               features->quirks &= ~WACOM_QUIRK_TOOLSERIAL;
+               __clear_bit(BTN_TOOL_BRUSH, wacom_wac->pen_input->keybit);
+               __clear_bit(BTN_TOOL_PENCIL, wacom_wac->pen_input->keybit);
+               __clear_bit(BTN_TOOL_AIRBRUSH, wacom_wac->pen_input->keybit);
+               __clear_bit(ABS_Z, wacom_wac->pen_input->absbit);
+               __clear_bit(ABS_DISTANCE, wacom_wac->pen_input->absbit);
+               __clear_bit(ABS_TILT_X, wacom_wac->pen_input->absbit);
+               __clear_bit(ABS_TILT_Y, wacom_wac->pen_input->absbit);
+               __clear_bit(ABS_WHEEL, wacom_wac->pen_input->absbit);
+               __clear_bit(ABS_MISC, wacom_wac->pen_input->absbit);
+               __clear_bit(MSC_SERIAL, wacom_wac->pen_input->mscbit);
+               __clear_bit(EV_MSC, wacom_wac->pen_input->evbit);
+       }
 }
 
 int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
index d44a783629425aef596d5152b23a18cdf3841c62..2827b87590d8d55893c87a1593be582994f4a7e5 100644 (file)
@@ -1139,6 +1139,34 @@ static inline u32 hid_report_len(struct hid_report *report)
 int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
                int interrupt);
 
+
+/**
+ * struct hid_scroll_counter - Utility class for processing high-resolution
+ *                             scroll events.
+ * @dev: the input device for which events should be reported.
+ * @microns_per_hi_res_unit: the amount moved by the user's finger for each
+ *                           high-resolution unit reported by the mouse, in
+ *                           microns.
+ * @resolution_multiplier: the wheel's resolution in high-resolution mode as a
+ *                         multiple of its lower resolution. For example, if
+ *                         moving the wheel by one "notch" would result in a
+ *                         value of 1 in low-resolution mode but 8 in
+ *                         high-resolution, the multiplier is 8.
+ * @remainder: counts the number of high-resolution units moved since the last
+ *             low-resolution event (REL_WHEEL or REL_HWHEEL) was sent. Should
+ *             only be used by class methods.
+ */
+struct hid_scroll_counter {
+       struct input_dev *dev;
+       int microns_per_hi_res_unit;
+       int resolution_multiplier;
+
+       int remainder;
+};
+
+void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
+                                     int hi_res_value);
+
 /* HID quirks API */
 unsigned long hid_lookup_quirk(const struct hid_device *hdev);
 int hid_quirks_init(char **quirks_param, __u16 bus, int count);
index 6e1ab9bead288fcbb1d97bca01dc11fb1bbd6e8e..7690f079396525232f37618d37e5a00808141724 100644 (file)
@@ -2132,6 +2132,7 @@ struct ec_response_get_next_event_v1 {
 /* Switches */
 #define EC_MKBP_LID_OPEN       0
 #define EC_MKBP_TABLET_MODE    1
+#define EC_MKBP_BASE_ATTACHED  2
 
 /*****************************************************************************/
 /* Temperature sensor commands */
index 53fbae27b280c5a3726f9cb0a578156d4d3b2258..6d180cc60a5df7cd28eabc6a1ab0db60cfd6a433 100644 (file)
 #define REL_DIAL               0x07
 #define REL_WHEEL              0x08
 #define REL_MISC               0x09
+/*
+ * 0x0a is reserved and should not be used in input drivers.
+ * It was used by HID as REL_MISC+1 and userspace needs to detect if
+ * the next REL_* event is correct or is just REL_MISC + n.
+ * We define here REL_RESERVED so userspace can rely on it and detect
+ * the situation described above.
+ */
+#define REL_RESERVED           0x0a
+#define REL_WHEEL_HI_RES       0x0b
 #define REL_MAX                        0x0f
 #define REL_CNT                        (REL_MAX+1)
 
 
 #define ABS_MISC               0x28
 
+/*
+ * 0x2e is reserved and should not be used in input drivers.
+ * It was used by HID as ABS_MISC+6 and userspace needs to detect if
+ * the next ABS_* event is correct or is just ABS_MISC + n.
+ * We define here ABS_RESERVED so userspace can rely on it and detect
+ * the situation described above.
+ */
+#define ABS_RESERVED           0x2e
+
 #define ABS_MT_SLOT            0x2f    /* MT slot being modified */
 #define ABS_MT_TOUCH_MAJOR     0x30    /* Major axis of touching ellipse */
 #define ABS_MT_TOUCH_MINOR     0x31    /* Minor axis (omit if circular) */