]> git.ipfire.org Git - people/arne_f/kernel.git/commitdiff
Merge branches 'for-4.10/asus', 'for-4.10/cp2112', 'for-4.10/i2c-hid-nopower', 'for...
authorJiri Kosina <jkosina@suse.cz>
Wed, 14 Dec 2016 09:12:26 +0000 (10:12 +0100)
committerJiri Kosina <jkosina@suse.cz>
Wed, 14 Dec 2016 09:12:26 +0000 (10:12 +0100)
25 files changed:
MAINTAINERS
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-core.c
drivers/hid/hid-cp2112.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-mf.c [new file with mode: 0644]
drivers/hid/hid-microsoft.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-sensor-hub.c
drivers/hid/hid-sony.c
drivers/hid/hid-udraw-ps3.c [new file with mode: 0644]
drivers/hid/i2c-hid/i2c-hid.c
drivers/hid/intel-ish-hid/ipc/ipc.c
drivers/hid/intel-ish-hid/ipc/utils.h [deleted file]
drivers/hid/intel-ish-hid/ishtp/bus.c
drivers/hid/intel-ish-hid/ishtp/hbm.c
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/hid-quirks.c
drivers/hid/wacom.h
drivers/hid/wacom_sys.c
drivers/hid/wacom_wac.c
drivers/hid/wacom_wac.h
include/linux/hid.h

index ad9b965e5e4450532daea69e8f1bb91ab3d21b1a..1f0c88ff9a45999fe7945a23e8adc01ac38a8449 100644 (file)
@@ -12342,6 +12342,12 @@ S:     Maintained
 F:     Documentation/filesystems/udf.txt
 F:     fs/udf/
 
+UDRAW TABLET
+M:     Bastien Nocera <hadess@hadess.net>
+L:     linux-input@vger.kernel.org
+S:     Maintained
+F:     drivers/hid/hid-udraw.c
+
 UFS FILESYSTEM
 M:     Evgeniy Dushistov <dushistov@mail.ru>
 S:     Maintained
index f8f251c20e2dd9e92d5505adcf923858046f1bce..4070b7386e9dea9965cc49ab077eb64ffc1f2455 100644 (file)
@@ -214,7 +214,7 @@ config HID_CMEDIA
 
 config HID_CP2112
        tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support"
-       depends on USB_HID && I2C && GPIOLIB
+       depends on USB_HID && I2C && GPIOLIB && GPIOLIB_IRQCHIP
        ---help---
        Support for Silicon Labs CP2112 HID USB to SMBus Master Bridge.
        This is a HID device driver which registers as an i2c adapter
@@ -512,6 +512,14 @@ config HID_MAGICMOUSE
        Say Y here if you want support for the multi-touch features of the
        Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad.
 
+config HID_MAYFLASH
+       tristate "Mayflash game controller adapter force feedback"
+       depends on HID
+       select INPUT_FF_MEMLESS
+       ---help---
+       Say Y here if you have HJZ Mayflash PS3 game controller adapters
+       and want to enable force feedback support.
+
 config HID_MICROSOFT
        tristate "Microsoft non-fully HID-compliant devices"
        depends on HID
@@ -861,6 +869,13 @@ config THRUSTMASTER_FF
          a THRUSTMASTER Dual Trigger 3-in-1 or a THRUSTMASTER Ferrari GT
          Rumble Force or Force Feedback Wheel.
 
+config HID_UDRAW_PS3
+       tristate "THQ PS3 uDraw tablet"
+       depends on HID
+       ---help---
+         Say Y here if you want to use the THQ uDraw gaming tablet for
+         the PS3.
+
 config HID_WACOM
        tristate "Wacom Intuos/Graphire tablet support (USB)"
        depends on HID
index 86b2b5785fd220974a0fe9814957e799e5bcec6a..4d111f23e801c28151a58a7e6745d8332758db76 100644 (file)
@@ -58,6 +58,7 @@ obj-$(CONFIG_HID_LOGITECH)    += hid-logitech.o
 obj-$(CONFIG_HID_LOGITECH_DJ)  += hid-logitech-dj.o
 obj-$(CONFIG_HID_LOGITECH_HIDPP)       += hid-logitech-hidpp.o
 obj-$(CONFIG_HID_MAGICMOUSE)   += hid-magicmouse.o
+obj-$(CONFIG_HID_MAYFLASH)     += hid-mf.o
 obj-$(CONFIG_HID_MICROSOFT)    += hid-microsoft.o
 obj-$(CONFIG_HID_MONTEREY)     += hid-monterey.o
 obj-$(CONFIG_HID_MULTITOUCH)   += hid-multitouch.o
@@ -96,6 +97,7 @@ obj-$(CONFIG_HID_TIVO)                += hid-tivo.o
 obj-$(CONFIG_HID_TOPSEED)      += hid-topseed.o
 obj-$(CONFIG_HID_TWINHAN)      += hid-twinhan.o
 obj-$(CONFIG_HID_UCLOGIC)      += hid-uclogic.o
+obj-$(CONFIG_HID_UDRAW_PS3)    += hid-udraw-ps3.o
 obj-$(CONFIG_HID_LED)          += hid-led.o
 obj-$(CONFIG_HID_XINMO)                += hid-xinmo.o
 obj-$(CONFIG_HID_ZEROPLUS)     += hid-zpff.o
index 689cd9cf502543c8c0162c8b9bdd08c9fcff86b4..cff060b56da9d6cb8cb138657cf4efca7debef61 100644 (file)
@@ -727,8 +727,9 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type)
            (hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 ||
             hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 ||
             hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP ||
+            hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4 ||
+            hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2 ||
             hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP ||
-            hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 ||
             hid->product == USB_DEVICE_ID_MS_POWER_COVER) &&
            hid->group == HID_GROUP_MULTITOUCH)
                hid->group = HID_GROUP_GENERIC;
@@ -1884,6 +1885,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_DELCOM, USB_DEVICE_ID_DELCOM_VISUAL_IND) },
        { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
        { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) },
+#if IS_ENABLED(CONFIG_HID_MAYFLASH)
+       { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3) },
+#endif
        { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_WN) },
        { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_FA) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
@@ -1984,8 +1988,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3KV1) },
@@ -2060,6 +2065,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER) },
@@ -2087,6 +2095,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW) },
        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) },
        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) },
        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) },
index 60d30203a5faf9c7b69dcddb09ec0a086ca96a08..f31a778b085148fea4a52599c6b89be203889781 100644 (file)
@@ -24,6 +24,7 @@
  *   http://www.silabs.com/Support%20Documents/TechnicalDocs/AN495.pdf
  */
 
+#include <linux/gpio.h>
 #include <linux/gpio/driver.h>
 #include <linux/hid.h>
 #include <linux/i2c.h>
@@ -168,6 +169,12 @@ struct cp2112_device {
        struct gpio_chip gc;
        u8 *in_out_buffer;
        spinlock_t lock;
+
+       struct gpio_desc *desc[8];
+       bool gpio_poll;
+       struct delayed_work gpio_poll_worker;
+       unsigned long irq_mask;
+       u8 gpio_prev_state;
 };
 
 static int gpio_push_pull = 0xFF;
@@ -233,7 +240,7 @@ static void cp2112_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
        spin_unlock_irqrestore(&dev->lock, flags);
 }
 
-static int cp2112_gpio_get(struct gpio_chip *chip, unsigned offset)
+static int cp2112_gpio_get_all(struct gpio_chip *chip)
 {
        struct cp2112_device *dev = gpiochip_get_data(chip);
        struct hid_device *hdev = dev->hdev;
@@ -252,7 +259,7 @@ static int cp2112_gpio_get(struct gpio_chip *chip, unsigned offset)
                goto exit;
        }
 
-       ret = (buf[1] >> offset) & 1;
+       ret = buf[1];
 
 exit:
        spin_unlock_irqrestore(&dev->lock, flags);
@@ -260,6 +267,17 @@ exit:
        return ret;
 }
 
+static int cp2112_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+       int ret;
+
+       ret = cp2112_gpio_get_all(chip);
+       if (ret < 0)
+               return ret;
+
+       return (ret >> offset) & 1;
+}
+
 static int cp2112_gpio_direction_output(struct gpio_chip *chip,
                                        unsigned offset, int value)
 {
@@ -1041,6 +1059,166 @@ static void chmod_sysfs_attrs(struct hid_device *hdev)
        }
 }
 
+static void cp2112_gpio_irq_ack(struct irq_data *d)
+{
+}
+
+static void cp2112_gpio_irq_mask(struct irq_data *d)
+{
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct cp2112_device *dev = gpiochip_get_data(gc);
+
+       __clear_bit(d->hwirq, &dev->irq_mask);
+}
+
+static void cp2112_gpio_irq_unmask(struct irq_data *d)
+{
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct cp2112_device *dev = gpiochip_get_data(gc);
+
+       __set_bit(d->hwirq, &dev->irq_mask);
+}
+
+static void cp2112_gpio_poll_callback(struct work_struct *work)
+{
+       struct cp2112_device *dev = container_of(work, struct cp2112_device,
+                                                gpio_poll_worker.work);
+       struct irq_data *d;
+       u8 gpio_mask;
+       u8 virqs = (u8)dev->irq_mask;
+       u32 irq_type;
+       int irq, virq, ret;
+
+       ret = cp2112_gpio_get_all(&dev->gc);
+       if (ret == -ENODEV) /* the hardware has been disconnected */
+               return;
+       if (ret < 0)
+               goto exit;
+
+       gpio_mask = ret;
+
+       while (virqs) {
+               virq = ffs(virqs) - 1;
+               virqs &= ~BIT(virq);
+
+               if (!dev->gc.to_irq)
+                       break;
+
+               irq = dev->gc.to_irq(&dev->gc, virq);
+
+               d = irq_get_irq_data(irq);
+               if (!d)
+                       continue;
+
+               irq_type = irqd_get_trigger_type(d);
+
+               if (gpio_mask & BIT(virq)) {
+                       /* Level High */
+
+                       if (irq_type & IRQ_TYPE_LEVEL_HIGH)
+                               handle_nested_irq(irq);
+
+                       if ((irq_type & IRQ_TYPE_EDGE_RISING) &&
+                           !(dev->gpio_prev_state & BIT(virq)))
+                               handle_nested_irq(irq);
+               } else {
+                       /* Level Low */
+
+                       if (irq_type & IRQ_TYPE_LEVEL_LOW)
+                               handle_nested_irq(irq);
+
+                       if ((irq_type & IRQ_TYPE_EDGE_FALLING) &&
+                           (dev->gpio_prev_state & BIT(virq)))
+                               handle_nested_irq(irq);
+               }
+       }
+
+       dev->gpio_prev_state = gpio_mask;
+
+exit:
+       if (dev->gpio_poll)
+               schedule_delayed_work(&dev->gpio_poll_worker, 10);
+}
+
+
+static unsigned int cp2112_gpio_irq_startup(struct irq_data *d)
+{
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct cp2112_device *dev = gpiochip_get_data(gc);
+
+       INIT_DELAYED_WORK(&dev->gpio_poll_worker, cp2112_gpio_poll_callback);
+
+       cp2112_gpio_direction_input(gc, d->hwirq);
+
+       if (!dev->gpio_poll) {
+               dev->gpio_poll = true;
+               schedule_delayed_work(&dev->gpio_poll_worker, 0);
+       }
+
+       cp2112_gpio_irq_unmask(d);
+       return 0;
+}
+
+static void cp2112_gpio_irq_shutdown(struct irq_data *d)
+{
+       struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+       struct cp2112_device *dev = gpiochip_get_data(gc);
+
+       cancel_delayed_work_sync(&dev->gpio_poll_worker);
+}
+
+static int cp2112_gpio_irq_type(struct irq_data *d, unsigned int type)
+{
+       return 0;
+}
+
+static struct irq_chip cp2112_gpio_irqchip = {
+       .name = "cp2112-gpio",
+       .irq_startup = cp2112_gpio_irq_startup,
+       .irq_shutdown = cp2112_gpio_irq_shutdown,
+       .irq_ack = cp2112_gpio_irq_ack,
+       .irq_mask = cp2112_gpio_irq_mask,
+       .irq_unmask = cp2112_gpio_irq_unmask,
+       .irq_set_type = cp2112_gpio_irq_type,
+};
+
+static int __maybe_unused cp2112_allocate_irq(struct cp2112_device *dev,
+                                             int pin)
+{
+       int ret;
+
+       if (dev->desc[pin])
+               return -EINVAL;
+
+       dev->desc[pin] = gpiochip_request_own_desc(&dev->gc, pin,
+                                                  "HID/I2C:Event");
+       if (IS_ERR(dev->desc[pin])) {
+               dev_err(dev->gc.parent, "Failed to request GPIO\n");
+               return PTR_ERR(dev->desc[pin]);
+       }
+
+       ret = gpiochip_lock_as_irq(&dev->gc, pin);
+       if (ret) {
+               dev_err(dev->gc.parent, "Failed to lock GPIO as interrupt\n");
+               goto err_desc;
+       }
+
+       ret = gpiod_to_irq(dev->desc[pin]);
+       if (ret < 0) {
+               dev_err(dev->gc.parent, "Failed to translate GPIO to IRQ\n");
+               goto err_lock;
+       }
+
+       return ret;
+
+err_lock:
+       gpiochip_unlock_as_irq(&dev->gc, pin);
+err_desc:
+       gpiochip_free_own_desc(dev->desc[pin]);
+       dev->desc[pin] = NULL;
+       return ret;
+}
+
 static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
        struct cp2112_device *dev;
@@ -1163,8 +1341,17 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
        chmod_sysfs_attrs(hdev);
        hid_hw_power(hdev, PM_HINT_NORMAL);
 
+       ret = gpiochip_irqchip_add(&dev->gc, &cp2112_gpio_irqchip, 0,
+                                  handle_simple_irq, IRQ_TYPE_NONE);
+       if (ret) {
+               dev_err(dev->gc.parent, "failed to add IRQ chip\n");
+               goto err_sysfs_remove;
+       }
+
        return ret;
 
+err_sysfs_remove:
+       sysfs_remove_group(&hdev->dev.kobj, &cp2112_attr_group);
 err_gpiochip_remove:
        gpiochip_remove(&dev->gc);
 err_free_i2c:
@@ -1181,10 +1368,22 @@ err_hid_stop:
 static void cp2112_remove(struct hid_device *hdev)
 {
        struct cp2112_device *dev = hid_get_drvdata(hdev);
+       int i;
 
        sysfs_remove_group(&hdev->dev.kobj, &cp2112_attr_group);
-       gpiochip_remove(&dev->gc);
        i2c_del_adapter(&dev->adap);
+
+       if (dev->gpio_poll) {
+               dev->gpio_poll = false;
+               cancel_delayed_work_sync(&dev->gpio_poll_worker);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(dev->desc); i++) {
+               gpiochip_unlock_as_irq(&dev->gc, i);
+               gpiochip_free_own_desc(dev->desc[i]);
+       }
+
+       gpiochip_remove(&dev->gc);
        /* i2c_del_adapter has finished removing all i2c devices from our
         * adapter. Well behaved devices should no longer call our cp2112_xfer
         * and should have waited for any pending calls to finish. It has also
index b531bd408f938859eae9f45568cecf965cc7b12c..ec277b96eaa1b33461aa7702f38864598b910e59 100644 (file)
 #define USB_VENDOR_ID_DMI              0x0c0b
 #define USB_DEVICE_ID_DMI_ENC          0x5fab
 
-#define USB_VENDOR_ID_DRAGONRISE       0x0079
-#define USB_DEVICE_ID_DRAGONRISE_WIIU  0x1800
+#define USB_VENDOR_ID_DRAGONRISE               0x0079
+#define USB_DEVICE_ID_DRAGONRISE_WIIU          0x1800
+#define USB_DEVICE_ID_DRAGONRISE_PS3           0x1801
+#define USB_DEVICE_ID_DRAGONRISE_GAMECUBE      0x1843
 
 #define USB_VENDOR_ID_DWAV             0x0eef
 #define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER   0x0001
 #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3    0x07dc
 #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2  0x07e2
 #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd
+#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4 0x07e4
+#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2 0x07e8
 #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP 0x07e9
-#define USB_DEVICE_ID_MS_TYPE_COVER_3    0x07de
 #define USB_DEVICE_ID_MS_POWER_COVER     0x07da
 
 #define USB_VENDOR_ID_MOJO             0x8282
 #define USB_DEVICE_ID_SONY_PS3_BDREMOTE                0x0306
 #define USB_DEVICE_ID_SONY_PS3_CONTROLLER      0x0268
 #define USB_DEVICE_ID_SONY_PS4_CONTROLLER      0x05c4
+#define USB_DEVICE_ID_SONY_PS4_CONTROLLER_2    0x09cc
+#define USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE       0x0ba0
 #define USB_DEVICE_ID_SONY_MOTION_CONTROLLER   0x03d5
 #define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER       0x042f
 #define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER             0x0002
 #define USB_VENDOR_ID_THINGM           0x27b8
 #define USB_DEVICE_ID_BLINK1           0x01ed
 
+#define USB_VENDOR_ID_THQ              0x20d6
+#define USB_DEVICE_ID_THQ_PS3_UDRAW    0xcb17
+
 #define USB_VENDOR_ID_THRUSTMASTER     0x044f
 
 #define USB_VENDOR_ID_TIVO             0x150a
 #define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH    0x0500
 #define USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET        0x0502
 
+#define        USB_VENDOR_ID_WEIDA             0x2575
+#define        USB_DEVICE_ID_WEIDA_8752        0xC300
+#define        USB_DEVICE_ID_WEIDA_8755        0xC301
+
 #define USB_VENDOR_ID_WISEGROUP                0x0925
 #define USB_DEVICE_ID_SMARTJOY_PLUS    0x0005
 #define USB_DEVICE_ID_SUPER_JOY_BOX_3  0x8888
index fb9ace1cef8b50cbcb52955eb41f4490cb7aff7f..d05f903c7614530625d9adc696cdc7f3661abe15 100644 (file)
@@ -253,6 +253,7 @@ __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
        case ABS_RX:
        case ABS_RY:
        case ABS_RZ:
+       case ABS_WHEEL:
        case ABS_TILT_X:
        case ABS_TILT_Y:
                if (field->unit == 0x14) {              /* If degrees */
@@ -1468,6 +1469,31 @@ static void hidinput_cleanup_hidinput(struct hid_device *hid,
        kfree(hidinput);
 }
 
+static struct hid_input *hidinput_match(struct hid_report *report)
+{
+       struct hid_device *hid = report->device;
+       struct hid_input *hidinput;
+
+       list_for_each_entry(hidinput, &hid->inputs, list) {
+               if (hidinput->report &&
+                   hidinput->report->id == report->id)
+                       return hidinput;
+       }
+
+       return NULL;
+}
+
+static inline void hidinput_configure_usages(struct hid_input *hidinput,
+                                            struct hid_report *report)
+{
+       int i, j;
+
+       for (i = 0; i < report->maxfield; i++)
+               for (j = 0; j < report->field[i]->maxusage; j++)
+                       hidinput_configure_usage(hidinput, report->field[i],
+                                                report->field[i]->usage + j);
+}
+
 /*
  * Register the input device; print a message.
  * Configure the input layer interface
@@ -1478,8 +1504,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
 {
        struct hid_driver *drv = hid->driver;
        struct hid_report *report;
-       struct hid_input *hidinput = NULL;
-       int i, j, k;
+       struct hid_input *next, *hidinput = NULL;
+       int i, k;
 
        INIT_LIST_HEAD(&hid->inputs);
        INIT_WORK(&hid->led_work, hidinput_led_worker);
@@ -1509,43 +1535,40 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
                        if (!report->maxfield)
                                continue;
 
+                       /*
+                        * Find the previous hidinput report attached
+                        * to this report id.
+                        */
+                       if (hid->quirks & HID_QUIRK_MULTI_INPUT)
+                               hidinput = hidinput_match(report);
+
                        if (!hidinput) {
                                hidinput = hidinput_allocate(hid);
                                if (!hidinput)
                                        goto out_unwind;
                        }
 
-                       for (i = 0; i < report->maxfield; i++)
-                               for (j = 0; j < report->field[i]->maxusage; j++)
-                                       hidinput_configure_usage(hidinput, report->field[i],
-                                                                report->field[i]->usage + j);
-
-                       if ((hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) &&
-                           !hidinput_has_been_populated(hidinput))
-                               continue;
+                       hidinput_configure_usages(hidinput, report);
 
-                       if (hid->quirks & HID_QUIRK_MULTI_INPUT) {
-                               /* This will leave hidinput NULL, so that it
-                                * allocates another one if we have more inputs on
-                                * the same interface. Some devices (e.g. Happ's
-                                * UGCI) cram a lot of unrelated inputs into the
-                                * same interface. */
+                       if (hid->quirks & HID_QUIRK_MULTI_INPUT)
                                hidinput->report = report;
-                               if (drv->input_configured &&
-                                   drv->input_configured(hid, hidinput))
-                                       goto out_cleanup;
-                               if (input_register_device(hidinput->input))
-                                       goto out_cleanup;
-                               hidinput = NULL;
-                       }
                }
        }
 
-       if (hidinput && (hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) &&
-           !hidinput_has_been_populated(hidinput)) {
-               /* no need to register an input device not populated */
-               hidinput_cleanup_hidinput(hid, hidinput);
-               hidinput = NULL;
+       list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
+               if ((hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) &&
+                   !hidinput_has_been_populated(hidinput)) {
+                       /* no need to register an input device not populated */
+                       hidinput_cleanup_hidinput(hid, hidinput);
+                       continue;
+               }
+
+               if (drv->input_configured &&
+                   drv->input_configured(hid, hidinput))
+                       goto out_unwind;
+               if (input_register_device(hidinput->input))
+                       goto out_unwind;
+               hidinput->registered = true;
        }
 
        if (list_empty(&hid->inputs)) {
@@ -1553,20 +1576,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
                goto out_unwind;
        }
 
-       if (hidinput) {
-               if (drv->input_configured &&
-                   drv->input_configured(hid, hidinput))
-                       goto out_cleanup;
-               if (input_register_device(hidinput->input))
-                       goto out_cleanup;
-       }
-
        return 0;
 
-out_cleanup:
-       list_del(&hidinput->list);
-       input_free_device(hidinput->input);
-       kfree(hidinput);
 out_unwind:
        /* unwind the ones we already registered */
        hidinput_disconnect(hid);
@@ -1583,7 +1594,10 @@ void hidinput_disconnect(struct hid_device *hid)
 
        list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
                list_del(&hidinput->list);
-               input_unregister_device(hidinput->input);
+               if (hidinput->registered)
+                       input_unregister_device(hidinput->input);
+               else
+                       input_free_device(hidinput->input);
                kfree(hidinput);
        }
 
diff --git a/drivers/hid/hid-mf.c b/drivers/hid/hid-mf.c
new file mode 100644 (file)
index 0000000..d909076
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Force feedback support for Mayflash game controller adapters.
+ *
+ * These devices are manufactured by Mayflash but identify themselves
+ * using the vendor ID of DragonRise Inc.
+ *
+ * Tested with:
+ * 0079:1801 "DragonRise Inc. Mayflash PS3 Game Controller Adapter"
+ *
+ * The following adapters probably work too, but need to be tested:
+ * 0079:1800 "DragonRise Inc. Mayflash WIIU Game Controller Adapter"
+ * 0079:1843 "DragonRise Inc. Mayflash GameCube Game Controller Adapter"
+ *
+ * Copyright (c) 2016 Marcel Hasler <mahasler@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+struct mf_device {
+       struct hid_report *report;
+};
+
+static int mf_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+{
+       struct hid_device *hid = input_get_drvdata(dev);
+       struct mf_device *mf = data;
+       int strong, weak;
+
+       strong = effect->u.rumble.strong_magnitude;
+       weak = effect->u.rumble.weak_magnitude;
+
+       dbg_hid("Called with 0x%04x 0x%04x.\n", strong, weak);
+
+       strong = strong * 0xff / 0xffff;
+       weak = weak * 0xff / 0xffff;
+
+       dbg_hid("Running with 0x%02x 0x%02x.\n", strong, weak);
+
+       mf->report->field[0]->value[0] = weak;
+       mf->report->field[0]->value[1] = strong;
+       hid_hw_request(hid, mf->report, HID_REQ_SET_REPORT);
+
+       return 0;
+}
+
+static int mf_init(struct hid_device *hid)
+{
+       struct mf_device *mf;
+
+       struct list_head *report_list =
+                       &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+
+       struct list_head *report_ptr;
+       struct hid_report *report;
+
+       struct list_head *input_ptr = &hid->inputs;
+       struct hid_input *input;
+
+       struct input_dev *dev;
+
+       int error;
+
+       /* Setup each of the four inputs */
+       list_for_each(report_ptr, report_list) {
+               report = list_entry(report_ptr, struct hid_report, list);
+
+               if (report->maxfield < 1 || report->field[0]->report_count < 2) {
+                       hid_err(hid, "Invalid report, this should never happen!\n");
+                       return -ENODEV;
+               }
+
+               if (list_is_last(input_ptr, &hid->inputs)) {
+                       hid_err(hid, "Missing input, this should never happen!\n");
+                       return -ENODEV;
+               }
+
+               input_ptr = input_ptr->next;
+               input = list_entry(input_ptr, struct hid_input, list);
+
+               mf = kzalloc(sizeof(struct mf_device), GFP_KERNEL);
+               if (!mf)
+                       return -ENOMEM;
+
+               dev = input->input;
+               set_bit(FF_RUMBLE, dev->ffbit);
+
+               error = input_ff_create_memless(dev, mf, mf_play);
+               if (error) {
+                       kfree(mf);
+                       return error;
+               }
+
+               mf->report = report;
+               mf->report->field[0]->value[0] = 0x00;
+               mf->report->field[0]->value[1] = 0x00;
+               hid_hw_request(hid, mf->report, HID_REQ_SET_REPORT);
+       }
+
+       hid_info(hid, "Force feedback for HJZ Mayflash game controller "
+                     "adapters by Marcel Hasler <mahasler@gmail.com>\n");
+
+       return 0;
+}
+
+static int mf_probe(struct hid_device *hid, const struct hid_device_id *id)
+{
+       int error;
+
+       dev_dbg(&hid->dev, "Mayflash HID hardware probe...\n");
+
+       /* Split device into four inputs */
+       hid->quirks |= HID_QUIRK_MULTI_INPUT;
+
+       error = hid_parse(hid);
+       if (error) {
+               hid_err(hid, "HID parse failed.\n");
+               return error;
+       }
+
+       error = hid_hw_start(hid, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+       if (error) {
+               hid_err(hid, "HID hw start failed\n");
+               return error;
+       }
+
+       error = mf_init(hid);
+       if (error) {
+               hid_err(hid, "Force feedback init failed.\n");
+               hid_hw_stop(hid);
+               return error;
+       }
+
+       return 0;
+}
+
+static const struct hid_device_id mf_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3),  },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, mf_devices);
+
+static struct hid_driver mf_driver = {
+       .name = "hid_mf",
+       .id_table = mf_devices,
+       .probe = mf_probe,
+};
+module_hid_driver(mf_driver);
+
+MODULE_LICENSE("GPL");
index c6cd392e9f99c6cba2c5948bc2c42f2cf066e525..74b7b84a0420b7e40ec0f11868d1d3ee187ebe6a 100644 (file)
@@ -280,9 +280,11 @@ static const struct hid_device_id ms_devices[] = {
                .driver_data = MS_HIDINPUT },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP),
                .driver_data = MS_HIDINPUT },
-       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP),
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4),
+               .driver_data = MS_HIDINPUT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2),
                .driver_data = MS_HIDINPUT },
-       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3),
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP),
                .driver_data = MS_HIDINPUT },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER),
                .driver_data = MS_HIDINPUT },
index fb6f1f4472795700c1f12889c405b9350b4ebcb0..6dca668068440213d837f69c7763bddbe91e561b 100644 (file)
@@ -108,6 +108,7 @@ struct mt_device {
        int cc_value_index;     /* contact count value index in the field */
        unsigned last_slot_field;       /* the last field of a slot */
        unsigned mt_report_id;  /* the report ID of the multitouch device */
+       unsigned long initial_quirks;   /* initial quirks state */
        __s16 inputmode;        /* InputMode HID feature, -1 if non-existent */
        __s16 inputmode_index;  /* InputMode HID feature index in the report */
        __s16 maxcontact_report_id;     /* Maximum Contact Number HID feature,
@@ -318,13 +319,10 @@ static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)
        u8 *buf;
 
        /*
-        * Only fetch the feature report if initial reports are not already
-        * been retrieved. Currently this is only done for Windows 8 touch
-        * devices.
+        * Do not fetch the feature report if the device has been explicitly
+        * marked as non-capable.
         */
-       if (!(hdev->quirks & HID_QUIRK_NO_INIT_REPORTS))
-               return;
-       if (td->mtclass.name != MT_CLS_WIN_8)
+       if (td->initial_quirks & HID_QUIRK_NO_INIT_REPORTS)
                return;
 
        buf = hid_alloc_report_buf(report, GFP_KERNEL);
@@ -567,6 +565,14 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 
        case HID_UP_BUTTON:
                code = BTN_MOUSE + ((usage->hid - 1) & HID_USAGE);
+               /*
+                * MS PTP spec says that external buttons left and right have
+                * usages 2 and 3.
+                */
+               if (cls->name == MT_CLS_WIN_8 &&
+                   field->application == HID_DG_TOUCHPAD &&
+                   (usage->hid & HID_USAGE) > 1)
+                       code--;
                hid_map_usage(hi, usage, bit, max, EV_KEY, code);
                input_set_capability(hi->input, EV_KEY, code);
                return 1;
@@ -842,7 +848,9 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
        if (!td->mtclass.export_all_inputs &&
            field->application != HID_DG_TOUCHSCREEN &&
            field->application != HID_DG_PEN &&
-           field->application != HID_DG_TOUCHPAD)
+           field->application != HID_DG_TOUCHPAD &&
+           field->application != HID_GD_KEYBOARD &&
+           field->application != HID_CP_CONSUMER_CONTROL)
                return -1;
 
        /*
@@ -1083,36 +1091,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
                }
        }
 
-       /* This allows the driver to correctly support devices
-        * that emit events over several HID messages.
-        */
-       hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
-
-       /*
-        * This allows the driver to handle different input sensors
-        * that emits events through different reports on the same HID
-        * device.
-        */
-       hdev->quirks |= HID_QUIRK_MULTI_INPUT;
-       hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT;
-
-       /*
-        * Handle special quirks for Windows 8 certified devices.
-        */
-       if (id->group == HID_GROUP_MULTITOUCH_WIN_8)
-               /*
-                * Some multitouch screens do not like to be polled for input
-                * reports. Fortunately, the Win8 spec says that all touches
-                * should be sent during each report, making the initialization
-                * of input reports unnecessary.
-                *
-                * In addition some touchpads do not behave well if we read
-                * all feature reports from them. Instead we prevent
-                * initial report fetching and then selectively fetch each
-                * report we are interested in.
-                */
-               hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
-
        td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL);
        if (!td) {
                dev_err(&hdev->dev, "cannot allocate multitouch data\n");
@@ -1136,6 +1114,39 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
        if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
                td->serial_maybe = true;
 
+       /*
+        * Store the initial quirk state
+        */
+       td->initial_quirks = hdev->quirks;
+
+       /* This allows the driver to correctly support devices
+        * that emit events over several HID messages.
+        */
+       hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
+
+       /*
+        * This allows the driver to handle different input sensors
+        * that emits events through different reports on the same HID
+        * device.
+        */
+       hdev->quirks |= HID_QUIRK_MULTI_INPUT;
+       hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT;
+
+       /*
+        * Some multitouch screens do not like to be polled for input
+        * reports. Fortunately, the Win8 spec says that all touches
+        * should be sent during each report, making the initialization
+        * of input reports unnecessary. For Win7 devices, well, let's hope
+        * they will still be happy (this is only be a problem if a touch
+        * was already there while probing the device).
+        *
+        * In addition some touchpads do not behave well if we read
+        * all feature reports from them. Instead we prevent
+        * initial report fetching and then selectively fetch each
+        * report we are interested in.
+        */
+       hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
+
        ret = hid_parse(hdev);
        if (ret != 0)
                return ret;
@@ -1204,8 +1215,11 @@ static int mt_resume(struct hid_device *hdev)
 
 static void mt_remove(struct hid_device *hdev)
 {
+       struct mt_device *td = hid_get_drvdata(hdev);
+
        sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
        hid_hw_stop(hdev);
+       hdev->quirks = td->initial_quirks;
 }
 
 /*
index 60875625cbdff45725532b92a395fe7f329163ee..5c925228847c8e88653ec5fb00edfc053fa81784 100644 (file)
@@ -795,6 +795,12 @@ static const struct hid_device_id sensor_hub_devices[] = {
        { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
                        USB_DEVICE_ID_MS_TYPE_COVER_2),
                        .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+       { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT,
+                       0x07bd), /* Microsoft Surface 3 */
+                       .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
+       { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROCHIP,
+                       0x0f01), /* MM7150 */
+                       .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
        { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0,
                        USB_DEVICE_ID_STM_HID_SENSOR),
                        .driver_data = HID_SENSOR_HUB_ENUM_QUIRK},
index b0bb99a821bd777692cc845ec2992d95834ffeb6..7687c0875395d6b351928a156be6f5268cf50cfe 100644 (file)
@@ -36,6 +36,8 @@
 #include <linux/list.h>
 #include <linux/idr.h>
 #include <linux/input/mt.h>
+#include <linux/crc32.h>
+#include <asm/unaligned.h>
 
 #include "hid-ids.h"
 
@@ -374,7 +376,7 @@ static u8 dualshock4_usb_rdesc[] = {
        0x65, 0x00,         /*      Unit,                           */
        0x05, 0x09,         /*      Usage Page (Button),            */
        0x19, 0x01,         /*      Usage Minimum (01h),            */
-       0x29, 0x0E,         /*      Usage Maximum (0Eh),            */
+       0x29, 0x0D,         /*      Usage Maximum (0Dh),            */
        0x15, 0x00,         /*      Logical Minimum (0),            */
        0x25, 0x01,         /*      Logical Maximum (1),            */
        0x75, 0x01,         /*      Report Size (1),                */
@@ -403,14 +405,14 @@ static u8 dualshock4_usb_rdesc[] = {
        0x19, 0x40,         /*      Usage Minimum (40h),            */
        0x29, 0x42,         /*      Usage Maximum (42h),            */
        0x16, 0x00, 0x80,   /*      Logical Minimum (-32768),       */
-       0x26, 0x00, 0x7F,   /*      Logical Maximum (32767),        */
+       0x26, 0xFF, 0x7F,   /*      Logical Maximum (32767),        */
        0x75, 0x10,         /*      Report Size (16),               */
        0x95, 0x03,         /*      Report Count (3),               */
        0x81, 0x02,         /*      Input (Variable),               */
        0x19, 0x43,         /*      Usage Minimum (43h),            */
        0x29, 0x45,         /*      Usage Maximum (45h),            */
-       0x16, 0x00, 0xE0,   /*      Logical Minimum (-8192),        */
-       0x26, 0xFF, 0x1F,   /*      Logical Maximum (8191),         */
+       0x16, 0x00, 0x80,   /*      Logical Minimum (-32768),       */
+       0x26, 0xFF, 0x7F,   /*      Logical Maximum (32767),        */
        0x95, 0x03,         /*      Report Count (3),               */
        0x81, 0x02,         /*      Input (Variable),               */
        0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */
@@ -687,7 +689,7 @@ static u8 dualshock4_bt_rdesc[] = {
        0x81, 0x42,         /*      Input (Variable, Null State),   */
        0x05, 0x09,         /*      Usage Page (Button),            */
        0x19, 0x01,         /*      Usage Minimum (01h),            */
-       0x29, 0x0E,         /*      Usage Maximum (0Eh),            */
+       0x29, 0x0D,         /*      Usage Maximum (0Dh),            */
        0x15, 0x00,         /*      Logical Minimum (0),            */
        0x25, 0x01,         /*      Logical Maximum (1),            */
        0x75, 0x01,         /*      Report Size (1),                */
@@ -712,14 +714,14 @@ static u8 dualshock4_bt_rdesc[] = {
        0x19, 0x40,         /*      Usage Minimum (40h),            */
        0x29, 0x42,         /*      Usage Maximum (42h),            */
        0x16, 0x00, 0x80,   /*      Logical Minimum (-32768),       */
-       0x26, 0x00, 0x7F,   /*      Logical Maximum (32767),        */
+       0x26, 0xFF, 0x7F,   /*      Logical Maximum (32767),        */
        0x75, 0x10,         /*      Report Size (16),               */
        0x95, 0x03,         /*      Report Count (3),               */
        0x81, 0x02,         /*      Input (Variable),               */
        0x19, 0x43,         /*      Usage Minimum (43h),            */
        0x29, 0x45,         /*      Usage Maximum (45h),            */
-       0x16, 0x00, 0xE0,   /*      Logical Minimum (-8192),        */
-       0x26, 0xFF, 0x1F,   /*      Logical Maximum (8191),         */
+       0x16, 0x00, 0x80,   /*      Logical Minimum (-32768),       */
+       0x26, 0xFF, 0x7F,   /*      Logical Maximum (32767),        */
        0x95, 0x03,         /*      Report Count (3),               */
        0x81, 0x02,         /*      Input (Variable),               */
        0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */
@@ -975,6 +977,32 @@ static const unsigned int buzz_keymap[] = {
        [20] = BTN_TRIGGER_HAPPY20,
 };
 
+static const unsigned int ds4_absmap[] = {
+       [0x30] = ABS_X,
+       [0x31] = ABS_Y,
+       [0x32] = ABS_RX, /* right stick X */
+       [0x33] = ABS_Z, /* L2 */
+       [0x34] = ABS_RZ, /* R2 */
+       [0x35] = ABS_RY, /* right stick Y */
+};
+
+static const unsigned int ds4_keymap[] = {
+       [0x1] = BTN_WEST, /* Square */
+       [0x2] = BTN_SOUTH, /* Cross */
+       [0x3] = BTN_EAST, /* Circle */
+       [0x4] = BTN_NORTH, /* Triangle */
+       [0x5] = BTN_TL, /* L1 */
+       [0x6] = BTN_TR, /* R1 */
+       [0x7] = BTN_TL2, /* L2 */
+       [0x8] = BTN_TR2, /* R2 */
+       [0x9] = BTN_SELECT, /* Share */
+       [0xa] = BTN_START, /* Options */
+       [0xb] = BTN_THUMBL, /* L3 */
+       [0xc] = BTN_THUMBR, /* R3 */
+       [0xd] = BTN_MODE, /* PS */
+};
+
+
 static enum power_supply_property sony_battery_props[] = {
        POWER_SUPPLY_PROP_PRESENT,
        POWER_SUPPLY_PROP_CAPACITY,
@@ -1019,14 +1047,24 @@ struct motion_output_report_02 {
        u8 rumble;
 };
 
-#define DS4_REPORT_0x02_SIZE 37
-#define DS4_REPORT_0x05_SIZE 32
-#define DS4_REPORT_0x11_SIZE 78
-#define DS4_REPORT_0x81_SIZE 7
+#define DS4_FEATURE_REPORT_0x02_SIZE 37
+#define DS4_FEATURE_REPORT_0x81_SIZE 7
+#define DS4_INPUT_REPORT_0x11_SIZE 78
+#define DS4_OUTPUT_REPORT_0x05_SIZE 32
+#define DS4_OUTPUT_REPORT_0x11_SIZE 78
 #define SIXAXIS_REPORT_0xF2_SIZE 17
 #define SIXAXIS_REPORT_0xF5_SIZE 8
 #define MOTION_REPORT_0x02_SIZE 49
 
+/* Offsets relative to USB input report (0x1). Bluetooth (0x11) requires an
+ * additional +2.
+ */
+#define DS4_INPUT_REPORT_BUTTON_OFFSET    5
+#define DS4_INPUT_REPORT_BATTERY_OFFSET  30
+#define DS4_INPUT_REPORT_TOUCHPAD_OFFSET 33
+
+#define DS4_TOUCHPAD_SUFFIX " Touchpad"
+
 static DEFINE_SPINLOCK(sony_dev_list_lock);
 static LIST_HEAD(sony_device_list);
 static DEFINE_IDA(sony_device_id_allocator);
@@ -1035,6 +1073,7 @@ struct sony_sc {
        spinlock_t lock;
        struct list_head list_node;
        struct hid_device *hdev;
+       struct input_dev *touchpad;
        struct led_classdev *leds[MAX_LEDS];
        unsigned long quirks;
        struct work_struct state_worker;
@@ -1130,6 +1169,37 @@ static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi,
        return 1;
 }
 
+static int ds4_mapping(struct hid_device *hdev, struct hid_input *hi,
+                      struct hid_field *field, struct hid_usage *usage,
+                      unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {
+               unsigned int key = usage->hid & HID_USAGE;
+
+               if (key >= ARRAY_SIZE(ds4_keymap))
+                       return -1;
+
+               key = ds4_keymap[key];
+               hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key);
+               return 1;
+       } else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK) {
+               unsigned int abs = usage->hid & HID_USAGE;
+
+               /* Let the HID parser deal with the HAT. */
+               if (usage->hid == HID_GD_HATSWITCH)
+                       return 0;
+
+               if (abs >= ARRAY_SIZE(ds4_absmap))
+                       return -1;
+
+               abs = ds4_absmap[abs];
+               hid_map_usage_clear(hi, usage, bit, max, EV_ABS, abs);
+               return 1;
+       }
+
+       return 0;
+}
+
 static u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc,
                unsigned int *rsize)
 {
@@ -1219,23 +1289,22 @@ static void sixaxis_parse_report(struct sony_sc *sc, u8 *rd, int size)
 
 static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
 {
-       struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
-                                               struct hid_input, list);
-       struct input_dev *input_dev = hidinput->input;
        unsigned long flags;
-       int n, offset;
+       int n, m, offset, num_touch_data, max_touch_data;
        u8 cable_state, battery_capacity, battery_charging;
 
-       /*
-        * Battery and touchpad data starts at byte 30 in the USB report and
-        * 32 in Bluetooth report.
-        */
-       offset = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 30 : 32;
+       /* When using Bluetooth the header is 2 bytes longer, so skip these. */
+       int data_offset = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 0 : 2;
+
+       /* Second bit of third button byte is for the touchpad button. */
+       offset = data_offset + DS4_INPUT_REPORT_BUTTON_OFFSET;
+       input_report_key(sc->touchpad, BTN_LEFT, rd[offset+2] & 0x2);
 
        /*
-        * The lower 4 bits of byte 30 contain the battery level
+        * The lower 4 bits of byte 30 (or 32 for BT) contain the battery level
         * and the 5th bit contains the USB cable state.
         */
+       offset = data_offset + DS4_INPUT_REPORT_BATTERY_OFFSET;
        cable_state = (rd[offset] >> 4) & 0x01;
        battery_capacity = rd[offset] & 0x0F;
 
@@ -1262,30 +1331,52 @@ static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
        sc->battery_charging = battery_charging;
        spin_unlock_irqrestore(&sc->lock, flags);
 
-       offset += 5;
-
        /*
-        * The Dualshock 4 multi-touch trackpad data starts at offset 35 on USB
-        * and 37 on Bluetooth.
-        * The first 7 bits of the first byte is a counter and bit 8 is a touch
-        * indicator that is 0 when pressed and 1 when not pressed.
-        * The next 3 bytes are two 12 bit touch coordinates, X and Y.
-        * The data for the second touch is in the same format and immediatly
-        * follows the data for the first.
+        * The Dualshock 4 multi-touch trackpad data starts at offset 33 on USB
+        * and 35 on Bluetooth.
+        * The first byte indicates the number of touch data in the report.
+        * Trackpad data starts 2 bytes later (e.g. 35 for USB).
         */
-       for (n = 0; n < 2; n++) {
-               u16 x, y;
+       offset = data_offset + DS4_INPUT_REPORT_TOUCHPAD_OFFSET;
+       max_touch_data = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 3 : 4;
+       if (rd[offset] > 0 && rd[offset] <= max_touch_data)
+               num_touch_data = rd[offset];
+       else
+               num_touch_data = 1;
+       offset += 1;
+
+       for (m = 0; m < num_touch_data; m++) {
+               /* Skip past timestamp */
+               offset += 1;
+
+               /*
+                * The first 7 bits of the first byte is a counter and bit 8 is
+                * a touch indicator that is 0 when pressed and 1 when not
+                * pressed.
+                * The next 3 bytes are two 12 bit touch coordinates, X and Y.
+                * The data for the second touch is in the same format and
+                * immediately follows the data for the first.
+                */
+               for (n = 0; n < 2; n++) {
+                       u16 x, y;
+                       bool active;
 
-               x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8);
-               y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4);
+                       x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8);
+                       y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4);
 
-               input_mt_slot(input_dev, n);
-               input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
-                                       !(rd[offset] >> 7));
-               input_report_abs(input_dev, ABS_MT_POSITION_X, x);
-               input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+                       active = !(rd[offset] >> 7);
+                       input_mt_slot(sc->touchpad, n);
+                       input_mt_report_slot_state(sc->touchpad, MT_TOOL_FINGER, active);
 
-               offset += 4;
+                       if (active) {
+                               input_report_abs(sc->touchpad, ABS_MT_POSITION_X, x);
+                               input_report_abs(sc->touchpad, ABS_MT_POSITION_Y, y);
+                       }
+
+                       offset += 4;
+               }
+               input_mt_sync_frame(sc->touchpad);
+               input_sync(sc->touchpad);
        }
 }
 
@@ -1324,6 +1415,21 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
        } else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 &&
                        size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT)
                        && rd[0] == 0x11 && size == 78)) {
+               if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
+                       /* CRC check */
+                       u8 bthdr = 0xA1;
+                       u32 crc;
+                       u32 report_crc;
+
+                       crc = crc32_le(0xFFFFFFFF, &bthdr, 1);
+                       crc = ~crc32_le(crc, rd, DS4_INPUT_REPORT_0x11_SIZE-4);
+                       report_crc = get_unaligned_le32(&rd[DS4_INPUT_REPORT_0x11_SIZE-4]);
+                       if (crc != report_crc) {
+                               hid_dbg(sc->hdev, "DualShock 4 input report's CRC check failed, received crc 0x%0x != 0x%0x\n",
+                                       report_crc, crc);
+                               return -EILSEQ;
+                       }
+               }
                dualshock4_parse_report(sc, rd, size);
        }
 
@@ -1367,47 +1473,84 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
        if (sc->quirks & PS3REMOTE)
                return ps3remote_mapping(hdev, hi, field, usage, bit, max);
 
+
+       if (sc->quirks & DUALSHOCK4_CONTROLLER)
+               return ds4_mapping(hdev, hi, field, usage, bit, max);
+
        /* Let hid-core decide for the others */
        return 0;
 }
 
-static int sony_register_touchpad(struct hid_input *hi, int touch_count,
+static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
                                        int w, int h)
 {
-       struct input_dev *input_dev = hi->input;
+       size_t name_sz;
+       char *name;
        int ret;
 
-       ret = input_mt_init_slots(input_dev, touch_count, 0);
+       sc->touchpad = input_allocate_device();
+       if (!sc->touchpad)
+               return -ENOMEM;
+
+       input_set_drvdata(sc->touchpad, sc);
+       sc->touchpad->dev.parent = &sc->hdev->dev;
+       sc->touchpad->phys = sc->hdev->phys;
+       sc->touchpad->uniq = sc->hdev->uniq;
+       sc->touchpad->id.bustype = sc->hdev->bus;
+       sc->touchpad->id.vendor = sc->hdev->vendor;
+       sc->touchpad->id.product = sc->hdev->product;
+       sc->touchpad->id.version = sc->hdev->version;
+
+       /* Append a suffix to the controller name as there are various
+        * DS4 compatible non-Sony devices with different names.
+        */
+       name_sz = strlen(sc->hdev->name) + sizeof(DS4_TOUCHPAD_SUFFIX);
+       name = kzalloc(name_sz, GFP_KERNEL);
+       if (!name) {
+               ret = -ENOMEM;
+               goto err;
+       }
+       snprintf(name, name_sz, "%s" DS4_TOUCHPAD_SUFFIX, sc->hdev->name);
+       sc->touchpad->name = name;
+
+       ret = input_mt_init_slots(sc->touchpad, touch_count, 0);
        if (ret < 0)
-               return ret;
+               goto err;
+
+       /* We map the button underneath the touchpad to BTN_LEFT. */
+       __set_bit(EV_KEY, sc->touchpad->evbit);
+       __set_bit(BTN_LEFT, sc->touchpad->keybit);
+       __set_bit(INPUT_PROP_BUTTONPAD, sc->touchpad->propbit);
 
-       input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, w, 0, 0);
-       input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, h, 0, 0);
+       input_set_abs_params(sc->touchpad, ABS_MT_POSITION_X, 0, w, 0, 0);
+       input_set_abs_params(sc->touchpad, ABS_MT_POSITION_Y, 0, h, 0, 0);
+
+       ret = input_register_device(sc->touchpad);
+       if (ret < 0)
+               goto err;
 
        return 0;
+
+err:
+       kfree(sc->touchpad->name);
+       sc->touchpad->name = NULL;
+
+       input_free_device(sc->touchpad);
+       sc->touchpad = NULL;
+
+       return ret;
 }
 
-static int sony_input_configured(struct hid_device *hdev,
-                                       struct hid_input *hidinput)
+static void sony_unregister_touchpad(struct sony_sc *sc)
 {
-       struct sony_sc *sc = hid_get_drvdata(hdev);
-       int ret;
+       if (!sc->touchpad)
+               return;
 
-       /*
-        * The Dualshock 4 touchpad supports 2 touches and has a
-        * resolution of 1920x942 (44.86 dots/mm).
-        */
-       if (sc->quirks & DUALSHOCK4_CONTROLLER) {
-               ret = sony_register_touchpad(hidinput, 2, 1920, 942);
-               if (ret) {
-                       hid_err(sc->hdev,
-                               "Unable to initialize multi-touch slots: %d\n",
-                               ret);
-                       return ret;
-               }
-       }
+       kfree(sc->touchpad->name);
+       sc->touchpad->name = NULL;
 
-       return 0;
+       input_unregister_device(sc->touchpad);
+       sc->touchpad = NULL;
 }
 
 /*
@@ -1483,11 +1626,11 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
        u8 *buf;
        int ret;
 
-       buf = kmalloc(DS4_REPORT_0x02_SIZE, GFP_KERNEL);
+       buf = kmalloc(DS4_FEATURE_REPORT_0x02_SIZE, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
 
-       ret = hid_hw_raw_request(hdev, 0x02, buf, DS4_REPORT_0x02_SIZE,
+       ret = hid_hw_raw_request(hdev, 0x02, buf, DS4_FEATURE_REPORT_0x02_SIZE,
                                HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
 
        kfree(buf);
@@ -1892,14 +2035,14 @@ static void dualshock4_send_output_report(struct sony_sc *sc)
         * 0xD0 - 66hz
         */
        if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
-               memset(buf, 0, DS4_REPORT_0x05_SIZE);
+               memset(buf, 0, DS4_OUTPUT_REPORT_0x05_SIZE);
                buf[0] = 0x05;
                buf[1] = 0xFF;
                offset = 4;
        } else {
-               memset(buf, 0, DS4_REPORT_0x11_SIZE);
+               memset(buf, 0, DS4_OUTPUT_REPORT_0x11_SIZE);
                buf[0] = 0x11;
-               buf[1] = 0x80;
+               buf[1] = 0xC0; /* HID + CRC */
                buf[3] = 0x0F;
                offset = 6;
        }
@@ -1925,10 +2068,17 @@ static void dualshock4_send_output_report(struct sony_sc *sc)
        buf[offset++] = sc->led_delay_off[3];
 
        if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
-               hid_hw_output_report(hdev, buf, DS4_REPORT_0x05_SIZE);
-       else
-               hid_hw_raw_request(hdev, 0x11, buf, DS4_REPORT_0x11_SIZE,
-                               HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
+               hid_hw_output_report(hdev, buf, DS4_OUTPUT_REPORT_0x05_SIZE);
+       else {
+               /* CRC generation */
+               u8 bthdr = 0xA2;
+               u32 crc;
+
+               crc = crc32_le(0xFFFFFFFF, &bthdr, 1);
+               crc = ~crc32_le(crc, buf, DS4_OUTPUT_REPORT_0x11_SIZE-4);
+               put_unaligned_le32(crc, &buf[74]);
+               hid_hw_output_report(hdev, buf, DS4_OUTPUT_REPORT_0x11_SIZE);
+       }
 }
 
 static void motion_send_output_report(struct sony_sc *sc)
@@ -1972,10 +2122,10 @@ static int sony_allocate_output_report(struct sony_sc *sc)
                        kmalloc(sizeof(union sixaxis_output_report_01),
                                GFP_KERNEL);
        else if (sc->quirks & DUALSHOCK4_CONTROLLER_BT)
-               sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x11_SIZE,
+               sc->output_report_dmabuf = kmalloc(DS4_OUTPUT_REPORT_0x11_SIZE,
                                                GFP_KERNEL);
        else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
-               sc->output_report_dmabuf = kmalloc(DS4_REPORT_0x05_SIZE,
+               sc->output_report_dmabuf = kmalloc(DS4_OUTPUT_REPORT_0x05_SIZE,
                                                GFP_KERNEL);
        else if (sc->quirks & MOTION_CONTROLLER)
                sc->output_report_dmabuf = kmalloc(MOTION_REPORT_0x02_SIZE,
@@ -2220,7 +2370,7 @@ static int sony_check_add(struct sony_sc *sc)
                        return 0;
                }
        } else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
-               buf = kmalloc(DS4_REPORT_0x81_SIZE, GFP_KERNEL);
+               buf = kmalloc(DS4_FEATURE_REPORT_0x81_SIZE, GFP_KERNEL);
                if (!buf)
                        return -ENOMEM;
 
@@ -2230,10 +2380,10 @@ static int sony_check_add(struct sony_sc *sc)
                 * offset 1.
                 */
                ret = hid_hw_raw_request(sc->hdev, 0x81, buf,
-                               DS4_REPORT_0x81_SIZE, HID_FEATURE_REPORT,
+                               DS4_FEATURE_REPORT_0x81_SIZE, HID_FEATURE_REPORT,
                                HID_REQ_GET_REPORT);
 
-               if (ret != DS4_REPORT_0x81_SIZE) {
+               if (ret != DS4_FEATURE_REPORT_0x81_SIZE) {
                        hid_err(sc->hdev, "failed to retrieve feature report 0x81 with the DualShock 4 MAC address\n");
                        ret = ret < 0 ? ret : -EINVAL;
                        goto out_free;
@@ -2329,45 +2479,12 @@ static inline void sony_cancel_work_sync(struct sony_sc *sc)
                cancel_work_sync(&sc->state_worker);
 }
 
-static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
+static int sony_input_configured(struct hid_device *hdev,
+                                       struct hid_input *hidinput)
 {
-       int ret;
+       struct sony_sc *sc = hid_get_drvdata(hdev);
        int append_dev_id;
-       unsigned long quirks = id->driver_data;
-       struct sony_sc *sc;
-       unsigned int connect_mask = HID_CONNECT_DEFAULT;
-
-       if (!strcmp(hdev->name, "FutureMax Dance Mat"))
-               quirks |= FUTUREMAX_DANCE_MAT;
-
-       sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL);
-       if (sc == NULL) {
-               hid_err(hdev, "can't alloc sony descriptor\n");
-               return -ENOMEM;
-       }
-
-       spin_lock_init(&sc->lock);
-
-       sc->quirks = quirks;
-       hid_set_drvdata(hdev, sc);
-       sc->hdev = hdev;
-
-       ret = hid_parse(hdev);
-       if (ret) {
-               hid_err(hdev, "parse failed\n");
-               return ret;
-       }
-
-       if (sc->quirks & VAIO_RDESC_CONSTANT)
-               connect_mask |= HID_CONNECT_HIDDEV_FORCE;
-       else if (sc->quirks & SIXAXIS_CONTROLLER)
-               connect_mask |= HID_CONNECT_HIDDEV_FORCE;
-
-       ret = hid_hw_start(hdev, connect_mask);
-       if (ret) {
-               hid_err(hdev, "hw start failed\n");
-               return ret;
-       }
+       int ret;
 
        ret = sony_set_device_id(sc);
        if (ret < 0) {
@@ -2415,11 +2532,6 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                sony_init_output_report(sc, sixaxis_send_output_report);
        } else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
                if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
-                       /*
-                        * The DualShock 4 wants output reports sent on the ctrl
-                        * endpoint when connected via Bluetooth.
-                        */
-                       hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
                        ret = dualshock4_set_operational_bt(hdev);
                        if (ret < 0) {
                                hid_err(hdev, "failed to set the Dualshock 4 operational mode\n");
@@ -2427,6 +2539,18 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                        }
                }
 
+               /*
+                * The Dualshock 4 touchpad supports 2 touches and has a
+                * resolution of 1920x942 (44.86 dots/mm).
+                */
+               ret = sony_register_touchpad(sc, 2, 1920, 942);
+               if (ret) {
+                       hid_err(sc->hdev,
+                       "Unable to initialize multi-touch slots: %d\n",
+                       ret);
+                       return ret;
+               }
+
                sony_init_output_report(sc, dualshock4_send_output_report);
        } else if (sc->quirks & MOTION_CONTROLLER) {
                sony_init_output_report(sc, motion_send_output_report);
@@ -2482,17 +2606,84 @@ err_stop:
        return ret;
 }
 
+static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+       unsigned long quirks = id->driver_data;
+       struct sony_sc *sc;
+       unsigned int connect_mask = HID_CONNECT_DEFAULT;
+
+       if (!strcmp(hdev->name, "FutureMax Dance Mat"))
+               quirks |= FUTUREMAX_DANCE_MAT;
+
+       sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL);
+       if (sc == NULL) {
+               hid_err(hdev, "can't alloc sony descriptor\n");
+               return -ENOMEM;
+       }
+
+       spin_lock_init(&sc->lock);
+
+       sc->quirks = quirks;
+       hid_set_drvdata(hdev, sc);
+       sc->hdev = hdev;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               hid_err(hdev, "parse failed\n");
+               return ret;
+       }
+
+       if (sc->quirks & VAIO_RDESC_CONSTANT)
+               connect_mask |= HID_CONNECT_HIDDEV_FORCE;
+       else if (sc->quirks & SIXAXIS_CONTROLLER)
+               connect_mask |= HID_CONNECT_HIDDEV_FORCE;
+
+       /* Patch the hw version on DS4 compatible devices, so applications can
+        * distinguish between the default HID mappings and the mappings defined
+        * by the Linux game controller spec. This is important for the SDL2
+        * library, which has a game controller database, which uses device ids
+        * in combination with version as a key.
+        */
+       if (sc->quirks & DUALSHOCK4_CONTROLLER)
+               hdev->version |= 0x8000;
+
+       ret = hid_hw_start(hdev, connect_mask);
+       if (ret) {
+               hid_err(hdev, "hw start failed\n");
+               return ret;
+       }
+
+       /* sony_input_configured can fail, but this doesn't result
+        * in hid_hw_start failures (intended). Check whether
+        * the HID layer claimed the device else fail.
+        * We don't know the actual reason for the failure, most
+        * likely it is due to EEXIST in case of double connection
+        * of USB and Bluetooth, but could have been due to ENOMEM
+        * or other reasons as well.
+        */
+       if (!(hdev->claimed & HID_CLAIMED_INPUT)) {
+               hid_err(hdev, "failed to claim input\n");
+               return -ENODEV;
+       }
+
+       return ret;
+}
+
 static void sony_remove(struct hid_device *hdev)
 {
        struct sony_sc *sc = hid_get_drvdata(hdev);
 
+       hid_hw_close(hdev);
+
        if (sc->quirks & SONY_LED_SUPPORT)
                sony_leds_remove(sc);
 
-       if (sc->quirks & SONY_BATTERY_SUPPORT) {
-               hid_hw_close(hdev);
+       if (sc->quirks & SONY_BATTERY_SUPPORT)
                sony_battery_remove(sc);
-       }
+
+       if (sc->touchpad)
+               sony_unregister_touchpad(sc);
 
        sony_cancel_work_sync(sc);
 
@@ -2596,6 +2787,12 @@ static const struct hid_device_id sony_devices[] = {
                .driver_data = DUALSHOCK4_CONTROLLER_USB },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER),
                .driver_data = DUALSHOCK4_CONTROLLER_BT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2),
+               .driver_data = DUALSHOCK4_CONTROLLER_USB },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2),
+               .driver_data = DUALSHOCK4_CONTROLLER_BT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE),
+               .driver_data = DUALSHOCK4_CONTROLLER_USB },
        /* Nyko Core Controller for PS3 */
        { HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER),
                .driver_data = SIXAXIS_CONTROLLER_USB | SINO_LITE_CONTROLLER },
diff --git a/drivers/hid/hid-udraw-ps3.c b/drivers/hid/hid-udraw-ps3.c
new file mode 100644 (file)
index 0000000..88ea390
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+ * HID driver for THQ PS3 uDraw tablet
+ *
+ * Copyright (C) 2016 Red Hat Inc. All Rights Reserved
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include "hid-ids.h"
+
+MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
+MODULE_DESCRIPTION("PS3 uDraw tablet driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Protocol information from:
+ * http://brandonw.net/udraw/
+ * and the source code of:
+ * https://vvvv.org/contribution/udraw-hid
+ */
+
+/*
+ * The device is setup with multiple input devices:
+ * - the touch area which works as a touchpad
+ * - the tablet area which works as a touchpad/drawing tablet
+ * - a joypad with a d-pad, and 7 buttons
+ * - an accelerometer device
+ */
+
+enum {
+       TOUCH_NONE,
+       TOUCH_PEN,
+       TOUCH_FINGER,
+       TOUCH_TWOFINGER
+};
+
+enum {
+       AXIS_X,
+       AXIS_Y,
+       AXIS_Z
+};
+
+/*
+ * Accelerometer min/max values
+ * in order, X, Y and Z
+ */
+static struct {
+       int min;
+       int max;
+} accel_limits[] = {
+       [AXIS_X] = { 490, 534 },
+       [AXIS_Y] = { 490, 534 },
+       [AXIS_Z] = { 492, 536 }
+};
+
+#define DEVICE_NAME "THQ uDraw Game Tablet for PS3"
+/* resolution in pixels */
+#define RES_X 1920
+#define RES_Y 1080
+/* size in mm */
+#define WIDTH  160
+#define HEIGHT 90
+#define PRESSURE_OFFSET 113
+#define MAX_PRESSURE (255 - PRESSURE_OFFSET)
+
+struct udraw {
+       struct input_dev *joy_input_dev;
+       struct input_dev *touch_input_dev;
+       struct input_dev *pen_input_dev;
+       struct input_dev *accel_input_dev;
+       struct hid_device *hdev;
+
+       /*
+        * The device's two-finger support is pretty unreliable, as
+        * the device could report a single touch when the two fingers
+        * are too close together, and the distance between fingers, even
+        * though reported is not in the same unit as the touches.
+        *
+        * We'll make do without it, and try to report the first touch
+        * as reliably as possible.
+        */
+       int last_one_finger_x;
+       int last_one_finger_y;
+       int last_two_finger_x;
+       int last_two_finger_y;
+};
+
+static int clamp_accel(int axis, int offset)
+{
+       axis = clamp(axis,
+                       accel_limits[offset].min,
+                       accel_limits[offset].max);
+       axis = (axis - accel_limits[offset].min) /
+                       ((accel_limits[offset].max -
+                         accel_limits[offset].min) * 0xFF);
+       return axis;
+}
+
+static int udraw_raw_event(struct hid_device *hdev, struct hid_report *report,
+        u8 *data, int len)
+{
+       struct udraw *udraw = hid_get_drvdata(hdev);
+       int touch;
+       int x, y, z;
+
+       if (len != 27)
+               return 0;
+
+       if (data[11] == 0x00)
+               touch = TOUCH_NONE;
+       else if (data[11] == 0x40)
+               touch = TOUCH_PEN;
+       else if (data[11] == 0x80)
+               touch = TOUCH_FINGER;
+       else
+               touch = TOUCH_TWOFINGER;
+
+       /* joypad */
+       input_report_key(udraw->joy_input_dev, BTN_WEST, data[0] & 1);
+       input_report_key(udraw->joy_input_dev, BTN_SOUTH, !!(data[0] & 2));
+       input_report_key(udraw->joy_input_dev, BTN_EAST, !!(data[0] & 4));
+       input_report_key(udraw->joy_input_dev, BTN_NORTH, !!(data[0] & 8));
+
+       input_report_key(udraw->joy_input_dev, BTN_SELECT, !!(data[1] & 1));
+       input_report_key(udraw->joy_input_dev, BTN_START, !!(data[1] & 2));
+       input_report_key(udraw->joy_input_dev, BTN_MODE, !!(data[1] & 16));
+
+       x = y = 0;
+       switch (data[2]) {
+       case 0x0:
+               y = -127;
+               break;
+       case 0x1:
+               y = -127;
+               x = 127;
+               break;
+       case 0x2:
+               x = 127;
+               break;
+       case 0x3:
+               y = 127;
+               x = 127;
+               break;
+       case 0x4:
+               y = 127;
+               break;
+       case 0x5:
+               y = 127;
+               x = -127;
+               break;
+       case 0x6:
+               x = -127;
+               break;
+       case 0x7:
+               y = -127;
+               x = -127;
+               break;
+       default:
+               break;
+       }
+
+       input_report_abs(udraw->joy_input_dev, ABS_X, x);
+       input_report_abs(udraw->joy_input_dev, ABS_Y, y);
+
+       input_sync(udraw->joy_input_dev);
+
+       /* For pen and touchpad */
+       x = y = 0;
+       if (touch != TOUCH_NONE) {
+               if (data[15] != 0x0F)
+                       x = data[15] * 256 + data[17];
+               if (data[16] != 0x0F)
+                       y = data[16] * 256 + data[18];
+       }
+
+       if (touch == TOUCH_FINGER) {
+               /* Save the last one-finger touch */
+               udraw->last_one_finger_x = x;
+               udraw->last_one_finger_y = y;
+               udraw->last_two_finger_x = -1;
+               udraw->last_two_finger_y = -1;
+       } else if (touch == TOUCH_TWOFINGER) {
+               /*
+                * We have a problem because x/y is the one for the
+                * second finger but we want the first finger given
+                * to user-space otherwise it'll look as if it jumped.
+                *
+                * See the udraw struct definition for why this was
+                * implemented this way.
+                */
+               if (udraw->last_two_finger_x == -1) {
+                       /* Save the position of the 2nd finger */
+                       udraw->last_two_finger_x = x;
+                       udraw->last_two_finger_y = y;
+
+                       x = udraw->last_one_finger_x;
+                       y = udraw->last_one_finger_y;
+               } else {
+                       /*
+                        * Offset the 2-finger coords using the
+                        * saved data from the first finger
+                        */
+                       x = x - (udraw->last_two_finger_x
+                               - udraw->last_one_finger_x);
+                       y = y - (udraw->last_two_finger_y
+                               - udraw->last_one_finger_y);
+               }
+       }
+
+       /* touchpad */
+       if (touch == TOUCH_FINGER || touch == TOUCH_TWOFINGER) {
+               input_report_key(udraw->touch_input_dev, BTN_TOUCH, 1);
+               input_report_key(udraw->touch_input_dev, BTN_TOOL_FINGER,
+                               touch == TOUCH_FINGER);
+               input_report_key(udraw->touch_input_dev, BTN_TOOL_DOUBLETAP,
+                               touch == TOUCH_TWOFINGER);
+
+               input_report_abs(udraw->touch_input_dev, ABS_X, x);
+               input_report_abs(udraw->touch_input_dev, ABS_Y, y);
+       } else {
+               input_report_key(udraw->touch_input_dev, BTN_TOUCH, 0);
+               input_report_key(udraw->touch_input_dev, BTN_TOOL_FINGER, 0);
+               input_report_key(udraw->touch_input_dev, BTN_TOOL_DOUBLETAP, 0);
+       }
+       input_sync(udraw->touch_input_dev);
+
+       /* pen */
+       if (touch == TOUCH_PEN) {
+               int level;
+
+               level = clamp(data[13] - PRESSURE_OFFSET,
+                               0, MAX_PRESSURE);
+
+               input_report_key(udraw->pen_input_dev, BTN_TOUCH, (level != 0));
+               input_report_key(udraw->pen_input_dev, BTN_TOOL_PEN, 1);
+               input_report_abs(udraw->pen_input_dev, ABS_PRESSURE, level);
+               input_report_abs(udraw->pen_input_dev, ABS_X, x);
+               input_report_abs(udraw->pen_input_dev, ABS_Y, y);
+       } else {
+               input_report_key(udraw->pen_input_dev, BTN_TOUCH, 0);
+               input_report_key(udraw->pen_input_dev, BTN_TOOL_PEN, 0);
+               input_report_abs(udraw->pen_input_dev, ABS_PRESSURE, 0);
+       }
+       input_sync(udraw->pen_input_dev);
+
+       /* accel */
+       x = (data[19] + (data[20] << 8));
+       x = clamp_accel(x, AXIS_X);
+       y = (data[21] + (data[22] << 8));
+       y = clamp_accel(y, AXIS_Y);
+       z = (data[23] + (data[24] << 8));
+       z = clamp_accel(z, AXIS_Z);
+       input_report_abs(udraw->accel_input_dev, ABS_X, x);
+       input_report_abs(udraw->accel_input_dev, ABS_Y, y);
+       input_report_abs(udraw->accel_input_dev, ABS_Z, z);
+       input_sync(udraw->accel_input_dev);
+
+       /* let hidraw and hiddev handle the report */
+       return 0;
+}
+
+static int udraw_open(struct input_dev *dev)
+{
+       struct udraw *udraw = input_get_drvdata(dev);
+
+       return hid_hw_open(udraw->hdev);
+}
+
+static void udraw_close(struct input_dev *dev)
+{
+       struct udraw *udraw = input_get_drvdata(dev);
+
+       hid_hw_close(udraw->hdev);
+}
+
+static struct input_dev *allocate_and_setup(struct hid_device *hdev,
+               const char *name)
+{
+       struct input_dev *input_dev;
+
+       input_dev = devm_input_allocate_device(&hdev->dev);
+       if (!input_dev)
+               return NULL;
+
+       input_dev->name = name;
+       input_dev->phys = hdev->phys;
+       input_dev->dev.parent = &hdev->dev;
+       input_dev->open = udraw_open;
+       input_dev->close = udraw_close;
+       input_dev->uniq = hdev->uniq;
+       input_dev->id.bustype = hdev->bus;
+       input_dev->id.vendor  = hdev->vendor;
+       input_dev->id.product = hdev->product;
+       input_dev->id.version = hdev->version;
+       input_set_drvdata(input_dev, hid_get_drvdata(hdev));
+
+       return input_dev;
+}
+
+static bool udraw_setup_touch(struct udraw *udraw,
+               struct hid_device *hdev)
+{
+       struct input_dev *input_dev;
+
+       input_dev = allocate_and_setup(hdev, DEVICE_NAME " Touchpad");
+       if (!input_dev)
+               return false;
+
+       input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
+
+       input_set_abs_params(input_dev, ABS_X, 0, RES_X, 1, 0);
+       input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH);
+       input_set_abs_params(input_dev, ABS_Y, 0, RES_Y, 1, 0);
+       input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT);
+
+       set_bit(BTN_TOUCH, input_dev->keybit);
+       set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+       set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+
+       set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
+       udraw->touch_input_dev = input_dev;
+
+       return true;
+}
+
+static bool udraw_setup_pen(struct udraw *udraw,
+               struct hid_device *hdev)
+{
+       struct input_dev *input_dev;
+
+       input_dev = allocate_and_setup(hdev, DEVICE_NAME " Pen");
+       if (!input_dev)
+               return false;
+
+       input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
+
+       input_set_abs_params(input_dev, ABS_X, 0, RES_X, 1, 0);
+       input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH);
+       input_set_abs_params(input_dev, ABS_Y, 0, RES_Y, 1, 0);
+       input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT);
+       input_set_abs_params(input_dev, ABS_PRESSURE,
+                       0, MAX_PRESSURE, 0, 0);
+
+       set_bit(BTN_TOUCH, input_dev->keybit);
+       set_bit(BTN_TOOL_PEN, input_dev->keybit);
+
+       set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
+       udraw->pen_input_dev = input_dev;
+
+       return true;
+}
+
+static bool udraw_setup_accel(struct udraw *udraw,
+               struct hid_device *hdev)
+{
+       struct input_dev *input_dev;
+
+       input_dev = allocate_and_setup(hdev, DEVICE_NAME " Accelerometer");
+       if (!input_dev)
+               return false;
+
+       input_dev->evbit[0] = BIT(EV_ABS);
+
+       /* 1G accel is reported as ~256, so clamp to 2G */
+       input_set_abs_params(input_dev, ABS_X, -512, 512, 0, 0);
+       input_set_abs_params(input_dev, ABS_Y, -512, 512, 0, 0);
+       input_set_abs_params(input_dev, ABS_Z, -512, 512, 0, 0);
+
+       set_bit(INPUT_PROP_ACCELEROMETER, input_dev->propbit);
+
+       udraw->accel_input_dev = input_dev;
+
+       return true;
+}
+
+static bool udraw_setup_joypad(struct udraw *udraw,
+               struct hid_device *hdev)
+{
+       struct input_dev *input_dev;
+
+       input_dev = allocate_and_setup(hdev, DEVICE_NAME " Joypad");
+       if (!input_dev)
+               return false;
+
+       input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+
+       set_bit(BTN_SOUTH, input_dev->keybit);
+       set_bit(BTN_NORTH, input_dev->keybit);
+       set_bit(BTN_EAST, input_dev->keybit);
+       set_bit(BTN_WEST, input_dev->keybit);
+       set_bit(BTN_SELECT, input_dev->keybit);
+       set_bit(BTN_START, input_dev->keybit);
+       set_bit(BTN_MODE, input_dev->keybit);
+
+       input_set_abs_params(input_dev, ABS_X, -127, 127, 0, 0);
+       input_set_abs_params(input_dev, ABS_Y, -127, 127, 0, 0);
+
+       udraw->joy_input_dev = input_dev;
+
+       return true;
+}
+
+static int udraw_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       struct udraw *udraw;
+       int ret;
+
+       udraw = devm_kzalloc(&hdev->dev, sizeof(struct udraw), GFP_KERNEL);
+       if (!udraw)
+               return -ENOMEM;
+
+       udraw->hdev = hdev;
+       udraw->last_two_finger_x = -1;
+       udraw->last_two_finger_y = -1;
+
+       hid_set_drvdata(hdev, udraw);
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               hid_err(hdev, "parse failed\n");
+               return ret;
+       }
+
+       if (!udraw_setup_joypad(udraw, hdev) ||
+           !udraw_setup_touch(udraw, hdev) ||
+           !udraw_setup_pen(udraw, hdev) ||
+           !udraw_setup_accel(udraw, hdev)) {
+               hid_err(hdev, "could not allocate interfaces\n");
+               return -ENOMEM;
+       }
+
+       ret = input_register_device(udraw->joy_input_dev) ||
+               input_register_device(udraw->touch_input_dev) ||
+               input_register_device(udraw->pen_input_dev) ||
+               input_register_device(udraw->accel_input_dev);
+       if (ret) {
+               hid_err(hdev, "failed to register interfaces\n");
+               return ret;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER);
+       if (ret) {
+               hid_err(hdev, "hw start failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct hid_device_id udraw_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, udraw_devices);
+
+static struct hid_driver udraw_driver = {
+       .name = "hid-udraw",
+       .id_table = udraw_devices,
+       .raw_event = udraw_raw_event,
+       .probe = udraw_probe,
+};
+module_hid_driver(udraw_driver);
index b3ec4f2de875e3e0e9626fd1296778089cb2b226..78fb32a7b103446136000c8ee9ac64cf7fba7054 100644 (file)
@@ -22,6 +22,7 @@
 #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/mutex.h>
 #include <linux/acpi.h>
 #include <linux/of.h>
-#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
 
 #include <linux/i2c/i2c-hid.h>
 
+#include "../hid-ids.h"
+
+/* quirks to control the device */
+#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV       BIT(0)
+
 /* flags */
 #define I2C_HID_STARTED                0
 #define I2C_HID_RESET_PENDING  1
@@ -143,10 +149,9 @@ struct i2c_hid {
        char                    *argsbuf;       /* Command arguments buffer */
 
        unsigned long           flags;          /* device flags */
+       unsigned long           quirks;         /* Various quirks */
 
        wait_queue_head_t       wait;           /* For waiting the interrupt */
-       struct gpio_desc        *desc;
-       int                     irq;
 
        struct i2c_hid_platform_data pdata;
 
@@ -154,6 +159,39 @@ struct i2c_hid {
        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 },
+       { 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,
@@ -346,11 +384,27 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state)
 
        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;
 }
 
@@ -716,9 +770,11 @@ static int i2c_hid_start(struct hid_device *hid)
        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;
@@ -806,18 +862,21 @@ static struct hid_ll_driver 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", ihid->irq);
+       dev_dbg(&client->dev, "Requesting IRQ: %d\n", client->irq);
 
-       ret = request_threaded_irq(ihid->irq, NULL, i2c_hid_irq,
-                       IRQF_TRIGGER_LOW | IRQF_ONESHOT,
-                       client->name, ihid);
+       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, ihid->irq, ret);
+                       client->name, client->irq, ret);
 
                return ret;
        }
@@ -864,14 +923,6 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
 }
 
 #ifdef CONFIG_ACPI
-
-/* Default GPIO mapping */
-static const struct acpi_gpio_params i2c_hid_irq_gpio = { 0, 0, true };
-static const struct acpi_gpio_mapping i2c_hid_acpi_gpios[] = {
-       { "gpios", &i2c_hid_irq_gpio, 1 },
-       { },
-};
-
 static int i2c_hid_acpi_pdata(struct i2c_client *client,
                struct i2c_hid_platform_data *pdata)
 {
@@ -882,7 +933,6 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,
        union acpi_object *obj;
        struct acpi_device *adev;
        acpi_handle handle;
-       int ret;
 
        handle = ACPI_HANDLE(&client->dev);
        if (!handle || acpi_bus_get_device(handle, &adev))
@@ -898,9 +948,7 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,
        pdata->hid_descriptor_address = obj->integer.value;
        ACPI_FREE(obj);
 
-       /* GPIOs are optional */
-       ret = acpi_dev_add_driver_gpios(adev, i2c_hid_acpi_gpios);
-       return ret < 0 && ret != -ENXIO ? ret : 0;
+       return 0;
 }
 
 static const struct acpi_device_id i2c_hid_acpi_match[] = {
@@ -964,6 +1012,19 @@ static int i2c_hid_probe(struct i2c_client *client,
 
        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 = kzalloc(sizeof(struct i2c_hid), GFP_KERNEL);
        if (!ihid)
                return -ENOMEM;
@@ -983,23 +1044,6 @@ static int i2c_hid_probe(struct i2c_client *client,
                ihid->pdata = *platform_data;
        }
 
-       if (client->irq > 0) {
-               ihid->irq = client->irq;
-       } else if (ACPI_COMPANION(&client->dev)) {
-               ihid->desc = gpiod_get(&client->dev, NULL, GPIOD_IN);
-               if (IS_ERR(ihid->desc)) {
-                       dev_err(&client->dev, "Failed to get GPIO interrupt\n");
-                       return PTR_ERR(ihid->desc);
-               }
-
-               ihid->irq = gpiod_to_irq(ihid->desc);
-               if (ihid->irq < 0) {
-                       gpiod_put(ihid->desc);
-                       dev_err(&client->dev, "Failed to convert GPIO to IRQ\n");
-                       return ihid->irq;
-               }
-       }
-
        i2c_set_clientdata(client, ihid);
 
        ihid->client = client;
@@ -1050,6 +1094,8 @@ static int i2c_hid_probe(struct i2c_client *client,
                 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)
@@ -1064,16 +1110,13 @@ err_mem_free:
        hid_destroy_device(hid);
 
 err_irq:
-       free_irq(ihid->irq, ihid);
+       free_irq(client->irq, ihid);
 
 err_pm:
        pm_runtime_put_noidle(&client->dev);
        pm_runtime_disable(&client->dev);
 
 err:
-       if (ihid->desc)
-               gpiod_put(ihid->desc);
-
        i2c_hid_free_buffers(ihid);
        kfree(ihid);
        return ret;
@@ -1092,18 +1135,13 @@ static int i2c_hid_remove(struct i2c_client *client)
        hid = ihid->hid;
        hid_destroy_device(hid);
 
-       free_irq(ihid->irq, ihid);
+       free_irq(client->irq, ihid);
 
        if (ihid->bufsize)
                i2c_hid_free_buffers(ihid);
 
-       if (ihid->desc)
-               gpiod_put(ihid->desc);
-
        kfree(ihid);
 
-       acpi_dev_remove_driver_gpios(ACPI_COMPANION(&client->dev));
-
        return 0;
 }
 
@@ -1142,11 +1180,11 @@ static int i2c_hid_suspend(struct device *dev)
                /* Save some power */
                i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
 
-               disable_irq(ihid->irq);
+               disable_irq(client->irq);
        }
 
        if (device_may_wakeup(&client->dev)) {
-               wake_status = enable_irq_wake(ihid->irq);
+               wake_status = enable_irq_wake(client->irq);
                if (!wake_status)
                        ihid->irq_wake_enabled = true;
                else
@@ -1166,7 +1204,7 @@ static int i2c_hid_resume(struct device *dev)
        int wake_status;
 
        if (device_may_wakeup(&client->dev) && ihid->irq_wake_enabled) {
-               wake_status = disable_irq_wake(ihid->irq);
+               wake_status = disable_irq_wake(client->irq);
                if (!wake_status)
                        ihid->irq_wake_enabled = false;
                else
@@ -1179,7 +1217,7 @@ static int i2c_hid_resume(struct device *dev)
        pm_runtime_set_active(dev);
        pm_runtime_enable(dev);
 
-       enable_irq(ihid->irq);
+       enable_irq(client->irq);
        ret = i2c_hid_hwreset(client);
        if (ret)
                return ret;
@@ -1197,19 +1235,17 @@ static int i2c_hid_resume(struct device *dev)
 static int i2c_hid_runtime_suspend(struct device *dev)
 {
        struct i2c_client *client = to_i2c_client(dev);
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
 
        i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
-       disable_irq(ihid->irq);
+       disable_irq(client->irq);
        return 0;
 }
 
 static int i2c_hid_runtime_resume(struct device *dev)
 {
        struct i2c_client *client = to_i2c_client(dev);
-       struct i2c_hid *ihid = i2c_get_clientdata(client);
 
-       enable_irq(ihid->irq);
+       enable_irq(client->irq);
        i2c_hid_set_power(client, I2C_HID_PWR_ON);
        return 0;
 }
index 0c9ac4d5d85007e52e45c7f2a3c413c304901264..842d8416a7a6f0c8d585eacece02aa319ea20ce3 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/jiffies.h>
 #include "client.h"
 #include "hw-ish.h"
-#include "utils.h"
 #include "hbm.h"
 
 /* For FW reset flow */
@@ -310,6 +309,7 @@ static int write_ipc_from_queue(struct ishtp_device *dev)
                                                ((uint32_t)tv_utc.tv_usec);
                ts_format.ts1_source = HOST_SYSTEM_TIME_USEC;
                ts_format.ts2_source = HOST_UTC_TIME_USEC;
+               ts_format.reserved = 0;
 
                time_update.primary_host_time = usec_system;
                time_update.secondary_host_time = usec_utc;
@@ -427,6 +427,59 @@ static int ipc_send_mng_msg(struct ishtp_device *dev, uint32_t msg_code,
                sizeof(uint32_t) + size);
 }
 
+#define WAIT_FOR_FW_RDY                        0x1
+#define WAIT_FOR_INPUT_RDY             0x2
+
+/**
+ * timed_wait_for_timeout() - wait special event with timeout
+ * @dev: ISHTP device pointer
+ * @condition: indicate the condition for waiting
+ * @timeinc: time slice for every wait cycle, in ms
+ * @timeout: time in ms for timeout
+ *
+ * This function will check special event to be ready in a loop, the loop
+ * period is specificd in timeinc. Wait timeout will causes failure.
+ *
+ * Return: 0 for success else failure code
+ */
+static int timed_wait_for_timeout(struct ishtp_device *dev, int condition,
+                               unsigned int timeinc, unsigned int timeout)
+{
+       bool complete = false;
+       int ret;
+
+       do {
+               if (condition == WAIT_FOR_FW_RDY) {
+                       complete = ishtp_fw_is_ready(dev);
+               } else if (condition == WAIT_FOR_INPUT_RDY) {
+                       complete = ish_is_input_ready(dev);
+               } else {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               if (!complete) {
+                       unsigned long left_time;
+
+                       left_time = msleep_interruptible(timeinc);
+                       timeout -= (timeinc - left_time);
+               }
+       } while (!complete && timeout > 0);
+
+       if (complete)
+               ret = 0;
+       else
+               ret = -EBUSY;
+
+out:
+       return ret;
+}
+
+#define TIME_SLICE_FOR_FW_RDY_MS               100
+#define TIME_SLICE_FOR_INPUT_RDY_MS            100
+#define TIMEOUT_FOR_FW_RDY_MS                  2000
+#define TIMEOUT_FOR_INPUT_RDY_MS               2000
+
 /**
  * ish_fw_reset_handler() - FW reset handler
  * @dev: ishtp device pointer
@@ -456,8 +509,8 @@ static int ish_fw_reset_handler(struct ishtp_device *dev)
        ishtp_reset_handler(dev);
 
        if (!ish_is_input_ready(dev))
-               timed_wait_for_timeout(WAIT_FOR_SEND_SLICE,
-                       ish_is_input_ready(dev), (2 * HZ));
+               timed_wait_for_timeout(dev, WAIT_FOR_INPUT_RDY,
+                       TIME_SLICE_FOR_INPUT_RDY_MS, TIMEOUT_FOR_INPUT_RDY_MS);
 
        /* ISH FW is dead */
        if (!ish_is_input_ready(dev))
@@ -472,8 +525,8 @@ static int ish_fw_reset_handler(struct ishtp_device *dev)
                         sizeof(uint32_t));
 
        /* Wait for ISH FW'es ILUP and ISHTP_READY */
-       timed_wait_for_timeout(WAIT_FOR_SEND_SLICE, ishtp_fw_is_ready(dev),
-               (2 * HZ));
+       timed_wait_for_timeout(dev, WAIT_FOR_FW_RDY,
+                       TIME_SLICE_FOR_FW_RDY_MS, TIMEOUT_FOR_FW_RDY_MS);
        if (!ishtp_fw_is_ready(dev)) {
                /* ISH FW is dead */
                uint32_t        ish_status;
@@ -487,6 +540,8 @@ static int ish_fw_reset_handler(struct ishtp_device *dev)
        return  0;
 }
 
+#define TIMEOUT_FOR_HW_RDY_MS                  300
+
 /**
  * ish_fw_reset_work_fn() - FW reset worker function
  * @unused: not used
@@ -500,7 +555,7 @@ static void fw_reset_work_fn(struct work_struct *unused)
        rv = ish_fw_reset_handler(ishtp_dev);
        if (!rv) {
                /* ISH is ILUP & ISHTP-ready. Restart ISHTP */
-               schedule_timeout(HZ / 3);
+               msleep_interruptible(TIMEOUT_FOR_HW_RDY_MS);
                ishtp_dev->recvd_hw_ready = 1;
                wake_up_interruptible(&ishtp_dev->wait_hw_ready);
 
diff --git a/drivers/hid/intel-ish-hid/ipc/utils.h b/drivers/hid/intel-ish-hid/ipc/utils.h
deleted file mode 100644 (file)
index 5a82123..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Utility macros of ISH
- *
- * Copyright (c) 2014-2016, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- */
-#ifndef UTILS__H
-#define UTILS__H
-
-#define        WAIT_FOR_SEND_SLICE     (HZ / 10)
-#define        WAIT_FOR_CONNECT_SLICE  (HZ / 10)
-
-/*
- * Waits for specified event when a thread that triggers event can't signal
- * Also, waits *at_least* `timeinc` after condition is satisfied
- */
-#define        timed_wait_for(timeinc, condition)                      \
-       do {                                                    \
-               int completed = 0;                              \
-               do {                                            \
-                       unsigned long   j;                      \
-                       int     done = 0;                       \
-                                                               \
-                       completed = (condition);                \
-                       for (j = jiffies, done = 0; !done; ) {  \
-                               schedule_timeout(timeinc);      \
-                               if (time_is_before_eq_jiffies(j + timeinc)) \
-                                       done = 1;               \
-                       }                                       \
-               } while (!(completed));                         \
-       } while (0)
-
-
-/*
- * Waits for specified event when a thread that triggers event
- * can't signal with timeout (use whenever we may hang)
- */
-#define        timed_wait_for_timeout(timeinc, condition, timeout)     \
-       do {                                                    \
-               int     t = timeout;                            \
-               do {                                            \
-                       unsigned long   j;                      \
-                       int     done = 0;                       \
-                                                               \
-                       for (j = jiffies, done = 0; !done; ) {  \
-                               schedule_timeout(timeinc);      \
-                               if (time_is_before_eq_jiffies(j + timeinc)) \
-                                       done = 1;               \
-                       } \
-                       t -= timeinc;                           \
-                       if (t <= 0)                             \
-                               break;                          \
-               } while (!(condition));                         \
-       } while (0)
-
-#endif /* UTILS__H */
index 256521509d200a4362df4a56467df5ccf261d837..f4cbc744e6571da94f5d0187ff4cf6e15a2fcd63 100644 (file)
@@ -585,14 +585,7 @@ int ishtp_bus_new_client(struct ishtp_device *dev)
         */
        i = dev->fw_client_presentation_num - 1;
        device_uuid = dev->fw_clients[i].props.protocol_name;
-       dev_name = kasprintf(GFP_KERNEL,
-               "{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
-               device_uuid.b[3], device_uuid.b[2], device_uuid.b[1],
-               device_uuid.b[0], device_uuid.b[5], device_uuid.b[4],
-               device_uuid.b[7], device_uuid.b[6], device_uuid.b[8],
-               device_uuid.b[9], device_uuid.b[10], device_uuid.b[11],
-               device_uuid.b[12], device_uuid.b[13], device_uuid.b[14],
-               device_uuid.b[15]);
+       dev_name = kasprintf(GFP_KERNEL, "{%pUL}", device_uuid.b);
        if (!dev_name)
                return  -ENOMEM;
 
index 74bffee60774f984ac1e3353e7adb4a8d5ecd4a4..59460b66e6890d06313d8098dcac152339f15b0b 100644 (file)
@@ -378,11 +378,10 @@ static void ishtp_hbm_cl_disconnect_res(struct ishtp_device *dev,
        list_for_each_entry(cl, &dev->cl_list, link) {
                if (!rs->status && ishtp_hbm_cl_addr_equal(cl, rs)) {
                        cl->state = ISHTP_CL_DISCONNECTED;
+                       wake_up_interruptible(&cl->wait_ctrl_res);
                        break;
                }
        }
-       if (cl)
-               wake_up_interruptible(&cl->wait_ctrl_res);
        spin_unlock_irqrestore(&dev->cl_list_lock, flags);
 }
 
@@ -431,11 +430,10 @@ static void ishtp_hbm_cl_connect_res(struct ishtp_device *dev,
                                cl->state = ISHTP_CL_DISCONNECTED;
                                cl->status = -ENODEV;
                        }
+                       wake_up_interruptible(&cl->wait_ctrl_res);
                        break;
                }
        }
-       if (cl)
-               wake_up_interruptible(&cl->wait_ctrl_res);
        spin_unlock_irqrestore(&dev->cl_list_lock, flags);
 }
 
index ae83af649a607f67239f1a64bf45dd4b5770cc7d..333108ef18cf2f3f94ee3816b2ba6522017295b6 100644 (file)
@@ -1459,7 +1459,7 @@ static int hid_post_reset(struct usb_interface *intf)
        rdesc = kmalloc(hid->dev_rsize, GFP_KERNEL);
        if (!rdesc) {
                dbg_hid("couldn't allocate rdesc memory (post_reset)\n");
-               return 1;
+               return -ENOMEM;
        }
        status = hid_get_class_descriptor(dev,
                                interface->desc.bInterfaceNumber,
@@ -1467,13 +1467,13 @@ static int hid_post_reset(struct usb_interface *intf)
        if (status < 0) {
                dbg_hid("reading report descriptor failed (post_reset)\n");
                kfree(rdesc);
-               return 1;
+               return status;
        }
        status = memcmp(rdesc, hid->dev_rdesc, hid->dev_rsize);
        kfree(rdesc);
        if (status != 0) {
                dbg_hid("report descriptor changed\n");
-               return 1;
+               return -EPERM;
        }
 
        /* No need to do another reset or clear a halted endpoint */
index e6cfd323babc62d653146730e4d7325ee8a24696..b3e01c82af0512dce7a68e6bde908d7e3afeaba8 100644 (file)
@@ -82,6 +82,8 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT },
+       { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3, HID_QUIRK_MULTI_INPUT },
+       { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE, HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_ELAN, HID_ANY_ID, HID_QUIRK_ALWAYS_POLL },
        { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
@@ -101,8 +103,9 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4, HID_QUIRK_NO_INIT_REPORTS },
+       { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_2, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP, HID_QUIRK_NO_INIT_REPORTS },
-       { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
index b4800ea891cb88ff43c194efe8d98fe7837c580a..d303e413306df41a1e3f080d5bdd3c00efa75caf 100644 (file)
@@ -210,7 +210,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
                                       struct wacom_wac *wacom_wac);
 void wacom_wac_usage_mapping(struct hid_device *hdev,
                struct hid_field *field, struct hid_usage *usage);
-int wacom_wac_event(struct hid_device *hdev, struct hid_field *field,
+void wacom_wac_event(struct hid_device *hdev, struct hid_field *field,
                struct hid_usage *usage, __s32 value);
 void wacom_wac_report(struct hid_device *hdev, struct hid_report *report);
 void wacom_battery_work(struct work_struct *work);
index 5e7a5648e7084e09e484b1c51cd38dd3d79a109f..b9779bcbd1403f00114f9565c543df79583baa38 100644 (file)
@@ -122,6 +122,7 @@ static void wacom_feature_mapping(struct hid_device *hdev,
        struct hid_data *hid_data = &wacom->wacom_wac.hid_data;
        u8 *data;
        int ret;
+       int n;
 
        switch (usage->hid) {
        case HID_DG_CONTACTMAX:
@@ -159,22 +160,48 @@ static void wacom_feature_mapping(struct hid_device *hdev,
 
        case HID_UP_DIGITIZER:
                if (field->report->id == 0x0B &&
-                   (field->application == WACOM_G9_DIGITIZER ||
-                    field->application == WACOM_G11_DIGITIZER)) {
+                   (field->application == WACOM_HID_G9_PEN ||
+                    field->application == WACOM_HID_G11_PEN)) {
                        wacom->wacom_wac.mode_report = field->report->id;
                        wacom->wacom_wac.mode_value = 0;
                }
                break;
 
-       case WACOM_G9_PAGE:
-       case WACOM_G11_PAGE:
+       case WACOM_HID_WD_DATAMODE:
+               wacom->wacom_wac.mode_report = field->report->id;
+               wacom->wacom_wac.mode_value = 2;
+               break;
+
+       case WACOM_HID_UP_G9:
+       case WACOM_HID_UP_G11:
                if (field->report->id == 0x03 &&
-                   (field->application == WACOM_G9_TOUCHSCREEN ||
-                    field->application == WACOM_G11_TOUCHSCREEN)) {
+                   (field->application == WACOM_HID_G9_TOUCHSCREEN ||
+                    field->application == WACOM_HID_G11_TOUCHSCREEN)) {
                        wacom->wacom_wac.mode_report = field->report->id;
                        wacom->wacom_wac.mode_value = 0;
                }
                break;
+       case WACOM_HID_WD_OFFSETLEFT:
+       case WACOM_HID_WD_OFFSETTOP:
+       case WACOM_HID_WD_OFFSETRIGHT:
+       case WACOM_HID_WD_OFFSETBOTTOM:
+               /* read manually */
+               n = hid_report_len(field->report);
+               data = hid_alloc_report_buf(field->report, GFP_KERNEL);
+               if (!data)
+                       break;
+               data[0] = field->report->id;
+               ret = wacom_get_report(hdev, HID_FEATURE_REPORT,
+                                       data, n, WAC_CMD_RETRIES);
+               if (ret == n) {
+                       ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT,
+                                                  data, n, 0);
+               } else {
+                       hid_warn(hdev, "%s: could not retrieve sensor offsets\n",
+                                __func__);
+               }
+               kfree(data);
+               break;
        }
 }
 
@@ -240,6 +267,30 @@ static void wacom_usage_mapping(struct hid_device *hdev,
                        features->touch_max = 1;
        }
 
+       /*
+        * ISDv4 devices which predate HID's adoption of the
+        * HID_DG_BARELSWITCH2 usage use 0x000D0000 in its
+        * position instead. We can accurately detect if a
+        * usage with that value should be HID_DG_BARRELSWITCH2
+        * based on the surrounding usages, which have remained
+        * constant across generations.
+        */
+       if (features->type == HID_GENERIC &&
+           usage->hid == 0x000D0000 &&
+           field->application == HID_DG_PEN &&
+           field->physical == HID_DG_STYLUS) {
+               int i = usage->usage_index;
+
+               if (i-4 >= 0 && i+1 < field->maxusage &&
+                   field->usage[i-4].hid == HID_DG_TIPSWITCH &&
+                   field->usage[i-3].hid == HID_DG_BARRELSWITCH &&
+                   field->usage[i-2].hid == HID_DG_ERASER &&
+                   field->usage[i-1].hid == HID_DG_INVERT &&
+                   field->usage[i+1].hid == HID_DG_INRANGE) {
+                       usage->hid = HID_DG_BARRELSWITCH2;
+               }
+       }
+
        switch (usage->hid) {
        case HID_GD_X:
                features->x_max = field->logical_maximum;
@@ -689,11 +740,6 @@ static int wacom_add_shared_data(struct hid_device *hdev)
                return retval;
        }
 
-       if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)
-               wacom_wac->shared->touch = hdev;
-       else if (wacom_wac->features.device_type & WACOM_DEVICETYPE_PEN)
-               wacom_wac->shared->pen = hdev;
-
 out:
        mutex_unlock(&wacom_udev_list_lock);
        return retval;
@@ -1916,6 +1962,19 @@ static void wacom_update_name(struct wacom *wacom, const char *suffix)
                                /* shift everything including the terminator */
                                memmove(gap, gap+1, strlen(gap));
                        }
+
+                       /* strip off excessive prefixing */
+                       if (strstr(name, "Wacom Co.,Ltd. Wacom ") == name) {
+                               int n = strlen(name);
+                               int x = strlen("Wacom Co.,Ltd. ");
+                               memmove(name, name+x, n-x+1);
+                       }
+                       if (strstr(name, "Wacom Co., Ltd. Wacom ") == name) {
+                               int n = strlen(name);
+                               int x = strlen("Wacom Co., Ltd. ");
+                               memmove(name, name+x, n-x+1);
+                       }
+
                        /* get rid of trailing whitespace */
                        if (name[strlen(name)-1] == ' ')
                                name[strlen(name)-1] = '\0';
@@ -1977,6 +2036,10 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
        if (error)
                goto fail;
 
+       error = wacom_add_shared_data(hdev);
+       if (error)
+               goto fail;
+
        /*
         * Bamboo Pad has a generic hid handling for the Pen, and we switch it
         * into debug mode for the touch part.
@@ -2017,9 +2080,10 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
 
        wacom_update_name(wacom, wireless ? " (WL)" : "");
 
-       error = wacom_add_shared_data(hdev);
-       if (error)
-               goto fail;
+       if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)
+               wacom_wac->shared->touch = hdev;
+       else if (wacom_wac->features.device_type & WACOM_DEVICETYPE_PEN)
+               wacom_wac->shared->pen = hdev;
 
        if (!(features->device_type & WACOM_DEVICETYPE_WL_MONITOR) &&
             (features->quirks & WACOM_QUIRK_BATTERY)) {
index 1cb79925730d931a0ee00f3ff0f470464731d3b3..b1a9a3ca6d564c72d3f445e663b196af87743ed1 100644 (file)
@@ -41,6 +41,8 @@ MODULE_PARM_DESC(touch_arbitration, " on (Y) off (N)");
 static void wacom_report_numbered_buttons(struct input_dev *input_dev,
                                int button_count, int mask);
 
+static int wacom_numbered_button_to_key(int n);
+
 /*
  * Percent of battery capacity for Graphire.
  * 8th value means AC online and show 100% capacity.
@@ -588,6 +590,11 @@ static int wacom_intuos_pad(struct wacom_wac *wacom)
        return 1;
 }
 
+static int wacom_intuos_id_mangle(int tool_id)
+{
+       return (tool_id & ~0xFFF) << 4 | (tool_id & 0xFFF);
+}
+
 static int wacom_intuos_get_tool_type(int tool_id)
 {
        int tool_type;
@@ -595,7 +602,7 @@ static int wacom_intuos_get_tool_type(int tool_id)
        switch (tool_id) {
        case 0x812: /* Inking pen */
        case 0x801: /* Intuos3 Inking pen */
-       case 0x120802: /* Intuos4/5 Inking Pen */
+       case 0x12802: /* Intuos4/5 Inking Pen */
        case 0x012:
                tool_type = BTN_TOOL_PENCIL;
                break;
@@ -610,11 +617,11 @@ static int wacom_intuos_get_tool_type(int tool_id)
        case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */
        case 0x8e2: /* IntuosHT2 pen */
        case 0x022:
-       case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */
-       case 0x140802: /* Intuos4/5 13HD/24HD Classic Pen */
-       case 0x160802: /* Cintiq 13HD Pro Pen */
-       case 0x180802: /* DTH2242 Pen */
-       case 0x100802: /* Intuos4/5 13HD/24HD General Pen */
+       case 0x10804: /* Intuos4/5 13HD/24HD Art Pen */
+       case 0x14802: /* Intuos4/5 13HD/24HD Classic Pen */
+       case 0x16802: /* Cintiq 13HD Pro Pen */
+       case 0x18802: /* DTH2242 Pen */
+       case 0x10802: /* Intuos4/5 13HD/24HD General Pen */
                tool_type = BTN_TOOL_PEN;
                break;
 
@@ -638,6 +645,7 @@ static int wacom_intuos_get_tool_type(int tool_id)
                break;
 
        case 0x82a: /* Eraser */
+       case 0x84a:
        case 0x85a:
        case 0x91a:
        case 0xd1a:
@@ -648,12 +656,12 @@ static int wacom_intuos_get_tool_type(int tool_id)
        case 0x80c: /* Intuos4/5 13HD/24HD Marker Pen Eraser */
        case 0x80a: /* Intuos4/5 13HD/24HD General Pen Eraser */
        case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
-       case 0x14080a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */
-       case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
-       case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */
-       case 0x16080a: /* Cintiq 13HD Pro Pen Eraser */
-       case 0x18080a: /* DTH2242 Eraser */
-       case 0x10080a: /* Intuos4/5 13HD/24HD General Pen Eraser */
+       case 0x1480a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */
+       case 0x1090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
+       case 0x1080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */
+       case 0x1680a: /* Cintiq 13HD Pro Pen Eraser */
+       case 0x1880a: /* DTH2242 Eraser */
+       case 0x1080a: /* Intuos4/5 13HD/24HD General Pen Eraser */
                tool_type = BTN_TOOL_RUBBER;
                break;
 
@@ -662,7 +670,7 @@ static int wacom_intuos_get_tool_type(int tool_id)
        case 0x112:
        case 0x913: /* Intuos3 Airbrush */
        case 0x902: /* Intuos4/5 13HD/24HD Airbrush */
-       case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */
+       case 0x10902: /* Intuos4/5 13HD/24HD Airbrush */
                tool_type = BTN_TOOL_AIRBRUSH;
                break;
 
@@ -693,7 +701,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
                        (data[6] << 4) + (data[7] >> 4);
 
                wacom->id[idx] = (data[2] << 4) | (data[3] >> 4) |
-                       ((data[7] & 0x0f) << 20) | ((data[8] & 0xf0) << 12);
+                    ((data[7] & 0x0f) << 16) | ((data[8] & 0xf0) << 8);
 
                wacom->tool[idx] = wacom_intuos_get_tool_type(wacom->id[idx]);
 
@@ -923,7 +931,7 @@ static int wacom_intuos_general(struct wacom_wac *wacom)
         * don't report events for invalid data
         */
        /* older I4 styli don't work with new Cintiqs */
-       if ((!((wacom->id[idx] >> 20) & 0x01) &&
+       if ((!((wacom->id[idx] >> 16) & 0x01) &&
                        (features->type == WACOM_21UX2)) ||
            /* Only large Intuos support Lense Cursor */
            (wacom->tool[idx] == BTN_TOOL_LENS &&
@@ -1059,7 +1067,8 @@ static int wacom_intuos_general(struct wacom_wac *wacom)
                break;
        }
 
-       input_report_abs(input, ABS_MISC, wacom->id[idx]); /* report tool id */
+       input_report_abs(input, ABS_MISC,
+                        wacom_intuos_id_mangle(wacom->id[idx])); /* report tool id */
        input_report_key(input, wacom->tool[idx], 1);
        input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
        wacom->reporting_data = true;
@@ -1435,11 +1444,59 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
        return 0;
 }
 
+static int wacom_equivalent_usage(int usage)
+{
+       if ((usage & HID_USAGE_PAGE) == WACOM_HID_UP_WACOMDIGITIZER) {
+               int subpage = (usage & 0xFF00) << 8;
+               int subusage = (usage & 0xFF);
+
+               if (subpage == WACOM_HID_SP_PAD ||
+                   subpage == WACOM_HID_SP_BUTTON ||
+                   subpage == WACOM_HID_SP_DIGITIZER ||
+                   subpage == WACOM_HID_SP_DIGITIZERINFO ||
+                   usage == WACOM_HID_WD_SENSE ||
+                   usage == WACOM_HID_WD_SERIALHI ||
+                   usage == WACOM_HID_WD_TOOLTYPE ||
+                   usage == WACOM_HID_WD_DISTANCE ||
+                   usage == WACOM_HID_WD_TOUCHSTRIP ||
+                   usage == WACOM_HID_WD_TOUCHSTRIP2 ||
+                   usage == WACOM_HID_WD_TOUCHRING ||
+                   usage == WACOM_HID_WD_TOUCHRINGSTATUS) {
+                       return usage;
+               }
+
+               if (subpage == HID_UP_UNDEFINED)
+                       subpage = HID_UP_DIGITIZER;
+
+               return subpage | subusage;
+       }
+
+       return usage;
+}
+
 static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage,
                struct hid_field *field, __u8 type, __u16 code, int fuzz)
 {
+       struct wacom *wacom = input_get_drvdata(input);
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct wacom_features *features = &wacom_wac->features;
        int fmin = field->logical_minimum;
        int fmax = field->logical_maximum;
+       unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid);
+       int resolution_code = code;
+
+       if (equivalent_usage == HID_DG_TWIST) {
+               resolution_code = ABS_RZ;
+       }
+
+       if (equivalent_usage == HID_GD_X) {
+               fmin += features->offset_left;
+               fmax -= features->offset_right;
+       }
+       if (equivalent_usage == HID_GD_Y) {
+               fmin += features->offset_top;
+               fmax -= features->offset_bottom;
+       }
 
        usage->type = type;
        usage->code = code;
@@ -1450,7 +1507,7 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage,
        case EV_ABS:
                input_set_abs_params(input, code, fmin, fmax, fuzz, 0);
                input_abs_set_res(input, code,
-                                 hidinput_calc_abs_res(field, code));
+                                 hidinput_calc_abs_res(field, resolution_code));
                break;
        case EV_KEY:
                input_set_capability(input, EV_KEY, code);
@@ -1458,6 +1515,172 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage,
        case EV_MSC:
                input_set_capability(input, EV_MSC, code);
                break;
+       case EV_SW:
+               input_set_capability(input, EV_SW, code);
+               break;
+       }
+}
+
+static void wacom_wac_pad_usage_mapping(struct hid_device *hdev,
+               struct hid_field *field, struct hid_usage *usage)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct wacom_features *features = &wacom_wac->features;
+       struct input_dev *input = wacom_wac->pad_input;
+       unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
+
+       switch (equivalent_usage) {
+       case WACOM_HID_WD_BATTERY_LEVEL:
+       case WACOM_HID_WD_BATTERY_CHARGING:
+               features->quirks |= WACOM_QUIRK_BATTERY;
+               break;
+       case WACOM_HID_WD_ACCELEROMETER_X:
+               __set_bit(INPUT_PROP_ACCELEROMETER, input->propbit);
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 0);
+               features->device_type |= WACOM_DEVICETYPE_PAD;
+               break;
+       case WACOM_HID_WD_ACCELEROMETER_Y:
+               __set_bit(INPUT_PROP_ACCELEROMETER, input->propbit);
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 0);
+               features->device_type |= WACOM_DEVICETYPE_PAD;
+               break;
+       case WACOM_HID_WD_ACCELEROMETER_Z:
+               __set_bit(INPUT_PROP_ACCELEROMETER, input->propbit);
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_Z, 0);
+               features->device_type |= WACOM_DEVICETYPE_PAD;
+               break;
+       case WACOM_HID_WD_BUTTONHOME:
+       case WACOM_HID_WD_BUTTONUP:
+       case WACOM_HID_WD_BUTTONDOWN:
+       case WACOM_HID_WD_BUTTONLEFT:
+       case WACOM_HID_WD_BUTTONRIGHT:
+       case WACOM_HID_WD_BUTTONCENTER:
+               wacom_map_usage(input, usage, field, EV_KEY,
+                               wacom_numbered_button_to_key(features->numbered_buttons),
+                               0);
+               features->numbered_buttons++;
+               features->device_type |= WACOM_DEVICETYPE_PAD;
+               break;
+       case WACOM_HID_WD_TOUCHONOFF:
+               wacom_map_usage(input, usage, field, EV_SW, SW_MUTE_DEVICE, 0);
+               features->device_type |= WACOM_DEVICETYPE_PAD;
+               break;
+       case WACOM_HID_WD_TOUCHSTRIP:
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_RX, 0);
+               features->device_type |= WACOM_DEVICETYPE_PAD;
+               break;
+       case WACOM_HID_WD_TOUCHSTRIP2:
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_RY, 0);
+               features->device_type |= WACOM_DEVICETYPE_PAD;
+               break;
+       case WACOM_HID_WD_TOUCHRING:
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0);
+               features->device_type |= WACOM_DEVICETYPE_PAD;
+               break;
+       }
+
+       switch (equivalent_usage & 0xfffffff0) {
+       case WACOM_HID_WD_EXPRESSKEY00:
+               wacom_map_usage(input, usage, field, EV_KEY,
+                               wacom_numbered_button_to_key(features->numbered_buttons),
+                               0);
+               features->numbered_buttons++;
+               features->device_type |= WACOM_DEVICETYPE_PAD;
+               break;
+       }
+}
+
+static void wacom_wac_pad_battery_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
+
+       switch (equivalent_usage) {
+       case WACOM_HID_WD_BATTERY_LEVEL:
+               wacom_wac->hid_data.battery_capacity = value;
+               wacom_wac->hid_data.bat_connected = 1;
+               break;
+
+       case WACOM_HID_WD_BATTERY_CHARGING:
+               wacom_wac->hid_data.bat_charging = value;
+               wacom_wac->hid_data.ps_connected = value;
+               wacom_wac->hid_data.bat_connected = 1;
+               break;
+       }
+}
+
+static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field,
+               struct hid_usage *usage, __s32 value)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct input_dev *input = wacom_wac->pad_input;
+       struct wacom_features *features = &wacom_wac->features;
+       unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
+
+       if (wacom_equivalent_usage(field->physical) == HID_DG_TABLETFUNCTIONKEY) {
+               wacom_wac->hid_data.inrange_state |= value;
+       }
+
+       switch (equivalent_usage) {
+       case WACOM_HID_WD_TOUCHRINGSTATUS:
+               break;
+
+       default:
+               features->input_event_flag = true;
+               input_event(input, usage->type, usage->code, value);
+               break;
+       }
+}
+
+static void wacom_wac_pad_pre_report(struct hid_device *hdev,
+               struct hid_report *report)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+
+       wacom_wac->hid_data.inrange_state = 0;
+}
+
+static void wacom_wac_pad_battery_report(struct hid_device *hdev,
+               struct hid_report *report)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct wacom_features *features = &wacom_wac->features;
+
+       if (features->quirks & WACOM_QUIRK_BATTERY) {
+               int capacity = wacom_wac->hid_data.battery_capacity;
+               bool charging = wacom_wac->hid_data.bat_charging;
+               bool connected = wacom_wac->hid_data.bat_connected;
+               bool powered = wacom_wac->hid_data.ps_connected;
+
+               wacom_notify_battery(wacom_wac, capacity, charging,
+                                    connected, powered);
+       }
+}
+
+static void wacom_wac_pad_report(struct hid_device *hdev,
+               struct hid_report *report)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct wacom_features *features = &wacom_wac->features;
+       struct input_dev *input = wacom_wac->pad_input;
+       bool active = wacom_wac->hid_data.inrange_state != 0;
+
+       /* report prox for expresskey events */
+       if (wacom_equivalent_usage(report->field[0]->physical) == HID_DG_TABLETFUNCTIONKEY) {
+               features->input_event_flag = true;
+               input_event(input, EV_ABS, ABS_MISC, active ? PAD_DEVICE_ID : 0);
+       }
+
+       if (features->input_event_flag) {
+               features->input_event_flag = false;
+               input_sync(input);
        }
 }
 
@@ -1466,25 +1689,43 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct wacom_features *features = &wacom_wac->features;
        struct input_dev *input = wacom_wac->pen_input;
+       unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
 
-       switch (usage->hid) {
+       switch (equivalent_usage) {
        case HID_GD_X:
                wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 4);
                break;
        case HID_GD_Y:
                wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 4);
                break;
+       case WACOM_HID_WD_DISTANCE:
+       case HID_GD_Z:
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_DISTANCE, 0);
+               break;
        case HID_DG_TIPPRESSURE:
                wacom_map_usage(input, usage, field, EV_ABS, ABS_PRESSURE, 0);
                break;
        case HID_DG_INRANGE:
                wacom_map_usage(input, usage, field, EV_KEY, BTN_TOOL_PEN, 0);
                break;
+       case HID_DG_BATTERYSTRENGTH:
+               features->quirks |= WACOM_QUIRK_BATTERY;
+               break;
        case HID_DG_INVERT:
                wacom_map_usage(input, usage, field, EV_KEY,
                                BTN_TOOL_RUBBER, 0);
                break;
+       case HID_DG_TILT_X:
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_TILT_X, 0);
+               break;
+       case HID_DG_TILT_Y:
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_TILT_Y, 0);
+               break;
+       case HID_DG_TWIST:
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_Z, 0);
+               break;
        case HID_DG_ERASER:
        case HID_DG_TIPSWITCH:
                wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0);
@@ -1498,39 +1739,131 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
        case HID_DG_TOOLSERIALNUMBER:
                wacom_map_usage(input, usage, field, EV_MSC, MSC_SERIAL, 0);
                break;
+       case WACOM_HID_WD_SENSE:
+               features->quirks |= WACOM_QUIRK_SENSE;
+               wacom_map_usage(input, usage, field, EV_KEY, BTN_TOOL_PEN, 0);
+               break;
+       case WACOM_HID_WD_SERIALHI:
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_MISC, 0);
+               set_bit(EV_KEY, input->evbit);
+               input_set_capability(input, EV_KEY, BTN_TOOL_PEN);
+               input_set_capability(input, EV_KEY, BTN_TOOL_RUBBER);
+               input_set_capability(input, EV_KEY, BTN_TOOL_BRUSH);
+               input_set_capability(input, EV_KEY, BTN_TOOL_PENCIL);
+               input_set_capability(input, EV_KEY, BTN_TOOL_AIRBRUSH);
+               input_set_capability(input, EV_KEY, BTN_TOOL_MOUSE);
+               input_set_capability(input, EV_KEY, BTN_TOOL_LENS);
+               break;
+       case WACOM_HID_WD_FINGERWHEEL:
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0);
+               break;
        }
 }
 
-static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field,
+static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field,
                struct hid_usage *usage, __s32 value)
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct wacom_features *features = &wacom_wac->features;
        struct input_dev *input = wacom_wac->pen_input;
+       unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
 
-       /* checking which Tool / tip switch to send */
-       switch (usage->hid) {
+       switch (equivalent_usage) {
+       case HID_GD_Z:
+               /*
+                * HID_GD_Z "should increase as the control's position is
+                * moved from high to low", while ABS_DISTANCE instead
+                * increases in value as the tool moves from low to high.
+                */
+               value = field->logical_maximum - value;
+               break;
        case HID_DG_INRANGE:
                wacom_wac->hid_data.inrange_state = value;
-               return 0;
+               if (!(features->quirks & WACOM_QUIRK_SENSE))
+                       wacom_wac->hid_data.sense_state = value;
+               return;
+       case HID_DG_BATTERYSTRENGTH:
+               wacom_wac->hid_data.battery_capacity = value;
+               wacom_wac->hid_data.bat_connected = 1;
+               break;
        case HID_DG_INVERT:
                wacom_wac->hid_data.invert_state = value;
-               return 0;
+               return;
        case HID_DG_ERASER:
        case HID_DG_TIPSWITCH:
                wacom_wac->hid_data.tipswitch |= value;
-               return 0;
+               return;
+       case HID_DG_TOOLSERIALNUMBER:
+               wacom_wac->serial[0] = (wacom_wac->serial[0] & ~0xFFFFFFFFULL);
+               wacom_wac->serial[0] |= value;
+               return;
+       case WACOM_HID_WD_SENSE:
+               wacom_wac->hid_data.sense_state = value;
+               return;
+       case WACOM_HID_WD_SERIALHI:
+               wacom_wac->serial[0] = (wacom_wac->serial[0] & 0xFFFFFFFF);
+               wacom_wac->serial[0] |= ((__u64)value) << 32;
+               /*
+                * Non-USI EMR devices may contain additional tool type
+                * information here. See WACOM_HID_WD_TOOLTYPE case for
+                * more details.
+                */
+               if (value >> 20 == 1) {
+                       wacom_wac->id[0] |= value & 0xFFFFF;
+               }
+               return;
+       case WACOM_HID_WD_TOOLTYPE:
+               /*
+                * Some devices (MobileStudio Pro, and possibly later
+                * devices as well) do not return the complete tool
+                * type in their WACOM_HID_WD_TOOLTYPE usage. Use a
+                * bitwise OR so the complete value can be built
+                * up over time :(
+                */
+               wacom_wac->id[0] |= value;
+               return;
+       case WACOM_HID_WD_OFFSETLEFT:
+               if (features->offset_left && value != features->offset_left)
+                       hid_warn(hdev, "%s: overriding exising left offset "
+                                "%d -> %d\n", __func__, value,
+                                features->offset_left);
+               features->offset_left = value;
+               return;
+       case WACOM_HID_WD_OFFSETRIGHT:
+               if (features->offset_right && value != features->offset_right)
+                       hid_warn(hdev, "%s: overriding exising right offset "
+                                "%d -> %d\n", __func__, value,
+                                features->offset_right);
+               features->offset_right = value;
+               return;
+       case WACOM_HID_WD_OFFSETTOP:
+               if (features->offset_top && value != features->offset_top)
+                       hid_warn(hdev, "%s: overriding exising top offset "
+                                "%d -> %d\n", __func__, value,
+                                features->offset_top);
+               features->offset_top = value;
+               return;
+       case WACOM_HID_WD_OFFSETBOTTOM:
+               if (features->offset_bottom && value != features->offset_bottom)
+                       hid_warn(hdev, "%s: overriding exising bottom offset "
+                                "%d -> %d\n", __func__, value,
+                                features->offset_bottom);
+               features->offset_bottom = value;
+               return;
        }
 
        /* send pen events only when touch is up or forced out
         * or touch arbitration is off
         */
        if (!usage->type || delay_pen_events(wacom_wac))
-               return 0;
+               return;
 
-       input_event(input, usage->type, usage->code, value);
+       /* send pen events only when the pen is in/entering/leaving proximity */
+       if (!wacom_wac->hid_data.inrange_state && !wacom_wac->tool[0])
+               return;
 
-       return 0;
+       input_event(input, usage->type, usage->code, value);
 }
 
 static void wacom_wac_pen_pre_report(struct hid_device *hdev,
@@ -1546,24 +1879,53 @@ static void wacom_wac_pen_report(struct hid_device *hdev,
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
        struct input_dev *input = wacom_wac->pen_input;
        bool prox = wacom_wac->hid_data.inrange_state;
+       bool range = wacom_wac->hid_data.sense_state;
 
-       if (!wacom_wac->shared->stylus_in_proximity) /* first in prox */
+       if (!wacom_wac->tool[0] && prox) { /* first in prox */
                /* Going into proximity select tool */
-               wacom_wac->tool[0] = wacom_wac->hid_data.invert_state ?
-                                               BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+               if (wacom_wac->hid_data.invert_state)
+                       wacom_wac->tool[0] = BTN_TOOL_RUBBER;
+               else if (wacom_wac->id[0])
+                       wacom_wac->tool[0] = wacom_intuos_get_tool_type(wacom_wac->id[0]);
+               else
+                       wacom_wac->tool[0] = BTN_TOOL_PEN;
+       }
 
        /* keep pen state for touch events */
-       wacom_wac->shared->stylus_in_proximity = prox;
+       wacom_wac->shared->stylus_in_proximity = range;
 
-       if (!delay_pen_events(wacom_wac)) {
+       if (!delay_pen_events(wacom_wac) && wacom_wac->tool[0]) {
+               int id = wacom_wac->id[0];
+
+               /*
+                * Non-USI EMR tools should have their IDs mangled to
+                * match the legacy behavior of wacom_intuos_general
+                */
+               if (wacom_wac->serial[0] >> 52 == 1)
+                       id = wacom_intuos_id_mangle(id);
+
+               /*
+                * To ensure compatibility with xf86-input-wacom, we should
+                * report the BTN_TOOL_* event prior to the ABS_MISC or
+                * MSC_SERIAL events.
+                */
                input_report_key(input, BTN_TOUCH,
                                wacom_wac->hid_data.tipswitch);
                input_report_key(input, wacom_wac->tool[0], prox);
+               if (wacom_wac->serial[0]) {
+                       input_event(input, EV_MSC, MSC_SERIAL, wacom_wac->serial[0]);
+                       input_report_abs(input, ABS_MISC, id);
+               }
 
                wacom_wac->hid_data.tipswitch = false;
 
                input_sync(input);
        }
+
+       if (!prox) {
+               wacom_wac->tool[0] = 0;
+               wacom_wac->id[0] = 0;
+       }
 }
 
 static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
@@ -1573,8 +1935,9 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
        struct input_dev *input = wacom_wac->touch_input;
        unsigned touch_max = wacom_wac->features.touch_max;
+       unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
 
-       switch (usage->hid) {
+       switch (equivalent_usage) {
        case HID_GD_X:
                if (touch_max == 1)
                        wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 4);
@@ -1644,13 +2007,14 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
        }
 }
 
-static int wacom_wac_finger_event(struct hid_device *hdev,
+static void wacom_wac_finger_event(struct hid_device *hdev,
                struct hid_field *field, struct hid_usage *usage, __s32 value)
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
 
-       switch (usage->hid) {
+       switch (equivalent_usage) {
        case HID_GD_X:
                wacom_wac->hid_data.x = value;
                break;
@@ -1673,11 +2037,9 @@ static int wacom_wac_finger_event(struct hid_device *hdev,
 
 
        if (usage->usage_index + 1 == field->report_count) {
-               if (usage->hid == wacom_wac->hid_data.last_slot_field)
+               if (equivalent_usage == wacom_wac->hid_data.last_slot_field)
                        wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input);
        }
-
-       return 0;
 }
 
 static void wacom_wac_finger_pre_report(struct hid_device *hdev,
@@ -1762,28 +2124,30 @@ void wacom_wac_usage_mapping(struct hid_device *hdev,
        /* currently, only direct devices have proper hid report descriptors */
        features->device_type |= WACOM_DEVICETYPE_DIRECT;
 
-       if (WACOM_PEN_FIELD(field))
-               return wacom_wac_pen_usage_mapping(hdev, field, usage);
-
-       if (WACOM_FINGER_FIELD(field))
-               return wacom_wac_finger_usage_mapping(hdev, field, usage);
+       if (WACOM_PAD_FIELD(field))
+               wacom_wac_pad_usage_mapping(hdev, field, usage);
+       else if (WACOM_PEN_FIELD(field))
+               wacom_wac_pen_usage_mapping(hdev, field, usage);
+       else if (WACOM_FINGER_FIELD(field))
+               wacom_wac_finger_usage_mapping(hdev, field, usage);
 }
 
-int wacom_wac_event(struct hid_device *hdev, struct hid_field *field,
+void wacom_wac_event(struct hid_device *hdev, struct hid_field *field,
                struct hid_usage *usage, __s32 value)
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
 
        if (wacom->wacom_wac.features.type != HID_GENERIC)
-               return 0;
-
-       if (WACOM_PEN_FIELD(field))
-               return wacom_wac_pen_event(hdev, field, usage, value);
-
-       if (WACOM_FINGER_FIELD(field))
-               return wacom_wac_finger_event(hdev, field, usage, value);
+               return;
 
-       return 0;
+       if (WACOM_PAD_FIELD(field)) {
+               wacom_wac_pad_battery_event(hdev, field, usage, value);
+               if (wacom->wacom_wac.pad_input)
+                       wacom_wac_pad_event(hdev, field, usage, value);
+       } else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input)
+               wacom_wac_pen_event(hdev, field, usage, value);
+       else if (WACOM_FINGER_FIELD(field) && wacom->wacom_wac.touch_input)
+               wacom_wac_finger_event(hdev, field, usage, value);
 }
 
 static void wacom_report_events(struct hid_device *hdev, struct hid_report *report)
@@ -1814,19 +2178,23 @@ void wacom_wac_report(struct hid_device *hdev, struct hid_report *report)
        if (wacom_wac->features.type != HID_GENERIC)
                return;
 
-       if (WACOM_PEN_FIELD(field))
+       if (WACOM_PAD_FIELD(field) && wacom->wacom_wac.pad_input)
+               wacom_wac_pad_pre_report(hdev, report);
+       else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input)
                wacom_wac_pen_pre_report(hdev, report);
-
-       if (WACOM_FINGER_FIELD(field))
+       else if (WACOM_FINGER_FIELD(field) && wacom->wacom_wac.touch_input)
                wacom_wac_finger_pre_report(hdev, report);
 
        wacom_report_events(hdev, report);
 
-       if (WACOM_PEN_FIELD(field))
-               return wacom_wac_pen_report(hdev, report);
-
-       if (WACOM_FINGER_FIELD(field))
-               return wacom_wac_finger_report(hdev, report);
+       if (WACOM_PAD_FIELD(field)) {
+               wacom_wac_pad_battery_report(hdev, report);
+               if (wacom->wacom_wac.pad_input)
+                       wacom_wac_pad_report(hdev, report);
+       } else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input)
+               wacom_wac_pen_report(hdev, report);
+       else if (WACOM_FINGER_FIELD(field) && wacom->wacom_wac.touch_input)
+               wacom_wac_finger_report(hdev, report);
 }
 
 static int wacom_bpt_touch(struct wacom_wac *wacom)
@@ -2399,6 +2767,8 @@ void wacom_setup_device_quirks(struct wacom *wacom)
        struct wacom_features *features = &wacom->wacom_wac.features;
 
        /* The pen and pad share the same interface on most devices */
+       if (features->numbered_buttons > 0)
+               features->device_type |= WACOM_DEVICETYPE_PAD;
        if (features->type == GRAPHIRE_BT || features->type == WACOM_G4 ||
            features->type == DTUS ||
            (features->type >= INTUOS3S && features->type <= WACOM_MO)) {
@@ -2448,7 +2818,7 @@ void wacom_setup_device_quirks(struct wacom *wacom)
        /*
         * Raw Wacom-mode pen and touch events both come from interface
         * 0, whose HID descriptor has an application usage of 0xFF0D
-        * (i.e., WACOM_VENDORDEFINED_PEN). We route pen packets back
+        * (i.e., WACOM_HID_WD_DIGITIZER). We route pen packets back
         * out through the HID_GENERIC device created for interface 1,
         * so rewrite this one to be of type WACOM_DEVICETYPE_TOUCH.
         */
@@ -2530,10 +2900,12 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
        __set_bit(BTN_TOUCH, input_dev->keybit);
        __set_bit(ABS_MISC, input_dev->absbit);
 
-       input_set_abs_params(input_dev, ABS_X, features->x_min,
-                            features->x_max, features->x_fuzz, 0);
-       input_set_abs_params(input_dev, ABS_Y, features->y_min,
-                            features->y_max, features->y_fuzz, 0);
+       input_set_abs_params(input_dev, ABS_X, 0 + features->offset_left,
+                            features->x_max - features->offset_right,
+                            features->x_fuzz, 0);
+       input_set_abs_params(input_dev, ABS_Y, 0 + features->offset_top,
+                            features->y_max - features->offset_bottom,
+                            features->y_fuzz, 0);
        input_set_abs_params(input_dev, ABS_PRESSURE, 0,
                features->pressure_max, features->pressure_fuzz, 0);
 
@@ -2769,17 +3141,29 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
        return 0;
 }
 
+static int wacom_numbered_button_to_key(int n)
+{
+       if (n < 10)
+               return BTN_0 + n;
+       else if (n < 16)
+               return BTN_A + (n-10);
+       else if (n < 18)
+               return BTN_BASE + (n-16);
+       else
+               return 0;
+}
+
 static void wacom_setup_numbered_buttons(struct input_dev *input_dev,
                                int button_count)
 {
        int i;
 
-       for (i = 0; i < button_count && i < 10; i++)
-               __set_bit(BTN_0 + i, input_dev->keybit);
-       for (i = 10; i < button_count && i < 16; i++)
-               __set_bit(BTN_A + (i-10), input_dev->keybit);
-       for (i = 16; i < button_count && i < 18; i++)
-               __set_bit(BTN_BASE + (i-16), input_dev->keybit);
+       for (i = 0; i < button_count; i++) {
+               int key = wacom_numbered_button_to_key(i);
+
+               if (key)
+                       __set_bit(key, input_dev->keybit);
+       }
 }
 
 static void wacom_24hd_update_leds(struct wacom *wacom, int mask, int group)
@@ -2881,12 +3265,12 @@ static void wacom_report_numbered_buttons(struct input_dev *input_dev,
        for (i = 0; i < wacom->led.count; i++)
                wacom_update_led(wacom,  button_count, mask, i);
 
-       for (i = 0; i < button_count && i < 10; i++)
-               input_report_key(input_dev, BTN_0 + i, mask & (1 << i));
-       for (i = 10; i < button_count && i < 16; i++)
-               input_report_key(input_dev, BTN_A + (i-10), mask & (1 << i));
-       for (i = 16; i < button_count && i < 18; i++)
-               input_report_key(input_dev, BTN_BASE + (i-16), mask & (1 << i));
+       for (i = 0; i < button_count; i++) {
+               int key = wacom_numbered_button_to_key(i);
+
+               if (key)
+                       input_report_key(input_dev, key, mask & (1 << i));
+       }
 }
 
 int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
@@ -2906,8 +3290,12 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
        __set_bit(ABS_MISC, input_dev->absbit);
 
        /* kept for making legacy xf86-input-wacom accepting the pad */
-       input_set_abs_params(input_dev, ABS_X, 0, 1, 0, 0);
-       input_set_abs_params(input_dev, ABS_Y, 0, 1, 0, 0);
+       if (!(input_dev->absinfo && (input_dev->absinfo[ABS_X].minimum ||
+             input_dev->absinfo[ABS_X].maximum)))
+               input_set_abs_params(input_dev, ABS_X, 0, 1, 0, 0);
+       if (!(input_dev->absinfo && (input_dev->absinfo[ABS_Y].minimum ||
+             input_dev->absinfo[ABS_Y].maximum)))
+               input_set_abs_params(input_dev, ABS_Y, 0, 1, 0, 0);
 
        /* kept for making udev and libwacom accepting the pad */
        __set_bit(BTN_STYLUS, input_dev->keybit);
@@ -3027,6 +3415,9 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
                input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
                break;
 
+       case HID_GENERIC:
+               break;
+
        default:
                /* no pad supported */
                return -ENODEV;
@@ -3233,26 +3624,30 @@ static const struct wacom_features wacom_features_0x317 =
          INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, .touch_max = 16,
          .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0xF4 =
-       { "Wacom Cintiq 24HD", 104080, 65200, 2047, 63,
+       { "Wacom Cintiq 24HD", 104480, 65600, 2047, 63,
          WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 16,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0xF8 =
-       { "Wacom Cintiq 24HD touch", 104080, 65200, 2047, 63, /* Pen */
+       { "Wacom Cintiq 24HD touch", 104480, 65600, 2047, 63, /* Pen */
          WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 16,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf6 };
 static const struct wacom_features wacom_features_0xF6 =
        { "Wacom Cintiq 24HD touch", .type = WACOM_24HDT, /* Touch */
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf8, .touch_max = 10,
          .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0x32A =
-       { "Wacom Cintiq 27QHD", 119740, 67520, 2047, 63,
+       { "Wacom Cintiq 27QHD", 120140, 67920, 2047, 63,
          WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 0,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0x32B =
-       { "Wacom Cintiq 27QHD touch", 119740, 67520, 2047, 63,
+       { "Wacom Cintiq 27QHD touch", 120140, 67920, 2047, 63,
          WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 0,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x32C };
 static const struct wacom_features wacom_features_0x32C =
        { "Wacom Cintiq 27QHD touch", .type = WACOM_27QHDT,
@@ -3267,13 +3662,15 @@ static const struct wacom_features wacom_features_0xC6 =
        { "Wacom Cintiq 12WX", 53020, 33440, 1023, 63,
          WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 10 };
 static const struct wacom_features wacom_features_0x304 =
-       { "Wacom Cintiq 13HD", 59152, 33448, 1023, 63,
+       { "Wacom Cintiq 13HD", 59552, 33848, 1023, 63,
          WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0x333 =
-       { "Wacom Cintiq 13HD touch", 59152, 33448, 2047, 63,
+       { "Wacom Cintiq 13HD touch", 59552, 33848, 2047, 63,
          WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x335 };
 static const struct wacom_features wacom_features_0x335 =
        { "Wacom Cintiq 13HD touch", .type = WACOM_24HDT, /* Touch */
@@ -3290,42 +3687,50 @@ static const struct wacom_features wacom_features_0xF0 =
        { "Wacom DTU1631", 34623, 19553, 511, 0,
          DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0xFB =
-       { "Wacom DTU1031", 21896, 13760, 511, 0,
+       { "Wacom DTU1031", 22096, 13960, 511, 0,
          DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4,
+         WACOM_DTU_OFFSET, WACOM_DTU_OFFSET,
          WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
 static const struct wacom_features wacom_features_0x32F =
-       { "Wacom DTU1031X", 22472, 12728, 511, 0,
+       { "Wacom DTU1031X", 22672, 12928, 511, 0,
          DTUSX, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 0,
+         WACOM_DTU_OFFSET, WACOM_DTU_OFFSET,
          WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
 static const struct wacom_features wacom_features_0x336 =
-       { "Wacom DTU1141", 23472, 13203, 1023, 0,
+       { "Wacom DTU1141", 23672, 13403, 1023, 0,
          DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4,
+         WACOM_DTU_OFFSET, WACOM_DTU_OFFSET,
          WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
 static const struct wacom_features wacom_features_0x57 =
-       { "Wacom DTK2241", 95640, 54060, 2047, 63,
+       { "Wacom DTK2241", 95840, 54260, 2047, 63,
          DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 6,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0x59 = /* Pen */
-       { "Wacom DTH2242", 95640, 54060, 2047, 63,
+       { "Wacom DTH2242", 95840, 54260, 2047, 63,
          DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 6,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5D };
 static const struct wacom_features wacom_features_0x5D = /* Touch */
        { "Wacom DTH2242",       .type = WACOM_24HDT,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x59, .touch_max = 10,
          .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0xCC =
-       { "Wacom Cintiq 21UX2", 86800, 65200, 2047, 63,
+       { "Wacom Cintiq 21UX2", 87200, 65600, 2047, 63,
          WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 18,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0xFA =
-       { "Wacom Cintiq 22HD", 95440, 53860, 2047, 63,
+       { "Wacom Cintiq 22HD", 95840, 54260, 2047, 63,
          WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 18,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0x5B =
-       { "Wacom Cintiq 22HDT", 95440, 53860, 2047, 63,
+       { "Wacom Cintiq 22HDT", 95840, 54260, 2047, 63,
          WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 18,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5e };
 static const struct wacom_features wacom_features_0x5E =
        { "Wacom Cintiq 22HDT", .type = WACOM_24HDT,
@@ -3469,18 +3874,20 @@ static const struct wacom_features wacom_features_0x6004 =
        { "ISD-V4", 12800, 8000, 255, 0,
          TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0x307 =
-       { "Wacom ISDv5 307", 59152, 33448, 2047, 63,
+       { "Wacom ISDv5 307", 59552, 33848, 2047, 63,
          CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x309 };
 static const struct wacom_features wacom_features_0x309 =
        { "Wacom ISDv5 309", .type = WACOM_24HDT, /* Touch */
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x0307, .touch_max = 10,
          .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0x30A =
-       { "Wacom ISDv5 30A", 59152, 33448, 2047, 63,
+       { "Wacom ISDv5 30A", 59552, 33848, 2047, 63,
          CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30C };
 static const struct wacom_features wacom_features_0x30C =
        { "Wacom ISDv5 30C", .type = WACOM_24HDT, /* Touch */
@@ -3496,6 +3903,7 @@ static const struct wacom_features wacom_features_0x325 =
        { "Wacom ISDv5 325", 59552, 33848, 2047, 63,
          CINTIQ_COMPANION_2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 11,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
+         WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x326 };
 static const struct wacom_features wacom_features_0x326 = /* Touch */
        { "Wacom ISDv5 326", .type = HID_GENERIC, .oVid = USB_VENDOR_ID_WACOM,
@@ -3525,8 +3933,9 @@ static const struct wacom_features wacom_features_0x33E =
          INTUOSHT2, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16,
          .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0x343 =
-       { "Wacom DTK1651", 34616, 19559, 1023, 0,
+       { "Wacom DTK1651", 34816, 19759, 1023, 0,
          DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4,
+         WACOM_DTU_OFFSET, WACOM_DTU_OFFSET,
          WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
 
 static const struct wacom_features wacom_features_HID_ANY_ID =
index 324c40b0c1194acd58310df39679a176d9011bb3..fb0e50acb10daad461f2bbcd16df45d5037b0db2 100644 (file)
@@ -74,6 +74,7 @@
 
 /* device quirks */
 #define WACOM_QUIRK_BBTOUCH_LOWRES     0x0001
+#define WACOM_QUIRK_SENSE              0x0002
 #define WACOM_QUIRK_BATTERY            0x0008
 
 /* device types */
 #define WACOM_DEVICETYPE_WL_MONITOR     0x0008
 #define WACOM_DEVICETYPE_DIRECT         0x0010
 
-#define WACOM_VENDORDEFINED_PEN                0xff0d0001
-#define WACOM_G9_PAGE                  0xff090000
-#define WACOM_G9_DIGITIZER             (WACOM_G9_PAGE | 0x02)
-#define WACOM_G9_TOUCHSCREEN           (WACOM_G9_PAGE | 0x11)
-#define WACOM_G11_PAGE                 0xff110000
-#define WACOM_G11_DIGITIZER            (WACOM_G11_PAGE | 0x02)
-#define WACOM_G11_TOUCHSCREEN          (WACOM_G11_PAGE | 0x11)
+#define WACOM_HID_UP_WACOMDIGITIZER     0xff0d0000
+#define WACOM_HID_SP_PAD                0x00040000
+#define WACOM_HID_SP_BUTTON             0x00090000
+#define WACOM_HID_SP_DIGITIZER          0x000d0000
+#define WACOM_HID_SP_DIGITIZERINFO      0x00100000
+#define WACOM_HID_WD_DIGITIZER          (WACOM_HID_UP_WACOMDIGITIZER | 0x01)
+#define WACOM_HID_WD_SENSE              (WACOM_HID_UP_WACOMDIGITIZER | 0x36)
+#define WACOM_HID_WD_DIGITIZERFNKEYS    (WACOM_HID_UP_WACOMDIGITIZER | 0x39)
+#define WACOM_HID_WD_SERIALHI           (WACOM_HID_UP_WACOMDIGITIZER | 0x5c)
+#define WACOM_HID_WD_TOOLTYPE           (WACOM_HID_UP_WACOMDIGITIZER | 0x77)
+#define WACOM_HID_WD_DISTANCE           (WACOM_HID_UP_WACOMDIGITIZER | 0x0132)
+#define WACOM_HID_WD_TOUCHSTRIP         (WACOM_HID_UP_WACOMDIGITIZER | 0x0136)
+#define WACOM_HID_WD_TOUCHSTRIP2        (WACOM_HID_UP_WACOMDIGITIZER | 0x0137)
+#define WACOM_HID_WD_TOUCHRING          (WACOM_HID_UP_WACOMDIGITIZER | 0x0138)
+#define WACOM_HID_WD_TOUCHRINGSTATUS    (WACOM_HID_UP_WACOMDIGITIZER | 0x0139)
+#define WACOM_HID_WD_ACCELEROMETER_X    (WACOM_HID_UP_WACOMDIGITIZER | 0x0401)
+#define WACOM_HID_WD_ACCELEROMETER_Y    (WACOM_HID_UP_WACOMDIGITIZER | 0x0402)
+#define WACOM_HID_WD_ACCELEROMETER_Z    (WACOM_HID_UP_WACOMDIGITIZER | 0x0403)
+#define WACOM_HID_WD_BATTERY_CHARGING   (WACOM_HID_UP_WACOMDIGITIZER | 0x0404)
+#define WACOM_HID_WD_BATTERY_LEVEL      (WACOM_HID_UP_WACOMDIGITIZER | 0x043b)
+#define WACOM_HID_WD_EXPRESSKEY00       (WACOM_HID_UP_WACOMDIGITIZER | 0x0910)
+#define WACOM_HID_WD_EXPRESSKEYCAP00    (WACOM_HID_UP_WACOMDIGITIZER | 0x0950)
+#define WACOM_HID_WD_BUTTONHOME         (WACOM_HID_UP_WACOMDIGITIZER | 0x0990)
+#define WACOM_HID_WD_BUTTONUP           (WACOM_HID_UP_WACOMDIGITIZER | 0x0991)
+#define WACOM_HID_WD_BUTTONDOWN         (WACOM_HID_UP_WACOMDIGITIZER | 0x0992)
+#define WACOM_HID_WD_BUTTONLEFT         (WACOM_HID_UP_WACOMDIGITIZER | 0x0993)
+#define WACOM_HID_WD_BUTTONRIGHT        (WACOM_HID_UP_WACOMDIGITIZER | 0x0994)
+#define WACOM_HID_WD_BUTTONCENTER       (WACOM_HID_UP_WACOMDIGITIZER | 0x0995)
+#define WACOM_HID_WD_TOUCHONOFF         (WACOM_HID_UP_WACOMDIGITIZER | 0x0996)
+#define WACOM_HID_WD_FINGERWHEEL        (WACOM_HID_UP_WACOMDIGITIZER | 0x0d03)
+#define WACOM_HID_WD_OFFSETLEFT         (WACOM_HID_UP_WACOMDIGITIZER | 0x0d30)
+#define WACOM_HID_WD_OFFSETTOP          (WACOM_HID_UP_WACOMDIGITIZER | 0x0d31)
+#define WACOM_HID_WD_OFFSETRIGHT        (WACOM_HID_UP_WACOMDIGITIZER | 0x0d32)
+#define WACOM_HID_WD_OFFSETBOTTOM       (WACOM_HID_UP_WACOMDIGITIZER | 0x0d33)
+#define WACOM_HID_WD_DATAMODE           (WACOM_HID_UP_WACOMDIGITIZER | 0x1002)
+#define WACOM_HID_WD_DIGITIZERINFO      (WACOM_HID_UP_WACOMDIGITIZER | 0x1013)
+#define WACOM_HID_UP_G9                 0xff090000
+#define WACOM_HID_G9_PEN                (WACOM_HID_UP_G9 | 0x02)
+#define WACOM_HID_G9_TOUCHSCREEN        (WACOM_HID_UP_G9 | 0x11)
+#define WACOM_HID_UP_G11                0xff110000
+#define WACOM_HID_G11_PEN               (WACOM_HID_UP_G11 | 0x02)
+#define WACOM_HID_G11_TOUCHSCREEN       (WACOM_HID_UP_G11 | 0x11)
+
+#define WACOM_PAD_FIELD(f)     (((f)->physical == HID_DG_TABLETFUNCTIONKEY) || \
+                                ((f)->physical == WACOM_HID_WD_DIGITIZERFNKEYS) || \
+                                ((f)->physical == WACOM_HID_WD_DIGITIZERINFO))
 
 #define WACOM_PEN_FIELD(f)     (((f)->logical == HID_DG_STYLUS) || \
                                 ((f)->physical == HID_DG_STYLUS) || \
                                 ((f)->physical == HID_DG_PEN) || \
                                 ((f)->application == HID_DG_PEN) || \
                                 ((f)->application == HID_DG_DIGITIZER) || \
-                                ((f)->application == WACOM_VENDORDEFINED_PEN))
+                                ((f)->application == WACOM_HID_WD_DIGITIZER) || \
+                                ((f)->application == WACOM_HID_G9_PEN) || \
+                                ((f)->application == WACOM_HID_G11_PEN))
 #define WACOM_FINGER_FIELD(f)  (((f)->logical == HID_DG_FINGER) || \
                                 ((f)->physical == HID_DG_FINGER) || \
-                                ((f)->application == HID_DG_TOUCHSCREEN))
+                                ((f)->application == HID_DG_TOUCHSCREEN) || \
+                                ((f)->application == WACOM_HID_G9_TOUCHSCREEN) || \
+                                ((f)->application == WACOM_HID_G11_TOUCHSCREEN))
 
 enum {
        PENPARTNER = 0,
@@ -167,8 +211,10 @@ struct wacom_features {
        int x_resolution;
        int y_resolution;
        int numbered_buttons;
-       int x_min;
-       int y_min;
+       int offset_left;
+       int offset_right;
+       int offset_top;
+       int offset_bottom;
        int device_type;
        int x_phy;
        int y_phy;
@@ -186,6 +232,7 @@ struct wacom_features {
        int pktlen;
        bool check_for_hid_type;
        int hid_type;
+       bool input_event_flag;
 };
 
 struct wacom_shared {
@@ -202,6 +249,7 @@ struct wacom_shared {
 struct hid_data {
        __s16 inputmode;        /* InputMode HID feature, -1 if non-existent */
        __s16 inputmode_index;  /* InputMode HID feature index in the report */
+       bool sense_state;
        bool inrange_state;
        bool invert_state;
        bool tipswitch;
@@ -217,6 +265,10 @@ struct hid_data {
        int last_slot_field;
        int num_expected;
        int num_received;
+       int battery_capacity;
+       int bat_charging;
+       int bat_connected;
+       int ps_connected;
 };
 
 struct wacom_remote_data {
@@ -234,7 +286,7 @@ struct wacom_wac {
        unsigned char data[WACOM_PKGLEN_MAX];
        int tool[2];
        int id[2];
-       __u32 serial[2];
+       __u64 serial[2];
        bool reporting_data;
        struct wacom_features features;
        struct wacom_shared *shared;
index b2ec82712baac8e5ead67ea20d4f55372a8e74a2..28f38e2b8f309387b7a983937b50908c3095f7d9 100644 (file)
@@ -231,7 +231,11 @@ struct hid_item {
 #define HID_DG_TAP             0x000d0035
 #define HID_DG_TABLETFUNCTIONKEY       0x000d0039
 #define HID_DG_PROGRAMCHANGEKEY        0x000d003a
+#define HID_DG_BATTERYSTRENGTH 0x000d003b
 #define HID_DG_INVERT          0x000d003c
+#define HID_DG_TILT_X          0x000d003d
+#define HID_DG_TILT_Y          0x000d003e
+#define HID_DG_TWIST           0x000d0041
 #define HID_DG_TIPSWITCH       0x000d0042
 #define HID_DG_TIPSWITCH2      0x000d0043
 #define HID_DG_BARRELSWITCH    0x000d0044
@@ -479,6 +483,7 @@ struct hid_input {
        struct list_head list;
        struct hid_report *report;
        struct input_dev *input;
+       bool registered;
 };
 
 enum hid_type {