Input: wacom - move the USB (now hid) Wacom driver in drivers/hid
authorBenjamin Tissoires <benjamin.tissoires@redhat.com>
Thu, 24 Jul 2014 20:10:09 +0000 (13:10 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Sat, 26 Jul 2014 01:55:38 +0000 (18:55 -0700)
wacom.ko is now a full HID driver, we have to move it into the proper
subdirectory: drivers/hid.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
12 files changed:
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/wacom.h [new file with mode: 0644]
drivers/hid/wacom_sys.c [new file with mode: 0644]
drivers/hid/wacom_wac.c [new file with mode: 0644]
drivers/hid/wacom_wac.h [new file with mode: 0644]
drivers/input/tablet/Kconfig
drivers/input/tablet/Makefile
drivers/input/tablet/wacom.h [deleted file]
drivers/input/tablet/wacom_sys.c [deleted file]
drivers/input/tablet/wacom_wac.c [deleted file]
drivers/input/tablet/wacom_wac.h [deleted file]

index 5e79c6ad914f301aa676a763412727987c8610a4..64366f1feb17dd79d9b470c3cf1712b0af638697 100644 (file)
@@ -755,6 +755,19 @@ config HID_WACOM
        ---help---
        Support for Wacom Graphire Bluetooth and Intuos4 WL tablets.
 
+config HID_USB_WACOM
+       tristate "Wacom Intuos/Graphire tablet support (USB)"
+       depends on HID
+       select POWER_SUPPLY
+       select NEW_LEDS
+       select LEDS_CLASS
+       help
+         Say Y here if you want to use the USB version of the Wacom Intuos
+         or Graphire tablet.
+
+         To compile this driver as a module, choose M here: the
+         module will be called wacom.
+
 config HID_WIIMOTE
        tristate "Nintendo Wii / Wii U peripherals"
        depends on HID
index a6fa6baf368e49a254ba79646bc29abbd6575519..55b69acdb680aaa0881fcc51fa1748ed27b6536f 100644 (file)
@@ -116,6 +116,9 @@ obj-$(CONFIG_HID_XINMO)             += hid-xinmo.o
 obj-$(CONFIG_HID_ZEROPLUS)     += hid-zpff.o
 obj-$(CONFIG_HID_ZYDACRON)     += hid-zydacron.o
 obj-$(CONFIG_HID_WACOM)                += hid-wacom.o
+
+wacom-objs                     := wacom_wac.o wacom_sys.o
+obj-$(CONFIG_HID_USB_WACOM)    += wacom.o
 obj-$(CONFIG_HID_WALTOP)       += hid-waltop.o
 obj-$(CONFIG_HID_WIIMOTE)      += hid-wiimote.o
 obj-$(CONFIG_HID_SENSOR_HUB)   += hid-sensor-hub.o
diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h
new file mode 100644 (file)
index 0000000..dd67b7d
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * drivers/input/tablet/wacom.h
+ *
+ *  USB Wacom tablet support
+ *
+ *  Copyright (c) 2000-2004 Vojtech Pavlik     <vojtech@ucw.cz>
+ *  Copyright (c) 2000 Andreas Bach Aaen       <abach@stofanet.dk>
+ *  Copyright (c) 2000 Clifford Wolf           <clifford@clifford.at>
+ *  Copyright (c) 2000 Sam Mosel               <sam.mosel@computer.org>
+ *  Copyright (c) 2000 James E. Blair          <corvus@gnu.org>
+ *  Copyright (c) 2000 Daniel Egger            <egger@suse.de>
+ *  Copyright (c) 2001 Frederic Lepied         <flepied@mandrakesoft.com>
+ *  Copyright (c) 2004 Panagiotis Issaris      <panagiotis.issaris@mech.kuleuven.ac.be>
+ *  Copyright (c) 2002-2011 Ping Cheng         <pingc@wacom.com>
+ *
+ *  ChangeLog:
+ *      v0.1 (vp)  - Initial release
+ *      v0.2 (aba) - Support for all buttons / combinations
+ *      v0.3 (vp)  - Support for Intuos added
+ *     v0.4 (sm)  - Support for more Intuos models, menustrip
+ *                     relative mode, proximity.
+ *     v0.5 (vp)  - Big cleanup, nifty features removed,
+ *                     they belong in userspace
+ *     v1.8 (vp)  - Submit URB only when operating, moved to CVS,
+ *                     use input_report_key instead of report_btn and
+ *                     other cleanups
+ *     v1.11 (vp) - Add URB ->dev setting for new kernels
+ *     v1.11 (jb) - Add support for the 4D Mouse & Lens
+ *     v1.12 (de) - Add support for two more inking pen IDs
+ *     v1.14 (vp) - Use new USB device id probing scheme.
+ *                  Fix Wacom Graphire mouse wheel
+ *     v1.18 (vp) - Fix mouse wheel direction
+ *                  Make mouse relative
+ *      v1.20 (fl) - Report tool id for Intuos devices
+ *                 - Multi tools support
+ *                 - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...)
+ *                 - Add PL models support
+ *                - Fix Wacom Graphire mouse wheel again
+ *     v1.21 (vp) - Removed protocol descriptions
+ *                - Added MISC_SERIAL for tool serial numbers
+ *           (gb) - Identify version on module load.
+ *    v1.21.1 (fl) - added Graphire2 support
+ *    v1.21.2 (fl) - added Intuos2 support
+ *                 - added all the PL ids
+ *    v1.21.3 (fl) - added another eraser id from Neil Okamoto
+ *                 - added smooth filter for Graphire from Peri Hankey
+ *                 - added PenPartner support from Olaf van Es
+ *                 - new tool ids from Ole Martin Bjoerndalen
+ *     v1.29 (pc) - Add support for more tablets
+ *                - Fix pressure reporting
+ *     v1.30 (vp) - Merge 2.4 and 2.5 drivers
+ *                - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse
+ *                - Cleanups here and there
+ *    v1.30.1 (pi) - Added Graphire3 support
+ *     v1.40 (pc) - Add support for several new devices, fix eraser reporting, ...
+ *     v1.43 (pc) - Added support for Cintiq 21UX
+ *                - Fixed a Graphire bug
+ *                - Merged wacom_intuos3_irq into wacom_intuos_irq
+ *     v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc.
+ *                - Report Device IDs
+ *      v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19
+ *                 - Minor data report fix
+ *      v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c,
+ *                - where wacom_sys.c deals with system specific code,
+ *                - and wacom_wac.c deals with Wacom specific code
+ *                - Support Intuos3 4x6
+ *      v1.47 (pc) - Added support for Bamboo
+ *      v1.48 (pc) - Added support for Bamboo1, BambooFun, and Cintiq 12WX
+ *      v1.49 (pc) - Added support for USB Tablet PC (0x90, 0x93, and 0x9A)
+ *      v1.50 (pc) - Fixed a TabletPC touch bug in 2.6.28
+ *      v1.51 (pc) - Added support for Intuos4
+ *      v1.52 (pc) - Query Wacom data upon system resume
+ *                 - add defines for features->type
+ *                 - add new devices (0x9F, 0xE2, and 0XE3)
+ */
+
+/*
+ * 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.
+ */
+#ifndef WACOM_H
+#define WACOM_H
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/usb/input.h>
+#include <linux/power_supply.h>
+#include <asm/unaligned.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.53"
+#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
+#define DRIVER_DESC "USB Wacom tablet driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_WACOM    0x056a
+#define USB_VENDOR_ID_LENOVO   0x17ef
+
+struct wacom {
+       struct usb_device *usbdev;
+       struct usb_interface *intf;
+       struct wacom_wac wacom_wac;
+       struct hid_device *hdev;
+       struct mutex lock;
+       struct work_struct work;
+       struct wacom_led {
+               u8 select[2]; /* status led selector (0..3) */
+               u8 llv;       /* status led brightness no button (1..127) */
+               u8 hlv;       /* status led brightness button pressed (1..127) */
+               u8 img_lum;   /* OLED matrix display brightness */
+       } led;
+       struct power_supply battery;
+};
+
+static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
+{
+       struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
+       schedule_work(&wacom->work);
+}
+
+extern const struct hid_device_id wacom_ids[];
+
+void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
+void wacom_setup_device_quirks(struct wacom_features *features);
+int wacom_setup_input_capabilities(struct input_dev *input_dev,
+                                  struct wacom_wac *wacom_wac);
+int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
+                                      struct wacom_wac *wacom_wac);
+#endif
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
new file mode 100644 (file)
index 0000000..06e304b
--- /dev/null
@@ -0,0 +1,1257 @@
+/*
+ * drivers/input/tablet/wacom_sys.c
+ *
+ *  USB Wacom tablet support - system specific code
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "wacom_wac.h"
+#include "wacom.h"
+#include <linux/hid.h>
+
+#define WAC_MSG_RETRIES                5
+
+#define WAC_CMD_LED_CONTROL    0x20
+#define WAC_CMD_ICON_START     0x21
+#define WAC_CMD_ICON_XFER      0x23
+#define WAC_CMD_RETRIES                10
+
+static int wacom_get_report(struct hid_device *hdev, u8 type, u8 id,
+                           void *buf, size_t size, unsigned int retries)
+{
+       int retval;
+
+       do {
+               retval = hid_hw_raw_request(hdev, id, buf, size, type,
+                               HID_REQ_GET_REPORT);
+       } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
+
+       return retval;
+}
+
+static int wacom_set_report(struct hid_device *hdev, u8 type, u8 id,
+                           void *buf, size_t size, unsigned int retries)
+{
+       int retval;
+
+       do {
+               retval = hid_hw_raw_request(hdev, id, buf, size, type,
+                               HID_REQ_SET_REPORT);
+       } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
+
+       return retval;
+}
+
+static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
+               u8 *raw_data, int size)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+
+       if (size > WACOM_PKGLEN_MAX)
+               return 1;
+
+       memcpy(wacom->wacom_wac.data, raw_data, size);
+
+       wacom_wac_irq(&wacom->wacom_wac, size);
+
+       return 0;
+}
+
+static int wacom_open(struct input_dev *dev)
+{
+       struct wacom *wacom = input_get_drvdata(dev);
+       int retval;
+
+       mutex_lock(&wacom->lock);
+       retval = hid_hw_open(wacom->hdev);
+       mutex_unlock(&wacom->lock);
+
+       return retval;
+}
+
+static void wacom_close(struct input_dev *dev)
+{
+       struct wacom *wacom = input_get_drvdata(dev);
+
+       mutex_lock(&wacom->lock);
+       hid_hw_close(wacom->hdev);
+       mutex_unlock(&wacom->lock);
+}
+
+/*
+ * Calculate the resolution of the X or Y axis using hidinput_calc_abs_res.
+ */
+static int wacom_calc_hid_res(int logical_extents, int physical_extents,
+                              unsigned unit, int exponent)
+{
+       struct hid_field field = {
+               .logical_maximum = logical_extents,
+               .physical_maximum = physical_extents,
+               .unit = unit,
+               .unit_exponent = exponent,
+       };
+
+       return hidinput_calc_abs_res(&field, ABS_X);
+}
+
+static void wacom_feature_mapping(struct hid_device *hdev,
+               struct hid_field *field, struct hid_usage *usage)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_features *features = &wacom->wacom_wac.features;
+
+       switch (usage->hid) {
+       case HID_DG_CONTACTMAX:
+               /* leave touch_max as is if predefined */
+               if (!features->touch_max)
+                       features->touch_max = field->value[0];
+               break;
+       }
+}
+
+/*
+ * Interface Descriptor of wacom devices can be incomplete and
+ * inconsistent so wacom_features table is used to store stylus
+ * device's packet lengths, various maximum values, and tablet
+ * resolution based on product ID's.
+ *
+ * For devices that contain 2 interfaces, wacom_features table is
+ * inaccurate for the touch interface.  Since the Interface Descriptor
+ * for touch interfaces has pretty complete data, this function exists
+ * to query tablet for this missing information instead of hard coding in
+ * an additional table.
+ *
+ * A typical Interface Descriptor for a stylus will contain a
+ * boot mouse application collection that is not of interest and this
+ * function will ignore it.
+ *
+ * It also contains a digitizer application collection that also is not
+ * of interest since any information it contains would be duplicate
+ * of what is in wacom_features. Usually it defines a report of an array
+ * of bytes that could be used as max length of the stylus packet returned.
+ * If it happens to define a Digitizer-Stylus Physical Collection then
+ * the X and Y logical values contain valid data but it is ignored.
+ *
+ * A typical Interface Descriptor for a touch interface will contain a
+ * Digitizer-Finger Physical Collection which will define both logical
+ * X/Y maximum as well as the physical size of tablet. Since touch
+ * interfaces haven't supported pressure or distance, this is enough
+ * information to override invalid values in the wacom_features table.
+ *
+ * Intuos5 touch interface and 3rd gen Bamboo Touch do not contain useful
+ * data. We deal with them after returning from this function.
+ */
+static void wacom_usage_mapping(struct hid_device *hdev,
+               struct hid_field *field, struct hid_usage *usage)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_features *features = &wacom->wacom_wac.features;
+       bool finger = (field->logical == HID_DG_FINGER) ||
+                     (field->physical == HID_DG_FINGER);
+       bool pen = (field->logical == HID_DG_STYLUS) ||
+                  (field->physical == HID_DG_STYLUS);
+
+       /*
+       * Requiring Stylus Usage will ignore boot mouse
+       * X/Y values and some cases of invalid Digitizer X/Y
+       * values commonly reported.
+       */
+       if (!pen && !finger)
+               return;
+
+       if (finger && !features->touch_max)
+               /* touch device at least supports one touch point */
+               features->touch_max = 1;
+
+       switch (usage->hid) {
+       case HID_GD_X:
+               features->x_max = field->logical_maximum;
+               if (finger) {
+                       features->device_type = BTN_TOOL_FINGER;
+                       features->x_phy = field->physical_maximum;
+                       if (features->type != BAMBOO_PT) {
+                               features->unit = field->unit;
+                               features->unitExpo = field->unit_exponent;
+                       }
+               } else {
+                       features->device_type = BTN_TOOL_PEN;
+               }
+               break;
+       case HID_GD_Y:
+               features->y_max = field->logical_maximum;
+               if (finger) {
+                       features->y_phy = field->physical_maximum;
+                       if (features->type != BAMBOO_PT) {
+                               features->unit = field->unit;
+                               features->unitExpo = field->unit_exponent;
+                       }
+               }
+               break;
+       case HID_DG_TIPPRESSURE:
+               if (pen)
+                       features->pressure_max = field->logical_maximum;
+               break;
+       }
+}
+
+static void wacom_parse_hid(struct hid_device *hdev,
+                          struct wacom_features *features)
+{
+       struct hid_report_enum *rep_enum;
+       struct hid_report *hreport;
+       int i, j;
+
+       /* check features first */
+       rep_enum = &hdev->report_enum[HID_FEATURE_REPORT];
+       list_for_each_entry(hreport, &rep_enum->report_list, list) {
+               for (i = 0; i < hreport->maxfield; i++) {
+                       /* Ignore if report count is out of bounds. */
+                       if (hreport->field[i]->report_count < 1)
+                               continue;
+
+                       for (j = 0; j < hreport->field[i]->maxusage; j++) {
+                               wacom_feature_mapping(hdev, hreport->field[i],
+                                               hreport->field[i]->usage + j);
+                       }
+               }
+       }
+
+       /* now check the input usages */
+       rep_enum = &hdev->report_enum[HID_INPUT_REPORT];
+       list_for_each_entry(hreport, &rep_enum->report_list, list) {
+
+               if (!hreport->maxfield)
+                       continue;
+
+               for (i = 0; i < hreport->maxfield; i++)
+                       for (j = 0; j < hreport->field[i]->maxusage; j++)
+                               wacom_usage_mapping(hdev, hreport->field[i],
+                                               hreport->field[i]->usage + j);
+       }
+}
+
+static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
+               int length, int mode)
+{
+       unsigned char *rep_data;
+       int error = -ENOMEM, limit = 0;
+
+       rep_data = kzalloc(length, GFP_KERNEL);
+       if (!rep_data)
+               return error;
+
+       do {
+               rep_data[0] = report_id;
+               rep_data[1] = mode;
+
+               error = wacom_set_report(hdev, HID_FEATURE_REPORT,
+                                        report_id, rep_data, length, 1);
+               if (error >= 0)
+                       error = wacom_get_report(hdev, HID_FEATURE_REPORT,
+                                                report_id, rep_data, length, 1);
+       } while ((error < 0 || rep_data[1] != mode) && limit++ < WAC_MSG_RETRIES);
+
+       kfree(rep_data);
+
+       return error < 0 ? error : 0;
+}
+
+/*
+ * Switch the tablet into its most-capable mode. Wacom tablets are
+ * typically configured to power-up in a mode which sends mouse-like
+ * reports to the OS. To get absolute position, pressure data, etc.
+ * from the tablet, it is necessary to switch the tablet out of this
+ * mode and into one which sends the full range of tablet data.
+ */
+static int wacom_query_tablet_data(struct hid_device *hdev,
+               struct wacom_features *features)
+{
+       if (features->device_type == BTN_TOOL_FINGER) {
+               if (features->type > TABLETPC) {
+                       /* MT Tablet PC touch */
+                       return wacom_set_device_mode(hdev, 3, 4, 4);
+               }
+               else if (features->type == WACOM_24HDT || features->type == CINTIQ_HYBRID) {
+                       return wacom_set_device_mode(hdev, 18, 3, 2);
+               }
+       } else if (features->device_type == BTN_TOOL_PEN) {
+               if (features->type <= BAMBOO_PT && features->type != WIRELESS) {
+                       return wacom_set_device_mode(hdev, 2, 2, 2);
+               }
+       }
+
+       return 0;
+}
+
+static void wacom_retrieve_hid_descriptor(struct hid_device *hdev,
+                                        struct wacom_features *features)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct usb_interface *intf = wacom->intf;
+
+       /* default features */
+       features->device_type = BTN_TOOL_PEN;
+       features->x_fuzz = 4;
+       features->y_fuzz = 4;
+       features->pressure_fuzz = 0;
+       features->distance_fuzz = 0;
+
+       /*
+        * The wireless device HID is basic and layout conflicts with
+        * other tablets (monitor and touch interface can look like pen).
+        * Skip the query for this type and modify defaults based on
+        * interface number.
+        */
+       if (features->type == WIRELESS) {
+               if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
+                       features->device_type = 0;
+               } else if (intf->cur_altsetting->desc.bInterfaceNumber == 2) {
+                       features->device_type = BTN_TOOL_FINGER;
+                       features->pktlen = WACOM_PKGLEN_BBTOUCH3;
+               }
+       }
+
+       /* only devices that support touch need to retrieve the info */
+       if (features->type < BAMBOO_PT)
+               return;
+
+       wacom_parse_hid(hdev, features);
+}
+
+struct wacom_hdev_data {
+       struct list_head list;
+       struct kref kref;
+       struct hid_device *dev;
+       struct wacom_shared shared;
+};
+
+static LIST_HEAD(wacom_udev_list);
+static DEFINE_MUTEX(wacom_udev_list_lock);
+
+static bool wacom_are_sibling(struct hid_device *hdev,
+               struct hid_device *sibling)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_features *features = &wacom->wacom_wac.features;
+       int vid = features->oVid;
+       int pid = features->oPid;
+       int n1,n2;
+
+       if (vid == 0 && pid == 0) {
+               vid = hdev->vendor;
+               pid = hdev->product;
+       }
+
+       if (vid != sibling->vendor || pid != sibling->product)
+               return false;
+
+       /* Compare the physical path. */
+       n1 = strrchr(hdev->phys, '.') - hdev->phys;
+       n2 = strrchr(sibling->phys, '.') - sibling->phys;
+       if (n1 != n2 || n1 <= 0 || n2 <= 0)
+               return false;
+
+       return !strncmp(hdev->phys, sibling->phys, n1);
+}
+
+static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev)
+{
+       struct wacom_hdev_data *data;
+
+       list_for_each_entry(data, &wacom_udev_list, list) {
+               if (wacom_are_sibling(hdev, data->dev)) {
+                       kref_get(&data->kref);
+                       return data;
+               }
+       }
+
+       return NULL;
+}
+
+static int wacom_add_shared_data(struct hid_device *hdev)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct wacom_hdev_data *data;
+       int retval = 0;
+
+       mutex_lock(&wacom_udev_list_lock);
+
+       data = wacom_get_hdev_data(hdev);
+       if (!data) {
+               data = kzalloc(sizeof(struct wacom_hdev_data), GFP_KERNEL);
+               if (!data) {
+                       retval = -ENOMEM;
+                       goto out;
+               }
+
+               kref_init(&data->kref);
+               data->dev = hdev;
+               list_add_tail(&data->list, &wacom_udev_list);
+       }
+
+       wacom_wac->shared = &data->shared;
+
+out:
+       mutex_unlock(&wacom_udev_list_lock);
+       return retval;
+}
+
+static void wacom_release_shared_data(struct kref *kref)
+{
+       struct wacom_hdev_data *data =
+               container_of(kref, struct wacom_hdev_data, kref);
+
+       mutex_lock(&wacom_udev_list_lock);
+       list_del(&data->list);
+       mutex_unlock(&wacom_udev_list_lock);
+
+       kfree(data);
+}
+
+static void wacom_remove_shared_data(struct wacom_wac *wacom)
+{
+       struct wacom_hdev_data *data;
+
+       if (wacom->shared) {
+               data = container_of(wacom->shared, struct wacom_hdev_data, shared);
+               kref_put(&data->kref, wacom_release_shared_data);
+               wacom->shared = NULL;
+       }
+}
+
+static int wacom_led_control(struct wacom *wacom)
+{
+       unsigned char *buf;
+       int retval;
+
+       buf = kzalloc(9, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       if (wacom->wacom_wac.features.type >= INTUOS5S &&
+           wacom->wacom_wac.features.type <= INTUOSPL) {
+               /*
+                * Touch Ring and crop mark LED luminance may take on
+                * one of four values:
+                *    0 = Low; 1 = Medium; 2 = High; 3 = Off
+                */
+               int ring_led = wacom->led.select[0] & 0x03;
+               int ring_lum = (((wacom->led.llv & 0x60) >> 5) - 1) & 0x03;
+               int crop_lum = 0;
+
+               buf[0] = WAC_CMD_LED_CONTROL;
+               buf[1] = (crop_lum << 4) | (ring_lum << 2) | (ring_led);
+       }
+       else {
+               int led = wacom->led.select[0] | 0x4;
+
+               if (wacom->wacom_wac.features.type == WACOM_21UX2 ||
+                   wacom->wacom_wac.features.type == WACOM_24HD)
+                       led |= (wacom->led.select[1] << 4) | 0x40;
+
+               buf[0] = WAC_CMD_LED_CONTROL;
+               buf[1] = led;
+               buf[2] = wacom->led.llv;
+               buf[3] = wacom->led.hlv;
+               buf[4] = wacom->led.img_lum;
+       }
+
+       retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
+                                 WAC_CMD_LED_CONTROL, buf, 9, WAC_CMD_RETRIES);
+       kfree(buf);
+
+       return retval;
+}
+
+static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *img)
+{
+       unsigned char *buf;
+       int i, retval;
+
+       buf = kzalloc(259, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       /* Send 'start' command */
+       buf[0] = WAC_CMD_ICON_START;
+       buf[1] = 1;
+       retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
+                                 WAC_CMD_ICON_START, buf, 2, WAC_CMD_RETRIES);
+       if (retval < 0)
+               goto out;
+
+       buf[0] = WAC_CMD_ICON_XFER;
+       buf[1] = button_id & 0x07;
+       for (i = 0; i < 4; i++) {
+               buf[2] = i;
+               memcpy(buf + 3, img + i * 256, 256);
+
+               retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
+                                         WAC_CMD_ICON_XFER,
+                                         buf, 259, WAC_CMD_RETRIES);
+               if (retval < 0)
+                       break;
+       }
+
+       /* Send 'stop' */
+       buf[0] = WAC_CMD_ICON_START;
+       buf[1] = 0;
+       wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, WAC_CMD_ICON_START,
+                        buf, 2, WAC_CMD_RETRIES);
+
+out:
+       kfree(buf);
+       return retval;
+}
+
+static ssize_t wacom_led_select_store(struct device *dev, int set_id,
+                                     const char *buf, size_t count)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       unsigned int id;
+       int err;
+
+       err = kstrtouint(buf, 10, &id);
+       if (err)
+               return err;
+
+       mutex_lock(&wacom->lock);
+
+       wacom->led.select[set_id] = id & 0x3;
+       err = wacom_led_control(wacom);
+
+       mutex_unlock(&wacom->lock);
+
+       return err < 0 ? err : count;
+}
+
+#define DEVICE_LED_SELECT_ATTR(SET_ID)                                 \
+static ssize_t wacom_led##SET_ID##_select_store(struct device *dev,    \
+       struct device_attribute *attr, const char *buf, size_t count)   \
+{                                                                      \
+       return wacom_led_select_store(dev, SET_ID, buf, count);         \
+}                                                                      \
+static ssize_t wacom_led##SET_ID##_select_show(struct device *dev,     \
+       struct device_attribute *attr, char *buf)                       \
+{                                                                      \
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);\
+       struct wacom *wacom = hid_get_drvdata(hdev);                    \
+       return snprintf(buf, 2, "%d\n", wacom->led.select[SET_ID]);     \
+}                                                                      \
+static DEVICE_ATTR(status_led##SET_ID##_select, S_IWUSR | S_IRUSR,     \
+                   wacom_led##SET_ID##_select_show,                    \
+                   wacom_led##SET_ID##_select_store)
+
+DEVICE_LED_SELECT_ATTR(0);
+DEVICE_LED_SELECT_ATTR(1);
+
+static ssize_t wacom_luminance_store(struct wacom *wacom, u8 *dest,
+                                    const char *buf, size_t count)
+{
+       unsigned int value;
+       int err;
+
+       err = kstrtouint(buf, 10, &value);
+       if (err)
+               return err;
+
+       mutex_lock(&wacom->lock);
+
+       *dest = value & 0x7f;
+       err = wacom_led_control(wacom);
+
+       mutex_unlock(&wacom->lock);
+
+       return err < 0 ? err : count;
+}
+
+#define DEVICE_LUMINANCE_ATTR(name, field)                             \
+static ssize_t wacom_##name##_luminance_store(struct device *dev,      \
+       struct device_attribute *attr, const char *buf, size_t count)   \
+{                                                                      \
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);\
+       struct wacom *wacom = hid_get_drvdata(hdev);                    \
+                                                                       \
+       return wacom_luminance_store(wacom, &wacom->led.field,          \
+                                    buf, count);                       \
+}                                                                      \
+static DEVICE_ATTR(name##_luminance, S_IWUSR,                          \
+                  NULL, wacom_##name##_luminance_store)
+
+DEVICE_LUMINANCE_ATTR(status0, llv);
+DEVICE_LUMINANCE_ATTR(status1, hlv);
+DEVICE_LUMINANCE_ATTR(buttons, img_lum);
+
+static ssize_t wacom_button_image_store(struct device *dev, int button_id,
+                                       const char *buf, size_t count)
+{
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       int err;
+
+       if (count != 1024)
+               return -EINVAL;
+
+       mutex_lock(&wacom->lock);
+
+       err = wacom_led_putimage(wacom, button_id, buf);
+
+       mutex_unlock(&wacom->lock);
+
+       return err < 0 ? err : count;
+}
+
+#define DEVICE_BTNIMG_ATTR(BUTTON_ID)                                  \
+static ssize_t wacom_btnimg##BUTTON_ID##_store(struct device *dev,     \
+       struct device_attribute *attr, const char *buf, size_t count)   \
+{                                                                      \
+       return wacom_button_image_store(dev, BUTTON_ID, buf, count);    \
+}                                                                      \
+static DEVICE_ATTR(button##BUTTON_ID##_rawimg, S_IWUSR,                        \
+                  NULL, wacom_btnimg##BUTTON_ID##_store)
+
+DEVICE_BTNIMG_ATTR(0);
+DEVICE_BTNIMG_ATTR(1);
+DEVICE_BTNIMG_ATTR(2);
+DEVICE_BTNIMG_ATTR(3);
+DEVICE_BTNIMG_ATTR(4);
+DEVICE_BTNIMG_ATTR(5);
+DEVICE_BTNIMG_ATTR(6);
+DEVICE_BTNIMG_ATTR(7);
+
+static struct attribute *cintiq_led_attrs[] = {
+       &dev_attr_status_led0_select.attr,
+       &dev_attr_status_led1_select.attr,
+       NULL
+};
+
+static struct attribute_group cintiq_led_attr_group = {
+       .name = "wacom_led",
+       .attrs = cintiq_led_attrs,
+};
+
+static struct attribute *intuos4_led_attrs[] = {
+       &dev_attr_status0_luminance.attr,
+       &dev_attr_status1_luminance.attr,
+       &dev_attr_status_led0_select.attr,
+       &dev_attr_buttons_luminance.attr,
+       &dev_attr_button0_rawimg.attr,
+       &dev_attr_button1_rawimg.attr,
+       &dev_attr_button2_rawimg.attr,
+       &dev_attr_button3_rawimg.attr,
+       &dev_attr_button4_rawimg.attr,
+       &dev_attr_button5_rawimg.attr,
+       &dev_attr_button6_rawimg.attr,
+       &dev_attr_button7_rawimg.attr,
+       NULL
+};
+
+static struct attribute_group intuos4_led_attr_group = {
+       .name = "wacom_led",
+       .attrs = intuos4_led_attrs,
+};
+
+static struct attribute *intuos5_led_attrs[] = {
+       &dev_attr_status0_luminance.attr,
+       &dev_attr_status_led0_select.attr,
+       NULL
+};
+
+static struct attribute_group intuos5_led_attr_group = {
+       .name = "wacom_led",
+       .attrs = intuos5_led_attrs,
+};
+
+static int wacom_initialize_leds(struct wacom *wacom)
+{
+       int error;
+
+       /* Initialize default values */
+       switch (wacom->wacom_wac.features.type) {
+       case INTUOS4S:
+       case INTUOS4:
+       case INTUOS4L:
+               wacom->led.select[0] = 0;
+               wacom->led.select[1] = 0;
+               wacom->led.llv = 10;
+               wacom->led.hlv = 20;
+               wacom->led.img_lum = 10;
+               error = sysfs_create_group(&wacom->hdev->dev.kobj,
+                                          &intuos4_led_attr_group);
+               break;
+
+       case WACOM_24HD:
+       case WACOM_21UX2:
+               wacom->led.select[0] = 0;
+               wacom->led.select[1] = 0;
+               wacom->led.llv = 0;
+               wacom->led.hlv = 0;
+               wacom->led.img_lum = 0;
+
+               error = sysfs_create_group(&wacom->hdev->dev.kobj,
+                                          &cintiq_led_attr_group);
+               break;
+
+       case INTUOS5S:
+       case INTUOS5:
+       case INTUOS5L:
+       case INTUOSPS:
+       case INTUOSPM:
+       case INTUOSPL:
+               if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN) {
+                       wacom->led.select[0] = 0;
+                       wacom->led.select[1] = 0;
+                       wacom->led.llv = 32;
+                       wacom->led.hlv = 0;
+                       wacom->led.img_lum = 0;
+
+                       error = sysfs_create_group(&wacom->hdev->dev.kobj,
+                                                 &intuos5_led_attr_group);
+               } else
+                       return 0;
+               break;
+
+       default:
+               return 0;
+       }
+
+       if (error) {
+               hid_err(wacom->hdev,
+                       "cannot create sysfs group err: %d\n", error);
+               return error;
+       }
+       wacom_led_control(wacom);
+
+       return 0;
+}
+
+static void wacom_destroy_leds(struct wacom *wacom)
+{
+       switch (wacom->wacom_wac.features.type) {
+       case INTUOS4S:
+       case INTUOS4:
+       case INTUOS4L:
+               sysfs_remove_group(&wacom->hdev->dev.kobj,
+                                  &intuos4_led_attr_group);
+               break;
+
+       case WACOM_24HD:
+       case WACOM_21UX2:
+               sysfs_remove_group(&wacom->hdev->dev.kobj,
+                                  &cintiq_led_attr_group);
+               break;
+
+       case INTUOS5S:
+       case INTUOS5:
+       case INTUOS5L:
+       case INTUOSPS:
+       case INTUOSPM:
+       case INTUOSPL:
+               if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN)
+                       sysfs_remove_group(&wacom->hdev->dev.kobj,
+                                          &intuos5_led_attr_group);
+               break;
+       }
+}
+
+static enum power_supply_property wacom_battery_props[] = {
+       POWER_SUPPLY_PROP_SCOPE,
+       POWER_SUPPLY_PROP_CAPACITY
+};
+
+static int wacom_battery_get_property(struct power_supply *psy,
+                                     enum power_supply_property psp,
+                                     union power_supply_propval *val)
+{
+       struct wacom *wacom = container_of(psy, struct wacom, battery);
+       int ret = 0;
+
+       switch (psp) {
+               case POWER_SUPPLY_PROP_SCOPE:
+                       val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+                       break;
+               case POWER_SUPPLY_PROP_CAPACITY:
+                       val->intval =
+                               wacom->wacom_wac.battery_capacity * 100 / 31;
+                       break;
+               default:
+                       ret = -EINVAL;
+                       break;
+       }
+
+       return ret;
+}
+
+static int wacom_initialize_battery(struct wacom *wacom)
+{
+       int error = 0;
+
+       if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR) {
+               wacom->battery.properties = wacom_battery_props;
+               wacom->battery.num_properties = ARRAY_SIZE(wacom_battery_props);
+               wacom->battery.get_property = wacom_battery_get_property;
+               wacom->battery.name = "wacom_battery";
+               wacom->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+               wacom->battery.use_for_apm = 0;
+
+               error = power_supply_register(&wacom->hdev->dev,
+                                             &wacom->battery);
+
+               if (!error)
+                       power_supply_powers(&wacom->battery,
+                                           &wacom->hdev->dev);
+       }
+
+       return error;
+}
+
+static void wacom_destroy_battery(struct wacom *wacom)
+{
+       if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR &&
+           wacom->battery.dev) {
+               power_supply_unregister(&wacom->battery);
+               wacom->battery.dev = NULL;
+       }
+}
+
+static struct input_dev *wacom_allocate_input(struct wacom *wacom)
+{
+       struct input_dev *input_dev;
+       struct hid_device *hdev = wacom->hdev;
+       struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
+
+       input_dev = input_allocate_device();
+       if (!input_dev)
+               return NULL;
+
+       input_dev->name = wacom_wac->name;
+       input_dev->phys = hdev->phys;
+       input_dev->dev.parent = &hdev->dev;
+       input_dev->open = wacom_open;
+       input_dev->close = wacom_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, wacom);
+
+       return input_dev;
+}
+
+static void wacom_unregister_inputs(struct wacom *wacom)
+{
+       if (wacom->wacom_wac.input)
+               input_unregister_device(wacom->wacom_wac.input);
+       if (wacom->wacom_wac.pad_input)
+               input_unregister_device(wacom->wacom_wac.pad_input);
+       wacom->wacom_wac.input = NULL;
+       wacom->wacom_wac.pad_input = NULL;
+}
+
+static int wacom_register_inputs(struct wacom *wacom)
+{
+       struct input_dev *input_dev, *pad_input_dev;
+       struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
+       int error;
+
+       input_dev = wacom_allocate_input(wacom);
+       pad_input_dev = wacom_allocate_input(wacom);
+       if (!input_dev || !pad_input_dev) {
+               error = -ENOMEM;
+               goto fail1;
+       }
+
+       wacom_wac->input = input_dev;
+       wacom_wac->pad_input = pad_input_dev;
+       wacom_wac->pad_input->name = wacom_wac->pad_name;
+
+       error = wacom_setup_input_capabilities(input_dev, wacom_wac);
+       if (error)
+               goto fail2;
+
+       error = input_register_device(input_dev);
+       if (error)
+               goto fail2;
+
+       error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac);
+       if (error) {
+               /* no pad in use on this interface */
+               input_free_device(pad_input_dev);
+               wacom_wac->pad_input = NULL;
+               pad_input_dev = NULL;
+       } else {
+               error = input_register_device(pad_input_dev);
+               if (error)
+                       goto fail3;
+       }
+
+       return 0;
+
+fail3:
+       input_unregister_device(input_dev);
+       input_dev = NULL;
+fail2:
+       wacom_wac->input = NULL;
+       wacom_wac->pad_input = NULL;
+fail1:
+       if (input_dev)
+               input_free_device(input_dev);
+       if (pad_input_dev)
+               input_free_device(pad_input_dev);
+       return error;
+}
+
+static void wacom_wireless_work(struct work_struct *work)
+{
+       struct wacom *wacom = container_of(work, struct wacom, work);
+       struct usb_device *usbdev = wacom->usbdev;
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct hid_device *hdev1, *hdev2;
+       struct wacom *wacom1, *wacom2;
+       struct wacom_wac *wacom_wac1, *wacom_wac2;
+       int error;
+
+       /*
+        * Regardless if this is a disconnect or a new tablet,
+        * remove any existing input and battery devices.
+        */
+
+       wacom_destroy_battery(wacom);
+
+       /* Stylus interface */
+       hdev1 = usb_get_intfdata(usbdev->config->interface[1]);
+       wacom1 = hid_get_drvdata(hdev1);
+       wacom_wac1 = &(wacom1->wacom_wac);
+       wacom_unregister_inputs(wacom1);
+
+       /* Touch interface */
+       hdev2 = usb_get_intfdata(usbdev->config->interface[2]);
+       wacom2 = hid_get_drvdata(hdev2);
+       wacom_wac2 = &(wacom2->wacom_wac);
+       wacom_unregister_inputs(wacom2);
+
+       if (wacom_wac->pid == 0) {
+               hid_info(wacom->hdev, "wireless tablet disconnected\n");
+       } else {
+               const struct hid_device_id *id = wacom_ids;
+
+               hid_info(wacom->hdev, "wireless tablet connected with PID %x\n",
+                        wacom_wac->pid);
+
+               while (id->bus) {
+                       if (id->vendor == USB_VENDOR_ID_WACOM &&
+                           id->product == wacom_wac->pid)
+                               break;
+                       id++;
+               }
+
+               if (!id->bus) {
+                       hid_info(wacom->hdev, "ignoring unknown PID.\n");
+                       return;
+               }
+
+               /* Stylus interface */
+               wacom_wac1->features =
+                       *((struct wacom_features *)id->driver_data);
+               wacom_wac1->features.device_type = BTN_TOOL_PEN;
+               snprintf(wacom_wac1->name, WACOM_NAME_MAX, "%s (WL) Pen",
+                        wacom_wac1->features.name);
+               snprintf(wacom_wac1->pad_name, WACOM_NAME_MAX, "%s (WL) Pad",
+                        wacom_wac1->features.name);
+               wacom_wac1->shared->touch_max = wacom_wac1->features.touch_max;
+               wacom_wac1->shared->type = wacom_wac1->features.type;
+               error = wacom_register_inputs(wacom1);
+               if (error)
+                       goto fail;
+
+               /* Touch interface */
+               if (wacom_wac1->features.touch_max ||
+                   wacom_wac1->features.type == INTUOSHT) {
+                       wacom_wac2->features =
+                               *((struct wacom_features *)id->driver_data);
+                       wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
+                       wacom_wac2->features.device_type = BTN_TOOL_FINGER;
+                       wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096;
+                       if (wacom_wac2->features.touch_max)
+                               snprintf(wacom_wac2->name, WACOM_NAME_MAX,
+                                        "%s (WL) Finger",wacom_wac2->features.name);
+                       else
+                               snprintf(wacom_wac2->name, WACOM_NAME_MAX,
+                                        "%s (WL) Pad",wacom_wac2->features.name);
+                       snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX,
+                                "%s (WL) Pad", wacom_wac2->features.name);
+                       error = wacom_register_inputs(wacom2);
+                       if (error)
+                               goto fail;
+
+                       if (wacom_wac1->features.type == INTUOSHT &&
+                           wacom_wac1->features.touch_max)
+                               wacom_wac->shared->touch_input = wacom_wac2->input;
+               }
+
+               error = wacom_initialize_battery(wacom);
+               if (error)
+                       goto fail;
+       }
+
+       return;
+
+fail:
+       wacom_unregister_inputs(wacom1);
+       wacom_unregister_inputs(wacom2);
+       return;
+}
+
+/*
+ * Not all devices report physical dimensions from HID.
+ * Compute the default from hardcoded logical dimension
+ * and resolution before driver overwrites them.
+ */
+static void wacom_set_default_phy(struct wacom_features *features)
+{
+       if (features->x_resolution) {
+               features->x_phy = (features->x_max * 100) /
+                                       features->x_resolution;
+               features->y_phy = (features->y_max * 100) /
+                                       features->y_resolution;
+       }
+}
+
+static void wacom_calculate_res(struct wacom_features *features)
+{
+       features->x_resolution = wacom_calc_hid_res(features->x_max,
+                                                   features->x_phy,
+                                                   features->unit,
+                                                   features->unitExpo);
+       features->y_resolution = wacom_calc_hid_res(features->y_max,
+                                                   features->y_phy,
+                                                   features->unit,
+                                                   features->unitExpo);
+}
+
+static int wacom_hid_report_len(struct hid_report *report)
+{
+       /* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */
+       return ((report->size - 1) >> 3) + 1 + (report->id > 0);
+}
+
+static size_t wacom_compute_pktlen(struct hid_device *hdev)
+{
+       struct hid_report_enum *report_enum;
+       struct hid_report *report;
+       size_t size = 0;
+
+       report_enum = hdev->report_enum + HID_INPUT_REPORT;
+
+       list_for_each_entry(report, &report_enum->report_list, list) {
+               size_t report_size = wacom_hid_report_len(report);
+               if (report_size > size)
+                       size = report_size;
+       }
+
+       return size;
+}
+
+static int wacom_probe(struct hid_device *hdev,
+               const struct hid_device_id *id)
+{
+       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
+       struct usb_device *dev = interface_to_usbdev(intf);
+       struct wacom *wacom;
+       struct wacom_wac *wacom_wac;
+       struct wacom_features *features;
+       int error;
+
+       if (!id->driver_data)
+               return -EINVAL;
+
+       wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
+       if (!wacom)
+               return -ENOMEM;
+
+       hid_set_drvdata(hdev, wacom);
+       wacom->hdev = hdev;
+
+       /* ask for the report descriptor to be loaded by HID */
+       error = hid_parse(hdev);
+       if (error) {
+               hid_err(hdev, "parse failed\n");
+               goto fail1;
+       }
+
+       wacom_wac = &wacom->wacom_wac;
+       wacom_wac->features = *((struct wacom_features *)id->driver_data);
+       features = &wacom_wac->features;
+       features->pktlen = wacom_compute_pktlen(hdev);
+       if (features->pktlen > WACOM_PKGLEN_MAX) {
+               error = -EINVAL;
+               goto fail1;
+       }
+
+       if (features->check_for_hid_type && features->hid_type != hdev->type) {
+               error = -ENODEV;
+               goto fail1;
+       }
+
+       wacom->usbdev = dev;
+       wacom->intf = intf;
+       mutex_init(&wacom->lock);
+       INIT_WORK(&wacom->work, wacom_wireless_work);
+
+       /* set the default size in case we do not get them from hid */
+       wacom_set_default_phy(features);
+
+       /* Retrieve the physical and logical size for touch devices */
+       wacom_retrieve_hid_descriptor(hdev, features);
+
+       /*
+        * Intuos5 has no useful data about its touch interface in its
+        * HID descriptor. If this is the touch interface (PacketSize
+        * of WACOM_PKGLEN_BBTOUCH3), override the table values.
+        */
+       if (features->type >= INTUOS5S && features->type <= INTUOSHT) {
+               if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
+                       features->device_type = BTN_TOOL_FINGER;
+
+                       features->x_max = 4096;
+                       features->y_max = 4096;
+               } else {
+                       features->device_type = BTN_TOOL_PEN;
+               }
+       }
+
+       /*
+        * Same thing for Bamboo 3rd gen.
+        */
+       if ((features->type == BAMBOO_PT) &&
+           (features->pktlen == WACOM_PKGLEN_BBTOUCH3) &&
+           (features->device_type == BTN_TOOL_PEN)) {
+               features->device_type = BTN_TOOL_FINGER;
+
+               features->x_max = 4096;
+               features->y_max = 4096;
+       }
+
+       wacom_setup_device_quirks(features);
+
+       /* set unit to "100th of a mm" for devices not reported by HID */
+       if (!features->unit) {
+               features->unit = 0x11;
+               features->unitExpo = -3;
+       }
+       wacom_calculate_res(features);
+
+       strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name));
+       snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
+               "%s Pad", features->name);
+
+       if (features->quirks & WACOM_QUIRK_MULTI_INPUT) {
+               /* Append the device type to the name */
+               if (features->device_type != BTN_TOOL_FINGER)
+                       strlcat(wacom_wac->name, " Pen", WACOM_NAME_MAX);
+               else if (features->touch_max)
+                       strlcat(wacom_wac->name, " Finger", WACOM_NAME_MAX);
+               else
+                       strlcat(wacom_wac->name, " Pad", WACOM_NAME_MAX);
+
+               error = wacom_add_shared_data(hdev);
+               if (error)
+                       goto fail1;
+       }
+
+       error = wacom_initialize_leds(wacom);
+       if (error)
+               goto fail2;
+
+       if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
+               error = wacom_register_inputs(wacom);
+               if (error)
+                       goto fail3;
+       }
+
+       /* Note that if query fails it is not a hard failure */
+       wacom_query_tablet_data(hdev, features);
+
+       /* Regular HID work starts now */
+       error = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+       if (error) {
+               hid_err(hdev, "hw start failed\n");
+               goto fail4;
+       }
+
+       if (features->quirks & WACOM_QUIRK_MONITOR)
+               error = hid_hw_open(hdev);
+
+       if (wacom_wac->features.type == INTUOSHT && wacom_wac->features.touch_max) {
+               if (wacom_wac->features.device_type == BTN_TOOL_FINGER)
+                       wacom_wac->shared->touch_input = wacom_wac->input;
+       }
+
+       return 0;
+
+ fail4:        wacom_unregister_inputs(wacom);
+ fail3:        wacom_destroy_leds(wacom);
+ fail2:        wacom_remove_shared_data(wacom_wac);
+ fail1:        kfree(wacom);
+       hid_set_drvdata(hdev, NULL);
+       return error;
+}
+
+static void wacom_remove(struct hid_device *hdev)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+
+       hid_hw_stop(hdev);
+
+       cancel_work_sync(&wacom->work);
+       wacom_unregister_inputs(wacom);
+       wacom_destroy_battery(wacom);
+       wacom_destroy_leds(wacom);
+       wacom_remove_shared_data(&wacom->wacom_wac);
+
+       hid_set_drvdata(hdev, NULL);
+       kfree(wacom);
+}
+
+static int wacom_resume(struct hid_device *hdev)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_features *features = &wacom->wacom_wac.features;
+
+       mutex_lock(&wacom->lock);
+
+       /* switch to wacom mode first */
+       wacom_query_tablet_data(hdev, features);
+       wacom_led_control(wacom);
+
+       mutex_unlock(&wacom->lock);
+
+       return 0;
+}
+
+static int wacom_reset_resume(struct hid_device *hdev)
+{
+       return wacom_resume(hdev);
+}
+
+static struct hid_driver wacom_driver = {
+       .name =         "wacom",
+       .id_table =     wacom_ids,
+       .probe =        wacom_probe,
+       .remove =       wacom_remove,
+#ifdef CONFIG_PM
+       .resume =       wacom_resume,
+       .reset_resume = wacom_reset_resume,
+#endif
+       .raw_event =    wacom_raw_event,
+};
+module_hid_driver(wacom_driver);
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
new file mode 100644 (file)
index 0000000..cd91521
--- /dev/null
@@ -0,0 +1,2548 @@
+/*
+ * drivers/input/tablet/wacom_wac.c
+ *
+ *  USB Wacom tablet support - Wacom specific code
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "wacom_wac.h"
+#include "wacom.h"
+#include <linux/input/mt.h>
+#include <linux/hid.h>
+
+/* resolution for penabled devices */
+#define WACOM_PL_RES           20
+#define WACOM_PENPRTN_RES      40
+#define WACOM_VOLITO_RES       50
+#define WACOM_GRAPHIRE_RES     80
+#define WACOM_INTUOS_RES       100
+#define WACOM_INTUOS3_RES      200
+
+/* Scale factor relating reported contact size to logical contact area.
+ * 2^14/pi is a good approximation on Intuos5 and 3rd-gen Bamboo
+ */
+#define WACOM_CONTACT_AREA_SCALE 2607
+
+static int wacom_penpartner_irq(struct wacom_wac *wacom)
+{
+       unsigned char *data = wacom->data;
+       struct input_dev *input = wacom->input;
+
+       switch (data[0]) {
+       case 1:
+               if (data[5] & 0x80) {
+                       wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+                       wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID;
+                       input_report_key(input, wacom->tool[0], 1);
+                       input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
+                       input_report_abs(input, ABS_X, get_unaligned_le16(&data[1]));
+                       input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3]));
+                       input_report_abs(input, ABS_PRESSURE, (signed char)data[6] + 127);
+                       input_report_key(input, BTN_TOUCH, ((signed char)data[6] > -127));
+                       input_report_key(input, BTN_STYLUS, (data[5] & 0x40));
+               } else {
+                       input_report_key(input, wacom->tool[0], 0);
+                       input_report_abs(input, ABS_MISC, 0); /* report tool id */
+                       input_report_abs(input, ABS_PRESSURE, -1);
+                       input_report_key(input, BTN_TOUCH, 0);
+               }
+               break;
+
+       case 2:
+               input_report_key(input, BTN_TOOL_PEN, 1);
+               input_report_abs(input, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */
+               input_report_abs(input, ABS_X, get_unaligned_le16(&data[1]));
+               input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3]));
+               input_report_abs(input, ABS_PRESSURE, (signed char)data[6] + 127);
+               input_report_key(input, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20));
+               input_report_key(input, BTN_STYLUS, (data[5] & 0x40));
+               break;
+
+       default:
+               dev_dbg(input->dev.parent,
+                       "%s: received unknown report #%d\n", __func__, data[0]);
+               return 0;
+        }
+
+       return 1;
+}
+
+static int wacom_pl_irq(struct wacom_wac *wacom)
+{
+       struct wacom_features *features = &wacom->features;
+       unsigned char *data = wacom->data;
+       struct input_dev *input = wacom->input;
+       int prox, pressure;
+
+       if (data[0] != WACOM_REPORT_PENABLED) {
+               dev_dbg(input->dev.parent,
+                       "%s: received unknown report #%d\n", __func__, data[0]);
+               return 0;
+       }
+
+       prox = data[1] & 0x40;
+
+       if (prox) {
+               wacom->id[0] = ERASER_DEVICE_ID;
+               pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
+               if (features->pressure_max > 255)
+                       pressure = (pressure << 1) | ((data[4] >> 6) & 1);
+               pressure += (features->pressure_max + 1) / 2;
+
+               /*
+                * if going from out of proximity into proximity select between the eraser
+                * and the pen based on the state of the stylus2 button, choose eraser if
+                * pressed else choose pen. if not a proximity change from out to in, send
+                * an out of proximity for previous tool then a in for new tool.
+                */
+               if (!wacom->tool[0]) {
+                       /* Eraser bit set for DTF */
+                       if (data[1] & 0x10)
+                               wacom->tool[1] = BTN_TOOL_RUBBER;
+                       else
+                               /* Going into proximity select tool */
+                               wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+               } else {
+                       /* was entered with stylus2 pressed */
+                       if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) {
+                               /* report out proximity for previous tool */
+                               input_report_key(input, wacom->tool[1], 0);
+                               input_sync(input);
+                               wacom->tool[1] = BTN_TOOL_PEN;
+                               return 0;
+                       }
+               }
+               if (wacom->tool[1] != BTN_TOOL_RUBBER) {
+                       /* Unknown tool selected default to pen tool */
+                       wacom->tool[1] = BTN_TOOL_PEN;
+                       wacom->id[0] = STYLUS_DEVICE_ID;
+               }
+               input_report_key(input, wacom->tool[1], prox); /* report in proximity for tool */
+               input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
+               input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
+               input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
+               input_report_abs(input, ABS_PRESSURE, pressure);
+
+               input_report_key(input, BTN_TOUCH, data[4] & 0x08);
+               input_report_key(input, BTN_STYLUS, data[4] & 0x10);
+               /* Only allow the stylus2 button to be reported for the pen tool. */
+               input_report_key(input, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
+       } else {
+               /* report proximity-out of a (valid) tool */
+               if (wacom->tool[1] != BTN_TOOL_RUBBER) {
+                       /* Unknown tool selected default to pen tool */
+                       wacom->tool[1] = BTN_TOOL_PEN;
+               }
+               input_report_key(input, wacom->tool[1], prox);
+       }
+
+       wacom->tool[0] = prox; /* Save proximity state */
+       return 1;
+}
+
+static int wacom_ptu_irq(struct wacom_wac *wacom)
+{
+       unsigned char *data = wacom->data;
+       struct input_dev *input = wacom->input;
+
+       if (data[0] != WACOM_REPORT_PENABLED) {
+               dev_dbg(input->dev.parent,
+                       "%s: received unknown report #%d\n", __func__, data[0]);
+               return 0;
+       }
+
+       if (data[1] & 0x04) {
+               input_report_key(input, BTN_TOOL_RUBBER, data[1] & 0x20);
+               input_report_key(input, BTN_TOUCH, data[1] & 0x08);
+               wacom->id[0] = ERASER_DEVICE_ID;
+       } else {
+               input_report_key(input, BTN_TOOL_PEN, data[1] & 0x20);
+               input_report_key(input, BTN_TOUCH, data[1] & 0x01);
+               wacom->id[0] = STYLUS_DEVICE_ID;
+       }
+       input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
+       input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
+       input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
+       input_report_abs(input, ABS_PRESSURE, le16_to_cpup((__le16 *)&data[6]));
+       input_report_key(input, BTN_STYLUS, data[1] & 0x02);
+       input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
+       return 1;
+}
+
+static int wacom_dtu_irq(struct wacom_wac *wacom)
+{
+       unsigned char *data = wacom->data;
+       struct input_dev *input = wacom->input;
+       int prox = data[1] & 0x20;
+
+       dev_dbg(input->dev.parent,
+               "%s: received report #%d", __func__, data[0]);
+
+       if (prox) {
+               /* Going into proximity select tool */
+               wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+               if (wacom->tool[0] == BTN_TOOL_PEN)
+                       wacom->id[0] = STYLUS_DEVICE_ID;
+               else
+                       wacom->id[0] = ERASER_DEVICE_ID;
+       }
+       input_report_key(input, BTN_STYLUS, data[1] & 0x02);
+       input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
+       input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
+       input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
+       input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x01) << 8) | data[6]);
+       input_report_key(input, BTN_TOUCH, data[1] & 0x05);
+       if (!prox) /* out-prox */
+               wacom->id[0] = 0;
+       input_report_key(input, wacom->tool[0], prox);
+       input_report_abs(input, ABS_MISC, wacom->id[0]);
+       return 1;
+}
+
+static int wacom_dtus_irq(struct wacom_wac *wacom)
+{
+       char *data = wacom->data;
+       struct input_dev *input = wacom->input;
+       unsigned short prox, pressure = 0;
+
+       if (data[0] != WACOM_REPORT_DTUS && data[0] != WACOM_REPORT_DTUSPAD) {
+               dev_dbg(input->dev.parent,
+                       "%s: received unknown report #%d", __func__, data[0]);
+               return 0;
+       } else if (data[0] == WACOM_REPORT_DTUSPAD) {
+               input = wacom->pad_input;
+               input_report_key(input, BTN_0, (data[1] & 0x01));
+               input_report_key(input, BTN_1, (data[1] & 0x02));
+               input_report_key(input, BTN_2, (data[1] & 0x04));
+               input_report_key(input, BTN_3, (data[1] & 0x08));
+               input_report_abs(input, ABS_MISC,
+                                data[1] & 0x0f ? PAD_DEVICE_ID : 0);
+               return 1;
+       } else {
+               prox = data[1] & 0x80;
+               if (prox) {
+                       switch ((data[1] >> 3) & 3) {
+                       case 1: /* Rubber */
+                               wacom->tool[0] = BTN_TOOL_RUBBER;
+                               wacom->id[0] = ERASER_DEVICE_ID;
+                               break;
+
+                       case 2: /* Pen */
+                               wacom->tool[0] = BTN_TOOL_PEN;
+                               wacom->id[0] = STYLUS_DEVICE_ID;
+                               break;
+                       }
+               }
+
+               input_report_key(input, BTN_STYLUS, data[1] & 0x20);
+               input_report_key(input, BTN_STYLUS2, data[1] & 0x40);
+               input_report_abs(input, ABS_X, get_unaligned_be16(&data[3]));
+               input_report_abs(input, ABS_Y, get_unaligned_be16(&data[5]));
+               pressure = ((data[1] & 0x03) << 8) | (data[2] & 0xff);
+               input_report_abs(input, ABS_PRESSURE, pressure);
+               input_report_key(input, BTN_TOUCH, pressure > 10);
+
+               if (!prox) /* out-prox */
+                       wacom->id[0] = 0;
+               input_report_key(input, wacom->tool[0], prox);
+               input_report_abs(input, ABS_MISC, wacom->id[0]);
+               return 1;
+       }
+}
+
+static int wacom_graphire_irq(struct wacom_wac *wacom)
+{
+       struct wacom_features *features = &wacom->features;
+       unsigned char *data = wacom->data;
+       struct input_dev *input = wacom->input;
+       struct input_dev *pad_input = wacom->pad_input;
+       int prox;
+       int rw = 0;
+       int retval = 0;
+
+       if (data[0] != WACOM_REPORT_PENABLED) {
+               dev_dbg(input->dev.parent,
+                       "%s: received unknown report #%d\n", __func__, data[0]);
+               goto exit;
+       }
+
+       prox = data[1] & 0x80;
+       if (prox || wacom->id[0]) {
+               if (prox) {
+                       switch ((data[1] >> 5) & 3) {
+
+                       case 0: /* Pen */
+                               wacom->tool[0] = BTN_TOOL_PEN;
+                               wacom->id[0] = STYLUS_DEVICE_ID;
+                               break;
+
+                       case 1: /* Rubber */
+                               wacom->tool[0] = BTN_TOOL_RUBBER;
+                               wacom->id[0] = ERASER_DEVICE_ID;
+                               break;
+
+                       case 2: /* Mouse with wheel */
+                               input_report_key(input, BTN_MIDDLE, data[1] & 0x04);
+                               /* fall through */
+
+                       case 3: /* Mouse without wheel */
+                               wacom->tool[0] = BTN_TOOL_MOUSE;
+                               wacom->id[0] = CURSOR_DEVICE_ID;
+                               break;
+                       }
+               }
+               input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
+               input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
+               if (wacom->tool[0] != BTN_TOOL_MOUSE) {
+                       input_report_abs(input, ABS_PRESSURE, data[6] | ((data[7] & 0x03) << 8));
+                       input_report_key(input, BTN_TOUCH, data[1] & 0x01);
+                       input_report_key(input, BTN_STYLUS, data[1] & 0x02);
+                       input_report_key(input, BTN_STYLUS2, data[1] & 0x04);
+               } else {
+                       input_report_key(input, BTN_LEFT, data[1] & 0x01);
+                       input_report_key(input, BTN_RIGHT, data[1] & 0x02);
+                       if (features->type == WACOM_G4 ||
+                                       features->type == WACOM_MO) {
+                               input_report_abs(input, ABS_DISTANCE, data[6] & 0x3f);
+                               rw = (data[7] & 0x04) - (data[7] & 0x03);
+                       } else {
+                               input_report_abs(input, ABS_DISTANCE, data[7] & 0x3f);
+                               rw = -(signed char)data[6];
+                       }
+                       input_report_rel(input, REL_WHEEL, rw);
+               }
+
+               if (!prox)
+                       wacom->id[0] = 0;
+               input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
+               input_report_key(input, wacom->tool[0], prox);
+               input_sync(input); /* sync last event */
+       }
+
+       /* send pad data */
+       switch (features->type) {
+       case WACOM_G4:
+               prox = data[7] & 0xf8;
+               if (prox || wacom->id[1]) {
+                       wacom->id[1] = PAD_DEVICE_ID;
+                       input_report_key(pad_input, BTN_BACK, (data[7] & 0x40));
+                       input_report_key(pad_input, BTN_FORWARD, (data[7] & 0x80));
+                       rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
+                       input_report_rel(pad_input, REL_WHEEL, rw);
+                       if (!prox)
+                               wacom->id[1] = 0;
+                       input_report_abs(pad_input, ABS_MISC, wacom->id[1]);
+                       retval = 1;
+               }
+               break;
+
+       case WACOM_MO:
+               prox = (data[7] & 0xf8) || data[8];
+               if (prox || wacom->id[1]) {
+                       wacom->id[1] = PAD_DEVICE_ID;
+                       input_report_key(pad_input, BTN_BACK, (data[7] & 0x08));
+                       input_report_key(pad_input, BTN_LEFT, (data[7] & 0x20));
+                       input_report_key(pad_input, BTN_FORWARD, (data[7] & 0x10));
+                       input_report_key(pad_input, BTN_RIGHT, (data[7] & 0x40));
+                       input_report_abs(pad_input, ABS_WHEEL, (data[8] & 0x7f));
+                       if (!prox)
+                               wacom->id[1] = 0;
+                       input_report_abs(pad_input, ABS_MISC, wacom->id[1]);
+                       retval = 1;
+               }
+               break;
+       }
+exit:
+       return retval;
+}
+
+static int wacom_intuos_inout(struct wacom_wac *wacom)
+{
+       struct wacom_features *features = &wacom->features;
+       unsigned char *data = wacom->data;
+       struct input_dev *input = wacom->input;
+       int idx = 0;
+
+       /* tool number */
+       if (features->type == INTUOS)
+               idx = data[1] & 0x01;
+
+       /* Enter report */
+       if ((data[1] & 0xfc) == 0xc0) {
+               if (features->quirks & WACOM_QUIRK_MULTI_INPUT)
+                       wacom->shared->stylus_in_proximity = true;
+
+               /* serial number of the tool */
+               wacom->serial[idx] = ((data[3] & 0x0f) << 28) +
+                       (data[4] << 20) + (data[5] << 12) +
+                       (data[6] << 4) + (data[7] >> 4);
+
+               wacom->id[idx] = (data[2] << 4) | (data[3] >> 4) |
+                       ((data[7] & 0x0f) << 20) | ((data[8] & 0xf0) << 12);
+
+               switch (wacom->id[idx]) {
+               case 0x812: /* Inking pen */
+               case 0x801: /* Intuos3 Inking pen */
+               case 0x120802: /* Intuos4/5 Inking Pen */
+               case 0x012:
+                       wacom->tool[idx] = BTN_TOOL_PENCIL;
+                       break;
+
+               case 0x822: /* Pen */
+               case 0x842:
+               case 0x852:
+               case 0x823: /* Intuos3 Grip Pen */
+               case 0x813: /* Intuos3 Classic Pen */
+               case 0x885: /* Intuos3 Marker Pen */
+               case 0x802: /* Intuos4/5 13HD/24HD General Pen */
+               case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */
+               case 0x022:
+               case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */
+               case 0x140802: /* Intuos4/5 13HD/24HD Classic Pen */
+               case 0x160802: /* Cintiq 13HD Pro Pen */
+               case 0x180802: /* DTH2242 Pen */
+               case 0x100802: /* Intuos4/5 13HD/24HD General Pen */
+                       wacom->tool[idx] = BTN_TOOL_PEN;
+                       break;
+
+               case 0x832: /* Stroke pen */
+               case 0x032:
+                       wacom->tool[idx] = BTN_TOOL_BRUSH;
+                       break;
+
+               case 0x007: /* Mouse 4D and 2D */
+               case 0x09c:
+               case 0x094:
+               case 0x017: /* Intuos3 2D Mouse */
+               case 0x806: /* Intuos4 Mouse */
+                       wacom->tool[idx] = BTN_TOOL_MOUSE;
+                       break;
+
+               case 0x096: /* Lens cursor */
+               case 0x097: /* Intuos3 Lens cursor */
+               case 0x006: /* Intuos4 Lens cursor */
+                       wacom->tool[idx] = BTN_TOOL_LENS;
+                       break;
+
+               case 0x82a: /* Eraser */
+               case 0x85a:
+               case 0x91a:
+               case 0xd1a:
+               case 0x0fa:
+               case 0x82b: /* Intuos3 Grip Pen Eraser */
+               case 0x81b: /* Intuos3 Classic Pen Eraser */
+               case 0x91b: /* Intuos3 Airbrush Eraser */
+               case 0x80c: /* Intuos4/5 13HD/24HD Marker Pen Eraser */
+               case 0x80a: /* Intuos4/5 13HD/24HD General Pen Eraser */
+               case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
+               case 0x14080a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */
+               case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
+               case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */
+               case 0x16080a: /* Cintiq 13HD Pro Pen Eraser */
+               case 0x18080a: /* DTH2242 Eraser */
+               case 0x10080a: /* Intuos4/5 13HD/24HD General Pen Eraser */
+                       wacom->tool[idx] = BTN_TOOL_RUBBER;
+                       break;
+
+               case 0xd12:
+               case 0x912:
+               case 0x112:
+               case 0x913: /* Intuos3 Airbrush */
+               case 0x902: /* Intuos4/5 13HD/24HD Airbrush */
+               case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */
+                       wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
+                       break;
+
+               default: /* Unknown tool */
+                       wacom->tool[idx] = BTN_TOOL_PEN;
+                       break;
+               }
+               return 1;
+       }
+
+       /* older I4 styli don't work with new Cintiqs */
+       if (!((wacom->id[idx] >> 20) & 0x01) &&
+                       (features->type == WACOM_21UX2))
+               return 1;
+
+       /* Range Report */
+       if ((data[1] & 0xfe) == 0x20) {
+               input_report_key(input, BTN_TOUCH, 0);
+               input_report_abs(input, ABS_PRESSURE, 0);
+               input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max);
+               if (features->quirks & WACOM_QUIRK_MULTI_INPUT)
+                       wacom->shared->stylus_in_proximity = true;
+       }
+
+       /* Exit report */
+       if ((data[1] & 0xfe) == 0x80) {
+               if (features->quirks & WACOM_QUIRK_MULTI_INPUT)
+                       wacom->shared->stylus_in_proximity = false;
+
+               /*
+                * Reset all states otherwise we lose the initial states
+                * when in-prox next time
+                */
+               input_report_abs(input, ABS_X, 0);
+               input_report_abs(input, ABS_Y, 0);
+               input_report_abs(input, ABS_DISTANCE, 0);
+               input_report_abs(input, ABS_TILT_X, 0);
+               input_report_abs(input, ABS_TILT_Y, 0);
+               if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
+                       input_report_key(input, BTN_LEFT, 0);
+                       input_report_key(input, BTN_MIDDLE, 0);
+                       input_report_key(input, BTN_RIGHT, 0);
+                       input_report_key(input, BTN_SIDE, 0);
+                       input_report_key(input, BTN_EXTRA, 0);
+                       input_report_abs(input, ABS_THROTTLE, 0);
+                       input_report_abs(input, ABS_RZ, 0);
+               } else {
+                       input_report_abs(input, ABS_PRESSURE, 0);
+                       input_report_key(input, BTN_STYLUS, 0);
+                       input_report_key(input, BTN_STYLUS2, 0);
+                       input_report_key(input, BTN_TOUCH, 0);
+                       input_report_abs(input, ABS_WHEEL, 0);
+                       if (features->type >= INTUOS3S)
+                               input_report_abs(input, ABS_Z, 0);
+               }
+               input_report_key(input, wacom->tool[idx], 0);
+               input_report_abs(input, ABS_MISC, 0); /* reset tool id */
+               input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+               wacom->id[idx] = 0;
+               return 2;
+       }
+       return 0;
+}
+
+static void wacom_intuos_general(struct wacom_wac *wacom)
+{
+       struct wacom_features *features = &wacom->features;
+       unsigned char *data = wacom->data;
+       struct input_dev *input = wacom->input;
+       unsigned int t;
+
+       /* general pen packet */
+       if ((data[1] & 0xb8) == 0xa0) {
+               t = (data[6] << 2) | ((data[7] >> 6) & 3);
+               if (features->type >= INTUOS4S && features->type <= CINTIQ_HYBRID) {
+                       t = (t << 1) | (data[1] & 1);
+               }
+               input_report_abs(input, ABS_PRESSURE, t);
+               input_report_abs(input, ABS_TILT_X,
+                               ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+               input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f);
+               input_report_key(input, BTN_STYLUS, data[1] & 2);
+               input_report_key(input, BTN_STYLUS2, data[1] & 4);
+               input_report_key(input, BTN_TOUCH, t > 10);
+       }
+
+       /* airbrush second packet */
+       if ((data[1] & 0xbc) == 0xb4) {
+               input_report_abs(input, ABS_WHEEL,
+                               (data[6] << 2) | ((data[7] >> 6) & 3));
+               input_report_abs(input, ABS_TILT_X,
+                               ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+               input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f);
+       }
+}
+
+static int wacom_intuos_irq(struct wacom_wac *wacom)
+{
+       struct wacom_features *features = &wacom->features;
+       unsigned char *data = wacom->data;
+       struct input_dev *input = wacom->input;
+       unsigned int t;
+       int idx = 0, result;
+
+       if (data[0] != WACOM_REPORT_PENABLED &&
+           data[0] != WACOM_REPORT_INTUOSREAD &&
+           data[0] != WACOM_REPORT_INTUOSWRITE &&
+           data[0] != WACOM_REPORT_INTUOSPAD &&
+           data[0] != WACOM_REPORT_INTUOS5PAD) {
+               dev_dbg(input->dev.parent,
+                       "%s: received unknown report #%d\n", __func__, data[0]);
+                return 0;
+       }
+
+       /* tool number */
+       if (features->type == INTUOS)
+               idx = data[1] & 0x01;
+
+       /* pad packets. Works as a second tool and is always in prox */
+       if (data[0] == WACOM_REPORT_INTUOSPAD || data[0] == WACOM_REPORT_INTUOS5PAD) {
+               input = wacom->pad_input;
+               if (features->type >= INTUOS4S && features->type <= INTUOS4L) {
+                       input_report_key(input, BTN_0, (data[2] & 0x01));
+                       input_report_key(input, BTN_1, (data[3] & 0x01));
+                       input_report_key(input, BTN_2, (data[3] & 0x02));
+                       input_report_key(input, BTN_3, (data[3] & 0x04));
+                       input_report_key(input, BTN_4, (data[3] & 0x08));
+                       input_report_key(input, BTN_5, (data[3] & 0x10));
+                       input_report_key(input, BTN_6, (data[3] & 0x20));
+                       if (data[1] & 0x80) {
+                               input_report_abs(input, ABS_WHEEL, (data[1] & 0x7f));
+                       } else {
+                               /* Out of proximity, clear wheel value. */
+                               input_report_abs(input, ABS_WHEEL, 0);
+                       }
+                       if (features->type != INTUOS4S) {
+                               input_report_key(input, BTN_7, (data[3] & 0x40));
+                               input_report_key(input, BTN_8, (data[3] & 0x80));
+                       }
+                       if (data[1] | (data[2] & 0x01) | data[3]) {
+                               input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
+                       } else {
+                               input_report_abs(input, ABS_MISC, 0);
+                       }
+               } else if (features->type == DTK) {
+                       input_report_key(input, BTN_0, (data[6] & 0x01));
+                       input_report_key(input, BTN_1, (data[6] & 0x02));
+                       input_report_key(input, BTN_2, (data[6] & 0x04));
+                       input_report_key(input, BTN_3, (data[6] & 0x08));
+                       input_report_key(input, BTN_4, (data[6] & 0x10));
+                       input_report_key(input, BTN_5, (data[6] & 0x20));
+                       if (data[6] & 0x3f) {
+                               input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
+                       } else {
+                               input_report_abs(input, ABS_MISC, 0);
+                       }
+               } else if (features->type == WACOM_13HD) {
+                       input_report_key(input, BTN_0, (data[3] & 0x01));
+                       input_report_key(input, BTN_1, (data[4] & 0x01));
+                       input_report_key(input, BTN_2, (data[4] & 0x02));
+                       input_report_key(input, BTN_3, (data[4] & 0x04));
+                       input_report_key(input, BTN_4, (data[4] & 0x08));
+                       input_report_key(input, BTN_5, (data[4] & 0x10));
+                       input_report_key(input, BTN_6, (data[4] & 0x20));
+                       input_report_key(input, BTN_7, (data[4] & 0x40));
+                       input_report_key(input, BTN_8, (data[4] & 0x80));
+                       if ((data[3] & 0x01) | data[4]) {
+                               input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
+                       } else {
+                               input_report_abs(input, ABS_MISC, 0);
+                       }
+               } else if (features->type == WACOM_24HD) {
+                       input_report_key(input, BTN_0, (data[6] & 0x01));
+                       input_report_key(input, BTN_1, (data[6] & 0x02));
+                       input_report_key(input, BTN_2, (data[6] & 0x04));
+                       input_report_key(input, BTN_3, (data[6] & 0x08));
+                       input_report_key(input, BTN_4, (data[6] & 0x10));
+                       input_report_key(input, BTN_5, (data[6] & 0x20));
+                       input_report_key(input, BTN_6, (data[6] & 0x40));
+                       input_report_key(input, BTN_7, (data[6] & 0x80));
+                       input_report_key(input, BTN_8, (data[8] & 0x01));
+                       input_report_key(input, BTN_9, (data[8] & 0x02));
+                       input_report_key(input, BTN_A, (data[8] & 0x04));
+                       input_report_key(input, BTN_B, (data[8] & 0x08));
+                       input_report_key(input, BTN_C, (data[8] & 0x10));
+                       input_report_key(input, BTN_X, (data[8] & 0x20));
+                       input_report_key(input, BTN_Y, (data[8] & 0x40));
+                       input_report_key(input, BTN_Z, (data[8] & 0x80));
+
+                       /*
+                        * Three "buttons" are available on the 24HD which are
+                        * physically implemented as a touchstrip. Each button
+                        * is approximately 3 bits wide with a 2 bit spacing.
+                        * The raw touchstrip bits are stored at:
+                        *    ((data[3] & 0x1f) << 8) | data[4])
+                        */
+                       input_report_key(input, KEY_PROG1, data[4] & 0x07);
+                       input_report_key(input, KEY_PROG2, data[4] & 0xE0);
+                       input_report_key(input, KEY_PROG3, data[3] & 0x1C);
+
+                       if (data[1] & 0x80) {
+                               input_report_abs(input, ABS_WHEEL, (data[1] & 0x7f));
+                       } else {
+                               /* Out of proximity, clear wheel value. */
+                               input_report_abs(input, ABS_WHEEL, 0);
+                       }
+
+                       if (data[2] & 0x80) {
+                               input_report_abs(input, ABS_THROTTLE, (data[2] & 0x7f));
+                       } else {
+                               /* Out of proximity, clear second wheel value. */
+                               input_report_abs(input, ABS_THROTTLE, 0);
+                       }
+
+                       if (data[1] | data[2] | (data[3] & 0x1f) | data[4] | data[6] | data[8]) {
+                               input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
+                       } else {
+                               input_report_abs(input, ABS_MISC, 0);
+                       }
+               } else if (features->type == CINTIQ_HYBRID) {
+                       /*
+                        * Do not send hardware buttons under Android. They
+                        * are already sent to the system through GPIO (and
+                        * have different meaning).
+                        */
+                       input_report_key(input, BTN_1, (data[4] & 0x01));
+                       input_report_key(input, BTN_2, (data[4] & 0x02));
+                       input_report_key(input, BTN_3, (data[4] & 0x04));
+                       input_report_key(input, BTN_4, (data[4] & 0x08));
+
+                       input_report_key(input, BTN_5, (data[4] & 0x10));  /* Right  */
+                       input_report_key(input, BTN_6, (data[4] & 0x20));  /* Up     */
+                       input_report_key(input, BTN_7, (data[4] & 0x40));  /* Left   */
+                       input_report_key(input, BTN_8, (data[4] & 0x80));  /* Down   */
+                       input_report_key(input, BTN_0, (data[3] & 0x01));  /* Center */
+               } else if (features->type >= INTUOS5S && features->type <= INTUOSPL) {
+                       int i;
+
+                       /* Touch ring mode switch has no capacitive sensor */
+                       input_report_key(input, BTN_0, (data[3] & 0x01));
+
+                       /*
+                        * ExpressKeys on Intuos5/Intuos Pro have a capacitive sensor in
+                        * addition to the mechanical switch. Switch data is
+                        * stored in data[4], capacitive data in data[5].
+                        */
+                       for (i = 0; i < 8; i++)
+                               input_report_key(input, BTN_1 + i, data[4] & (1 << i));
+
+                       if (data[2] & 0x80) {
+                               input_report_abs(input, ABS_WHEEL, (data[2] & 0x7f));
+                       } else {
+                               /* Out of proximity, clear wheel value. */
+                               input_report_abs(input, ABS_WHEEL, 0);
+                       }
+
+                       if (data[2] | (data[3] & 0x01) | data[4] | data[5]) {
+                               input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
+                       } else {
+                               input_report_abs(input, ABS_MISC, 0);
+                       }
+               } else {
+                       if (features->type == WACOM_21UX2 || features->type == WACOM_22HD) {
+                               input_report_key(input, BTN_0, (data[5] & 0x01));
+                               input_report_key(input, BTN_1, (data[6] & 0x01));
+                               input_report_key(input, BTN_2, (data[6] & 0x02));
+                               input_report_key(input, BTN_3, (data[6] & 0x04));
+                               input_report_key(input, BTN_4, (data[6] & 0x08));
+                               input_report_key(input, BTN_5, (data[6] & 0x10));
+                               input_report_key(input, BTN_6, (data[6] & 0x20));
+                               input_report_key(input, BTN_7, (data[6] & 0x40));
+                               input_report_key(input, BTN_8, (data[6] & 0x80));
+                               input_report_key(input, BTN_9, (data[7] & 0x01));
+                               input_report_key(input, BTN_A, (data[8] & 0x01));
+                               input_report_key(input, BTN_B, (data[8] & 0x02));
+                               input_report_key(input, BTN_C, (data[8] & 0x04));
+                               input_report_key(input, BTN_X, (data[8] & 0x08));
+                               input_report_key(input, BTN_Y, (data[8] & 0x10));
+                               input_report_key(input, BTN_Z, (data[8] & 0x20));
+                               input_report_key(input, BTN_BASE, (data[8] & 0x40));
+                               input_report_key(input, BTN_BASE2, (data[8] & 0x80));
+
+                               if (features->type == WACOM_22HD) {
+                                       input_report_key(input, KEY_PROG1, data[9] & 0x01);
+                                       input_report_key(input, KEY_PROG2, data[9] & 0x02);
+                                       input_report_key(input, KEY_PROG3, data[9] & 0x04);
+                               }
+                       } else {
+                               input_report_key(input, BTN_0, (data[5] & 0x01));
+                               input_report_key(input, BTN_1, (data[5] & 0x02));
+                               input_report_key(input, BTN_2, (data[5] & 0x04));
+                               input_report_key(input, BTN_3, (data[5] & 0x08));
+                               input_report_key(input, BTN_4, (data[6] & 0x01));
+                               input_report_key(input, BTN_5, (data[6] & 0x02));
+                               input_report_key(input, BTN_6, (data[6] & 0x04));
+                               input_report_key(input, BTN_7, (data[6] & 0x08));
+                               input_report_key(input, BTN_8, (data[5] & 0x10));
+                               input_report_key(input, BTN_9, (data[6] & 0x10));
+                       }
+                       input_report_abs(input, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
+                       input_report_abs(input, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
+
+                       if ((data[5] & 0x1f) | data[6] | (data[1] & 0x1f) |
+                               data[2] | (data[3] & 0x1f) | data[4] | data[8] |
+                               (data[7] & 0x01)) {
+                               input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
+                       } else {
+                               input_report_abs(input, ABS_MISC, 0);
+                       }
+               }
+                return 1;
+       }
+
+       /* process in/out prox events */
+       result = wacom_intuos_inout(wacom);
+       if (result)
+                return result - 1;
+
+       /* don't proceed if we don't know the ID */
+       if (!wacom->id[idx])
+               return 0;
+
+       /* Only large Intuos support Lense Cursor */
+       if (wacom->tool[idx] == BTN_TOOL_LENS &&
+           (features->type == INTUOS3 ||
+            features->type == INTUOS3S ||
+            features->type == INTUOS4 ||
+            features->type == INTUOS4S ||
+            features->type == INTUOS5 ||
+            features->type == INTUOS5S ||
+            features->type == INTUOSPM ||
+            features->type == INTUOSPS)) {
+
+               return 0;
+       }
+
+       /* Cintiq doesn't send data when RDY bit isn't set */
+       if (features->type == CINTIQ && !(data[1] & 0x40))
+                 return 0;
+
+       if (features->type >= INTUOS3S) {
+               input_report_abs(input, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1));
+               input_report_abs(input, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1));
+               input_report_abs(input, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
+       } else {
+               input_report_abs(input, ABS_X, be16_to_cpup((__be16 *)&data[2]));
+               input_report_abs(input, ABS_Y, be16_to_cpup((__be16 *)&data[4]));
+               input_report_abs(input, ABS_DISTANCE, ((data[9] >> 3) & 0x1f));
+       }
+
+       /* process general packets */
+       wacom_intuos_general(wacom);
+
+       /* 4D mouse, 2D mouse, marker pen rotation, tilt mouse, or Lens cursor packets */
+       if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0 || (data[1] & 0xbc) == 0xac) {
+
+               if (data[1] & 0x02) {
+                       /* Rotation packet */
+                       if (features->type >= INTUOS3S) {
+                               /* I3 marker pen rotation */
+                               t = (data[6] << 3) | ((data[7] >> 5) & 7);
+                               t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
+                                       ((t-1) / 2 + 450)) : (450 - t / 2) ;
+                               input_report_abs(input, ABS_Z, t);
+                       } else {
+                               /* 4D mouse rotation packet */
+                               t = (data[6] << 3) | ((data[7] >> 5) & 7);
+                               input_report_abs(input, ABS_RZ, (data[7] & 0x20) ?
+                                       ((t - 1) / 2) : -t / 2);
+                       }
+
+               } else if (!(data[1] & 0x10) && features->type < INTUOS3S) {
+                       /* 4D mouse packet */
+                       input_report_key(input, BTN_LEFT,   data[8] & 0x01);
+                       input_report_key(input, BTN_MIDDLE, data[8] & 0x02);
+                       input_report_key(input, BTN_RIGHT,  data[8] & 0x04);
+
+                       input_report_key(input, BTN_SIDE,   data[8] & 0x20);
+                       input_report_key(input, BTN_EXTRA,  data[8] & 0x10);
+                       t = (data[6] << 2) | ((data[7] >> 6) & 3);
+                       input_report_abs(input, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
+
+               } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
+                       /* I4 mouse */
+                       if (features->type >= INTUOS4S && features->type <= INTUOSPL) {
+                               input_report_key(input, BTN_LEFT,   data[6] & 0x01);
+                               input_report_key(input, BTN_MIDDLE, data[6] & 0x02);
+                               input_report_key(input, BTN_RIGHT,  data[6] & 0x04);
+                               input_report_rel(input, REL_WHEEL, ((data[7] & 0x80) >> 7)
+                                                - ((data[7] & 0x40) >> 6));
+                               input_report_key(input, BTN_SIDE,   data[6] & 0x08);
+                               input_report_key(input, BTN_EXTRA,  data[6] & 0x10);
+
+                               input_report_abs(input, ABS_TILT_X,
+                                       ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+                               input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f);
+                       } else {
+                               /* 2D mouse packet */
+                               input_report_key(input, BTN_LEFT,   data[8] & 0x04);
+                               input_report_key(input, BTN_MIDDLE, data[8] & 0x08);
+                               input_report_key(input, BTN_RIGHT,  data[8] & 0x10);
+                               input_report_rel(input, REL_WHEEL, (data[8] & 0x01)
+                                                - ((data[8] & 0x02) >> 1));
+
+                               /* I3 2D mouse side buttons */
+                               if (features->type >= INTUOS3S && features->type <= INTUOS3L) {
+                                       input_report_key(input, BTN_SIDE,   data[8] & 0x40);
+                                       input_report_key(input, BTN_EXTRA,  data[8] & 0x20);
+                               }
+                       }
+               } else if ((features->type < INTUOS3S || features->type == INTUOS3L ||
+                               features->type == INTUOS4L || features->type == INTUOS5L ||
+                               features->type == INTUOSPL) &&
+                          wacom->tool[idx] == BTN_TOOL_LENS) {
+                       /* Lens cursor packets */
+                       input_report_key(input, BTN_LEFT,   data[8] & 0x01);
+                       input_report_key(input, BTN_MIDDLE, data[8] & 0x02);
+                       input_report_key(input, BTN_RIGHT,  data[8] & 0x04);
+                       input_report_key(input, BTN_SIDE,   data[8] & 0x10);
+                       input_report_key(input, BTN_EXTRA,  data[8] & 0x08);
+               }
+       }
+
+       input_report_abs(input, ABS_MISC, wacom->id[idx]); /* report tool id */
+       input_report_key(input, wacom->tool[idx], 1);
+       input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+       return 1;
+}
+
+static int int_dist(int x1, int y1, int x2, int y2)
+{
+       int x = x2 - x1;
+       int y = y2 - y1;
+
+       return int_sqrt(x*x + y*y);
+}
+
+static int wacom_24hdt_irq(struct wacom_wac *wacom)
+{
+       struct input_dev *input = wacom->input;
+       unsigned char *data = wacom->data;
+       int i;
+       int current_num_contacts = data[61];
+       int contacts_to_send = 0;
+
+       /*
+        * First packet resets the counter since only the first
+        * packet in series will have non-zero current_num_contacts.
+        */
+       if (current_num_contacts)
+               wacom->num_contacts_left = current_num_contacts;
+
+       /* There are at most 4 contacts per packet */
+       contacts_to_send = min(4, wacom->num_contacts_left);
+
+       for (i = 0; i < contacts_to_send; i++) {
+               int offset = (WACOM_BYTES_PER_24HDT_PACKET * i) + 1;
+               bool touch = data[offset] & 0x1 && !wacom->shared->stylus_in_proximity;
+               int slot = input_mt_get_slot_by_key(input, data[offset + 1]);
+
+               if (slot < 0)
+                       continue;
+               input_mt_slot(input, slot);
+               input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+
+               if (touch) {
+                       int t_x = get_unaligned_le16(&data[offset + 2]);
+                       int c_x = get_unaligned_le16(&data[offset + 4]);
+                       int t_y = get_unaligned_le16(&data[offset + 6]);
+                       int c_y = get_unaligned_le16(&data[offset + 8]);
+                       int w = get_unaligned_le16(&data[offset + 10]);
+                       int h = get_unaligned_le16(&data[offset + 12]);
+
+                       input_report_abs(input, ABS_MT_POSITION_X, t_x);
+                       input_report_abs(input, ABS_MT_POSITION_Y, t_y);
+                       input_report_abs(input, ABS_MT_TOUCH_MAJOR, min(w,h));
+                       input_report_abs(input, ABS_MT_WIDTH_MAJOR, min(w, h) + int_dist(t_x, t_y, c_x, c_y));
+                       input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h));
+                       input_report_abs(input, ABS_MT_ORIENTATION, w > h);
+               }
+       }
+       input_mt_report_pointer_emulation(input, true);
+
+       wacom->num_contacts_left -= contacts_to_send;
+       if (wacom->num_contacts_left <= 0)
+               wacom->num_contacts_left = 0;
+
+       return 1;
+}
+
+static int wacom_mt_touch(struct wacom_wac *wacom)
+{
+       struct input_dev *input = wacom->input;
+       unsigned char *data = wacom->data;
+       int i;
+       int current_num_contacts = data[2];
+       int contacts_to_send = 0;
+       int x_offset = 0;
+
+       /* MTTPC does not support Height and Width */
+       if (wacom->features.type == MTTPC || wacom->features.type == MTTPC_B)
+               x_offset = -4;
+
+       /*
+        * First packet resets the counter since only the first
+        * packet in series will have non-zero current_num_contacts.
+        */
+       if (current_num_contacts)
+               wacom->num_contacts_left = current_num_contacts;
+
+       /* There are at most 5 contacts per packet */
+       contacts_to_send = min(5, wacom->num_contacts_left);
+
+       for (i = 0; i < contacts_to_send; i++) {
+               int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3;
+               bool touch = data[offset] & 0x1;
+               int id = get_unaligned_le16(&data[offset + 1]);
+               int slot = input_mt_get_slot_by_key(input, id);
+
+               if (slot < 0)
+                       continue;
+
+               input_mt_slot(input, slot);
+               input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+               if (touch) {
+                       int x = get_unaligned_le16(&data[offset + x_offset + 7]);
+                       int y = get_unaligned_le16(&data[offset + x_offset + 9]);
+                       input_report_abs(input, ABS_MT_POSITION_X, x);
+                       input_report_abs(input, ABS_MT_POSITION_Y, y);
+               }
+       }
+       input_mt_report_pointer_emulation(input, true);
+
+       wacom->num_contacts_left -= contacts_to_send;
+       if (wacom->num_contacts_left < 0)
+               wacom->num_contacts_left = 0;
+
+       return 1;
+}
+
+static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
+{
+       struct input_dev *input = wacom->input;
+       unsigned char *data = wacom->data;
+       int contact_with_no_pen_down_count = 0;
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               int p = data[1] & (1 << i);
+               bool touch = p && !wacom->shared->stylus_in_proximity;
+
+               input_mt_slot(input, i);
+               input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+               if (touch) {
+                       int x = le16_to_cpup((__le16 *)&data[i * 2 + 2]) & 0x7fff;
+                       int y = le16_to_cpup((__le16 *)&data[i * 2 + 6]) & 0x7fff;
+
+                       input_report_abs(input, ABS_MT_POSITION_X, x);
+                       input_report_abs(input, ABS_MT_POSITION_Y, y);
+                       contact_with_no_pen_down_count++;
+               }
+       }
+       input_mt_report_pointer_emulation(input, true);
+
+       /* keep touch state for pen event */
+       wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
+
+       return 1;
+}
+
+static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
+{
+       unsigned char *data = wacom->data;
+       struct input_dev *input = wacom->input;
+       bool prox;
+       int x = 0, y = 0;
+
+       if (wacom->features.touch_max > 1 || len > WACOM_PKGLEN_TPC2FG)
+               return 0;
+
+       if (!wacom->shared->stylus_in_proximity) {
+               if (len == WACOM_PKGLEN_TPC1FG) {
+                       prox = data[0] & 0x01;
+                       x = get_unaligned_le16(&data[1]);
+                       y = get_unaligned_le16(&data[3]);
+               } else if (len == WACOM_PKGLEN_TPC1FG_B) {
+                       prox = data[2] & 0x01;
+                       x = get_unaligned_le16(&data[3]);
+                       y = get_unaligned_le16(&data[5]);
+               } else {
+                       prox = data[1] & 0x01;
+                       x = le16_to_cpup((__le16 *)&data[2]);
+                       y = le16_to_cpup((__le16 *)&data[4]);
+               }
+       } else
+               /* force touch out when pen is in prox */
+               prox = 0;
+
+       if (prox) {
+               input_report_abs(input, ABS_X, x);
+               input_report_abs(input, ABS_Y, y);
+       }
+       input_report_key(input, BTN_TOUCH, prox);
+
+       /* keep touch state for pen events */
+       wacom->shared->touch_down = prox;
+
+       return 1;
+}
+
+static int wacom_tpc_pen(struct wacom_wac *wacom)
+{
+       unsigned char *data = wacom->data;
+       struct input_dev *input = wacom->input;
+       bool prox = data[1] & 0x20;
+
+       if (!wacom->shared->stylus_in_proximity) /* first in prox */
+               /* Going into proximity select tool */
+               wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+
+       /* keep pen state for touch events */
+       wacom->shared->stylus_in_proximity = prox;
+
+       /* send pen events only when touch is up or forced out */
+       if (!wacom->shared->touch_down) {
+               input_report_key(input, BTN_STYLUS, data[1] & 0x02);
+               input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
+               input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
+               input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
+               input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x03) << 8) | data[6]);
+               input_report_key(input, BTN_TOUCH, data[1] & 0x05);
+               input_report_key(input, wacom->tool[0], prox);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
+{
+       unsigned char *data = wacom->data;
+
+       dev_dbg(wacom->input->dev.parent,
+               "%s: received report #%d\n", __func__, data[0]);
+
+       switch (len) {
+       case WACOM_PKGLEN_TPC1FG:
+               return wacom_tpc_single_touch(wacom, len);
+
+       case WACOM_PKGLEN_TPC2FG:
+               return wacom_tpc_mt_touch(wacom);
+
+       case WACOM_PKGLEN_PENABLED:
+               return wacom_tpc_pen(wacom);
+
+       default:
+               switch (data[0]) {
+               case WACOM_REPORT_TPC1FG:
+               case WACOM_REPORT_TPCHID:
+               case WACOM_REPORT_TPCST:
+               case WACOM_REPORT_TPC1FGE:
+                       return wacom_tpc_single_touch(wacom, len);
+
+               case WACOM_REPORT_TPCMT:
+               case WACOM_REPORT_TPCMT2:
+                       return wacom_mt_touch(wacom);
+
+               case WACOM_REPORT_PENABLED:
+                       return wacom_tpc_pen(wacom);
+               }
+       }
+
+       return 0;
+}
+
+static int wacom_bpt_touch(struct wacom_wac *wacom)
+{
+       struct wacom_features *features = &wacom->features;
+       struct input_dev *input = wacom->input;
+       struct input_dev *pad_input = wacom->pad_input;
+       unsigned char *data = wacom->data;
+       int i;
+
+       if (data[0] != 0x02)
+           return 0;
+
+       for (i = 0; i < 2; i++) {
+               int offset = (data[1] & 0x80) ? (8 * i) : (9 * i);
+               bool touch = data[offset + 3] & 0x80;
+
+               /*
+                * Touch events need to be disabled while stylus is
+                * in proximity because user's hand is resting on touchpad
+                * and sending unwanted events.  User expects tablet buttons
+                * to continue working though.
+                */
+               touch = touch && !wacom->shared->stylus_in_proximity;
+
+               input_mt_slot(input, i);
+               input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+               if (touch) {
+                       int x = get_unaligned_be16(&data[offset + 3]) & 0x7ff;
+                       int y = get_unaligned_be16(&data[offset + 5]) & 0x7ff;
+                       if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) {
+                               x <<= 5;
+                               y <<= 5;
+                       }
+                       input_report_abs(input, ABS_MT_POSITION_X, x);
+                       input_report_abs(input, ABS_MT_POSITION_Y, y);
+               }
+       }
+
+       input_mt_report_pointer_emulation(input, true);
+
+       input_report_key(pad_input, BTN_LEFT, (data[1] & 0x08) != 0);
+       input_report_key(pad_input, BTN_FORWARD, (data[1] & 0x04) != 0);
+       input_report_key(pad_input, BTN_BACK, (data[1] & 0x02) != 0);
+       input_report_key(pad_input, BTN_RIGHT, (data[1] & 0x01) != 0);
+
+       return 1;
+}
+
+static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
+{
+       struct wacom_features *features = &wacom->features;
+       struct input_dev *input = wacom->input;
+       bool touch = data[1] & 0x80;
+       int slot = input_mt_get_slot_by_key(input, data[0]);
+
+       if (slot < 0)
+               return;
+
+       touch = touch && !wacom->shared->stylus_in_proximity;
+
+       input_mt_slot(input, slot);
+       input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+
+       if (touch) {
+               int x = (data[2] << 4) | (data[4] >> 4);
+               int y = (data[3] << 4) | (data[4] & 0x0f);
+               int width, height;
+
+               if (features->type >= INTUOSPS && features->type <= INTUOSPL) {
+                       width  = data[5] * 100;
+                       height = data[6] * 100;
+               } else {
+                       /*
+                        * "a" is a scaled-down area which we assume is
+                        * roughly circular and which can be described as:
+                        * a=(pi*r^2)/C.
+                        */
+                       int a = data[5];
+                       int x_res  = input_abs_get_res(input, ABS_X);
+                       int y_res  = input_abs_get_res(input, ABS_Y);
+                       width  = 2 * int_sqrt(a * WACOM_CONTACT_AREA_SCALE);
+                       height = width * y_res / x_res;
+               }
+
+               input_report_abs(input, ABS_MT_POSITION_X, x);
+               input_report_abs(input, ABS_MT_POSITION_Y, y);
+               input_report_abs(input, ABS_MT_TOUCH_MAJOR, width);
+               input_report_abs(input, ABS_MT_TOUCH_MINOR, height);
+       }
+}
+
+static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data)
+{
+       struct input_dev *input = wacom->pad_input;
+       struct wacom_features *features = &wacom->features;
+
+       if (features->type == INTUOSHT) {
+               input_report_key(input, BTN_LEFT, (data[1] & 0x02) != 0);
+               input_report_key(input, BTN_BACK, (data[1] & 0x08) != 0);
+       } else {
+               input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0);
+               input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0);
+       }
+       input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0);
+       input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0);
+}
+
+static int wacom_bpt3_touch(struct wacom_wac *wacom)
+{
+       struct input_dev *input = wacom->input;
+       unsigned char *data = wacom->data;
+       int count = data[1] & 0x07;
+       int i;
+
+       if (data[0] != 0x02)
+           return 0;
+
+       /* data has up to 7 fixed sized 8-byte messages starting at data[2] */
+       for (i = 0; i < count; i++) {
+               int offset = (8 * i) + 2;
+               int msg_id = data[offset];
+
+               if (msg_id >= 2 && msg_id <= 17)
+                       wacom_bpt3_touch_msg(wacom, data + offset);
+               else if (msg_id == 128)
+                       wacom_bpt3_button_msg(wacom, data + offset);
+
+       }
+       input_mt_report_pointer_emulation(input, true);
+
+       return 1;
+}
+
+static int wacom_bpt_pen(struct wacom_wac *wacom)
+{
+       struct wacom_features *features = &wacom->features;
+       struct input_dev *input = wacom->input;
+       unsigned char *data = wacom->data;
+       int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0;
+
+       if (data[0] != WACOM_REPORT_PENABLED && data[0] != WACOM_REPORT_USB)
+           return 0;
+
+       if (data[0] == WACOM_REPORT_USB) {
+               if (features->type == INTUOSHT && features->touch_max) {
+                       input_report_switch(wacom->shared->touch_input,
+                                           SW_MUTE_DEVICE, data[8] & 0x40);
+                       input_sync(wacom->shared->touch_input);
+               }
+               return 0;
+       }
+
+       prox = (data[1] & 0x20) == 0x20;
+
+       /*
+        * All reports shared between PEN and RUBBER tool must be
+        * forced to a known starting value (zero) when transitioning to
+        * out-of-prox.
+        *
+        * If not reset then, to userspace, it will look like lost events
+        * if new tool comes in-prox with same values as previous tool sent.
+        *
+        * Hardware does report zero in most out-of-prox cases but not all.
+        */
+       if (prox) {
+               if (!wacom->shared->stylus_in_proximity) {
+                       if (data[1] & 0x08) {
+                               wacom->tool[0] = BTN_TOOL_RUBBER;
+                               wacom->id[0] = ERASER_DEVICE_ID;
+                       } else {
+                               wacom->tool[0] = BTN_TOOL_PEN;
+                               wacom->id[0] = STYLUS_DEVICE_ID;
+                       }
+                       wacom->shared->stylus_in_proximity = true;
+               }
+               x = le16_to_cpup((__le16 *)&data[2]);
+               y = le16_to_cpup((__le16 *)&data[4]);
+               p = le16_to_cpup((__le16 *)&data[6]);
+               /*
+                * Convert distance from out prox to distance from tablet.
+                * distance will be greater than distance_max once
+                * touching and applying pressure; do not report negative
+                * distance.
+                */
+               if (data[8] <= features->distance_max)
+                       d = features->distance_max - data[8];
+
+               pen = data[1] & 0x01;
+               btn1 = data[1] & 0x02;
+               btn2 = data[1] & 0x04;
+       }
+
+       input_report_key(input, BTN_TOUCH, pen);
+       input_report_key(input, BTN_STYLUS, btn1);
+       input_report_key(input, BTN_STYLUS2, btn2);
+
+       input_report_abs(input, ABS_X, x);
+       input_report_abs(input, ABS_Y, y);
+       input_report_abs(input, ABS_PRESSURE, p);
+       input_report_abs(input, ABS_DISTANCE, d);
+
+       if (!prox) {
+               wacom->id[0] = 0;
+               wacom->shared->stylus_in_proximity = false;
+       }
+
+       input_report_key(input, wacom->tool[0], prox); /* PEN or RUBBER */
+       input_report_abs(input, ABS_MISC, wacom->id[0]); /* TOOL ID */
+
+       return 1;
+}
+
+static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len)
+{
+       if (len == WACOM_PKGLEN_BBTOUCH)
+               return wacom_bpt_touch(wacom);
+       else if (len == WACOM_PKGLEN_BBTOUCH3)
+               return wacom_bpt3_touch(wacom);
+       else if (len == WACOM_PKGLEN_BBFUN || len == WACOM_PKGLEN_BBPEN)
+               return wacom_bpt_pen(wacom);
+
+       return 0;
+}
+
+static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len)
+{
+       unsigned char *data = wacom->data;
+       int connected;
+
+       if (len != WACOM_PKGLEN_WIRELESS || data[0] != WACOM_REPORT_WL)
+               return 0;
+
+       connected = data[1] & 0x01;
+       if (connected) {
+               int pid, battery;
+
+               if ((wacom->shared->type == INTUOSHT) &&
+                               wacom->shared->touch_max) {
+                       input_report_switch(wacom->shared->touch_input,
+                                       SW_MUTE_DEVICE, data[5] & 0x40);
+                       input_sync(wacom->shared->touch_input);
+               }
+
+               pid = get_unaligned_be16(&data[6]);
+               battery = data[5] & 0x3f;
+               if (wacom->pid != pid) {
+                       wacom->pid = pid;
+                       wacom_schedule_work(wacom);
+               }
+               wacom->battery_capacity = battery;
+       } else if (wacom->pid != 0) {
+               /* disconnected while previously connected */
+               wacom->pid = 0;
+               wacom_schedule_work(wacom);
+               wacom->battery_capacity = 0;
+       }
+
+       return 0;
+}
+
+void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
+{
+       bool sync;
+
+       switch (wacom_wac->features.type) {
+       case PENPARTNER:
+               sync = wacom_penpartner_irq(wacom_wac);
+               break;
+
+       case PL:
+               sync = wacom_pl_irq(wacom_wac);
+               break;
+
+       case WACOM_G4:
+       case GRAPHIRE:
+       case WACOM_MO:
+               sync = wacom_graphire_irq(wacom_wac);
+               break;
+
+       case PTU:
+               sync = wacom_ptu_irq(wacom_wac);
+               break;
+
+       case DTU:
+               sync = wacom_dtu_irq(wacom_wac);
+               break;
+
+       case DTUS:
+               sync = wacom_dtus_irq(wacom_wac);
+               break;
+
+       case INTUOS:
+       case INTUOS3S:
+       case INTUOS3:
+       case INTUOS3L:
+       case INTUOS4S:
+       case INTUOS4:
+       case INTUOS4L:
+       case CINTIQ:
+       case WACOM_BEE:
+       case WACOM_13HD:
+       case WACOM_21UX2:
+       case WACOM_22HD:
+       case WACOM_24HD:
+       case DTK:
+       case CINTIQ_HYBRID:
+               sync = wacom_intuos_irq(wacom_wac);
+               break;
+
+       case WACOM_24HDT:
+               sync = wacom_24hdt_irq(wacom_wac);
+               break;
+
+       case INTUOS5S:
+       case INTUOS5:
+       case INTUOS5L:
+       case INTUOSPS:
+       case INTUOSPM:
+       case INTUOSPL:
+               if (len == WACOM_PKGLEN_BBTOUCH3)
+                       sync = wacom_bpt3_touch(wacom_wac);
+               else
+                       sync = wacom_intuos_irq(wacom_wac);
+               break;
+
+       case TABLETPC:
+       case TABLETPCE:
+       case TABLETPC2FG:
+       case MTSCREEN:
+       case MTTPC:
+       case MTTPC_B:
+               sync = wacom_tpc_irq(wacom_wac, len);
+               break;
+
+       case BAMBOO_PT:
+       case INTUOSHT:
+               sync = wacom_bpt_irq(wacom_wac, len);
+               break;
+
+       case WIRELESS:
+               sync = wacom_wireless_irq(wacom_wac, len);
+               break;
+
+       default:
+               sync = false;
+               break;
+       }
+
+       if (sync) {
+               input_sync(wacom_wac->input);
+               if (wacom_wac->pad_input)
+                       input_sync(wacom_wac->pad_input);
+       }
+}
+
+static void wacom_setup_cintiq(struct wacom_wac *wacom_wac)
+{
+       struct input_dev *input_dev = wacom_wac->input;
+
+       input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
+
+       __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
+       __set_bit(BTN_TOOL_PEN, input_dev->keybit);
+       __set_bit(BTN_TOOL_BRUSH, input_dev->keybit);
+       __set_bit(BTN_TOOL_PENCIL, input_dev->keybit);
+       __set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit);
+       __set_bit(BTN_STYLUS, input_dev->keybit);
+       __set_bit(BTN_STYLUS2, input_dev->keybit);
+
+       input_set_abs_params(input_dev, ABS_DISTANCE,
+                            0, wacom_wac->features.distance_max, 0, 0);
+       input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
+       input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
+       input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
+}
+
+static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
+{
+       struct input_dev *input_dev = wacom_wac->input;
+
+       input_set_capability(input_dev, EV_REL, REL_WHEEL);
+
+       wacom_setup_cintiq(wacom_wac);
+
+       __set_bit(BTN_LEFT, input_dev->keybit);
+       __set_bit(BTN_RIGHT, input_dev->keybit);
+       __set_bit(BTN_MIDDLE, input_dev->keybit);
+       __set_bit(BTN_SIDE, input_dev->keybit);
+       __set_bit(BTN_EXTRA, input_dev->keybit);
+       __set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
+       __set_bit(BTN_TOOL_LENS, input_dev->keybit);
+
+       input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
+       input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
+}
+
+void wacom_setup_device_quirks(struct wacom_features *features)
+{
+
+       /* touch device found but size is not defined. use default */
+       if (features->device_type == BTN_TOOL_FINGER && !features->x_max) {
+               features->x_max = 1023;
+               features->y_max = 1023;
+       }
+
+       /* these device have multiple inputs */
+       if (features->type >= WIRELESS ||
+           (features->type >= INTUOS5S && features->type <= INTUOSHT) ||
+           (features->oVid && features->oPid))
+               features->quirks |= WACOM_QUIRK_MULTI_INPUT;
+
+       /* quirk for bamboo touch with 2 low res touches */
+       if (features->type == BAMBOO_PT &&
+           features->pktlen == WACOM_PKGLEN_BBTOUCH) {
+               features->x_max <<= 5;
+               features->y_max <<= 5;
+               features->x_fuzz <<= 5;
+               features->y_fuzz <<= 5;
+               features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES;
+       }
+
+       if (features->type == WIRELESS) {
+
+               /* monitor never has input and pen/touch have delayed create */
+               features->quirks |= WACOM_QUIRK_NO_INPUT;
+
+               /* must be monitor interface if no device_type set */
+               if (!features->device_type)
+                       features->quirks |= WACOM_QUIRK_MONITOR;
+       }
+}
+
+static void wacom_abs_set_axis(struct input_dev *input_dev,
+                              struct wacom_wac *wacom_wac)
+{
+       struct wacom_features *features = &wacom_wac->features;
+
+       if (features->device_type == BTN_TOOL_PEN) {
+               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_PRESSURE, 0,
+                       features->pressure_max, features->pressure_fuzz, 0);
+
+               /* penabled devices have fixed resolution for each model */
+               input_abs_set_res(input_dev, ABS_X, features->x_resolution);
+               input_abs_set_res(input_dev, ABS_Y, features->y_resolution);
+       } else {
+               if (features->touch_max <= 2) {
+                       input_set_abs_params(input_dev, ABS_X, 0,
+                               features->x_max, features->x_fuzz, 0);
+                       input_set_abs_params(input_dev, ABS_Y, 0,
+                               features->y_max, features->y_fuzz, 0);
+                       input_abs_set_res(input_dev, ABS_X,
+                                         features->x_resolution);
+                       input_abs_set_res(input_dev, ABS_Y,
+                                         features->y_resolution);
+               }
+
+               if (features->touch_max > 1) {
+                       input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+                               features->x_max, features->x_fuzz, 0);
+                       input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+                               features->y_max, features->y_fuzz, 0);
+                       input_abs_set_res(input_dev, ABS_MT_POSITION_X,
+                                         features->x_resolution);
+                       input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
+                                         features->y_resolution);
+               }
+       }
+}
+
+int wacom_setup_input_capabilities(struct input_dev *input_dev,
+                                  struct wacom_wac *wacom_wac)
+{
+       struct wacom_features *features = &wacom_wac->features;
+
+       input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+       __set_bit(BTN_TOUCH, input_dev->keybit);
+       __set_bit(ABS_MISC, input_dev->absbit);
+
+       wacom_abs_set_axis(input_dev, wacom_wac);
+
+       switch (features->type) {
+       case WACOM_MO:
+               input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
+               /* fall through */
+
+       case WACOM_G4:
+               /* fall through */
+
+       case GRAPHIRE:
+               input_set_capability(input_dev, EV_REL, REL_WHEEL);
+
+               __set_bit(BTN_LEFT, input_dev->keybit);
+               __set_bit(BTN_RIGHT, input_dev->keybit);
+               __set_bit(BTN_MIDDLE, input_dev->keybit);
+
+               __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
+               __set_bit(BTN_TOOL_PEN, input_dev->keybit);
+               __set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
+               __set_bit(BTN_STYLUS, input_dev->keybit);
+               __set_bit(BTN_STYLUS2, input_dev->keybit);
+
+               __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+               break;
+
+       case WACOM_24HD:
+               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+               input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0);
+               /* fall through */
+
+       case DTK:
+               __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+               wacom_setup_cintiq(wacom_wac);
+               break;
+
+       case WACOM_22HD:
+       case WACOM_21UX2:
+       case WACOM_BEE:
+       case CINTIQ:
+               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+
+               __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+               wacom_setup_cintiq(wacom_wac);
+               break;
+
+       case WACOM_13HD:
+               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+               __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+               wacom_setup_cintiq(wacom_wac);
+               break;
+
+       case INTUOS3:
+       case INTUOS3L:
+       case INTUOS3S:
+               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+               /* fall through */
+
+       case INTUOS:
+               __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
+               wacom_setup_intuos(wacom_wac);
+               break;
+
+       case INTUOS5:
+       case INTUOS5L:
+       case INTUOSPM:
+       case INTUOSPL:
+       case INTUOS5S:
+       case INTUOSPS:
+               __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
+               if (features->device_type == BTN_TOOL_PEN) {
+                       input_set_abs_params(input_dev, ABS_DISTANCE, 0,
+                                             features->distance_max,
+                                             0, 0);
+
+                       input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+
+                       wacom_setup_intuos(wacom_wac);
+               } else if (features->device_type == BTN_TOOL_FINGER) {
+                       __clear_bit(ABS_MISC, input_dev->absbit);
+
+                       input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+                                            0, features->x_max, 0, 0);
+                       input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
+                                            0, features->y_max, 0, 0);
+                       input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
+               }
+               break;
+
+       case INTUOS4:
+       case INTUOS4L:
+       case INTUOS4S:
+               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+               wacom_setup_intuos(wacom_wac);
+
+               __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+               break;
+
+       case WACOM_24HDT:
+               if (features->device_type == BTN_TOOL_FINGER) {
+                       input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0);
+                       input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, features->x_max, 0, 0);
+                       input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR, 0, features->y_max, 0, 0);
+                       input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+               }
+               /* fall through */
+
+       case MTSCREEN:
+       case MTTPC:
+       case MTTPC_B:
+       case TABLETPC2FG:
+               if (features->device_type == BTN_TOOL_FINGER) {
+                       unsigned int flags = INPUT_MT_DIRECT;
+
+                       if (wacom_wac->features.type == TABLETPC2FG)
+                               flags = 0;
+
+                       input_mt_init_slots(input_dev, features->touch_max, flags);
+               }
+               /* fall through */
+
+       case TABLETPC:
+       case TABLETPCE:
+               __clear_bit(ABS_MISC, input_dev->absbit);
+
+               __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+               if (features->device_type != BTN_TOOL_PEN)
+                       break;  /* no need to process stylus stuff */
+
+               /* fall through */
+
+       case DTUS:
+       case PL:
+       case DTU:
+               __set_bit(BTN_TOOL_PEN, input_dev->keybit);
+               __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
+               __set_bit(BTN_STYLUS, input_dev->keybit);
+               __set_bit(BTN_STYLUS2, input_dev->keybit);
+
+               __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+               break;
+
+       case PTU:
+               __set_bit(BTN_STYLUS2, input_dev->keybit);
+               /* fall through */
+
+       case PENPARTNER:
+               __set_bit(BTN_TOOL_PEN, input_dev->keybit);
+               __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
+               __set_bit(BTN_STYLUS, input_dev->keybit);
+
+               __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+               break;
+
+       case INTUOSHT:
+               if (features->touch_max &&
+                   features->device_type == BTN_TOOL_FINGER) {
+                       input_dev->evbit[0] |= BIT_MASK(EV_SW);
+                       __set_bit(SW_MUTE_DEVICE, input_dev->swbit);
+               }
+               /* fall through */
+
+       case BAMBOO_PT:
+               __clear_bit(ABS_MISC, input_dev->absbit);
+
+               if (features->device_type == BTN_TOOL_FINGER) {
+
+                       if (features->touch_max) {
+                               /* touch interface */
+                               unsigned int flags = INPUT_MT_POINTER;
+
+                               __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+                               if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
+                                       input_set_abs_params(input_dev,
+                                                    ABS_MT_TOUCH_MAJOR,
+                                                    0, features->x_max, 0, 0);
+                                       input_set_abs_params(input_dev,
+                                                    ABS_MT_TOUCH_MINOR,
+                                                    0, features->y_max, 0, 0);
+                               } else {
+                                       __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+                                       __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+                                       flags = 0;
+                               }
+                               input_mt_init_slots(input_dev, features->touch_max, flags);
+                       } else {
+                               /* buttons/keys only interface */
+                               __clear_bit(ABS_X, input_dev->absbit);
+                               __clear_bit(ABS_Y, input_dev->absbit);
+                               __clear_bit(BTN_TOUCH, input_dev->keybit);
+                       }
+               } else if (features->device_type == BTN_TOOL_PEN) {
+                       __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+                       __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
+                       __set_bit(BTN_TOOL_PEN, input_dev->keybit);
+                       __set_bit(BTN_STYLUS, input_dev->keybit);
+                       __set_bit(BTN_STYLUS2, input_dev->keybit);
+                       input_set_abs_params(input_dev, ABS_DISTANCE, 0,
+                                             features->distance_max,
+                                             0, 0);
+               }
+               break;
+
+       case CINTIQ_HYBRID:
+               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+               __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+               wacom_setup_cintiq(wacom_wac);
+               break;
+       }
+       return 0;
+}
+
+int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
+                                  struct wacom_wac *wacom_wac)
+{
+       struct wacom_features *features = &wacom_wac->features;
+       int i;
+
+       input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+
+       /* kept for making legacy xf86-input-wacom working with the wheels */
+       __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);
+
+       switch (features->type) {
+       case WACOM_MO:
+               __set_bit(BTN_BACK, input_dev->keybit);
+               __set_bit(BTN_LEFT, input_dev->keybit);
+               __set_bit(BTN_FORWARD, input_dev->keybit);
+               __set_bit(BTN_RIGHT, input_dev->keybit);
+               input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
+               break;
+
+       case WACOM_G4:
+               __set_bit(BTN_BACK, input_dev->keybit);
+               __set_bit(BTN_LEFT, input_dev->keybit);
+               __set_bit(BTN_FORWARD, input_dev->keybit);
+               __set_bit(BTN_RIGHT, input_dev->keybit);
+               input_set_capability(input_dev, EV_REL, REL_WHEEL);
+               break;
+
+       case WACOM_24HD:
+               __set_bit(BTN_A, input_dev->keybit);
+               __set_bit(BTN_B, input_dev->keybit);
+               __set_bit(BTN_C, input_dev->keybit);
+               __set_bit(BTN_X, input_dev->keybit);
+               __set_bit(BTN_Y, input_dev->keybit);
+               __set_bit(BTN_Z, input_dev->keybit);
+
+               for (i = 0; i < 10; i++)
+                       __set_bit(BTN_0 + i, input_dev->keybit);
+
+               __set_bit(KEY_PROG1, input_dev->keybit);
+               __set_bit(KEY_PROG2, input_dev->keybit);
+               __set_bit(KEY_PROG3, input_dev->keybit);
+
+               input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
+               input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0);
+               break;
+
+       case DTK:
+               for (i = 0; i < 6; i++)
+                       __set_bit(BTN_0 + i, input_dev->keybit);
+
+               break;
+
+       case WACOM_22HD:
+               __set_bit(KEY_PROG1, input_dev->keybit);
+               __set_bit(KEY_PROG2, input_dev->keybit);
+               __set_bit(KEY_PROG3, input_dev->keybit);
+               /* fall through */
+
+       case WACOM_21UX2:
+               __set_bit(BTN_A, input_dev->keybit);
+               __set_bit(BTN_B, input_dev->keybit);
+               __set_bit(BTN_C, input_dev->keybit);
+               __set_bit(BTN_X, input_dev->keybit);
+               __set_bit(BTN_Y, input_dev->keybit);
+               __set_bit(BTN_Z, input_dev->keybit);
+               __set_bit(BTN_BASE, input_dev->keybit);
+               __set_bit(BTN_BASE2, input_dev->keybit);
+               /* fall through */
+
+       case WACOM_BEE:
+               __set_bit(BTN_8, input_dev->keybit);
+               __set_bit(BTN_9, input_dev->keybit);
+               /* fall through */
+
+       case CINTIQ:
+               for (i = 0; i < 8; i++)
+                       __set_bit(BTN_0 + i, input_dev->keybit);
+
+               input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
+               input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
+               break;
+
+       case WACOM_13HD:
+               for (i = 0; i < 9; i++)
+                       __set_bit(BTN_0 + i, input_dev->keybit);
+
+               input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
+               break;
+
+       case INTUOS3:
+       case INTUOS3L:
+               __set_bit(BTN_4, input_dev->keybit);
+               __set_bit(BTN_5, input_dev->keybit);
+               __set_bit(BTN_6, input_dev->keybit);
+               __set_bit(BTN_7, input_dev->keybit);
+
+               input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
+               /* fall through */
+
+       case INTUOS3S:
+               __set_bit(BTN_0, input_dev->keybit);
+               __set_bit(BTN_1, input_dev->keybit);
+               __set_bit(BTN_2, input_dev->keybit);
+               __set_bit(BTN_3, input_dev->keybit);
+
+               input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
+               break;
+
+       case INTUOS5:
+       case INTUOS5L:
+       case INTUOSPM:
+       case INTUOSPL:
+               __set_bit(BTN_7, input_dev->keybit);
+               __set_bit(BTN_8, input_dev->keybit);
+               /* fall through */
+
+       case INTUOS5S:
+       case INTUOSPS:
+               /* touch interface does not have the pad device */
+               if (features->device_type != BTN_TOOL_PEN)
+                       return 1;
+
+               for (i = 0; i < 7; i++)
+                       __set_bit(BTN_0 + i, input_dev->keybit);
+
+               input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
+               break;
+
+       case INTUOS4:
+       case INTUOS4L:
+               __set_bit(BTN_7, input_dev->keybit);
+               __set_bit(BTN_8, input_dev->keybit);
+               /* fall through */
+
+       case INTUOS4S:
+               for (i = 0; i < 7; i++)
+                       __set_bit(BTN_0 + i, input_dev->keybit);
+
+               input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
+               break;
+
+       case CINTIQ_HYBRID:
+               for (i = 0; i < 9; i++)
+                       __set_bit(BTN_0 + i, input_dev->keybit);
+
+               break;
+
+       case DTUS:
+               for (i = 0; i < 4; i++)
+                       __set_bit(BTN_0 + i, input_dev->keybit);
+               break;
+
+       case INTUOSHT:
+       case BAMBOO_PT:
+               /* pad device is on the touch interface */
+               if (features->device_type != BTN_TOOL_FINGER)
+                       return 1;
+
+               __clear_bit(ABS_MISC, input_dev->absbit);
+
+               __set_bit(BTN_LEFT, input_dev->keybit);
+               __set_bit(BTN_FORWARD, input_dev->keybit);
+               __set_bit(BTN_BACK, input_dev->keybit);
+               __set_bit(BTN_RIGHT, input_dev->keybit);
+
+               break;
+
+       default:
+               /* no pad supported */
+               return 1;
+       }
+       return 0;
+}
+
+static const struct wacom_features wacom_features_0x00 =
+       { "Wacom Penpartner", 5040, 3780, 255, 0,
+         PENPARTNER, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES };
+static const struct wacom_features wacom_features_0x10 =
+       { "Wacom Graphire", 10206, 7422, 511, 63,
+         GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+static const struct wacom_features wacom_features_0x11 =
+       { "Wacom Graphire2 4x5", 10206, 7422, 511, 63,
+         GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+static const struct wacom_features wacom_features_0x12 =
+       { "Wacom Graphire2 5x7", 13918, 10206, 511, 63,
+         GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+static const struct wacom_features wacom_features_0x13 =
+       { "Wacom Graphire3", 10208, 7424, 511, 63,
+         GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+static const struct wacom_features wacom_features_0x14 =
+       { "Wacom Graphire3 6x8", 16704, 12064, 511, 63,
+         GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+static const struct wacom_features wacom_features_0x15 =
+       { "Wacom Graphire4 4x5", 10208, 7424, 511, 63,
+         WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+static const struct wacom_features wacom_features_0x16 =
+       { "Wacom Graphire4 6x8", 16704, 12064, 511, 63,
+         WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+static const struct wacom_features wacom_features_0x17 =
+       { "Wacom BambooFun 4x5", 14760, 9225, 511, 63,
+         WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x18 =
+       { "Wacom BambooFun 6x8", 21648, 13530, 511, 63,
+         WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x19 =
+       { "Wacom Bamboo1 Medium", 16704, 12064, 511, 63,
+         GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+static const struct wacom_features wacom_features_0x60 =
+       { "Wacom Volito", 5104, 3712, 511, 63,
+         GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
+static const struct wacom_features wacom_features_0x61 =
+       { "Wacom PenStation2", 3250, 2320, 255, 63,
+         GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
+static const struct wacom_features wacom_features_0x62 =
+       { "Wacom Volito2 4x5", 5104, 3712, 511, 63,
+         GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
+static const struct wacom_features wacom_features_0x63 =
+       { "Wacom Volito2 2x3", 3248, 2320, 511, 63,
+         GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
+static const struct wacom_features wacom_features_0x64 =
+       { "Wacom PenPartner2", 3250, 2320, 511, 63,
+         GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
+static const struct wacom_features wacom_features_0x65 =
+       { "Wacom Bamboo", 14760, 9225, 511, 63,
+         WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x69 =
+       { "Wacom Bamboo1", 5104, 3712, 511, 63,
+         GRAPHIRE, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES };
+static const struct wacom_features wacom_features_0x6A =
+       { "Wacom Bamboo1 4x6", 14760, 9225, 1023, 63,
+         GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x6B =
+       { "Wacom Bamboo1 5x8", 21648, 13530, 1023, 63,
+         GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x20 =
+       { "Wacom Intuos 4x5", 12700, 10600, 1023, 31,
+         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x21 =
+       { "Wacom Intuos 6x8", 20320, 16240, 1023, 31,
+         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x22 =
+       { "Wacom Intuos 9x12", 30480, 24060, 1023, 31,
+         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x23 =
+       { "Wacom Intuos 12x12", 30480, 31680, 1023, 31,
+         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x24 =
+       { "Wacom Intuos 12x18", 45720, 31680, 1023, 31,
+         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x30 =
+       { "Wacom PL400", 5408, 4056, 255, 0,
+         PL, WACOM_PL_RES, WACOM_PL_RES };
+static const struct wacom_features wacom_features_0x31 =
+       { "Wacom PL500", 6144, 4608, 255, 0,
+         PL, WACOM_PL_RES, WACOM_PL_RES };
+static const struct wacom_features wacom_features_0x32 =
+       { "Wacom PL600", 6126, 4604, 255, 0,
+         PL, WACOM_PL_RES, WACOM_PL_RES };
+static const struct wacom_features wacom_features_0x33 =
+       { "Wacom PL600SX", 6260, 5016, 255, 0,
+         PL, WACOM_PL_RES, WACOM_PL_RES };
+static const struct wacom_features wacom_features_0x34 =
+       { "Wacom PL550", 6144, 4608, 511, 0,
+         PL, WACOM_PL_RES, WACOM_PL_RES };
+static const struct wacom_features wacom_features_0x35 =
+       { "Wacom PL800", 7220, 5780, 511, 0,
+         PL, WACOM_PL_RES, WACOM_PL_RES };
+static const struct wacom_features wacom_features_0x37 =
+       { "Wacom PL700", 6758, 5406, 511, 0,
+         PL, WACOM_PL_RES, WACOM_PL_RES };
+static const struct wacom_features wacom_features_0x38 =
+       { "Wacom PL510", 6282, 4762, 511, 0,
+         PL, WACOM_PL_RES, WACOM_PL_RES };
+static const struct wacom_features wacom_features_0x39 =
+       { "Wacom DTU710", 34080, 27660, 511, 0,
+         PL, WACOM_PL_RES, WACOM_PL_RES };
+static const struct wacom_features wacom_features_0xC4 =
+       { "Wacom DTF521", 6282, 4762, 511, 0,
+         PL, WACOM_PL_RES, WACOM_PL_RES };
+static const struct wacom_features wacom_features_0xC0 =
+       { "Wacom DTF720", 6858, 5506, 511, 0,
+         PL, WACOM_PL_RES, WACOM_PL_RES };
+static const struct wacom_features wacom_features_0xC2 =
+       { "Wacom DTF720a", 6858, 5506, 511, 0,
+         PL, WACOM_PL_RES, WACOM_PL_RES };
+static const struct wacom_features wacom_features_0x03 =
+       { "Wacom Cintiq Partner", 20480, 15360, 511, 0,
+         PTU, WACOM_PL_RES, WACOM_PL_RES };
+static const struct wacom_features wacom_features_0x41 =
+       { "Wacom Intuos2 4x5", 12700, 10600, 1023, 31,
+         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x42 =
+       { "Wacom Intuos2 6x8", 20320, 16240, 1023, 31,
+         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x43 =
+       { "Wacom Intuos2 9x12", 30480, 24060, 1023, 31,
+         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x44 =
+       { "Wacom Intuos2 12x12", 30480, 31680, 1023, 31,
+         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x45 =
+       { "Wacom Intuos2 12x18", 45720, 31680, 1023, 31,
+         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0xB0 =
+       { "Wacom Intuos3 4x5", 25400, 20320, 1023, 63,
+         INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xB1 =
+       { "Wacom Intuos3 6x8", 40640, 30480, 1023, 63,
+         INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xB2 =
+       { "Wacom Intuos3 9x12", 60960, 45720, 1023, 63,
+         INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xB3 =
+       { "Wacom Intuos3 12x12", 60960, 60960, 1023, 63,
+         INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xB4 =
+       { "Wacom Intuos3 12x19", 97536, 60960, 1023, 63,
+         INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xB5 =
+       { "Wacom Intuos3 6x11", 54204, 31750, 1023, 63,
+         INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xB7 =
+       { "Wacom Intuos3 4x6", 31496, 19685, 1023, 63,
+         INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xB8 =
+       { "Wacom Intuos4 4x6", 31496, 19685, 2047, 63,
+         INTUOS4S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xB9 =
+       { "Wacom Intuos4 6x9", 44704, 27940, 2047, 63,
+         INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xBA =
+       { "Wacom Intuos4 8x13", 65024, 40640, 2047, 63,
+         INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xBB =
+       { "Wacom Intuos4 12x19", 97536, 60960, 2047, 63,
+         INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xBC =
+       { "Wacom Intuos4 WL", 40640, 25400, 2047, 63,
+         INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0x26 =
+       { "Wacom Intuos5 touch S", 31496, 19685, 2047, 63,
+         INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 };
+static const struct wacom_features wacom_features_0x27 =
+       { "Wacom Intuos5 touch M", 44704, 27940, 2047, 63,
+         INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 };
+static const struct wacom_features wacom_features_0x28 =
+       { "Wacom Intuos5 touch L", 65024, 40640, 2047, 63,
+         INTUOS5L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 };
+static const struct wacom_features wacom_features_0x29 =
+       { "Wacom Intuos5 S", 31496, 19685, 2047, 63,
+         INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0x2A =
+       { "Wacom Intuos5 M", 44704, 27940, 2047, 63,
+         INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0x314 =
+       { "Wacom Intuos Pro S", 31496, 19685, 2047, 63,
+         INTUOSPS, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16,
+         .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
+static const struct wacom_features wacom_features_0x315 =
+       { "Wacom Intuos Pro M", 44704, 27940, 2047, 63,
+         INTUOSPM, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16,
+         .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
+static const struct wacom_features wacom_features_0x317 =
+       { "Wacom Intuos Pro L", 65024, 40640, 2047, 63,
+         INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16,
+         .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
+static const struct wacom_features wacom_features_0xF4 =
+       { "Wacom Cintiq 24HD", 104280, 65400, 2047, 63,
+         WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
+static const struct wacom_features wacom_features_0xF8 =
+       { "Wacom Cintiq 24HD touch", 104280, 65400, 2047, 63, /* Pen */
+         WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
+         .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_0x3F =
+       { "Wacom Cintiq 21UX", 87200, 65600, 1023, 63,
+         CINTIQ, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xC5 =
+       { "Wacom Cintiq 20WSX", 86680, 54180, 1023, 63,
+         WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xC6 =
+       { "Wacom Cintiq 12WX", 53020, 33440, 1023, 63,
+         WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0x304 =
+       { "Wacom Cintiq 13HD", 59352, 33648, 1023, 63,
+         WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
+static const struct wacom_features wacom_features_0xC7 =
+       { "Wacom DTU1931", 37832, 30305, 511, 0,
+         PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0xCE =
+       { "Wacom DTU2231", 47864, 27011, 511, 0,
+         DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
+         .check_for_hid_type = true, .hid_type = HID_TYPE_USBMOUSE };
+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", 22096, 13960, 511, 0,
+         DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x57 =
+       { "Wacom DTK2241", 95640, 54060, 2047, 63,
+         DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
+static const struct wacom_features wacom_features_0x59 = /* Pen */
+       { "Wacom DTH2242", 95640, 54060, 2047, 63,
+         DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
+         .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", 87000, 65400, 2047, 63,
+         WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
+static const struct wacom_features wacom_features_0xFA =
+       { "Wacom Cintiq 22HD", 95640, 54060, 2047, 63,
+         WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
+static const struct wacom_features wacom_features_0x5B =
+       { "Wacom Cintiq 22HDT", 95640, 54060, 2047, 63,
+         WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
+         .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5e };
+static const struct wacom_features wacom_features_0x5E =
+       { "Wacom Cintiq 22HDT", .type = WACOM_24HDT,
+         .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5b, .touch_max = 10,
+         .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
+static const struct wacom_features wacom_features_0x90 =
+       { "Wacom ISDv4 90", 26202, 16325, 255, 0,
+         TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x93 =
+       { "Wacom ISDv4 93", 26202, 16325, 255, 0,
+         TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x97 =
+       { "Wacom ISDv4 97", 26202, 16325, 511, 0,
+         TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x9A =
+       { "Wacom ISDv4 9A", 26202, 16325, 255, 0,
+         TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x9F =
+       { "Wacom ISDv4 9F", 26202, 16325, 255, 0,
+         TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0xE2 =
+       { "Wacom ISDv4 E2", 26202, 16325, 255, 0,
+         TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
+static const struct wacom_features wacom_features_0xE3 =
+       { "Wacom ISDv4 E3", 26202, 16325, 255, 0,
+         TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
+static const struct wacom_features wacom_features_0xE5 =
+       { "Wacom ISDv4 E5", 26202, 16325, 255, 0,
+         MTSCREEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0xE6 =
+       { "Wacom ISDv4 E6", 27760, 15694, 255, 0,
+         TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
+static const struct wacom_features wacom_features_0xEC =
+       { "Wacom ISDv4 EC", 25710, 14500, 255, 0,
+         TABLETPC,    WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0xED =
+       { "Wacom ISDv4 ED", 26202, 16325, 255, 0,
+         TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0xEF =
+       { "Wacom ISDv4 EF", 26202, 16325, 255, 0,
+         TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x100 =
+       { "Wacom ISDv4 100", 26202, 16325, 255, 0,
+         MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x101 =
+       { "Wacom ISDv4 101", 26202, 16325, 255, 0,
+         MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x10D =
+       { "Wacom ISDv4 10D", 26202, 16325, 255, 0,
+         MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x10E =
+       { "Wacom ISDv4 10E", 27760, 15694, 255, 0,
+         MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x10F =
+       { "Wacom ISDv4 10F", 27760, 15694, 255, 0,
+         MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x116 =
+       { "Wacom ISDv4 116", 26202, 16325, 255, 0,
+         TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x4001 =
+       { "Wacom ISDv4 4001", 26202, 16325, 255, 0,
+         MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x4004 =
+       { "Wacom ISDv4 4004", 11060, 6220, 255, 0,
+         MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x5000 =
+       { "Wacom ISDv4 5000", 27848, 15752, 1023, 0,
+         MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x5002 =
+       { "Wacom ISDv4 5002", 29576, 16724, 1023, 0,
+         MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x47 =
+       { "Wacom Intuos2 6x8", 20320, 16240, 1023, 31,
+         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x84 =
+       { "Wacom Wireless Receiver", 0, 0, 0, 0,
+         WIRELESS, 0, 0, .touch_max = 16 };
+static const struct wacom_features wacom_features_0xD0 =
+       { "Wacom Bamboo 2FG", 14720, 9200, 1023, 31,
+         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
+static const struct wacom_features wacom_features_0xD1 =
+       { "Wacom Bamboo 2FG 4x5", 14720, 9200, 1023, 31,
+         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
+static const struct wacom_features wacom_features_0xD2 =
+       { "Wacom Bamboo Craft", 14720, 9200, 1023, 31,
+         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
+static const struct wacom_features wacom_features_0xD3 =
+       { "Wacom Bamboo 2FG 6x8", 21648, 13700, 1023, 31,
+         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
+static const struct wacom_features wacom_features_0xD4 =
+       { "Wacom Bamboo Pen", 14720, 9200, 1023, 31,
+         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0xD5 =
+       { "Wacom Bamboo Pen 6x8", 21648, 13700, 1023, 31,
+         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0xD6 =
+       { "Wacom BambooPT 2FG 4x5", 14720, 9200, 1023, 31,
+         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
+static const struct wacom_features wacom_features_0xD7 =
+       { "Wacom BambooPT 2FG Small", 14720, 9200, 1023, 31,
+         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
+static const struct wacom_features wacom_features_0xD8 =
+       { "Wacom Bamboo Comic 2FG", 21648, 13700, 1023, 31,
+         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
+static const struct wacom_features wacom_features_0xDA =
+       { "Wacom Bamboo 2FG 4x5 SE", 14720, 9200, 1023, 31,
+         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
+static const struct wacom_features wacom_features_0xDB =
+       { "Wacom Bamboo 2FG 6x8 SE", 21648, 13700, 1023, 31,
+         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
+static const struct wacom_features wacom_features_0xDD =
+        { "Wacom Bamboo Connect", 14720, 9200, 1023, 31,
+          BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0xDE =
+        { "Wacom Bamboo 16FG 4x5", 14720, 9200, 1023, 31,
+         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16 };
+static const struct wacom_features wacom_features_0xDF =
+        { "Wacom Bamboo 16FG 6x8", 21648, 13700, 1023, 31,
+         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16 };
+static const struct wacom_features wacom_features_0x300 =
+       { "Wacom Bamboo One S", 14720, 9225, 1023, 31,
+         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x301 =
+       { "Wacom Bamboo One M", 21648, 13530, 1023, 31,
+         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x302 =
+       { "Wacom Intuos PT S", 15200, 9500, 1023, 31,
+         INTUOSHT, 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_0x303 =
+       { "Wacom Intuos PT M", 21600, 13500, 1023, 31,
+         INTUOSHT, 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_0x30E =
+       { "Wacom Intuos S", 15200, 9500, 1023, 31,
+         INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
+         .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
+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", 59352, 33648, 2047, 63,
+         CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
+         .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 };
+
+#define USB_DEVICE_WACOM(prod)                                         \
+       HID_DEVICE(BUS_USB, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\
+       .driver_data = (kernel_ulong_t)&wacom_features_##prod
+
+#define USB_DEVICE_LENOVO(prod)                                        \
+       HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, prod),                     \
+       .driver_data = (kernel_ulong_t)&wacom_features_##prod
+
+const struct hid_device_id wacom_ids[] = {
+       { USB_DEVICE_WACOM(0x00) },
+       { USB_DEVICE_WACOM(0x03) },
+       { USB_DEVICE_WACOM(0x10) },
+       { USB_DEVICE_WACOM(0x11) },
+       { USB_DEVICE_WACOM(0x12) },
+       { USB_DEVICE_WACOM(0x13) },
+       { USB_DEVICE_WACOM(0x14) },
+       { USB_DEVICE_WACOM(0x15) },
+       { USB_DEVICE_WACOM(0x16) },
+       { USB_DEVICE_WACOM(0x17) },
+       { USB_DEVICE_WACOM(0x18) },
+       { USB_DEVICE_WACOM(0x19) },
+       { USB_DEVICE_WACOM(0x20) },
+       { USB_DEVICE_WACOM(0x21) },
+       { USB_DEVICE_WACOM(0x22) },
+       { USB_DEVICE_WACOM(0x23) },
+       { USB_DEVICE_WACOM(0x24) },
+       { USB_DEVICE_WACOM(0x26) },
+       { USB_DEVICE_WACOM(0x27) },
+       { USB_DEVICE_WACOM(0x28) },
+       { USB_DEVICE_WACOM(0x29) },
+       { USB_DEVICE_WACOM(0x2A) },
+       { USB_DEVICE_WACOM(0x30) },
+       { USB_DEVICE_WACOM(0x31) },
+       { USB_DEVICE_WACOM(0x32) },
+       { USB_DEVICE_WACOM(0x33) },
+       { USB_DEVICE_WACOM(0x34) },
+       { USB_DEVICE_WACOM(0x35) },
+       { USB_DEVICE_WACOM(0x37) },
+       { USB_DEVICE_WACOM(0x38) },
+       { USB_DEVICE_WACOM(0x39) },
+       { USB_DEVICE_WACOM(0x3F) },
+       { USB_DEVICE_WACOM(0x41) },
+       { USB_DEVICE_WACOM(0x42) },
+       { USB_DEVICE_WACOM(0x43) },
+       { USB_DEVICE_WACOM(0x44) },
+       { USB_DEVICE_WACOM(0x45) },
+       { USB_DEVICE_WACOM(0x47) },
+       { USB_DEVICE_WACOM(0x57) },
+       { USB_DEVICE_WACOM(0x59) },
+       { USB_DEVICE_WACOM(0x5B) },
+       { USB_DEVICE_WACOM(0x5D) },
+       { USB_DEVICE_WACOM(0x5E) },
+       { USB_DEVICE_WACOM(0x60) },
+       { USB_DEVICE_WACOM(0x61) },
+       { USB_DEVICE_WACOM(0x62) },
+       { USB_DEVICE_WACOM(0x63) },
+       { USB_DEVICE_WACOM(0x64) },
+       { USB_DEVICE_WACOM(0x65) },
+       { USB_DEVICE_WACOM(0x69) },
+       { USB_DEVICE_WACOM(0x6A) },
+       { USB_DEVICE_WACOM(0x6B) },
+       { USB_DEVICE_WACOM(0x84) },
+       { USB_DEVICE_WACOM(0x90) },
+       { USB_DEVICE_WACOM(0x93) },
+       { USB_DEVICE_WACOM(0x97) },
+       { USB_DEVICE_WACOM(0x9A) },
+       { USB_DEVICE_WACOM(0x9F) },
+       { USB_DEVICE_WACOM(0xB0) },
+       { USB_DEVICE_WACOM(0xB1) },
+       { USB_DEVICE_WACOM(0xB2) },
+       { USB_DEVICE_WACOM(0xB3) },
+       { USB_DEVICE_WACOM(0xB4) },
+       { USB_DEVICE_WACOM(0xB5) },
+       { USB_DEVICE_WACOM(0xB7) },
+       { USB_DEVICE_WACOM(0xB8) },
+       { USB_DEVICE_WACOM(0xB9) },
+       { USB_DEVICE_WACOM(0xBA) },
+       { USB_DEVICE_WACOM(0xBB) },
+       { USB_DEVICE_WACOM(0xBC) },
+       { USB_DEVICE_WACOM(0xC0) },
+       { USB_DEVICE_WACOM(0xC2) },
+       { USB_DEVICE_WACOM(0xC4) },
+       { USB_DEVICE_WACOM(0xC5) },
+       { USB_DEVICE_WACOM(0xC6) },
+       { USB_DEVICE_WACOM(0xC7) },
+       { USB_DEVICE_WACOM(0xCC) },
+       { USB_DEVICE_WACOM(0xCE) },
+       { USB_DEVICE_WACOM(0xD0) },
+       { USB_DEVICE_WACOM(0xD1) },
+       { USB_DEVICE_WACOM(0xD2) },
+       { USB_DEVICE_WACOM(0xD3) },
+       { USB_DEVICE_WACOM(0xD4) },
+       { USB_DEVICE_WACOM(0xD5) },
+       { USB_DEVICE_WACOM(0xD6) },
+       { USB_DEVICE_WACOM(0xD7) },
+       { USB_DEVICE_WACOM(0xD8) },
+       { USB_DEVICE_WACOM(0xDA) },
+       { USB_DEVICE_WACOM(0xDB) },
+       { USB_DEVICE_WACOM(0xDD) },
+       { USB_DEVICE_WACOM(0xDE) },
+       { USB_DEVICE_WACOM(0xDF) },
+       { USB_DEVICE_WACOM(0xE2) },
+       { USB_DEVICE_WACOM(0xE3) },
+       { USB_DEVICE_WACOM(0xE5) },
+       { USB_DEVICE_WACOM(0xE6) },
+       { USB_DEVICE_WACOM(0xEC) },
+       { USB_DEVICE_WACOM(0xED) },
+       { USB_DEVICE_WACOM(0xEF) },
+       { USB_DEVICE_WACOM(0xF0) },
+       { USB_DEVICE_WACOM(0xF4) },
+       { USB_DEVICE_WACOM(0xF6) },
+       { USB_DEVICE_WACOM(0xF8) },
+       { USB_DEVICE_WACOM(0xFA) },
+       { USB_DEVICE_WACOM(0xFB) },
+       { USB_DEVICE_WACOM(0x100) },
+       { USB_DEVICE_WACOM(0x101) },
+       { USB_DEVICE_WACOM(0x10D) },
+       { USB_DEVICE_WACOM(0x10E) },
+       { USB_DEVICE_WACOM(0x10F) },
+       { USB_DEVICE_WACOM(0x116) },
+       { USB_DEVICE_WACOM(0x300) },
+       { USB_DEVICE_WACOM(0x301) },
+       { USB_DEVICE_WACOM(0x302) },
+       { USB_DEVICE_WACOM(0x303) },
+       { USB_DEVICE_WACOM(0x304) },
+       { USB_DEVICE_WACOM(0x307) },
+       { USB_DEVICE_WACOM(0x309) },
+       { USB_DEVICE_WACOM(0x30E) },
+       { USB_DEVICE_WACOM(0x314) },
+       { USB_DEVICE_WACOM(0x315) },
+       { USB_DEVICE_WACOM(0x317) },
+       { USB_DEVICE_WACOM(0x4001) },
+       { USB_DEVICE_WACOM(0x4004) },
+       { USB_DEVICE_WACOM(0x5000) },
+       { USB_DEVICE_WACOM(0x5002) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, wacom_ids);
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
new file mode 100644 (file)
index 0000000..4c59247
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * drivers/input/tablet/wacom_wac.h
+ *
+ * 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.
+ */
+#ifndef WACOM_WAC_H
+#define WACOM_WAC_H
+
+#include <linux/types.h>
+
+/* maximum packet length for USB devices */
+#define WACOM_PKGLEN_MAX       68
+
+#define WACOM_NAME_MAX         64
+
+/* packet length for individual models */
+#define WACOM_PKGLEN_PENPRTN    7
+#define WACOM_PKGLEN_GRAPHIRE   8
+#define WACOM_PKGLEN_BBFUN      9
+#define WACOM_PKGLEN_INTUOS    10
+#define WACOM_PKGLEN_TPC1FG     5
+#define WACOM_PKGLEN_TPC1FG_B  10
+#define WACOM_PKGLEN_TPC2FG    14
+#define WACOM_PKGLEN_BBTOUCH   20
+#define WACOM_PKGLEN_BBTOUCH3  64
+#define WACOM_PKGLEN_BBPEN     10
+#define WACOM_PKGLEN_WIRELESS  32
+#define WACOM_PKGLEN_MTOUCH    62
+#define WACOM_PKGLEN_MTTPC     40
+#define WACOM_PKGLEN_DTUS      68
+#define WACOM_PKGLEN_PENABLED   8
+
+/* wacom data size per MT contact */
+#define WACOM_BYTES_PER_MT_PACKET      11
+#define WACOM_BYTES_PER_24HDT_PACKET   14
+
+/* device IDs */
+#define STYLUS_DEVICE_ID       0x02
+#define TOUCH_DEVICE_ID                0x03
+#define CURSOR_DEVICE_ID       0x06
+#define ERASER_DEVICE_ID       0x0A
+#define PAD_DEVICE_ID          0x0F
+
+/* wacom data packet report IDs */
+#define WACOM_REPORT_PENABLED          2
+#define WACOM_REPORT_INTUOSREAD                5
+#define WACOM_REPORT_INTUOSWRITE       6
+#define WACOM_REPORT_INTUOSPAD         12
+#define WACOM_REPORT_INTUOS5PAD                3
+#define WACOM_REPORT_DTUSPAD           21
+#define WACOM_REPORT_TPC1FG            6
+#define WACOM_REPORT_TPC2FG            13
+#define WACOM_REPORT_TPCMT             13
+#define WACOM_REPORT_TPCMT2            3
+#define WACOM_REPORT_TPCHID            15
+#define WACOM_REPORT_TPCST             16
+#define WACOM_REPORT_DTUS              17
+#define WACOM_REPORT_TPC1FGE           18
+#define WACOM_REPORT_24HDT             1
+#define WACOM_REPORT_WL                        128
+#define WACOM_REPORT_USB               192
+
+/* device quirks */
+#define WACOM_QUIRK_MULTI_INPUT                0x0001
+#define WACOM_QUIRK_BBTOUCH_LOWRES     0x0002
+#define WACOM_QUIRK_NO_INPUT           0x0004
+#define WACOM_QUIRK_MONITOR            0x0008
+
+enum {
+       PENPARTNER = 0,
+       GRAPHIRE,
+       WACOM_G4,
+       PTU,
+       PL,
+       DTU,
+       DTUS,
+       INTUOS,
+       INTUOS3S,
+       INTUOS3,
+       INTUOS3L,
+       INTUOS4S,
+       INTUOS4,
+       INTUOS4L,
+       INTUOS5S,
+       INTUOS5,
+       INTUOS5L,
+       INTUOSPS,
+       INTUOSPM,
+       INTUOSPL,
+       INTUOSHT,
+       WACOM_21UX2,
+       WACOM_22HD,
+       DTK,
+       WACOM_24HD,
+       CINTIQ_HYBRID,
+       CINTIQ,
+       WACOM_BEE,
+       WACOM_13HD,
+       WACOM_MO,
+       WIRELESS,
+       BAMBOO_PT,
+       WACOM_24HDT,
+       TABLETPC,   /* add new TPC below */
+       TABLETPCE,
+       TABLETPC2FG,
+       MTSCREEN,
+       MTTPC,
+       MTTPC_B,
+       MAX_TYPE
+};
+
+struct wacom_features {
+       const char *name;
+       int x_max;
+       int y_max;
+       int pressure_max;
+       int distance_max;
+       int type;
+       int x_resolution;
+       int y_resolution;
+       int x_min;
+       int y_min;
+       int device_type;
+       int x_phy;
+       int y_phy;
+       unsigned unit;
+       int unitExpo;
+       int x_fuzz;
+       int y_fuzz;
+       int pressure_fuzz;
+       int distance_fuzz;
+       unsigned quirks;
+       unsigned touch_max;
+       int oVid;
+       int oPid;
+       int pktlen;
+       bool check_for_hid_type;
+       int hid_type;
+};
+
+struct wacom_shared {
+       bool stylus_in_proximity;
+       bool touch_down;
+       /* for wireless device to access USB interfaces */
+       unsigned touch_max;
+       int type;
+       struct input_dev *touch_input;
+};
+
+struct wacom_wac {
+       char name[WACOM_NAME_MAX];
+       char pad_name[WACOM_NAME_MAX];
+       unsigned char data[WACOM_PKGLEN_MAX];
+       int tool[2];
+       int id[2];
+       __u32 serial[2];
+       struct wacom_features features;
+       struct wacom_shared *shared;
+       struct input_dev *input;
+       struct input_dev *pad_input;
+       int pid;
+       int battery_capacity;
+       int num_contacts_left;
+};
+
+#endif
index bed7cbf84cfd513a2a05b7553c3318698e3d5d7e..cd9c79e0a11b075e56be84bb56fb537f5b4900c7 100644 (file)
@@ -73,20 +73,4 @@ config TABLET_USB_KBTAB
          To compile this driver as a module, choose M here: the
          module will be called kbtab.
 
-config TABLET_USB_WACOM
-       tristate "Wacom Intuos/Graphire tablet support (USB)"
-       depends on USB_ARCH_HAS_HCD
-       select POWER_SUPPLY
-       select USB
-       select NEW_LEDS
-       select LEDS_CLASS
-       help
-         Say Y here if you want to use the USB version of the Wacom Intuos
-         or Graphire tablet.  Make sure to say Y to "Mouse support"
-         (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
-         (CONFIG_INPUT_EVDEV) as well.
-
-         To compile this driver as a module, choose M here: the
-         module will be called wacom.
-
 endif
index 3f6c25220638c179de271d1145f25054a3fb137d..cc3bc17d5b08742ad63cb114c0e35e9fb331eac1 100644 (file)
@@ -2,12 +2,9 @@
 # Makefile for the tablet drivers
 #
 
-# Multipart objects.
-wacom-objs     := wacom_wac.o wacom_sys.o
 
 obj-$(CONFIG_TABLET_USB_ACECAD)        += acecad.o
 obj-$(CONFIG_TABLET_USB_AIPTEK)        += aiptek.o
 obj-$(CONFIG_TABLET_USB_GTCO)  += gtco.o
 obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o
 obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o
-obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o
diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h
deleted file mode 100644 (file)
index dd67b7d..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * drivers/input/tablet/wacom.h
- *
- *  USB Wacom tablet support
- *
- *  Copyright (c) 2000-2004 Vojtech Pavlik     <vojtech@ucw.cz>
- *  Copyright (c) 2000 Andreas Bach Aaen       <abach@stofanet.dk>
- *  Copyright (c) 2000 Clifford Wolf           <clifford@clifford.at>
- *  Copyright (c) 2000 Sam Mosel               <sam.mosel@computer.org>
- *  Copyright (c) 2000 James E. Blair          <corvus@gnu.org>
- *  Copyright (c) 2000 Daniel Egger            <egger@suse.de>
- *  Copyright (c) 2001 Frederic Lepied         <flepied@mandrakesoft.com>
- *  Copyright (c) 2004 Panagiotis Issaris      <panagiotis.issaris@mech.kuleuven.ac.be>
- *  Copyright (c) 2002-2011 Ping Cheng         <pingc@wacom.com>
- *
- *  ChangeLog:
- *      v0.1 (vp)  - Initial release
- *      v0.2 (aba) - Support for all buttons / combinations
- *      v0.3 (vp)  - Support for Intuos added
- *     v0.4 (sm)  - Support for more Intuos models, menustrip
- *                     relative mode, proximity.
- *     v0.5 (vp)  - Big cleanup, nifty features removed,
- *                     they belong in userspace
- *     v1.8 (vp)  - Submit URB only when operating, moved to CVS,
- *                     use input_report_key instead of report_btn and
- *                     other cleanups
- *     v1.11 (vp) - Add URB ->dev setting for new kernels
- *     v1.11 (jb) - Add support for the 4D Mouse & Lens
- *     v1.12 (de) - Add support for two more inking pen IDs
- *     v1.14 (vp) - Use new USB device id probing scheme.
- *                  Fix Wacom Graphire mouse wheel
- *     v1.18 (vp) - Fix mouse wheel direction
- *                  Make mouse relative
- *      v1.20 (fl) - Report tool id for Intuos devices
- *                 - Multi tools support
- *                 - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...)
- *                 - Add PL models support
- *                - Fix Wacom Graphire mouse wheel again
- *     v1.21 (vp) - Removed protocol descriptions
- *                - Added MISC_SERIAL for tool serial numbers
- *           (gb) - Identify version on module load.
- *    v1.21.1 (fl) - added Graphire2 support
- *    v1.21.2 (fl) - added Intuos2 support
- *                 - added all the PL ids
- *    v1.21.3 (fl) - added another eraser id from Neil Okamoto
- *                 - added smooth filter for Graphire from Peri Hankey
- *                 - added PenPartner support from Olaf van Es
- *                 - new tool ids from Ole Martin Bjoerndalen
- *     v1.29 (pc) - Add support for more tablets
- *                - Fix pressure reporting
- *     v1.30 (vp) - Merge 2.4 and 2.5 drivers
- *                - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse
- *                - Cleanups here and there
- *    v1.30.1 (pi) - Added Graphire3 support
- *     v1.40 (pc) - Add support for several new devices, fix eraser reporting, ...
- *     v1.43 (pc) - Added support for Cintiq 21UX
- *                - Fixed a Graphire bug
- *                - Merged wacom_intuos3_irq into wacom_intuos_irq
- *     v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc.
- *                - Report Device IDs
- *      v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19
- *                 - Minor data report fix
- *      v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c,
- *                - where wacom_sys.c deals with system specific code,
- *                - and wacom_wac.c deals with Wacom specific code
- *                - Support Intuos3 4x6
- *      v1.47 (pc) - Added support for Bamboo
- *      v1.48 (pc) - Added support for Bamboo1, BambooFun, and Cintiq 12WX
- *      v1.49 (pc) - Added support for USB Tablet PC (0x90, 0x93, and 0x9A)
- *      v1.50 (pc) - Fixed a TabletPC touch bug in 2.6.28
- *      v1.51 (pc) - Added support for Intuos4
- *      v1.52 (pc) - Query Wacom data upon system resume
- *                 - add defines for features->type
- *                 - add new devices (0x9F, 0xE2, and 0XE3)
- */
-
-/*
- * 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.
- */
-#ifndef WACOM_H
-#define WACOM_H
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/mod_devicetable.h>
-#include <linux/usb/input.h>
-#include <linux/power_supply.h>
-#include <asm/unaligned.h>
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v1.53"
-#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
-#define DRIVER_DESC "USB Wacom tablet driver"
-#define DRIVER_LICENSE "GPL"
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE(DRIVER_LICENSE);
-
-#define USB_VENDOR_ID_WACOM    0x056a
-#define USB_VENDOR_ID_LENOVO   0x17ef
-
-struct wacom {
-       struct usb_device *usbdev;
-       struct usb_interface *intf;
-       struct wacom_wac wacom_wac;
-       struct hid_device *hdev;
-       struct mutex lock;
-       struct work_struct work;
-       struct wacom_led {
-               u8 select[2]; /* status led selector (0..3) */
-               u8 llv;       /* status led brightness no button (1..127) */
-               u8 hlv;       /* status led brightness button pressed (1..127) */
-               u8 img_lum;   /* OLED matrix display brightness */
-       } led;
-       struct power_supply battery;
-};
-
-static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
-{
-       struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
-       schedule_work(&wacom->work);
-}
-
-extern const struct hid_device_id wacom_ids[];
-
-void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
-void wacom_setup_device_quirks(struct wacom_features *features);
-int wacom_setup_input_capabilities(struct input_dev *input_dev,
-                                  struct wacom_wac *wacom_wac);
-int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
-                                      struct wacom_wac *wacom_wac);
-#endif
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
deleted file mode 100644 (file)
index 06e304b..0000000
+++ /dev/null
@@ -1,1257 +0,0 @@
-/*
- * drivers/input/tablet/wacom_sys.c
- *
- *  USB Wacom tablet support - system specific code
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include "wacom_wac.h"
-#include "wacom.h"
-#include <linux/hid.h>
-
-#define WAC_MSG_RETRIES                5
-
-#define WAC_CMD_LED_CONTROL    0x20
-#define WAC_CMD_ICON_START     0x21
-#define WAC_CMD_ICON_XFER      0x23
-#define WAC_CMD_RETRIES                10
-
-static int wacom_get_report(struct hid_device *hdev, u8 type, u8 id,
-                           void *buf, size_t size, unsigned int retries)
-{
-       int retval;
-
-       do {
-               retval = hid_hw_raw_request(hdev, id, buf, size, type,
-                               HID_REQ_GET_REPORT);
-       } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
-
-       return retval;
-}
-
-static int wacom_set_report(struct hid_device *hdev, u8 type, u8 id,
-                           void *buf, size_t size, unsigned int retries)
-{
-       int retval;
-
-       do {
-               retval = hid_hw_raw_request(hdev, id, buf, size, type,
-                               HID_REQ_SET_REPORT);
-       } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
-
-       return retval;
-}
-
-static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
-               u8 *raw_data, int size)
-{
-       struct wacom *wacom = hid_get_drvdata(hdev);
-
-       if (size > WACOM_PKGLEN_MAX)
-               return 1;
-
-       memcpy(wacom->wacom_wac.data, raw_data, size);
-
-       wacom_wac_irq(&wacom->wacom_wac, size);
-
-       return 0;
-}
-
-static int wacom_open(struct input_dev *dev)
-{
-       struct wacom *wacom = input_get_drvdata(dev);
-       int retval;
-
-       mutex_lock(&wacom->lock);
-       retval = hid_hw_open(wacom->hdev);
-       mutex_unlock(&wacom->lock);
-
-       return retval;
-}
-
-static void wacom_close(struct input_dev *dev)
-{
-       struct wacom *wacom = input_get_drvdata(dev);
-
-       mutex_lock(&wacom->lock);
-       hid_hw_close(wacom->hdev);
-       mutex_unlock(&wacom->lock);
-}
-
-/*
- * Calculate the resolution of the X or Y axis using hidinput_calc_abs_res.
- */
-static int wacom_calc_hid_res(int logical_extents, int physical_extents,
-                              unsigned unit, int exponent)
-{
-       struct hid_field field = {
-               .logical_maximum = logical_extents,
-               .physical_maximum = physical_extents,
-               .unit = unit,
-               .unit_exponent = exponent,
-       };
-
-       return hidinput_calc_abs_res(&field, ABS_X);
-}
-
-static void wacom_feature_mapping(struct hid_device *hdev,
-               struct hid_field *field, struct hid_usage *usage)
-{
-       struct wacom *wacom = hid_get_drvdata(hdev);
-       struct wacom_features *features = &wacom->wacom_wac.features;
-
-       switch (usage->hid) {
-       case HID_DG_CONTACTMAX:
-               /* leave touch_max as is if predefined */
-               if (!features->touch_max)
-                       features->touch_max = field->value[0];
-               break;
-       }
-}
-
-/*
- * Interface Descriptor of wacom devices can be incomplete and
- * inconsistent so wacom_features table is used to store stylus
- * device's packet lengths, various maximum values, and tablet
- * resolution based on product ID's.
- *
- * For devices that contain 2 interfaces, wacom_features table is
- * inaccurate for the touch interface.  Since the Interface Descriptor
- * for touch interfaces has pretty complete data, this function exists
- * to query tablet for this missing information instead of hard coding in
- * an additional table.
- *
- * A typical Interface Descriptor for a stylus will contain a
- * boot mouse application collection that is not of interest and this
- * function will ignore it.
- *
- * It also contains a digitizer application collection that also is not
- * of interest since any information it contains would be duplicate
- * of what is in wacom_features. Usually it defines a report of an array
- * of bytes that could be used as max length of the stylus packet returned.
- * If it happens to define a Digitizer-Stylus Physical Collection then
- * the X and Y logical values contain valid data but it is ignored.
- *
- * A typical Interface Descriptor for a touch interface will contain a
- * Digitizer-Finger Physical Collection which will define both logical
- * X/Y maximum as well as the physical size of tablet. Since touch
- * interfaces haven't supported pressure or distance, this is enough
- * information to override invalid values in the wacom_features table.
- *
- * Intuos5 touch interface and 3rd gen Bamboo Touch do not contain useful
- * data. We deal with them after returning from this function.
- */
-static void wacom_usage_mapping(struct hid_device *hdev,
-               struct hid_field *field, struct hid_usage *usage)
-{
-       struct wacom *wacom = hid_get_drvdata(hdev);
-       struct wacom_features *features = &wacom->wacom_wac.features;
-       bool finger = (field->logical == HID_DG_FINGER) ||
-                     (field->physical == HID_DG_FINGER);
-       bool pen = (field->logical == HID_DG_STYLUS) ||
-                  (field->physical == HID_DG_STYLUS);
-
-       /*
-       * Requiring Stylus Usage will ignore boot mouse
-       * X/Y values and some cases of invalid Digitizer X/Y
-       * values commonly reported.
-       */
-       if (!pen && !finger)
-               return;
-
-       if (finger && !features->touch_max)
-               /* touch device at least supports one touch point */
-               features->touch_max = 1;
-
-       switch (usage->hid) {
-       case HID_GD_X:
-               features->x_max = field->logical_maximum;
-               if (finger) {
-                       features->device_type = BTN_TOOL_FINGER;
-                       features->x_phy = field->physical_maximum;
-                       if (features->type != BAMBOO_PT) {
-                               features->unit = field->unit;
-                               features->unitExpo = field->unit_exponent;
-                       }
-               } else {
-                       features->device_type = BTN_TOOL_PEN;
-               }
-               break;
-       case HID_GD_Y:
-               features->y_max = field->logical_maximum;
-               if (finger) {
-                       features->y_phy = field->physical_maximum;
-                       if (features->type != BAMBOO_PT) {
-                               features->unit = field->unit;
-                               features->unitExpo = field->unit_exponent;
-                       }
-               }
-               break;
-       case HID_DG_TIPPRESSURE:
-               if (pen)
-                       features->pressure_max = field->logical_maximum;
-               break;
-       }
-}
-
-static void wacom_parse_hid(struct hid_device *hdev,
-                          struct wacom_features *features)
-{
-       struct hid_report_enum *rep_enum;
-       struct hid_report *hreport;
-       int i, j;
-
-       /* check features first */
-       rep_enum = &hdev->report_enum[HID_FEATURE_REPORT];
-       list_for_each_entry(hreport, &rep_enum->report_list, list) {
-               for (i = 0; i < hreport->maxfield; i++) {
-                       /* Ignore if report count is out of bounds. */
-                       if (hreport->field[i]->report_count < 1)
-                               continue;
-
-                       for (j = 0; j < hreport->field[i]->maxusage; j++) {
-                               wacom_feature_mapping(hdev, hreport->field[i],
-                                               hreport->field[i]->usage + j);
-                       }
-               }
-       }
-
-       /* now check the input usages */
-       rep_enum = &hdev->report_enum[HID_INPUT_REPORT];
-       list_for_each_entry(hreport, &rep_enum->report_list, list) {
-
-               if (!hreport->maxfield)
-                       continue;
-
-               for (i = 0; i < hreport->maxfield; i++)
-                       for (j = 0; j < hreport->field[i]->maxusage; j++)
-                               wacom_usage_mapping(hdev, hreport->field[i],
-                                               hreport->field[i]->usage + j);
-       }
-}
-
-static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
-               int length, int mode)
-{
-       unsigned char *rep_data;
-       int error = -ENOMEM, limit = 0;
-
-       rep_data = kzalloc(length, GFP_KERNEL);
-       if (!rep_data)
-               return error;
-
-       do {
-               rep_data[0] = report_id;
-               rep_data[1] = mode;
-
-               error = wacom_set_report(hdev, HID_FEATURE_REPORT,
-                                        report_id, rep_data, length, 1);
-               if (error >= 0)
-                       error = wacom_get_report(hdev, HID_FEATURE_REPORT,
-                                                report_id, rep_data, length, 1);
-       } while ((error < 0 || rep_data[1] != mode) && limit++ < WAC_MSG_RETRIES);
-
-       kfree(rep_data);
-
-       return error < 0 ? error : 0;
-}
-
-/*
- * Switch the tablet into its most-capable mode. Wacom tablets are
- * typically configured to power-up in a mode which sends mouse-like
- * reports to the OS. To get absolute position, pressure data, etc.
- * from the tablet, it is necessary to switch the tablet out of this
- * mode and into one which sends the full range of tablet data.
- */
-static int wacom_query_tablet_data(struct hid_device *hdev,
-               struct wacom_features *features)
-{
-       if (features->device_type == BTN_TOOL_FINGER) {
-               if (features->type > TABLETPC) {
-                       /* MT Tablet PC touch */
-                       return wacom_set_device_mode(hdev, 3, 4, 4);
-               }
-               else if (features->type == WACOM_24HDT || features->type == CINTIQ_HYBRID) {
-                       return wacom_set_device_mode(hdev, 18, 3, 2);
-               }
-       } else if (features->device_type == BTN_TOOL_PEN) {
-               if (features->type <= BAMBOO_PT && features->type != WIRELESS) {
-                       return wacom_set_device_mode(hdev, 2, 2, 2);
-               }
-       }
-
-       return 0;
-}
-
-static void wacom_retrieve_hid_descriptor(struct hid_device *hdev,
-                                        struct wacom_features *features)
-{
-       struct wacom *wacom = hid_get_drvdata(hdev);
-       struct usb_interface *intf = wacom->intf;
-
-       /* default features */
-       features->device_type = BTN_TOOL_PEN;
-       features->x_fuzz = 4;
-       features->y_fuzz = 4;
-       features->pressure_fuzz = 0;
-       features->distance_fuzz = 0;
-
-       /*
-        * The wireless device HID is basic and layout conflicts with
-        * other tablets (monitor and touch interface can look like pen).
-        * Skip the query for this type and modify defaults based on
-        * interface number.
-        */
-       if (features->type == WIRELESS) {
-               if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
-                       features->device_type = 0;
-               } else if (intf->cur_altsetting->desc.bInterfaceNumber == 2) {
-                       features->device_type = BTN_TOOL_FINGER;
-                       features->pktlen = WACOM_PKGLEN_BBTOUCH3;
-               }
-       }
-
-       /* only devices that support touch need to retrieve the info */
-       if (features->type < BAMBOO_PT)
-               return;
-
-       wacom_parse_hid(hdev, features);
-}
-
-struct wacom_hdev_data {
-       struct list_head list;
-       struct kref kref;
-       struct hid_device *dev;
-       struct wacom_shared shared;
-};
-
-static LIST_HEAD(wacom_udev_list);
-static DEFINE_MUTEX(wacom_udev_list_lock);
-
-static bool wacom_are_sibling(struct hid_device *hdev,
-               struct hid_device *sibling)
-{
-       struct wacom *wacom = hid_get_drvdata(hdev);
-       struct wacom_features *features = &wacom->wacom_wac.features;
-       int vid = features->oVid;
-       int pid = features->oPid;
-       int n1,n2;
-
-       if (vid == 0 && pid == 0) {
-               vid = hdev->vendor;
-               pid = hdev->product;
-       }
-
-       if (vid != sibling->vendor || pid != sibling->product)
-               return false;
-
-       /* Compare the physical path. */
-       n1 = strrchr(hdev->phys, '.') - hdev->phys;
-       n2 = strrchr(sibling->phys, '.') - sibling->phys;
-       if (n1 != n2 || n1 <= 0 || n2 <= 0)
-               return false;
-
-       return !strncmp(hdev->phys, sibling->phys, n1);
-}
-
-static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev)
-{
-       struct wacom_hdev_data *data;
-
-       list_for_each_entry(data, &wacom_udev_list, list) {
-               if (wacom_are_sibling(hdev, data->dev)) {
-                       kref_get(&data->kref);
-                       return data;
-               }
-       }
-
-       return NULL;
-}
-
-static int wacom_add_shared_data(struct hid_device *hdev)
-{
-       struct wacom *wacom = hid_get_drvdata(hdev);
-       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
-       struct wacom_hdev_data *data;
-       int retval = 0;
-
-       mutex_lock(&wacom_udev_list_lock);
-
-       data = wacom_get_hdev_data(hdev);
-       if (!data) {
-               data = kzalloc(sizeof(struct wacom_hdev_data), GFP_KERNEL);
-               if (!data) {
-                       retval = -ENOMEM;
-                       goto out;
-               }
-
-               kref_init(&data->kref);
-               data->dev = hdev;
-               list_add_tail(&data->list, &wacom_udev_list);
-       }
-
-       wacom_wac->shared = &data->shared;
-
-out:
-       mutex_unlock(&wacom_udev_list_lock);
-       return retval;
-}
-
-static void wacom_release_shared_data(struct kref *kref)
-{
-       struct wacom_hdev_data *data =
-               container_of(kref, struct wacom_hdev_data, kref);
-
-       mutex_lock(&wacom_udev_list_lock);
-       list_del(&data->list);
-       mutex_unlock(&wacom_udev_list_lock);
-
-       kfree(data);
-}
-
-static void wacom_remove_shared_data(struct wacom_wac *wacom)
-{
-       struct wacom_hdev_data *data;
-
-       if (wacom->shared) {
-               data = container_of(wacom->shared, struct wacom_hdev_data, shared);
-               kref_put(&data->kref, wacom_release_shared_data);
-               wacom->shared = NULL;
-       }
-}
-
-static int wacom_led_control(struct wacom *wacom)
-{
-       unsigned char *buf;
-       int retval;
-
-       buf = kzalloc(9, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       if (wacom->wacom_wac.features.type >= INTUOS5S &&
-           wacom->wacom_wac.features.type <= INTUOSPL) {
-               /*
-                * Touch Ring and crop mark LED luminance may take on
-                * one of four values:
-                *    0 = Low; 1 = Medium; 2 = High; 3 = Off
-                */
-               int ring_led = wacom->led.select[0] & 0x03;
-               int ring_lum = (((wacom->led.llv & 0x60) >> 5) - 1) & 0x03;
-               int crop_lum = 0;
-
-               buf[0] = WAC_CMD_LED_CONTROL;
-               buf[1] = (crop_lum << 4) | (ring_lum << 2) | (ring_led);
-       }
-       else {
-               int led = wacom->led.select[0] | 0x4;
-
-               if (wacom->wacom_wac.features.type == WACOM_21UX2 ||
-                   wacom->wacom_wac.features.type == WACOM_24HD)
-                       led |= (wacom->led.select[1] << 4) | 0x40;
-
-               buf[0] = WAC_CMD_LED_CONTROL;
-               buf[1] = led;
-               buf[2] = wacom->led.llv;
-               buf[3] = wacom->led.hlv;
-               buf[4] = wacom->led.img_lum;
-       }
-
-       retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
-                                 WAC_CMD_LED_CONTROL, buf, 9, WAC_CMD_RETRIES);
-       kfree(buf);
-
-       return retval;
-}
-
-static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *img)
-{
-       unsigned char *buf;
-       int i, retval;
-
-       buf = kzalloc(259, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       /* Send 'start' command */
-       buf[0] = WAC_CMD_ICON_START;
-       buf[1] = 1;
-       retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
-                                 WAC_CMD_ICON_START, buf, 2, WAC_CMD_RETRIES);
-       if (retval < 0)
-               goto out;
-
-       buf[0] = WAC_CMD_ICON_XFER;
-       buf[1] = button_id & 0x07;
-       for (i = 0; i < 4; i++) {
-               buf[2] = i;
-               memcpy(buf + 3, img + i * 256, 256);
-
-               retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
-                                         WAC_CMD_ICON_XFER,
-                                         buf, 259, WAC_CMD_RETRIES);
-               if (retval < 0)
-                       break;
-       }
-
-       /* Send 'stop' */
-       buf[0] = WAC_CMD_ICON_START;
-       buf[1] = 0;
-       wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, WAC_CMD_ICON_START,
-                        buf, 2, WAC_CMD_RETRIES);
-
-out:
-       kfree(buf);
-       return retval;
-}
-
-static ssize_t wacom_led_select_store(struct device *dev, int set_id,
-                                     const char *buf, size_t count)
-{
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct wacom *wacom = hid_get_drvdata(hdev);
-       unsigned int id;
-       int err;
-
-       err = kstrtouint(buf, 10, &id);
-       if (err)
-               return err;
-
-       mutex_lock(&wacom->lock);
-
-       wacom->led.select[set_id] = id & 0x3;
-       err = wacom_led_control(wacom);
-
-       mutex_unlock(&wacom->lock);
-
-       return err < 0 ? err : count;
-}
-
-#define DEVICE_LED_SELECT_ATTR(SET_ID)                                 \
-static ssize_t wacom_led##SET_ID##_select_store(struct device *dev,    \
-       struct device_attribute *attr, const char *buf, size_t count)   \
-{                                                                      \
-       return wacom_led_select_store(dev, SET_ID, buf, count);         \
-}                                                                      \
-static ssize_t wacom_led##SET_ID##_select_show(struct device *dev,     \
-       struct device_attribute *attr, char *buf)                       \
-{                                                                      \
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);\
-       struct wacom *wacom = hid_get_drvdata(hdev);                    \
-       return snprintf(buf, 2, "%d\n", wacom->led.select[SET_ID]);     \
-}                                                                      \
-static DEVICE_ATTR(status_led##SET_ID##_select, S_IWUSR | S_IRUSR,     \
-                   wacom_led##SET_ID##_select_show,                    \
-                   wacom_led##SET_ID##_select_store)
-
-DEVICE_LED_SELECT_ATTR(0);
-DEVICE_LED_SELECT_ATTR(1);
-
-static ssize_t wacom_luminance_store(struct wacom *wacom, u8 *dest,
-                                    const char *buf, size_t count)
-{
-       unsigned int value;
-       int err;
-
-       err = kstrtouint(buf, 10, &value);
-       if (err)
-               return err;
-
-       mutex_lock(&wacom->lock);
-
-       *dest = value & 0x7f;
-       err = wacom_led_control(wacom);
-
-       mutex_unlock(&wacom->lock);
-
-       return err < 0 ? err : count;
-}
-
-#define DEVICE_LUMINANCE_ATTR(name, field)                             \
-static ssize_t wacom_##name##_luminance_store(struct device *dev,      \
-       struct device_attribute *attr, const char *buf, size_t count)   \
-{                                                                      \
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);\
-       struct wacom *wacom = hid_get_drvdata(hdev);                    \
-                                                                       \
-       return wacom_luminance_store(wacom, &wacom->led.field,          \
-                                    buf, count);                       \
-}                                                                      \
-static DEVICE_ATTR(name##_luminance, S_IWUSR,                          \
-                  NULL, wacom_##name##_luminance_store)
-
-DEVICE_LUMINANCE_ATTR(status0, llv);
-DEVICE_LUMINANCE_ATTR(status1, hlv);
-DEVICE_LUMINANCE_ATTR(buttons, img_lum);
-
-static ssize_t wacom_button_image_store(struct device *dev, int button_id,
-                                       const char *buf, size_t count)
-{
-       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct wacom *wacom = hid_get_drvdata(hdev);
-       int err;
-
-       if (count != 1024)
-               return -EINVAL;
-
-       mutex_lock(&wacom->lock);
-
-       err = wacom_led_putimage(wacom, button_id, buf);
-
-       mutex_unlock(&wacom->lock);
-
-       return err < 0 ? err : count;
-}
-
-#define DEVICE_BTNIMG_ATTR(BUTTON_ID)                                  \
-static ssize_t wacom_btnimg##BUTTON_ID##_store(struct device *dev,     \
-       struct device_attribute *attr, const char *buf, size_t count)   \
-{                                                                      \
-       return wacom_button_image_store(dev, BUTTON_ID, buf, count);    \
-}                                                                      \
-static DEVICE_ATTR(button##BUTTON_ID##_rawimg, S_IWUSR,                        \
-                  NULL, wacom_btnimg##BUTTON_ID##_store)
-
-DEVICE_BTNIMG_ATTR(0);
-DEVICE_BTNIMG_ATTR(1);
-DEVICE_BTNIMG_ATTR(2);
-DEVICE_BTNIMG_ATTR(3);
-DEVICE_BTNIMG_ATTR(4);
-DEVICE_BTNIMG_ATTR(5);
-DEVICE_BTNIMG_ATTR(6);
-DEVICE_BTNIMG_ATTR(7);
-
-static struct attribute *cintiq_led_attrs[] = {
-       &dev_attr_status_led0_select.attr,
-       &dev_attr_status_led1_select.attr,
-       NULL
-};
-
-static struct attribute_group cintiq_led_attr_group = {
-       .name = "wacom_led",
-       .attrs = cintiq_led_attrs,
-};
-
-static struct attribute *intuos4_led_attrs[] = {
-       &dev_attr_status0_luminance.attr,
-       &dev_attr_status1_luminance.attr,
-       &dev_attr_status_led0_select.attr,
-       &dev_attr_buttons_luminance.attr,
-       &dev_attr_button0_rawimg.attr,
-       &dev_attr_button1_rawimg.attr,
-       &dev_attr_button2_rawimg.attr,
-       &dev_attr_button3_rawimg.attr,
-       &dev_attr_button4_rawimg.attr,
-       &dev_attr_button5_rawimg.attr,
-       &dev_attr_button6_rawimg.attr,
-       &dev_attr_button7_rawimg.attr,
-       NULL
-};
-
-static struct attribute_group intuos4_led_attr_group = {
-       .name = "wacom_led",
-       .attrs = intuos4_led_attrs,
-};
-
-static struct attribute *intuos5_led_attrs[] = {
-       &dev_attr_status0_luminance.attr,
-       &dev_attr_status_led0_select.attr,
-       NULL
-};
-
-static struct attribute_group intuos5_led_attr_group = {
-       .name = "wacom_led",
-       .attrs = intuos5_led_attrs,
-};
-
-static int wacom_initialize_leds(struct wacom *wacom)
-{
-       int error;
-
-       /* Initialize default values */
-       switch (wacom->wacom_wac.features.type) {
-       case INTUOS4S:
-       case INTUOS4:
-       case INTUOS4L:
-               wacom->led.select[0] = 0;
-               wacom->led.select[1] = 0;
-               wacom->led.llv = 10;
-               wacom->led.hlv = 20;
-               wacom->led.img_lum = 10;
-               error = sysfs_create_group(&wacom->hdev->dev.kobj,
-                                          &intuos4_led_attr_group);
-               break;
-
-       case WACOM_24HD:
-       case WACOM_21UX2:
-               wacom->led.select[0] = 0;
-               wacom->led.select[1] = 0;
-               wacom->led.llv = 0;
-               wacom->led.hlv = 0;
-               wacom->led.img_lum = 0;
-
-               error = sysfs_create_group(&wacom->hdev->dev.kobj,
-                                          &cintiq_led_attr_group);
-               break;
-
-       case INTUOS5S:
-       case INTUOS5:
-       case INTUOS5L:
-       case INTUOSPS:
-       case INTUOSPM:
-       case INTUOSPL:
-               if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN) {
-                       wacom->led.select[0] = 0;
-                       wacom->led.select[1] = 0;
-                       wacom->led.llv = 32;
-                       wacom->led.hlv = 0;
-                       wacom->led.img_lum = 0;
-
-                       error = sysfs_create_group(&wacom->hdev->dev.kobj,
-                                                 &intuos5_led_attr_group);
-               } else
-                       return 0;
-               break;
-
-       default:
-               return 0;
-       }
-
-       if (error) {
-               hid_err(wacom->hdev,
-                       "cannot create sysfs group err: %d\n", error);
-               return error;
-       }
-       wacom_led_control(wacom);
-
-       return 0;
-}
-
-static void wacom_destroy_leds(struct wacom *wacom)
-{
-       switch (wacom->wacom_wac.features.type) {
-       case INTUOS4S:
-       case INTUOS4:
-       case INTUOS4L:
-               sysfs_remove_group(&wacom->hdev->dev.kobj,
-                                  &intuos4_led_attr_group);
-               break;
-
-       case WACOM_24HD:
-       case WACOM_21UX2:
-               sysfs_remove_group(&wacom->hdev->dev.kobj,
-                                  &cintiq_led_attr_group);
-               break;
-
-       case INTUOS5S:
-       case INTUOS5:
-       case INTUOS5L:
-       case INTUOSPS:
-       case INTUOSPM:
-       case INTUOSPL:
-               if (wacom->wacom_wac.features.device_type == BTN_TOOL_PEN)
-                       sysfs_remove_group(&wacom->hdev->dev.kobj,
-                                          &intuos5_led_attr_group);
-               break;
-       }
-}
-
-static enum power_supply_property wacom_battery_props[] = {
-       POWER_SUPPLY_PROP_SCOPE,
-       POWER_SUPPLY_PROP_CAPACITY
-};
-
-static int wacom_battery_get_property(struct power_supply *psy,
-                                     enum power_supply_property psp,
-                                     union power_supply_propval *val)
-{
-       struct wacom *wacom = container_of(psy, struct wacom, battery);
-       int ret = 0;
-
-       switch (psp) {
-               case POWER_SUPPLY_PROP_SCOPE:
-                       val->intval = POWER_SUPPLY_SCOPE_DEVICE;
-                       break;
-               case POWER_SUPPLY_PROP_CAPACITY:
-                       val->intval =
-                               wacom->wacom_wac.battery_capacity * 100 / 31;
-                       break;
-               default:
-                       ret = -EINVAL;
-                       break;
-       }
-
-       return ret;
-}
-
-static int wacom_initialize_battery(struct wacom *wacom)
-{
-       int error = 0;
-
-       if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR) {
-               wacom->battery.properties = wacom_battery_props;
-               wacom->battery.num_properties = ARRAY_SIZE(wacom_battery_props);
-               wacom->battery.get_property = wacom_battery_get_property;
-               wacom->battery.name = "wacom_battery";
-               wacom->battery.type = POWER_SUPPLY_TYPE_BATTERY;
-               wacom->battery.use_for_apm = 0;
-
-               error = power_supply_register(&wacom->hdev->dev,
-                                             &wacom->battery);
-
-               if (!error)
-                       power_supply_powers(&wacom->battery,
-                                           &wacom->hdev->dev);
-       }
-
-       return error;
-}
-
-static void wacom_destroy_battery(struct wacom *wacom)
-{
-       if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR &&
-           wacom->battery.dev) {
-               power_supply_unregister(&wacom->battery);
-               wacom->battery.dev = NULL;
-       }
-}
-
-static struct input_dev *wacom_allocate_input(struct wacom *wacom)
-{
-       struct input_dev *input_dev;
-       struct hid_device *hdev = wacom->hdev;
-       struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
-
-       input_dev = input_allocate_device();
-       if (!input_dev)
-               return NULL;
-
-       input_dev->name = wacom_wac->name;
-       input_dev->phys = hdev->phys;
-       input_dev->dev.parent = &hdev->dev;
-       input_dev->open = wacom_open;
-       input_dev->close = wacom_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, wacom);
-
-       return input_dev;
-}
-
-static void wacom_unregister_inputs(struct wacom *wacom)
-{
-       if (wacom->wacom_wac.input)
-               input_unregister_device(wacom->wacom_wac.input);
-       if (wacom->wacom_wac.pad_input)
-               input_unregister_device(wacom->wacom_wac.pad_input);
-       wacom->wacom_wac.input = NULL;
-       wacom->wacom_wac.pad_input = NULL;
-}
-
-static int wacom_register_inputs(struct wacom *wacom)
-{
-       struct input_dev *input_dev, *pad_input_dev;
-       struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
-       int error;
-
-       input_dev = wacom_allocate_input(wacom);
-       pad_input_dev = wacom_allocate_input(wacom);
-       if (!input_dev || !pad_input_dev) {
-               error = -ENOMEM;
-               goto fail1;
-       }
-
-       wacom_wac->input = input_dev;
-       wacom_wac->pad_input = pad_input_dev;
-       wacom_wac->pad_input->name = wacom_wac->pad_name;
-
-       error = wacom_setup_input_capabilities(input_dev, wacom_wac);
-       if (error)
-               goto fail2;
-
-       error = input_register_device(input_dev);
-       if (error)
-               goto fail2;
-
-       error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac);
-       if (error) {
-               /* no pad in use on this interface */
-               input_free_device(pad_input_dev);
-               wacom_wac->pad_input = NULL;
-               pad_input_dev = NULL;
-       } else {
-               error = input_register_device(pad_input_dev);
-               if (error)
-                       goto fail3;
-       }
-
-       return 0;
-
-fail3:
-       input_unregister_device(input_dev);
-       input_dev = NULL;
-fail2:
-       wacom_wac->input = NULL;
-       wacom_wac->pad_input = NULL;
-fail1:
-       if (input_dev)
-               input_free_device(input_dev);
-       if (pad_input_dev)
-               input_free_device(pad_input_dev);
-       return error;
-}
-
-static void wacom_wireless_work(struct work_struct *work)
-{
-       struct wacom *wacom = container_of(work, struct wacom, work);
-       struct usb_device *usbdev = wacom->usbdev;
-       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
-       struct hid_device *hdev1, *hdev2;
-       struct wacom *wacom1, *wacom2;
-       struct wacom_wac *wacom_wac1, *wacom_wac2;
-       int error;
-
-       /*
-        * Regardless if this is a disconnect or a new tablet,
-        * remove any existing input and battery devices.
-        */
-
-       wacom_destroy_battery(wacom);
-
-       /* Stylus interface */
-       hdev1 = usb_get_intfdata(usbdev->config->interface[1]);
-       wacom1 = hid_get_drvdata(hdev1);
-       wacom_wac1 = &(wacom1->wacom_wac);
-       wacom_unregister_inputs(wacom1);
-
-       /* Touch interface */
-       hdev2 = usb_get_intfdata(usbdev->config->interface[2]);
-       wacom2 = hid_get_drvdata(hdev2);
-       wacom_wac2 = &(wacom2->wacom_wac);
-       wacom_unregister_inputs(wacom2);
-
-       if (wacom_wac->pid == 0) {
-               hid_info(wacom->hdev, "wireless tablet disconnected\n");
-       } else {
-               const struct hid_device_id *id = wacom_ids;
-
-               hid_info(wacom->hdev, "wireless tablet connected with PID %x\n",
-                        wacom_wac->pid);
-
-               while (id->bus) {
-                       if (id->vendor == USB_VENDOR_ID_WACOM &&
-                           id->product == wacom_wac->pid)
-                               break;
-                       id++;
-               }
-
-               if (!id->bus) {
-                       hid_info(wacom->hdev, "ignoring unknown PID.\n");
-                       return;
-               }
-
-               /* Stylus interface */
-               wacom_wac1->features =
-                       *((struct wacom_features *)id->driver_data);
-               wacom_wac1->features.device_type = BTN_TOOL_PEN;
-               snprintf(wacom_wac1->name, WACOM_NAME_MAX, "%s (WL) Pen",
-                        wacom_wac1->features.name);
-               snprintf(wacom_wac1->pad_name, WACOM_NAME_MAX, "%s (WL) Pad",
-                        wacom_wac1->features.name);
-               wacom_wac1->shared->touch_max = wacom_wac1->features.touch_max;
-               wacom_wac1->shared->type = wacom_wac1->features.type;
-               error = wacom_register_inputs(wacom1);
-               if (error)
-                       goto fail;
-
-               /* Touch interface */
-               if (wacom_wac1->features.touch_max ||
-                   wacom_wac1->features.type == INTUOSHT) {
-                       wacom_wac2->features =
-                               *((struct wacom_features *)id->driver_data);
-                       wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
-                       wacom_wac2->features.device_type = BTN_TOOL_FINGER;
-                       wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096;
-                       if (wacom_wac2->features.touch_max)
-                               snprintf(wacom_wac2->name, WACOM_NAME_MAX,
-                                        "%s (WL) Finger",wacom_wac2->features.name);
-                       else
-                               snprintf(wacom_wac2->name, WACOM_NAME_MAX,
-                                        "%s (WL) Pad",wacom_wac2->features.name);
-                       snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX,
-                                "%s (WL) Pad", wacom_wac2->features.name);
-                       error = wacom_register_inputs(wacom2);
-                       if (error)
-                               goto fail;
-
-                       if (wacom_wac1->features.type == INTUOSHT &&
-                           wacom_wac1->features.touch_max)
-                               wacom_wac->shared->touch_input = wacom_wac2->input;
-               }
-
-               error = wacom_initialize_battery(wacom);
-               if (error)
-                       goto fail;
-       }
-
-       return;
-
-fail:
-       wacom_unregister_inputs(wacom1);
-       wacom_unregister_inputs(wacom2);
-       return;
-}
-
-/*
- * Not all devices report physical dimensions from HID.
- * Compute the default from hardcoded logical dimension
- * and resolution before driver overwrites them.
- */
-static void wacom_set_default_phy(struct wacom_features *features)
-{
-       if (features->x_resolution) {
-               features->x_phy = (features->x_max * 100) /
-                                       features->x_resolution;
-               features->y_phy = (features->y_max * 100) /
-                                       features->y_resolution;
-       }
-}
-
-static void wacom_calculate_res(struct wacom_features *features)
-{
-       features->x_resolution = wacom_calc_hid_res(features->x_max,
-                                                   features->x_phy,
-                                                   features->unit,
-                                                   features->unitExpo);
-       features->y_resolution = wacom_calc_hid_res(features->y_max,
-                                                   features->y_phy,
-                                                   features->unit,
-                                                   features->unitExpo);
-}
-
-static int wacom_hid_report_len(struct hid_report *report)
-{
-       /* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */
-       return ((report->size - 1) >> 3) + 1 + (report->id > 0);
-}
-
-static size_t wacom_compute_pktlen(struct hid_device *hdev)
-{
-       struct hid_report_enum *report_enum;
-       struct hid_report *report;
-       size_t size = 0;
-
-       report_enum = hdev->report_enum + HID_INPUT_REPORT;
-
-       list_for_each_entry(report, &report_enum->report_list, list) {
-               size_t report_size = wacom_hid_report_len(report);
-               if (report_size > size)
-                       size = report_size;
-       }
-
-       return size;
-}
-
-static int wacom_probe(struct hid_device *hdev,
-               const struct hid_device_id *id)
-{
-       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
-       struct usb_device *dev = interface_to_usbdev(intf);
-       struct wacom *wacom;
-       struct wacom_wac *wacom_wac;
-       struct wacom_features *features;
-       int error;
-
-       if (!id->driver_data)
-               return -EINVAL;
-
-       wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
-       if (!wacom)
-               return -ENOMEM;
-
-       hid_set_drvdata(hdev, wacom);
-       wacom->hdev = hdev;
-
-       /* ask for the report descriptor to be loaded by HID */
-       error = hid_parse(hdev);
-       if (error) {
-               hid_err(hdev, "parse failed\n");
-               goto fail1;
-       }
-
-       wacom_wac = &wacom->wacom_wac;
-       wacom_wac->features = *((struct wacom_features *)id->driver_data);
-       features = &wacom_wac->features;
-       features->pktlen = wacom_compute_pktlen(hdev);
-       if (features->pktlen > WACOM_PKGLEN_MAX) {
-               error = -EINVAL;
-               goto fail1;
-       }
-
-       if (features->check_for_hid_type && features->hid_type != hdev->type) {
-               error = -ENODEV;
-               goto fail1;
-       }
-
-       wacom->usbdev = dev;
-       wacom->intf = intf;
-       mutex_init(&wacom->lock);
-       INIT_WORK(&wacom->work, wacom_wireless_work);
-
-       /* set the default size in case we do not get them from hid */
-       wacom_set_default_phy(features);
-
-       /* Retrieve the physical and logical size for touch devices */
-       wacom_retrieve_hid_descriptor(hdev, features);
-
-       /*
-        * Intuos5 has no useful data about its touch interface in its
-        * HID descriptor. If this is the touch interface (PacketSize
-        * of WACOM_PKGLEN_BBTOUCH3), override the table values.
-        */
-       if (features->type >= INTUOS5S && features->type <= INTUOSHT) {
-               if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
-                       features->device_type = BTN_TOOL_FINGER;
-
-                       features->x_max = 4096;
-                       features->y_max = 4096;
-               } else {
-                       features->device_type = BTN_TOOL_PEN;
-               }
-       }
-
-       /*
-        * Same thing for Bamboo 3rd gen.
-        */
-       if ((features->type == BAMBOO_PT) &&
-           (features->pktlen == WACOM_PKGLEN_BBTOUCH3) &&
-           (features->device_type == BTN_TOOL_PEN)) {
-               features->device_type = BTN_TOOL_FINGER;
-
-               features->x_max = 4096;
-               features->y_max = 4096;
-       }
-
-       wacom_setup_device_quirks(features);
-
-       /* set unit to "100th of a mm" for devices not reported by HID */
-       if (!features->unit) {
-               features->unit = 0x11;
-               features->unitExpo = -3;
-       }
-       wacom_calculate_res(features);
-
-       strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name));
-       snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name),
-               "%s Pad", features->name);
-
-       if (features->quirks & WACOM_QUIRK_MULTI_INPUT) {
-               /* Append the device type to the name */
-               if (features->device_type != BTN_TOOL_FINGER)
-                       strlcat(wacom_wac->name, " Pen", WACOM_NAME_MAX);
-               else if (features->touch_max)
-                       strlcat(wacom_wac->name, " Finger", WACOM_NAME_MAX);
-               else
-                       strlcat(wacom_wac->name, " Pad", WACOM_NAME_MAX);
-
-               error = wacom_add_shared_data(hdev);
-               if (error)
-                       goto fail1;
-       }
-
-       error = wacom_initialize_leds(wacom);
-       if (error)
-               goto fail2;
-
-       if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
-               error = wacom_register_inputs(wacom);
-               if (error)
-                       goto fail3;
-       }
-
-       /* Note that if query fails it is not a hard failure */
-       wacom_query_tablet_data(hdev, features);
-
-       /* Regular HID work starts now */
-       error = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
-       if (error) {
-               hid_err(hdev, "hw start failed\n");
-               goto fail4;
-       }
-
-       if (features->quirks & WACOM_QUIRK_MONITOR)
-               error = hid_hw_open(hdev);
-
-       if (wacom_wac->features.type == INTUOSHT && wacom_wac->features.touch_max) {
-               if (wacom_wac->features.device_type == BTN_TOOL_FINGER)
-                       wacom_wac->shared->touch_input = wacom_wac->input;
-       }
-
-       return 0;
-
- fail4:        wacom_unregister_inputs(wacom);
- fail3:        wacom_destroy_leds(wacom);
- fail2:        wacom_remove_shared_data(wacom_wac);
- fail1:        kfree(wacom);
-       hid_set_drvdata(hdev, NULL);
-       return error;
-}
-
-static void wacom_remove(struct hid_device *hdev)
-{
-       struct wacom *wacom = hid_get_drvdata(hdev);
-
-       hid_hw_stop(hdev);
-
-       cancel_work_sync(&wacom->work);
-       wacom_unregister_inputs(wacom);
-       wacom_destroy_battery(wacom);
-       wacom_destroy_leds(wacom);
-       wacom_remove_shared_data(&wacom->wacom_wac);
-
-       hid_set_drvdata(hdev, NULL);
-       kfree(wacom);
-}
-
-static int wacom_resume(struct hid_device *hdev)
-{
-       struct wacom *wacom = hid_get_drvdata(hdev);
-       struct wacom_features *features = &wacom->wacom_wac.features;
-
-       mutex_lock(&wacom->lock);
-
-       /* switch to wacom mode first */
-       wacom_query_tablet_data(hdev, features);
-       wacom_led_control(wacom);
-
-       mutex_unlock(&wacom->lock);
-
-       return 0;
-}
-
-static int wacom_reset_resume(struct hid_device *hdev)
-{
-       return wacom_resume(hdev);
-}
-
-static struct hid_driver wacom_driver = {
-       .name =         "wacom",
-       .id_table =     wacom_ids,
-       .probe =        wacom_probe,
-       .remove =       wacom_remove,
-#ifdef CONFIG_PM
-       .resume =       wacom_resume,
-       .reset_resume = wacom_reset_resume,
-#endif
-       .raw_event =    wacom_raw_event,
-};
-module_hid_driver(wacom_driver);
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
deleted file mode 100644 (file)
index cd91521..0000000
+++ /dev/null
@@ -1,2548 +0,0 @@
-/*
- * drivers/input/tablet/wacom_wac.c
- *
- *  USB Wacom tablet support - Wacom specific code
- *
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include "wacom_wac.h"
-#include "wacom.h"
-#include <linux/input/mt.h>
-#include <linux/hid.h>
-
-/* resolution for penabled devices */
-#define WACOM_PL_RES           20
-#define WACOM_PENPRTN_RES      40
-#define WACOM_VOLITO_RES       50
-#define WACOM_GRAPHIRE_RES     80
-#define WACOM_INTUOS_RES       100
-#define WACOM_INTUOS3_RES      200
-
-/* Scale factor relating reported contact size to logical contact area.
- * 2^14/pi is a good approximation on Intuos5 and 3rd-gen Bamboo
- */
-#define WACOM_CONTACT_AREA_SCALE 2607
-
-static int wacom_penpartner_irq(struct wacom_wac *wacom)
-{
-       unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
-
-       switch (data[0]) {
-       case 1:
-               if (data[5] & 0x80) {
-                       wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
-                       wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID;
-                       input_report_key(input, wacom->tool[0], 1);
-                       input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
-                       input_report_abs(input, ABS_X, get_unaligned_le16(&data[1]));
-                       input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3]));
-                       input_report_abs(input, ABS_PRESSURE, (signed char)data[6] + 127);
-                       input_report_key(input, BTN_TOUCH, ((signed char)data[6] > -127));
-                       input_report_key(input, BTN_STYLUS, (data[5] & 0x40));
-               } else {
-                       input_report_key(input, wacom->tool[0], 0);
-                       input_report_abs(input, ABS_MISC, 0); /* report tool id */
-                       input_report_abs(input, ABS_PRESSURE, -1);
-                       input_report_key(input, BTN_TOUCH, 0);
-               }
-               break;
-
-       case 2:
-               input_report_key(input, BTN_TOOL_PEN, 1);
-               input_report_abs(input, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */
-               input_report_abs(input, ABS_X, get_unaligned_le16(&data[1]));
-               input_report_abs(input, ABS_Y, get_unaligned_le16(&data[3]));
-               input_report_abs(input, ABS_PRESSURE, (signed char)data[6] + 127);
-               input_report_key(input, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20));
-               input_report_key(input, BTN_STYLUS, (data[5] & 0x40));
-               break;
-
-       default:
-               dev_dbg(input->dev.parent,
-                       "%s: received unknown report #%d\n", __func__, data[0]);
-               return 0;
-        }
-
-       return 1;
-}
-
-static int wacom_pl_irq(struct wacom_wac *wacom)
-{
-       struct wacom_features *features = &wacom->features;
-       unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
-       int prox, pressure;
-
-       if (data[0] != WACOM_REPORT_PENABLED) {
-               dev_dbg(input->dev.parent,
-                       "%s: received unknown report #%d\n", __func__, data[0]);
-               return 0;
-       }
-
-       prox = data[1] & 0x40;
-
-       if (prox) {
-               wacom->id[0] = ERASER_DEVICE_ID;
-               pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
-               if (features->pressure_max > 255)
-                       pressure = (pressure << 1) | ((data[4] >> 6) & 1);
-               pressure += (features->pressure_max + 1) / 2;
-
-               /*
-                * if going from out of proximity into proximity select between the eraser
-                * and the pen based on the state of the stylus2 button, choose eraser if
-                * pressed else choose pen. if not a proximity change from out to in, send
-                * an out of proximity for previous tool then a in for new tool.
-                */
-               if (!wacom->tool[0]) {
-                       /* Eraser bit set for DTF */
-                       if (data[1] & 0x10)
-                               wacom->tool[1] = BTN_TOOL_RUBBER;
-                       else
-                               /* Going into proximity select tool */
-                               wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
-               } else {
-                       /* was entered with stylus2 pressed */
-                       if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) {
-                               /* report out proximity for previous tool */
-                               input_report_key(input, wacom->tool[1], 0);
-                               input_sync(input);
-                               wacom->tool[1] = BTN_TOOL_PEN;
-                               return 0;
-                       }
-               }
-               if (wacom->tool[1] != BTN_TOOL_RUBBER) {
-                       /* Unknown tool selected default to pen tool */
-                       wacom->tool[1] = BTN_TOOL_PEN;
-                       wacom->id[0] = STYLUS_DEVICE_ID;
-               }
-               input_report_key(input, wacom->tool[1], prox); /* report in proximity for tool */
-               input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
-               input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
-               input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
-               input_report_abs(input, ABS_PRESSURE, pressure);
-
-               input_report_key(input, BTN_TOUCH, data[4] & 0x08);
-               input_report_key(input, BTN_STYLUS, data[4] & 0x10);
-               /* Only allow the stylus2 button to be reported for the pen tool. */
-               input_report_key(input, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
-       } else {
-               /* report proximity-out of a (valid) tool */
-               if (wacom->tool[1] != BTN_TOOL_RUBBER) {
-                       /* Unknown tool selected default to pen tool */
-                       wacom->tool[1] = BTN_TOOL_PEN;
-               }
-               input_report_key(input, wacom->tool[1], prox);
-       }
-
-       wacom->tool[0] = prox; /* Save proximity state */
-       return 1;
-}
-
-static int wacom_ptu_irq(struct wacom_wac *wacom)
-{
-       unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
-
-       if (data[0] != WACOM_REPORT_PENABLED) {
-               dev_dbg(input->dev.parent,
-                       "%s: received unknown report #%d\n", __func__, data[0]);
-               return 0;
-       }
-
-       if (data[1] & 0x04) {
-               input_report_key(input, BTN_TOOL_RUBBER, data[1] & 0x20);
-               input_report_key(input, BTN_TOUCH, data[1] & 0x08);
-               wacom->id[0] = ERASER_DEVICE_ID;
-       } else {
-               input_report_key(input, BTN_TOOL_PEN, data[1] & 0x20);
-               input_report_key(input, BTN_TOUCH, data[1] & 0x01);
-               wacom->id[0] = STYLUS_DEVICE_ID;
-       }
-       input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
-       input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
-       input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
-       input_report_abs(input, ABS_PRESSURE, le16_to_cpup((__le16 *)&data[6]));
-       input_report_key(input, BTN_STYLUS, data[1] & 0x02);
-       input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
-       return 1;
-}
-
-static int wacom_dtu_irq(struct wacom_wac *wacom)
-{
-       unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
-       int prox = data[1] & 0x20;
-
-       dev_dbg(input->dev.parent,
-               "%s: received report #%d", __func__, data[0]);
-
-       if (prox) {
-               /* Going into proximity select tool */
-               wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
-               if (wacom->tool[0] == BTN_TOOL_PEN)
-                       wacom->id[0] = STYLUS_DEVICE_ID;
-               else
-                       wacom->id[0] = ERASER_DEVICE_ID;
-       }
-       input_report_key(input, BTN_STYLUS, data[1] & 0x02);
-       input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
-       input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
-       input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
-       input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x01) << 8) | data[6]);
-       input_report_key(input, BTN_TOUCH, data[1] & 0x05);
-       if (!prox) /* out-prox */
-               wacom->id[0] = 0;
-       input_report_key(input, wacom->tool[0], prox);
-       input_report_abs(input, ABS_MISC, wacom->id[0]);
-       return 1;
-}
-
-static int wacom_dtus_irq(struct wacom_wac *wacom)
-{
-       char *data = wacom->data;
-       struct input_dev *input = wacom->input;
-       unsigned short prox, pressure = 0;
-
-       if (data[0] != WACOM_REPORT_DTUS && data[0] != WACOM_REPORT_DTUSPAD) {
-               dev_dbg(input->dev.parent,
-                       "%s: received unknown report #%d", __func__, data[0]);
-               return 0;
-       } else if (data[0] == WACOM_REPORT_DTUSPAD) {
-               input = wacom->pad_input;
-               input_report_key(input, BTN_0, (data[1] & 0x01));
-               input_report_key(input, BTN_1, (data[1] & 0x02));
-               input_report_key(input, BTN_2, (data[1] & 0x04));
-               input_report_key(input, BTN_3, (data[1] & 0x08));
-               input_report_abs(input, ABS_MISC,
-                                data[1] & 0x0f ? PAD_DEVICE_ID : 0);
-               return 1;
-       } else {
-               prox = data[1] & 0x80;
-               if (prox) {
-                       switch ((data[1] >> 3) & 3) {
-                       case 1: /* Rubber */
-                               wacom->tool[0] = BTN_TOOL_RUBBER;
-                               wacom->id[0] = ERASER_DEVICE_ID;
-                               break;
-
-                       case 2: /* Pen */
-                               wacom->tool[0] = BTN_TOOL_PEN;
-                               wacom->id[0] = STYLUS_DEVICE_ID;
-                               break;
-                       }
-               }
-
-               input_report_key(input, BTN_STYLUS, data[1] & 0x20);
-               input_report_key(input, BTN_STYLUS2, data[1] & 0x40);
-               input_report_abs(input, ABS_X, get_unaligned_be16(&data[3]));
-               input_report_abs(input, ABS_Y, get_unaligned_be16(&data[5]));
-               pressure = ((data[1] & 0x03) << 8) | (data[2] & 0xff);
-               input_report_abs(input, ABS_PRESSURE, pressure);
-               input_report_key(input, BTN_TOUCH, pressure > 10);
-
-               if (!prox) /* out-prox */
-                       wacom->id[0] = 0;
-               input_report_key(input, wacom->tool[0], prox);
-               input_report_abs(input, ABS_MISC, wacom->id[0]);
-               return 1;
-       }
-}
-
-static int wacom_graphire_irq(struct wacom_wac *wacom)
-{
-       struct wacom_features *features = &wacom->features;
-       unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
-       struct input_dev *pad_input = wacom->pad_input;
-       int prox;
-       int rw = 0;
-       int retval = 0;
-
-       if (data[0] != WACOM_REPORT_PENABLED) {
-               dev_dbg(input->dev.parent,
-                       "%s: received unknown report #%d\n", __func__, data[0]);
-               goto exit;
-       }
-
-       prox = data[1] & 0x80;
-       if (prox || wacom->id[0]) {
-               if (prox) {
-                       switch ((data[1] >> 5) & 3) {
-
-                       case 0: /* Pen */
-                               wacom->tool[0] = BTN_TOOL_PEN;
-                               wacom->id[0] = STYLUS_DEVICE_ID;
-                               break;
-
-                       case 1: /* Rubber */
-                               wacom->tool[0] = BTN_TOOL_RUBBER;
-                               wacom->id[0] = ERASER_DEVICE_ID;
-                               break;
-
-                       case 2: /* Mouse with wheel */
-                               input_report_key(input, BTN_MIDDLE, data[1] & 0x04);
-                               /* fall through */
-
-                       case 3: /* Mouse without wheel */
-                               wacom->tool[0] = BTN_TOOL_MOUSE;
-                               wacom->id[0] = CURSOR_DEVICE_ID;
-                               break;
-                       }
-               }
-               input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
-               input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
-               if (wacom->tool[0] != BTN_TOOL_MOUSE) {
-                       input_report_abs(input, ABS_PRESSURE, data[6] | ((data[7] & 0x03) << 8));
-                       input_report_key(input, BTN_TOUCH, data[1] & 0x01);
-                       input_report_key(input, BTN_STYLUS, data[1] & 0x02);
-                       input_report_key(input, BTN_STYLUS2, data[1] & 0x04);
-               } else {
-                       input_report_key(input, BTN_LEFT, data[1] & 0x01);
-                       input_report_key(input, BTN_RIGHT, data[1] & 0x02);
-                       if (features->type == WACOM_G4 ||
-                                       features->type == WACOM_MO) {
-                               input_report_abs(input, ABS_DISTANCE, data[6] & 0x3f);
-                               rw = (data[7] & 0x04) - (data[7] & 0x03);
-                       } else {
-                               input_report_abs(input, ABS_DISTANCE, data[7] & 0x3f);
-                               rw = -(signed char)data[6];
-                       }
-                       input_report_rel(input, REL_WHEEL, rw);
-               }
-
-               if (!prox)
-                       wacom->id[0] = 0;
-               input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
-               input_report_key(input, wacom->tool[0], prox);
-               input_sync(input); /* sync last event */
-       }
-
-       /* send pad data */
-       switch (features->type) {
-       case WACOM_G4:
-               prox = data[7] & 0xf8;
-               if (prox || wacom->id[1]) {
-                       wacom->id[1] = PAD_DEVICE_ID;
-                       input_report_key(pad_input, BTN_BACK, (data[7] & 0x40));
-                       input_report_key(pad_input, BTN_FORWARD, (data[7] & 0x80));
-                       rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
-                       input_report_rel(pad_input, REL_WHEEL, rw);
-                       if (!prox)
-                               wacom->id[1] = 0;
-                       input_report_abs(pad_input, ABS_MISC, wacom->id[1]);
-                       retval = 1;
-               }
-               break;
-
-       case WACOM_MO:
-               prox = (data[7] & 0xf8) || data[8];
-               if (prox || wacom->id[1]) {
-                       wacom->id[1] = PAD_DEVICE_ID;
-                       input_report_key(pad_input, BTN_BACK, (data[7] & 0x08));
-                       input_report_key(pad_input, BTN_LEFT, (data[7] & 0x20));
-                       input_report_key(pad_input, BTN_FORWARD, (data[7] & 0x10));
-                       input_report_key(pad_input, BTN_RIGHT, (data[7] & 0x40));
-                       input_report_abs(pad_input, ABS_WHEEL, (data[8] & 0x7f));
-                       if (!prox)
-                               wacom->id[1] = 0;
-                       input_report_abs(pad_input, ABS_MISC, wacom->id[1]);
-                       retval = 1;
-               }
-               break;
-       }
-exit:
-       return retval;
-}
-
-static int wacom_intuos_inout(struct wacom_wac *wacom)
-{
-       struct wacom_features *features = &wacom->features;
-       unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
-       int idx = 0;
-
-       /* tool number */
-       if (features->type == INTUOS)
-               idx = data[1] & 0x01;
-
-       /* Enter report */
-       if ((data[1] & 0xfc) == 0xc0) {
-               if (features->quirks & WACOM_QUIRK_MULTI_INPUT)
-                       wacom->shared->stylus_in_proximity = true;
-
-               /* serial number of the tool */
-               wacom->serial[idx] = ((data[3] & 0x0f) << 28) +
-                       (data[4] << 20) + (data[5] << 12) +
-                       (data[6] << 4) + (data[7] >> 4);
-
-               wacom->id[idx] = (data[2] << 4) | (data[3] >> 4) |
-                       ((data[7] & 0x0f) << 20) | ((data[8] & 0xf0) << 12);
-
-               switch (wacom->id[idx]) {
-               case 0x812: /* Inking pen */
-               case 0x801: /* Intuos3 Inking pen */
-               case 0x120802: /* Intuos4/5 Inking Pen */
-               case 0x012:
-                       wacom->tool[idx] = BTN_TOOL_PENCIL;
-                       break;
-
-               case 0x822: /* Pen */
-               case 0x842:
-               case 0x852:
-               case 0x823: /* Intuos3 Grip Pen */
-               case 0x813: /* Intuos3 Classic Pen */
-               case 0x885: /* Intuos3 Marker Pen */
-               case 0x802: /* Intuos4/5 13HD/24HD General Pen */
-               case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */
-               case 0x022:
-               case 0x100804: /* Intuos4/5 13HD/24HD Art Pen */
-               case 0x140802: /* Intuos4/5 13HD/24HD Classic Pen */
-               case 0x160802: /* Cintiq 13HD Pro Pen */
-               case 0x180802: /* DTH2242 Pen */
-               case 0x100802: /* Intuos4/5 13HD/24HD General Pen */
-                       wacom->tool[idx] = BTN_TOOL_PEN;
-                       break;
-
-               case 0x832: /* Stroke pen */
-               case 0x032:
-                       wacom->tool[idx] = BTN_TOOL_BRUSH;
-                       break;
-
-               case 0x007: /* Mouse 4D and 2D */
-               case 0x09c:
-               case 0x094:
-               case 0x017: /* Intuos3 2D Mouse */
-               case 0x806: /* Intuos4 Mouse */
-                       wacom->tool[idx] = BTN_TOOL_MOUSE;
-                       break;
-
-               case 0x096: /* Lens cursor */
-               case 0x097: /* Intuos3 Lens cursor */
-               case 0x006: /* Intuos4 Lens cursor */
-                       wacom->tool[idx] = BTN_TOOL_LENS;
-                       break;
-
-               case 0x82a: /* Eraser */
-               case 0x85a:
-               case 0x91a:
-               case 0xd1a:
-               case 0x0fa:
-               case 0x82b: /* Intuos3 Grip Pen Eraser */
-               case 0x81b: /* Intuos3 Classic Pen Eraser */
-               case 0x91b: /* Intuos3 Airbrush Eraser */
-               case 0x80c: /* Intuos4/5 13HD/24HD Marker Pen Eraser */
-               case 0x80a: /* Intuos4/5 13HD/24HD General Pen Eraser */
-               case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
-               case 0x14080a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */
-               case 0x10090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
-               case 0x10080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */
-               case 0x16080a: /* Cintiq 13HD Pro Pen Eraser */
-               case 0x18080a: /* DTH2242 Eraser */
-               case 0x10080a: /* Intuos4/5 13HD/24HD General Pen Eraser */
-                       wacom->tool[idx] = BTN_TOOL_RUBBER;
-                       break;
-
-               case 0xd12:
-               case 0x912:
-               case 0x112:
-               case 0x913: /* Intuos3 Airbrush */
-               case 0x902: /* Intuos4/5 13HD/24HD Airbrush */
-               case 0x100902: /* Intuos4/5 13HD/24HD Airbrush */
-                       wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
-                       break;
-
-               default: /* Unknown tool */
-                       wacom->tool[idx] = BTN_TOOL_PEN;
-                       break;
-               }
-               return 1;
-       }
-
-       /* older I4 styli don't work with new Cintiqs */
-       if (!((wacom->id[idx] >> 20) & 0x01) &&
-                       (features->type == WACOM_21UX2))
-               return 1;
-
-       /* Range Report */
-       if ((data[1] & 0xfe) == 0x20) {
-               input_report_key(input, BTN_TOUCH, 0);
-               input_report_abs(input, ABS_PRESSURE, 0);
-               input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max);
-               if (features->quirks & WACOM_QUIRK_MULTI_INPUT)
-                       wacom->shared->stylus_in_proximity = true;
-       }
-
-       /* Exit report */
-       if ((data[1] & 0xfe) == 0x80) {
-               if (features->quirks & WACOM_QUIRK_MULTI_INPUT)
-                       wacom->shared->stylus_in_proximity = false;
-
-               /*
-                * Reset all states otherwise we lose the initial states
-                * when in-prox next time
-                */
-               input_report_abs(input, ABS_X, 0);
-               input_report_abs(input, ABS_Y, 0);
-               input_report_abs(input, ABS_DISTANCE, 0);
-               input_report_abs(input, ABS_TILT_X, 0);
-               input_report_abs(input, ABS_TILT_Y, 0);
-               if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
-                       input_report_key(input, BTN_LEFT, 0);
-                       input_report_key(input, BTN_MIDDLE, 0);
-                       input_report_key(input, BTN_RIGHT, 0);
-                       input_report_key(input, BTN_SIDE, 0);
-                       input_report_key(input, BTN_EXTRA, 0);
-                       input_report_abs(input, ABS_THROTTLE, 0);
-                       input_report_abs(input, ABS_RZ, 0);
-               } else {
-                       input_report_abs(input, ABS_PRESSURE, 0);
-                       input_report_key(input, BTN_STYLUS, 0);
-                       input_report_key(input, BTN_STYLUS2, 0);
-                       input_report_key(input, BTN_TOUCH, 0);
-                       input_report_abs(input, ABS_WHEEL, 0);
-                       if (features->type >= INTUOS3S)
-                               input_report_abs(input, ABS_Z, 0);
-               }
-               input_report_key(input, wacom->tool[idx], 0);
-               input_report_abs(input, ABS_MISC, 0); /* reset tool id */
-               input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
-               wacom->id[idx] = 0;
-               return 2;
-       }
-       return 0;
-}
-
-static void wacom_intuos_general(struct wacom_wac *wacom)
-{
-       struct wacom_features *features = &wacom->features;
-       unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
-       unsigned int t;
-
-       /* general pen packet */
-       if ((data[1] & 0xb8) == 0xa0) {
-               t = (data[6] << 2) | ((data[7] >> 6) & 3);
-               if (features->type >= INTUOS4S && features->type <= CINTIQ_HYBRID) {
-                       t = (t << 1) | (data[1] & 1);
-               }
-               input_report_abs(input, ABS_PRESSURE, t);
-               input_report_abs(input, ABS_TILT_X,
-                               ((data[7] << 1) & 0x7e) | (data[8] >> 7));
-               input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f);
-               input_report_key(input, BTN_STYLUS, data[1] & 2);
-               input_report_key(input, BTN_STYLUS2, data[1] & 4);
-               input_report_key(input, BTN_TOUCH, t > 10);
-       }
-
-       /* airbrush second packet */
-       if ((data[1] & 0xbc) == 0xb4) {
-               input_report_abs(input, ABS_WHEEL,
-                               (data[6] << 2) | ((data[7] >> 6) & 3));
-               input_report_abs(input, ABS_TILT_X,
-                               ((data[7] << 1) & 0x7e) | (data[8] >> 7));
-               input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f);
-       }
-}
-
-static int wacom_intuos_irq(struct wacom_wac *wacom)
-{
-       struct wacom_features *features = &wacom->features;
-       unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
-       unsigned int t;
-       int idx = 0, result;
-
-       if (data[0] != WACOM_REPORT_PENABLED &&
-           data[0] != WACOM_REPORT_INTUOSREAD &&
-           data[0] != WACOM_REPORT_INTUOSWRITE &&
-           data[0] != WACOM_REPORT_INTUOSPAD &&
-           data[0] != WACOM_REPORT_INTUOS5PAD) {
-               dev_dbg(input->dev.parent,
-                       "%s: received unknown report #%d\n", __func__, data[0]);
-                return 0;
-       }
-
-       /* tool number */
-       if (features->type == INTUOS)
-               idx = data[1] & 0x01;
-
-       /* pad packets. Works as a second tool and is always in prox */
-       if (data[0] == WACOM_REPORT_INTUOSPAD || data[0] == WACOM_REPORT_INTUOS5PAD) {
-               input = wacom->pad_input;
-               if (features->type >= INTUOS4S && features->type <= INTUOS4L) {
-                       input_report_key(input, BTN_0, (data[2] & 0x01));
-                       input_report_key(input, BTN_1, (data[3] & 0x01));
-                       input_report_key(input, BTN_2, (data[3] & 0x02));
-                       input_report_key(input, BTN_3, (data[3] & 0x04));
-                       input_report_key(input, BTN_4, (data[3] & 0x08));
-                       input_report_key(input, BTN_5, (data[3] & 0x10));
-                       input_report_key(input, BTN_6, (data[3] & 0x20));
-                       if (data[1] & 0x80) {
-                               input_report_abs(input, ABS_WHEEL, (data[1] & 0x7f));
-                       } else {
-                               /* Out of proximity, clear wheel value. */
-                               input_report_abs(input, ABS_WHEEL, 0);
-                       }
-                       if (features->type != INTUOS4S) {
-                               input_report_key(input, BTN_7, (data[3] & 0x40));
-                               input_report_key(input, BTN_8, (data[3] & 0x80));
-                       }
-                       if (data[1] | (data[2] & 0x01) | data[3]) {
-                               input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
-                       } else {
-                               input_report_abs(input, ABS_MISC, 0);
-                       }
-               } else if (features->type == DTK) {
-                       input_report_key(input, BTN_0, (data[6] & 0x01));
-                       input_report_key(input, BTN_1, (data[6] & 0x02));
-                       input_report_key(input, BTN_2, (data[6] & 0x04));
-                       input_report_key(input, BTN_3, (data[6] & 0x08));
-                       input_report_key(input, BTN_4, (data[6] & 0x10));
-                       input_report_key(input, BTN_5, (data[6] & 0x20));
-                       if (data[6] & 0x3f) {
-                               input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
-                       } else {
-                               input_report_abs(input, ABS_MISC, 0);
-                       }
-               } else if (features->type == WACOM_13HD) {
-                       input_report_key(input, BTN_0, (data[3] & 0x01));
-                       input_report_key(input, BTN_1, (data[4] & 0x01));
-                       input_report_key(input, BTN_2, (data[4] & 0x02));
-                       input_report_key(input, BTN_3, (data[4] & 0x04));
-                       input_report_key(input, BTN_4, (data[4] & 0x08));
-                       input_report_key(input, BTN_5, (data[4] & 0x10));
-                       input_report_key(input, BTN_6, (data[4] & 0x20));
-                       input_report_key(input, BTN_7, (data[4] & 0x40));
-                       input_report_key(input, BTN_8, (data[4] & 0x80));
-                       if ((data[3] & 0x01) | data[4]) {
-                               input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
-                       } else {
-                               input_report_abs(input, ABS_MISC, 0);
-                       }
-               } else if (features->type == WACOM_24HD) {
-                       input_report_key(input, BTN_0, (data[6] & 0x01));
-                       input_report_key(input, BTN_1, (data[6] & 0x02));
-                       input_report_key(input, BTN_2, (data[6] & 0x04));
-                       input_report_key(input, BTN_3, (data[6] & 0x08));
-                       input_report_key(input, BTN_4, (data[6] & 0x10));
-                       input_report_key(input, BTN_5, (data[6] & 0x20));
-                       input_report_key(input, BTN_6, (data[6] & 0x40));
-                       input_report_key(input, BTN_7, (data[6] & 0x80));
-                       input_report_key(input, BTN_8, (data[8] & 0x01));
-                       input_report_key(input, BTN_9, (data[8] & 0x02));
-                       input_report_key(input, BTN_A, (data[8] & 0x04));
-                       input_report_key(input, BTN_B, (data[8] & 0x08));
-                       input_report_key(input, BTN_C, (data[8] & 0x10));
-                       input_report_key(input, BTN_X, (data[8] & 0x20));
-                       input_report_key(input, BTN_Y, (data[8] & 0x40));
-                       input_report_key(input, BTN_Z, (data[8] & 0x80));
-
-                       /*
-                        * Three "buttons" are available on the 24HD which are
-                        * physically implemented as a touchstrip. Each button
-                        * is approximately 3 bits wide with a 2 bit spacing.
-                        * The raw touchstrip bits are stored at:
-                        *    ((data[3] & 0x1f) << 8) | data[4])
-                        */
-                       input_report_key(input, KEY_PROG1, data[4] & 0x07);
-                       input_report_key(input, KEY_PROG2, data[4] & 0xE0);
-                       input_report_key(input, KEY_PROG3, data[3] & 0x1C);
-
-                       if (data[1] & 0x80) {
-                               input_report_abs(input, ABS_WHEEL, (data[1] & 0x7f));
-                       } else {
-                               /* Out of proximity, clear wheel value. */
-                               input_report_abs(input, ABS_WHEEL, 0);
-                       }
-
-                       if (data[2] & 0x80) {
-                               input_report_abs(input, ABS_THROTTLE, (data[2] & 0x7f));
-                       } else {
-                               /* Out of proximity, clear second wheel value. */
-                               input_report_abs(input, ABS_THROTTLE, 0);
-                       }
-
-                       if (data[1] | data[2] | (data[3] & 0x1f) | data[4] | data[6] | data[8]) {
-                               input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
-                       } else {
-                               input_report_abs(input, ABS_MISC, 0);
-                       }
-               } else if (features->type == CINTIQ_HYBRID) {
-                       /*
-                        * Do not send hardware buttons under Android. They
-                        * are already sent to the system through GPIO (and
-                        * have different meaning).
-                        */
-                       input_report_key(input, BTN_1, (data[4] & 0x01));
-                       input_report_key(input, BTN_2, (data[4] & 0x02));
-                       input_report_key(input, BTN_3, (data[4] & 0x04));
-                       input_report_key(input, BTN_4, (data[4] & 0x08));
-
-                       input_report_key(input, BTN_5, (data[4] & 0x10));  /* Right  */
-                       input_report_key(input, BTN_6, (data[4] & 0x20));  /* Up     */
-                       input_report_key(input, BTN_7, (data[4] & 0x40));  /* Left   */
-                       input_report_key(input, BTN_8, (data[4] & 0x80));  /* Down   */
-                       input_report_key(input, BTN_0, (data[3] & 0x01));  /* Center */
-               } else if (features->type >= INTUOS5S && features->type <= INTUOSPL) {
-                       int i;
-
-                       /* Touch ring mode switch has no capacitive sensor */
-                       input_report_key(input, BTN_0, (data[3] & 0x01));
-
-                       /*
-                        * ExpressKeys on Intuos5/Intuos Pro have a capacitive sensor in
-                        * addition to the mechanical switch. Switch data is
-                        * stored in data[4], capacitive data in data[5].
-                        */
-                       for (i = 0; i < 8; i++)
-                               input_report_key(input, BTN_1 + i, data[4] & (1 << i));
-
-                       if (data[2] & 0x80) {
-                               input_report_abs(input, ABS_WHEEL, (data[2] & 0x7f));
-                       } else {
-                               /* Out of proximity, clear wheel value. */
-                               input_report_abs(input, ABS_WHEEL, 0);
-                       }
-
-                       if (data[2] | (data[3] & 0x01) | data[4] | data[5]) {
-                               input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
-                       } else {
-                               input_report_abs(input, ABS_MISC, 0);
-                       }
-               } else {
-                       if (features->type == WACOM_21UX2 || features->type == WACOM_22HD) {
-                               input_report_key(input, BTN_0, (data[5] & 0x01));
-                               input_report_key(input, BTN_1, (data[6] & 0x01));
-                               input_report_key(input, BTN_2, (data[6] & 0x02));
-                               input_report_key(input, BTN_3, (data[6] & 0x04));
-                               input_report_key(input, BTN_4, (data[6] & 0x08));
-                               input_report_key(input, BTN_5, (data[6] & 0x10));
-                               input_report_key(input, BTN_6, (data[6] & 0x20));
-                               input_report_key(input, BTN_7, (data[6] & 0x40));
-                               input_report_key(input, BTN_8, (data[6] & 0x80));
-                               input_report_key(input, BTN_9, (data[7] & 0x01));
-                               input_report_key(input, BTN_A, (data[8] & 0x01));
-                               input_report_key(input, BTN_B, (data[8] & 0x02));
-                               input_report_key(input, BTN_C, (data[8] & 0x04));
-                               input_report_key(input, BTN_X, (data[8] & 0x08));
-                               input_report_key(input, BTN_Y, (data[8] & 0x10));
-                               input_report_key(input, BTN_Z, (data[8] & 0x20));
-                               input_report_key(input, BTN_BASE, (data[8] & 0x40));
-                               input_report_key(input, BTN_BASE2, (data[8] & 0x80));
-
-                               if (features->type == WACOM_22HD) {
-                                       input_report_key(input, KEY_PROG1, data[9] & 0x01);
-                                       input_report_key(input, KEY_PROG2, data[9] & 0x02);
-                                       input_report_key(input, KEY_PROG3, data[9] & 0x04);
-                               }
-                       } else {
-                               input_report_key(input, BTN_0, (data[5] & 0x01));
-                               input_report_key(input, BTN_1, (data[5] & 0x02));
-                               input_report_key(input, BTN_2, (data[5] & 0x04));
-                               input_report_key(input, BTN_3, (data[5] & 0x08));
-                               input_report_key(input, BTN_4, (data[6] & 0x01));
-                               input_report_key(input, BTN_5, (data[6] & 0x02));
-                               input_report_key(input, BTN_6, (data[6] & 0x04));
-                               input_report_key(input, BTN_7, (data[6] & 0x08));
-                               input_report_key(input, BTN_8, (data[5] & 0x10));
-                               input_report_key(input, BTN_9, (data[6] & 0x10));
-                       }
-                       input_report_abs(input, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
-                       input_report_abs(input, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
-
-                       if ((data[5] & 0x1f) | data[6] | (data[1] & 0x1f) |
-                               data[2] | (data[3] & 0x1f) | data[4] | data[8] |
-                               (data[7] & 0x01)) {
-                               input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
-                       } else {
-                               input_report_abs(input, ABS_MISC, 0);
-                       }
-               }
-                return 1;
-       }
-
-       /* process in/out prox events */
-       result = wacom_intuos_inout(wacom);
-       if (result)
-                return result - 1;
-
-       /* don't proceed if we don't know the ID */
-       if (!wacom->id[idx])
-               return 0;
-
-       /* Only large Intuos support Lense Cursor */
-       if (wacom->tool[idx] == BTN_TOOL_LENS &&
-           (features->type == INTUOS3 ||
-            features->type == INTUOS3S ||
-            features->type == INTUOS4 ||
-            features->type == INTUOS4S ||
-            features->type == INTUOS5 ||
-            features->type == INTUOS5S ||
-            features->type == INTUOSPM ||
-            features->type == INTUOSPS)) {
-
-               return 0;
-       }
-
-       /* Cintiq doesn't send data when RDY bit isn't set */
-       if (features->type == CINTIQ && !(data[1] & 0x40))
-                 return 0;
-
-       if (features->type >= INTUOS3S) {
-               input_report_abs(input, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1));
-               input_report_abs(input, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1));
-               input_report_abs(input, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
-       } else {
-               input_report_abs(input, ABS_X, be16_to_cpup((__be16 *)&data[2]));
-               input_report_abs(input, ABS_Y, be16_to_cpup((__be16 *)&data[4]));
-               input_report_abs(input, ABS_DISTANCE, ((data[9] >> 3) & 0x1f));
-       }
-
-       /* process general packets */
-       wacom_intuos_general(wacom);
-
-       /* 4D mouse, 2D mouse, marker pen rotation, tilt mouse, or Lens cursor packets */
-       if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0 || (data[1] & 0xbc) == 0xac) {
-
-               if (data[1] & 0x02) {
-                       /* Rotation packet */
-                       if (features->type >= INTUOS3S) {
-                               /* I3 marker pen rotation */
-                               t = (data[6] << 3) | ((data[7] >> 5) & 7);
-                               t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
-                                       ((t-1) / 2 + 450)) : (450 - t / 2) ;
-                               input_report_abs(input, ABS_Z, t);
-                       } else {
-                               /* 4D mouse rotation packet */
-                               t = (data[6] << 3) | ((data[7] >> 5) & 7);
-                               input_report_abs(input, ABS_RZ, (data[7] & 0x20) ?
-                                       ((t - 1) / 2) : -t / 2);
-                       }
-
-               } else if (!(data[1] & 0x10) && features->type < INTUOS3S) {
-                       /* 4D mouse packet */
-                       input_report_key(input, BTN_LEFT,   data[8] & 0x01);
-                       input_report_key(input, BTN_MIDDLE, data[8] & 0x02);
-                       input_report_key(input, BTN_RIGHT,  data[8] & 0x04);
-
-                       input_report_key(input, BTN_SIDE,   data[8] & 0x20);
-                       input_report_key(input, BTN_EXTRA,  data[8] & 0x10);
-                       t = (data[6] << 2) | ((data[7] >> 6) & 3);
-                       input_report_abs(input, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
-
-               } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
-                       /* I4 mouse */
-                       if (features->type >= INTUOS4S && features->type <= INTUOSPL) {
-                               input_report_key(input, BTN_LEFT,   data[6] & 0x01);
-                               input_report_key(input, BTN_MIDDLE, data[6] & 0x02);
-                               input_report_key(input, BTN_RIGHT,  data[6] & 0x04);
-                               input_report_rel(input, REL_WHEEL, ((data[7] & 0x80) >> 7)
-                                                - ((data[7] & 0x40) >> 6));
-                               input_report_key(input, BTN_SIDE,   data[6] & 0x08);
-                               input_report_key(input, BTN_EXTRA,  data[6] & 0x10);
-
-                               input_report_abs(input, ABS_TILT_X,
-                                       ((data[7] << 1) & 0x7e) | (data[8] >> 7));
-                               input_report_abs(input, ABS_TILT_Y, data[8] & 0x7f);
-                       } else {
-                               /* 2D mouse packet */
-                               input_report_key(input, BTN_LEFT,   data[8] & 0x04);
-                               input_report_key(input, BTN_MIDDLE, data[8] & 0x08);
-                               input_report_key(input, BTN_RIGHT,  data[8] & 0x10);
-                               input_report_rel(input, REL_WHEEL, (data[8] & 0x01)
-                                                - ((data[8] & 0x02) >> 1));
-
-                               /* I3 2D mouse side buttons */
-                               if (features->type >= INTUOS3S && features->type <= INTUOS3L) {
-                                       input_report_key(input, BTN_SIDE,   data[8] & 0x40);
-                                       input_report_key(input, BTN_EXTRA,  data[8] & 0x20);
-                               }
-                       }
-               } else if ((features->type < INTUOS3S || features->type == INTUOS3L ||
-                               features->type == INTUOS4L || features->type == INTUOS5L ||
-                               features->type == INTUOSPL) &&
-                          wacom->tool[idx] == BTN_TOOL_LENS) {
-                       /* Lens cursor packets */
-                       input_report_key(input, BTN_LEFT,   data[8] & 0x01);
-                       input_report_key(input, BTN_MIDDLE, data[8] & 0x02);
-                       input_report_key(input, BTN_RIGHT,  data[8] & 0x04);
-                       input_report_key(input, BTN_SIDE,   data[8] & 0x10);
-                       input_report_key(input, BTN_EXTRA,  data[8] & 0x08);
-               }
-       }
-
-       input_report_abs(input, ABS_MISC, wacom->id[idx]); /* report tool id */
-       input_report_key(input, wacom->tool[idx], 1);
-       input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
-       return 1;
-}
-
-static int int_dist(int x1, int y1, int x2, int y2)
-{
-       int x = x2 - x1;
-       int y = y2 - y1;
-
-       return int_sqrt(x*x + y*y);
-}
-
-static int wacom_24hdt_irq(struct wacom_wac *wacom)
-{
-       struct input_dev *input = wacom->input;
-       unsigned char *data = wacom->data;
-       int i;
-       int current_num_contacts = data[61];
-       int contacts_to_send = 0;
-
-       /*
-        * First packet resets the counter since only the first
-        * packet in series will have non-zero current_num_contacts.
-        */
-       if (current_num_contacts)
-               wacom->num_contacts_left = current_num_contacts;
-
-       /* There are at most 4 contacts per packet */
-       contacts_to_send = min(4, wacom->num_contacts_left);
-
-       for (i = 0; i < contacts_to_send; i++) {
-               int offset = (WACOM_BYTES_PER_24HDT_PACKET * i) + 1;
-               bool touch = data[offset] & 0x1 && !wacom->shared->stylus_in_proximity;
-               int slot = input_mt_get_slot_by_key(input, data[offset + 1]);
-
-               if (slot < 0)
-                       continue;
-               input_mt_slot(input, slot);
-               input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
-
-               if (touch) {
-                       int t_x = get_unaligned_le16(&data[offset + 2]);
-                       int c_x = get_unaligned_le16(&data[offset + 4]);
-                       int t_y = get_unaligned_le16(&data[offset + 6]);
-                       int c_y = get_unaligned_le16(&data[offset + 8]);
-                       int w = get_unaligned_le16(&data[offset + 10]);
-                       int h = get_unaligned_le16(&data[offset + 12]);
-
-                       input_report_abs(input, ABS_MT_POSITION_X, t_x);
-                       input_report_abs(input, ABS_MT_POSITION_Y, t_y);
-                       input_report_abs(input, ABS_MT_TOUCH_MAJOR, min(w,h));
-                       input_report_abs(input, ABS_MT_WIDTH_MAJOR, min(w, h) + int_dist(t_x, t_y, c_x, c_y));
-                       input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h));
-                       input_report_abs(input, ABS_MT_ORIENTATION, w > h);
-               }
-       }
-       input_mt_report_pointer_emulation(input, true);
-
-       wacom->num_contacts_left -= contacts_to_send;
-       if (wacom->num_contacts_left <= 0)
-               wacom->num_contacts_left = 0;
-
-       return 1;
-}
-
-static int wacom_mt_touch(struct wacom_wac *wacom)
-{
-       struct input_dev *input = wacom->input;
-       unsigned char *data = wacom->data;
-       int i;
-       int current_num_contacts = data[2];
-       int contacts_to_send = 0;
-       int x_offset = 0;
-
-       /* MTTPC does not support Height and Width */
-       if (wacom->features.type == MTTPC || wacom->features.type == MTTPC_B)
-               x_offset = -4;
-
-       /*
-        * First packet resets the counter since only the first
-        * packet in series will have non-zero current_num_contacts.
-        */
-       if (current_num_contacts)
-               wacom->num_contacts_left = current_num_contacts;
-
-       /* There are at most 5 contacts per packet */
-       contacts_to_send = min(5, wacom->num_contacts_left);
-
-       for (i = 0; i < contacts_to_send; i++) {
-               int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3;
-               bool touch = data[offset] & 0x1;
-               int id = get_unaligned_le16(&data[offset + 1]);
-               int slot = input_mt_get_slot_by_key(input, id);
-
-               if (slot < 0)
-                       continue;
-
-               input_mt_slot(input, slot);
-               input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
-               if (touch) {
-                       int x = get_unaligned_le16(&data[offset + x_offset + 7]);
-                       int y = get_unaligned_le16(&data[offset + x_offset + 9]);
-                       input_report_abs(input, ABS_MT_POSITION_X, x);
-                       input_report_abs(input, ABS_MT_POSITION_Y, y);
-               }
-       }
-       input_mt_report_pointer_emulation(input, true);
-
-       wacom->num_contacts_left -= contacts_to_send;
-       if (wacom->num_contacts_left < 0)
-               wacom->num_contacts_left = 0;
-
-       return 1;
-}
-
-static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
-{
-       struct input_dev *input = wacom->input;
-       unsigned char *data = wacom->data;
-       int contact_with_no_pen_down_count = 0;
-       int i;
-
-       for (i = 0; i < 2; i++) {
-               int p = data[1] & (1 << i);
-               bool touch = p && !wacom->shared->stylus_in_proximity;
-
-               input_mt_slot(input, i);
-               input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
-               if (touch) {
-                       int x = le16_to_cpup((__le16 *)&data[i * 2 + 2]) & 0x7fff;
-                       int y = le16_to_cpup((__le16 *)&data[i * 2 + 6]) & 0x7fff;
-
-                       input_report_abs(input, ABS_MT_POSITION_X, x);
-                       input_report_abs(input, ABS_MT_POSITION_Y, y);
-                       contact_with_no_pen_down_count++;
-               }
-       }
-       input_mt_report_pointer_emulation(input, true);
-
-       /* keep touch state for pen event */
-       wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
-
-       return 1;
-}
-
-static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
-{
-       unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
-       bool prox;
-       int x = 0, y = 0;
-
-       if (wacom->features.touch_max > 1 || len > WACOM_PKGLEN_TPC2FG)
-               return 0;
-
-       if (!wacom->shared->stylus_in_proximity) {
-               if (len == WACOM_PKGLEN_TPC1FG) {
-                       prox = data[0] & 0x01;
-                       x = get_unaligned_le16(&data[1]);
-                       y = get_unaligned_le16(&data[3]);
-               } else if (len == WACOM_PKGLEN_TPC1FG_B) {
-                       prox = data[2] & 0x01;
-                       x = get_unaligned_le16(&data[3]);
-                       y = get_unaligned_le16(&data[5]);
-               } else {
-                       prox = data[1] & 0x01;
-                       x = le16_to_cpup((__le16 *)&data[2]);
-                       y = le16_to_cpup((__le16 *)&data[4]);
-               }
-       } else
-               /* force touch out when pen is in prox */
-               prox = 0;
-
-       if (prox) {
-               input_report_abs(input, ABS_X, x);
-               input_report_abs(input, ABS_Y, y);
-       }
-       input_report_key(input, BTN_TOUCH, prox);
-
-       /* keep touch state for pen events */
-       wacom->shared->touch_down = prox;
-
-       return 1;
-}
-
-static int wacom_tpc_pen(struct wacom_wac *wacom)
-{
-       unsigned char *data = wacom->data;
-       struct input_dev *input = wacom->input;
-       bool prox = data[1] & 0x20;
-
-       if (!wacom->shared->stylus_in_proximity) /* first in prox */
-               /* Going into proximity select tool */
-               wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
-
-       /* keep pen state for touch events */
-       wacom->shared->stylus_in_proximity = prox;
-
-       /* send pen events only when touch is up or forced out */
-       if (!wacom->shared->touch_down) {
-               input_report_key(input, BTN_STYLUS, data[1] & 0x02);
-               input_report_key(input, BTN_STYLUS2, data[1] & 0x10);
-               input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
-               input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
-               input_report_abs(input, ABS_PRESSURE, ((data[7] & 0x03) << 8) | data[6]);
-               input_report_key(input, BTN_TOUCH, data[1] & 0x05);
-               input_report_key(input, wacom->tool[0], prox);
-               return 1;
-       }
-
-       return 0;
-}
-
-static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
-{
-       unsigned char *data = wacom->data;
-
-       dev_dbg(wacom->input->dev.parent,
-               "%s: received report #%d\n", __func__, data[0]);
-
-       switch (len) {
-       case WACOM_PKGLEN_TPC1FG:
-               return wacom_tpc_single_touch(wacom, len);
-
-       case WACOM_PKGLEN_TPC2FG:
-               return wacom_tpc_mt_touch(wacom);
-
-       case WACOM_PKGLEN_PENABLED:
-               return wacom_tpc_pen(wacom);
-
-       default:
-               switch (data[0]) {
-               case WACOM_REPORT_TPC1FG:
-               case WACOM_REPORT_TPCHID:
-               case WACOM_REPORT_TPCST:
-               case WACOM_REPORT_TPC1FGE:
-                       return wacom_tpc_single_touch(wacom, len);
-
-               case WACOM_REPORT_TPCMT:
-               case WACOM_REPORT_TPCMT2:
-                       return wacom_mt_touch(wacom);
-
-               case WACOM_REPORT_PENABLED:
-                       return wacom_tpc_pen(wacom);
-               }
-       }
-
-       return 0;
-}
-
-static int wacom_bpt_touch(struct wacom_wac *wacom)
-{
-       struct wacom_features *features = &wacom->features;
-       struct input_dev *input = wacom->input;
-       struct input_dev *pad_input = wacom->pad_input;
-       unsigned char *data = wacom->data;
-       int i;
-
-       if (data[0] != 0x02)
-           return 0;
-
-       for (i = 0; i < 2; i++) {
-               int offset = (data[1] & 0x80) ? (8 * i) : (9 * i);
-               bool touch = data[offset + 3] & 0x80;
-
-               /*
-                * Touch events need to be disabled while stylus is
-                * in proximity because user's hand is resting on touchpad
-                * and sending unwanted events.  User expects tablet buttons
-                * to continue working though.
-                */
-               touch = touch && !wacom->shared->stylus_in_proximity;
-
-               input_mt_slot(input, i);
-               input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
-               if (touch) {
-                       int x = get_unaligned_be16(&data[offset + 3]) & 0x7ff;
-                       int y = get_unaligned_be16(&data[offset + 5]) & 0x7ff;
-                       if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) {
-                               x <<= 5;
-                               y <<= 5;
-                       }
-                       input_report_abs(input, ABS_MT_POSITION_X, x);
-                       input_report_abs(input, ABS_MT_POSITION_Y, y);
-               }
-       }
-
-       input_mt_report_pointer_emulation(input, true);
-
-       input_report_key(pad_input, BTN_LEFT, (data[1] & 0x08) != 0);
-       input_report_key(pad_input, BTN_FORWARD, (data[1] & 0x04) != 0);
-       input_report_key(pad_input, BTN_BACK, (data[1] & 0x02) != 0);
-       input_report_key(pad_input, BTN_RIGHT, (data[1] & 0x01) != 0);
-
-       return 1;
-}
-
-static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
-{
-       struct wacom_features *features = &wacom->features;
-       struct input_dev *input = wacom->input;
-       bool touch = data[1] & 0x80;
-       int slot = input_mt_get_slot_by_key(input, data[0]);
-
-       if (slot < 0)
-               return;
-
-       touch = touch && !wacom->shared->stylus_in_proximity;
-
-       input_mt_slot(input, slot);
-       input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
-
-       if (touch) {
-               int x = (data[2] << 4) | (data[4] >> 4);
-               int y = (data[3] << 4) | (data[4] & 0x0f);
-               int width, height;
-
-               if (features->type >= INTUOSPS && features->type <= INTUOSPL) {
-                       width  = data[5] * 100;
-                       height = data[6] * 100;
-               } else {
-                       /*
-                        * "a" is a scaled-down area which we assume is
-                        * roughly circular and which can be described as:
-                        * a=(pi*r^2)/C.
-                        */
-                       int a = data[5];
-                       int x_res  = input_abs_get_res(input, ABS_X);
-                       int y_res  = input_abs_get_res(input, ABS_Y);
-                       width  = 2 * int_sqrt(a * WACOM_CONTACT_AREA_SCALE);
-                       height = width * y_res / x_res;
-               }
-
-               input_report_abs(input, ABS_MT_POSITION_X, x);
-               input_report_abs(input, ABS_MT_POSITION_Y, y);
-               input_report_abs(input, ABS_MT_TOUCH_MAJOR, width);
-               input_report_abs(input, ABS_MT_TOUCH_MINOR, height);
-       }
-}
-
-static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data)
-{
-       struct input_dev *input = wacom->pad_input;
-       struct wacom_features *features = &wacom->features;
-
-       if (features->type == INTUOSHT) {
-               input_report_key(input, BTN_LEFT, (data[1] & 0x02) != 0);
-               input_report_key(input, BTN_BACK, (data[1] & 0x08) != 0);
-       } else {
-               input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0);
-               input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0);
-       }
-       input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0);
-       input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0);
-}
-
-static int wacom_bpt3_touch(struct wacom_wac *wacom)
-{
-       struct input_dev *input = wacom->input;
-       unsigned char *data = wacom->data;
-       int count = data[1] & 0x07;
-       int i;
-
-       if (data[0] != 0x02)
-           return 0;
-
-       /* data has up to 7 fixed sized 8-byte messages starting at data[2] */
-       for (i = 0; i < count; i++) {
-               int offset = (8 * i) + 2;
-               int msg_id = data[offset];
-
-               if (msg_id >= 2 && msg_id <= 17)
-                       wacom_bpt3_touch_msg(wacom, data + offset);
-               else if (msg_id == 128)
-                       wacom_bpt3_button_msg(wacom, data + offset);
-
-       }
-       input_mt_report_pointer_emulation(input, true);
-
-       return 1;
-}
-
-static int wacom_bpt_pen(struct wacom_wac *wacom)
-{
-       struct wacom_features *features = &wacom->features;
-       struct input_dev *input = wacom->input;
-       unsigned char *data = wacom->data;
-       int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0;
-
-       if (data[0] != WACOM_REPORT_PENABLED && data[0] != WACOM_REPORT_USB)
-           return 0;
-
-       if (data[0] == WACOM_REPORT_USB) {
-               if (features->type == INTUOSHT && features->touch_max) {
-                       input_report_switch(wacom->shared->touch_input,
-                                           SW_MUTE_DEVICE, data[8] & 0x40);
-                       input_sync(wacom->shared->touch_input);
-               }
-               return 0;
-       }
-
-       prox = (data[1] & 0x20) == 0x20;
-
-       /*
-        * All reports shared between PEN and RUBBER tool must be
-        * forced to a known starting value (zero) when transitioning to
-        * out-of-prox.
-        *
-        * If not reset then, to userspace, it will look like lost events
-        * if new tool comes in-prox with same values as previous tool sent.
-        *
-        * Hardware does report zero in most out-of-prox cases but not all.
-        */
-       if (prox) {
-               if (!wacom->shared->stylus_in_proximity) {
-                       if (data[1] & 0x08) {
-                               wacom->tool[0] = BTN_TOOL_RUBBER;
-                               wacom->id[0] = ERASER_DEVICE_ID;
-                       } else {
-                               wacom->tool[0] = BTN_TOOL_PEN;
-                               wacom->id[0] = STYLUS_DEVICE_ID;
-                       }
-                       wacom->shared->stylus_in_proximity = true;
-               }
-               x = le16_to_cpup((__le16 *)&data[2]);
-               y = le16_to_cpup((__le16 *)&data[4]);
-               p = le16_to_cpup((__le16 *)&data[6]);
-               /*
-                * Convert distance from out prox to distance from tablet.
-                * distance will be greater than distance_max once
-                * touching and applying pressure; do not report negative
-                * distance.
-                */
-               if (data[8] <= features->distance_max)
-                       d = features->distance_max - data[8];
-
-               pen = data[1] & 0x01;
-               btn1 = data[1] & 0x02;
-               btn2 = data[1] & 0x04;
-       }
-
-       input_report_key(input, BTN_TOUCH, pen);
-       input_report_key(input, BTN_STYLUS, btn1);
-       input_report_key(input, BTN_STYLUS2, btn2);
-
-       input_report_abs(input, ABS_X, x);
-       input_report_abs(input, ABS_Y, y);
-       input_report_abs(input, ABS_PRESSURE, p);
-       input_report_abs(input, ABS_DISTANCE, d);
-
-       if (!prox) {
-               wacom->id[0] = 0;
-               wacom->shared->stylus_in_proximity = false;
-       }
-
-       input_report_key(input, wacom->tool[0], prox); /* PEN or RUBBER */
-       input_report_abs(input, ABS_MISC, wacom->id[0]); /* TOOL ID */
-
-       return 1;
-}
-
-static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len)
-{
-       if (len == WACOM_PKGLEN_BBTOUCH)
-               return wacom_bpt_touch(wacom);
-       else if (len == WACOM_PKGLEN_BBTOUCH3)
-               return wacom_bpt3_touch(wacom);
-       else if (len == WACOM_PKGLEN_BBFUN || len == WACOM_PKGLEN_BBPEN)
-               return wacom_bpt_pen(wacom);
-
-       return 0;
-}
-
-static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len)
-{
-       unsigned char *data = wacom->data;
-       int connected;
-
-       if (len != WACOM_PKGLEN_WIRELESS || data[0] != WACOM_REPORT_WL)
-               return 0;
-
-       connected = data[1] & 0x01;
-       if (connected) {
-               int pid, battery;
-
-               if ((wacom->shared->type == INTUOSHT) &&
-                               wacom->shared->touch_max) {
-                       input_report_switch(wacom->shared->touch_input,
-                                       SW_MUTE_DEVICE, data[5] & 0x40);
-                       input_sync(wacom->shared->touch_input);
-               }
-
-               pid = get_unaligned_be16(&data[6]);
-               battery = data[5] & 0x3f;
-               if (wacom->pid != pid) {
-                       wacom->pid = pid;
-                       wacom_schedule_work(wacom);
-               }
-               wacom->battery_capacity = battery;
-       } else if (wacom->pid != 0) {
-               /* disconnected while previously connected */
-               wacom->pid = 0;
-               wacom_schedule_work(wacom);
-               wacom->battery_capacity = 0;
-       }
-
-       return 0;
-}
-
-void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
-{
-       bool sync;
-
-       switch (wacom_wac->features.type) {
-       case PENPARTNER:
-               sync = wacom_penpartner_irq(wacom_wac);
-               break;
-
-       case PL:
-               sync = wacom_pl_irq(wacom_wac);
-               break;
-
-       case WACOM_G4:
-       case GRAPHIRE:
-       case WACOM_MO:
-               sync = wacom_graphire_irq(wacom_wac);
-               break;
-
-       case PTU:
-               sync = wacom_ptu_irq(wacom_wac);
-               break;
-
-       case DTU:
-               sync = wacom_dtu_irq(wacom_wac);
-               break;
-
-       case DTUS:
-               sync = wacom_dtus_irq(wacom_wac);
-               break;
-
-       case INTUOS:
-       case INTUOS3S:
-       case INTUOS3:
-       case INTUOS3L:
-       case INTUOS4S:
-       case INTUOS4:
-       case INTUOS4L:
-       case CINTIQ:
-       case WACOM_BEE:
-       case WACOM_13HD:
-       case WACOM_21UX2:
-       case WACOM_22HD:
-       case WACOM_24HD:
-       case DTK:
-       case CINTIQ_HYBRID:
-               sync = wacom_intuos_irq(wacom_wac);
-               break;
-
-       case WACOM_24HDT:
-               sync = wacom_24hdt_irq(wacom_wac);
-               break;
-
-       case INTUOS5S:
-       case INTUOS5:
-       case INTUOS5L:
-       case INTUOSPS:
-       case INTUOSPM:
-       case INTUOSPL:
-               if (len == WACOM_PKGLEN_BBTOUCH3)
-                       sync = wacom_bpt3_touch(wacom_wac);
-               else
-                       sync = wacom_intuos_irq(wacom_wac);
-               break;
-
-       case TABLETPC:
-       case TABLETPCE:
-       case TABLETPC2FG:
-       case MTSCREEN:
-       case MTTPC:
-       case MTTPC_B:
-               sync = wacom_tpc_irq(wacom_wac, len);
-               break;
-
-       case BAMBOO_PT:
-       case INTUOSHT:
-               sync = wacom_bpt_irq(wacom_wac, len);
-               break;
-
-       case WIRELESS:
-               sync = wacom_wireless_irq(wacom_wac, len);
-               break;
-
-       default:
-               sync = false;
-               break;
-       }
-
-       if (sync) {
-               input_sync(wacom_wac->input);
-               if (wacom_wac->pad_input)
-                       input_sync(wacom_wac->pad_input);
-       }
-}
-
-static void wacom_setup_cintiq(struct wacom_wac *wacom_wac)
-{
-       struct input_dev *input_dev = wacom_wac->input;
-
-       input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
-
-       __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
-       __set_bit(BTN_TOOL_PEN, input_dev->keybit);
-       __set_bit(BTN_TOOL_BRUSH, input_dev->keybit);
-       __set_bit(BTN_TOOL_PENCIL, input_dev->keybit);
-       __set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit);
-       __set_bit(BTN_STYLUS, input_dev->keybit);
-       __set_bit(BTN_STYLUS2, input_dev->keybit);
-
-       input_set_abs_params(input_dev, ABS_DISTANCE,
-                            0, wacom_wac->features.distance_max, 0, 0);
-       input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
-       input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
-       input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
-}
-
-static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
-{
-       struct input_dev *input_dev = wacom_wac->input;
-
-       input_set_capability(input_dev, EV_REL, REL_WHEEL);
-
-       wacom_setup_cintiq(wacom_wac);
-
-       __set_bit(BTN_LEFT, input_dev->keybit);
-       __set_bit(BTN_RIGHT, input_dev->keybit);
-       __set_bit(BTN_MIDDLE, input_dev->keybit);
-       __set_bit(BTN_SIDE, input_dev->keybit);
-       __set_bit(BTN_EXTRA, input_dev->keybit);
-       __set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
-       __set_bit(BTN_TOOL_LENS, input_dev->keybit);
-
-       input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
-       input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
-}
-
-void wacom_setup_device_quirks(struct wacom_features *features)
-{
-
-       /* touch device found but size is not defined. use default */
-       if (features->device_type == BTN_TOOL_FINGER && !features->x_max) {
-               features->x_max = 1023;
-               features->y_max = 1023;
-       }
-
-       /* these device have multiple inputs */
-       if (features->type >= WIRELESS ||
-           (features->type >= INTUOS5S && features->type <= INTUOSHT) ||
-           (features->oVid && features->oPid))
-               features->quirks |= WACOM_QUIRK_MULTI_INPUT;
-
-       /* quirk for bamboo touch with 2 low res touches */
-       if (features->type == BAMBOO_PT &&
-           features->pktlen == WACOM_PKGLEN_BBTOUCH) {
-               features->x_max <<= 5;
-               features->y_max <<= 5;
-               features->x_fuzz <<= 5;
-               features->y_fuzz <<= 5;
-               features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES;
-       }
-
-       if (features->type == WIRELESS) {
-
-               /* monitor never has input and pen/touch have delayed create */
-               features->quirks |= WACOM_QUIRK_NO_INPUT;
-
-               /* must be monitor interface if no device_type set */
-               if (!features->device_type)
-                       features->quirks |= WACOM_QUIRK_MONITOR;
-       }
-}
-
-static void wacom_abs_set_axis(struct input_dev *input_dev,
-                              struct wacom_wac *wacom_wac)
-{
-       struct wacom_features *features = &wacom_wac->features;
-
-       if (features->device_type == BTN_TOOL_PEN) {
-               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_PRESSURE, 0,
-                       features->pressure_max, features->pressure_fuzz, 0);
-
-               /* penabled devices have fixed resolution for each model */
-               input_abs_set_res(input_dev, ABS_X, features->x_resolution);
-               input_abs_set_res(input_dev, ABS_Y, features->y_resolution);
-       } else {
-               if (features->touch_max <= 2) {
-                       input_set_abs_params(input_dev, ABS_X, 0,
-                               features->x_max, features->x_fuzz, 0);
-                       input_set_abs_params(input_dev, ABS_Y, 0,
-                               features->y_max, features->y_fuzz, 0);
-                       input_abs_set_res(input_dev, ABS_X,
-                                         features->x_resolution);
-                       input_abs_set_res(input_dev, ABS_Y,
-                                         features->y_resolution);
-               }
-
-               if (features->touch_max > 1) {
-                       input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
-                               features->x_max, features->x_fuzz, 0);
-                       input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
-                               features->y_max, features->y_fuzz, 0);
-                       input_abs_set_res(input_dev, ABS_MT_POSITION_X,
-                                         features->x_resolution);
-                       input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
-                                         features->y_resolution);
-               }
-       }
-}
-
-int wacom_setup_input_capabilities(struct input_dev *input_dev,
-                                  struct wacom_wac *wacom_wac)
-{
-       struct wacom_features *features = &wacom_wac->features;
-
-       input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
-
-       __set_bit(BTN_TOUCH, input_dev->keybit);
-       __set_bit(ABS_MISC, input_dev->absbit);
-
-       wacom_abs_set_axis(input_dev, wacom_wac);
-
-       switch (features->type) {
-       case WACOM_MO:
-               input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
-               /* fall through */
-
-       case WACOM_G4:
-               /* fall through */
-
-       case GRAPHIRE:
-               input_set_capability(input_dev, EV_REL, REL_WHEEL);
-
-               __set_bit(BTN_LEFT, input_dev->keybit);
-               __set_bit(BTN_RIGHT, input_dev->keybit);
-               __set_bit(BTN_MIDDLE, input_dev->keybit);
-
-               __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
-               __set_bit(BTN_TOOL_PEN, input_dev->keybit);
-               __set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
-               __set_bit(BTN_STYLUS, input_dev->keybit);
-               __set_bit(BTN_STYLUS2, input_dev->keybit);
-
-               __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
-               break;
-
-       case WACOM_24HD:
-               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
-               input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0);
-               /* fall through */
-
-       case DTK:
-               __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
-
-               wacom_setup_cintiq(wacom_wac);
-               break;
-
-       case WACOM_22HD:
-       case WACOM_21UX2:
-       case WACOM_BEE:
-       case CINTIQ:
-               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
-
-               __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
-
-               wacom_setup_cintiq(wacom_wac);
-               break;
-
-       case WACOM_13HD:
-               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
-               __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
-               wacom_setup_cintiq(wacom_wac);
-               break;
-
-       case INTUOS3:
-       case INTUOS3L:
-       case INTUOS3S:
-               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
-               /* fall through */
-
-       case INTUOS:
-               __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
-
-               wacom_setup_intuos(wacom_wac);
-               break;
-
-       case INTUOS5:
-       case INTUOS5L:
-       case INTUOSPM:
-       case INTUOSPL:
-       case INTUOS5S:
-       case INTUOSPS:
-               __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
-
-               if (features->device_type == BTN_TOOL_PEN) {
-                       input_set_abs_params(input_dev, ABS_DISTANCE, 0,
-                                             features->distance_max,
-                                             0, 0);
-
-                       input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
-
-                       wacom_setup_intuos(wacom_wac);
-               } else if (features->device_type == BTN_TOOL_FINGER) {
-                       __clear_bit(ABS_MISC, input_dev->absbit);
-
-                       input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
-                                            0, features->x_max, 0, 0);
-                       input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
-                                            0, features->y_max, 0, 0);
-                       input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER);
-               }
-               break;
-
-       case INTUOS4:
-       case INTUOS4L:
-       case INTUOS4S:
-               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
-               wacom_setup_intuos(wacom_wac);
-
-               __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
-               break;
-
-       case WACOM_24HDT:
-               if (features->device_type == BTN_TOOL_FINGER) {
-                       input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0);
-                       input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, features->x_max, 0, 0);
-                       input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR, 0, features->y_max, 0, 0);
-                       input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0);
-               }
-               /* fall through */
-
-       case MTSCREEN:
-       case MTTPC:
-       case MTTPC_B:
-       case TABLETPC2FG:
-               if (features->device_type == BTN_TOOL_FINGER) {
-                       unsigned int flags = INPUT_MT_DIRECT;
-
-                       if (wacom_wac->features.type == TABLETPC2FG)
-                               flags = 0;
-
-                       input_mt_init_slots(input_dev, features->touch_max, flags);
-               }
-               /* fall through */
-
-       case TABLETPC:
-       case TABLETPCE:
-               __clear_bit(ABS_MISC, input_dev->absbit);
-
-               __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
-
-               if (features->device_type != BTN_TOOL_PEN)
-                       break;  /* no need to process stylus stuff */
-
-               /* fall through */
-
-       case DTUS:
-       case PL:
-       case DTU:
-               __set_bit(BTN_TOOL_PEN, input_dev->keybit);
-               __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
-               __set_bit(BTN_STYLUS, input_dev->keybit);
-               __set_bit(BTN_STYLUS2, input_dev->keybit);
-
-               __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
-               break;
-
-       case PTU:
-               __set_bit(BTN_STYLUS2, input_dev->keybit);
-               /* fall through */
-
-       case PENPARTNER:
-               __set_bit(BTN_TOOL_PEN, input_dev->keybit);
-               __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
-               __set_bit(BTN_STYLUS, input_dev->keybit);
-
-               __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
-               break;
-
-       case INTUOSHT:
-               if (features->touch_max &&
-                   features->device_type == BTN_TOOL_FINGER) {
-                       input_dev->evbit[0] |= BIT_MASK(EV_SW);
-                       __set_bit(SW_MUTE_DEVICE, input_dev->swbit);
-               }
-               /* fall through */
-
-       case BAMBOO_PT:
-               __clear_bit(ABS_MISC, input_dev->absbit);
-
-               if (features->device_type == BTN_TOOL_FINGER) {
-
-                       if (features->touch_max) {
-                               /* touch interface */
-                               unsigned int flags = INPUT_MT_POINTER;
-
-                               __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
-                               if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
-                                       input_set_abs_params(input_dev,
-                                                    ABS_MT_TOUCH_MAJOR,
-                                                    0, features->x_max, 0, 0);
-                                       input_set_abs_params(input_dev,
-                                                    ABS_MT_TOUCH_MINOR,
-                                                    0, features->y_max, 0, 0);
-                               } else {
-                                       __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
-                                       __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
-                                       flags = 0;
-                               }
-                               input_mt_init_slots(input_dev, features->touch_max, flags);
-                       } else {
-                               /* buttons/keys only interface */
-                               __clear_bit(ABS_X, input_dev->absbit);
-                               __clear_bit(ABS_Y, input_dev->absbit);
-                               __clear_bit(BTN_TOUCH, input_dev->keybit);
-                       }
-               } else if (features->device_type == BTN_TOOL_PEN) {
-                       __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
-                       __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
-                       __set_bit(BTN_TOOL_PEN, input_dev->keybit);
-                       __set_bit(BTN_STYLUS, input_dev->keybit);
-                       __set_bit(BTN_STYLUS2, input_dev->keybit);
-                       input_set_abs_params(input_dev, ABS_DISTANCE, 0,
-                                             features->distance_max,
-                                             0, 0);
-               }
-               break;
-
-       case CINTIQ_HYBRID:
-               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
-               __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
-
-               wacom_setup_cintiq(wacom_wac);
-               break;
-       }
-       return 0;
-}
-
-int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
-                                  struct wacom_wac *wacom_wac)
-{
-       struct wacom_features *features = &wacom_wac->features;
-       int i;
-
-       input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
-
-       /* kept for making legacy xf86-input-wacom working with the wheels */
-       __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);
-
-       switch (features->type) {
-       case WACOM_MO:
-               __set_bit(BTN_BACK, input_dev->keybit);
-               __set_bit(BTN_LEFT, input_dev->keybit);
-               __set_bit(BTN_FORWARD, input_dev->keybit);
-               __set_bit(BTN_RIGHT, input_dev->keybit);
-               input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
-               break;
-
-       case WACOM_G4:
-               __set_bit(BTN_BACK, input_dev->keybit);
-               __set_bit(BTN_LEFT, input_dev->keybit);
-               __set_bit(BTN_FORWARD, input_dev->keybit);
-               __set_bit(BTN_RIGHT, input_dev->keybit);
-               input_set_capability(input_dev, EV_REL, REL_WHEEL);
-               break;
-
-       case WACOM_24HD:
-               __set_bit(BTN_A, input_dev->keybit);
-               __set_bit(BTN_B, input_dev->keybit);
-               __set_bit(BTN_C, input_dev->keybit);
-               __set_bit(BTN_X, input_dev->keybit);
-               __set_bit(BTN_Y, input_dev->keybit);
-               __set_bit(BTN_Z, input_dev->keybit);
-
-               for (i = 0; i < 10; i++)
-                       __set_bit(BTN_0 + i, input_dev->keybit);
-
-               __set_bit(KEY_PROG1, input_dev->keybit);
-               __set_bit(KEY_PROG2, input_dev->keybit);
-               __set_bit(KEY_PROG3, input_dev->keybit);
-
-               input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
-               input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0);
-               break;
-
-       case DTK:
-               for (i = 0; i < 6; i++)
-                       __set_bit(BTN_0 + i, input_dev->keybit);
-
-               break;
-
-       case WACOM_22HD:
-               __set_bit(KEY_PROG1, input_dev->keybit);
-               __set_bit(KEY_PROG2, input_dev->keybit);
-               __set_bit(KEY_PROG3, input_dev->keybit);
-               /* fall through */
-
-       case WACOM_21UX2:
-               __set_bit(BTN_A, input_dev->keybit);
-               __set_bit(BTN_B, input_dev->keybit);
-               __set_bit(BTN_C, input_dev->keybit);
-               __set_bit(BTN_X, input_dev->keybit);
-               __set_bit(BTN_Y, input_dev->keybit);
-               __set_bit(BTN_Z, input_dev->keybit);
-               __set_bit(BTN_BASE, input_dev->keybit);
-               __set_bit(BTN_BASE2, input_dev->keybit);
-               /* fall through */
-
-       case WACOM_BEE:
-               __set_bit(BTN_8, input_dev->keybit);
-               __set_bit(BTN_9, input_dev->keybit);
-               /* fall through */
-
-       case CINTIQ:
-               for (i = 0; i < 8; i++)
-                       __set_bit(BTN_0 + i, input_dev->keybit);
-
-               input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
-               input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
-               break;
-
-       case WACOM_13HD:
-               for (i = 0; i < 9; i++)
-                       __set_bit(BTN_0 + i, input_dev->keybit);
-
-               input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
-               break;
-
-       case INTUOS3:
-       case INTUOS3L:
-               __set_bit(BTN_4, input_dev->keybit);
-               __set_bit(BTN_5, input_dev->keybit);
-               __set_bit(BTN_6, input_dev->keybit);
-               __set_bit(BTN_7, input_dev->keybit);
-
-               input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
-               /* fall through */
-
-       case INTUOS3S:
-               __set_bit(BTN_0, input_dev->keybit);
-               __set_bit(BTN_1, input_dev->keybit);
-               __set_bit(BTN_2, input_dev->keybit);
-               __set_bit(BTN_3, input_dev->keybit);
-
-               input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
-               break;
-
-       case INTUOS5:
-       case INTUOS5L:
-       case INTUOSPM:
-       case INTUOSPL:
-               __set_bit(BTN_7, input_dev->keybit);
-               __set_bit(BTN_8, input_dev->keybit);
-               /* fall through */
-
-       case INTUOS5S:
-       case INTUOSPS:
-               /* touch interface does not have the pad device */
-               if (features->device_type != BTN_TOOL_PEN)
-                       return 1;
-
-               for (i = 0; i < 7; i++)
-                       __set_bit(BTN_0 + i, input_dev->keybit);
-
-               input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
-               break;
-
-       case INTUOS4:
-       case INTUOS4L:
-               __set_bit(BTN_7, input_dev->keybit);
-               __set_bit(BTN_8, input_dev->keybit);
-               /* fall through */
-
-       case INTUOS4S:
-               for (i = 0; i < 7; i++)
-                       __set_bit(BTN_0 + i, input_dev->keybit);
-
-               input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
-               break;
-
-       case CINTIQ_HYBRID:
-               for (i = 0; i < 9; i++)
-                       __set_bit(BTN_0 + i, input_dev->keybit);
-
-               break;
-
-       case DTUS:
-               for (i = 0; i < 4; i++)
-                       __set_bit(BTN_0 + i, input_dev->keybit);
-               break;
-
-       case INTUOSHT:
-       case BAMBOO_PT:
-               /* pad device is on the touch interface */
-               if (features->device_type != BTN_TOOL_FINGER)
-                       return 1;
-
-               __clear_bit(ABS_MISC, input_dev->absbit);
-
-               __set_bit(BTN_LEFT, input_dev->keybit);
-               __set_bit(BTN_FORWARD, input_dev->keybit);
-               __set_bit(BTN_BACK, input_dev->keybit);
-               __set_bit(BTN_RIGHT, input_dev->keybit);
-
-               break;
-
-       default:
-               /* no pad supported */
-               return 1;
-       }
-       return 0;
-}
-
-static const struct wacom_features wacom_features_0x00 =
-       { "Wacom Penpartner", 5040, 3780, 255, 0,
-         PENPARTNER, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES };
-static const struct wacom_features wacom_features_0x10 =
-       { "Wacom Graphire", 10206, 7422, 511, 63,
-         GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
-static const struct wacom_features wacom_features_0x11 =
-       { "Wacom Graphire2 4x5", 10206, 7422, 511, 63,
-         GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
-static const struct wacom_features wacom_features_0x12 =
-       { "Wacom Graphire2 5x7", 13918, 10206, 511, 63,
-         GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
-static const struct wacom_features wacom_features_0x13 =
-       { "Wacom Graphire3", 10208, 7424, 511, 63,
-         GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
-static const struct wacom_features wacom_features_0x14 =
-       { "Wacom Graphire3 6x8", 16704, 12064, 511, 63,
-         GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
-static const struct wacom_features wacom_features_0x15 =
-       { "Wacom Graphire4 4x5", 10208, 7424, 511, 63,
-         WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
-static const struct wacom_features wacom_features_0x16 =
-       { "Wacom Graphire4 6x8", 16704, 12064, 511, 63,
-         WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
-static const struct wacom_features wacom_features_0x17 =
-       { "Wacom BambooFun 4x5", 14760, 9225, 511, 63,
-         WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x18 =
-       { "Wacom BambooFun 6x8", 21648, 13530, 511, 63,
-         WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x19 =
-       { "Wacom Bamboo1 Medium", 16704, 12064, 511, 63,
-         GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
-static const struct wacom_features wacom_features_0x60 =
-       { "Wacom Volito", 5104, 3712, 511, 63,
-         GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
-static const struct wacom_features wacom_features_0x61 =
-       { "Wacom PenStation2", 3250, 2320, 255, 63,
-         GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
-static const struct wacom_features wacom_features_0x62 =
-       { "Wacom Volito2 4x5", 5104, 3712, 511, 63,
-         GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
-static const struct wacom_features wacom_features_0x63 =
-       { "Wacom Volito2 2x3", 3248, 2320, 511, 63,
-         GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
-static const struct wacom_features wacom_features_0x64 =
-       { "Wacom PenPartner2", 3250, 2320, 511, 63,
-         GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
-static const struct wacom_features wacom_features_0x65 =
-       { "Wacom Bamboo", 14760, 9225, 511, 63,
-         WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x69 =
-       { "Wacom Bamboo1", 5104, 3712, 511, 63,
-         GRAPHIRE, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES };
-static const struct wacom_features wacom_features_0x6A =
-       { "Wacom Bamboo1 4x6", 14760, 9225, 1023, 63,
-         GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x6B =
-       { "Wacom Bamboo1 5x8", 21648, 13530, 1023, 63,
-         GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x20 =
-       { "Wacom Intuos 4x5", 12700, 10600, 1023, 31,
-         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x21 =
-       { "Wacom Intuos 6x8", 20320, 16240, 1023, 31,
-         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x22 =
-       { "Wacom Intuos 9x12", 30480, 24060, 1023, 31,
-         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x23 =
-       { "Wacom Intuos 12x12", 30480, 31680, 1023, 31,
-         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x24 =
-       { "Wacom Intuos 12x18", 45720, 31680, 1023, 31,
-         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x30 =
-       { "Wacom PL400", 5408, 4056, 255, 0,
-         PL, WACOM_PL_RES, WACOM_PL_RES };
-static const struct wacom_features wacom_features_0x31 =
-       { "Wacom PL500", 6144, 4608, 255, 0,
-         PL, WACOM_PL_RES, WACOM_PL_RES };
-static const struct wacom_features wacom_features_0x32 =
-       { "Wacom PL600", 6126, 4604, 255, 0,
-         PL, WACOM_PL_RES, WACOM_PL_RES };
-static const struct wacom_features wacom_features_0x33 =
-       { "Wacom PL600SX", 6260, 5016, 255, 0,
-         PL, WACOM_PL_RES, WACOM_PL_RES };
-static const struct wacom_features wacom_features_0x34 =
-       { "Wacom PL550", 6144, 4608, 511, 0,
-         PL, WACOM_PL_RES, WACOM_PL_RES };
-static const struct wacom_features wacom_features_0x35 =
-       { "Wacom PL800", 7220, 5780, 511, 0,
-         PL, WACOM_PL_RES, WACOM_PL_RES };
-static const struct wacom_features wacom_features_0x37 =
-       { "Wacom PL700", 6758, 5406, 511, 0,
-         PL, WACOM_PL_RES, WACOM_PL_RES };
-static const struct wacom_features wacom_features_0x38 =
-       { "Wacom PL510", 6282, 4762, 511, 0,
-         PL, WACOM_PL_RES, WACOM_PL_RES };
-static const struct wacom_features wacom_features_0x39 =
-       { "Wacom DTU710", 34080, 27660, 511, 0,
-         PL, WACOM_PL_RES, WACOM_PL_RES };
-static const struct wacom_features wacom_features_0xC4 =
-       { "Wacom DTF521", 6282, 4762, 511, 0,
-         PL, WACOM_PL_RES, WACOM_PL_RES };
-static const struct wacom_features wacom_features_0xC0 =
-       { "Wacom DTF720", 6858, 5506, 511, 0,
-         PL, WACOM_PL_RES, WACOM_PL_RES };
-static const struct wacom_features wacom_features_0xC2 =
-       { "Wacom DTF720a", 6858, 5506, 511, 0,
-         PL, WACOM_PL_RES, WACOM_PL_RES };
-static const struct wacom_features wacom_features_0x03 =
-       { "Wacom Cintiq Partner", 20480, 15360, 511, 0,
-         PTU, WACOM_PL_RES, WACOM_PL_RES };
-static const struct wacom_features wacom_features_0x41 =
-       { "Wacom Intuos2 4x5", 12700, 10600, 1023, 31,
-         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x42 =
-       { "Wacom Intuos2 6x8", 20320, 16240, 1023, 31,
-         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x43 =
-       { "Wacom Intuos2 9x12", 30480, 24060, 1023, 31,
-         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x44 =
-       { "Wacom Intuos2 12x12", 30480, 31680, 1023, 31,
-         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x45 =
-       { "Wacom Intuos2 12x18", 45720, 31680, 1023, 31,
-         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0xB0 =
-       { "Wacom Intuos3 4x5", 25400, 20320, 1023, 63,
-         INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0xB1 =
-       { "Wacom Intuos3 6x8", 40640, 30480, 1023, 63,
-         INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0xB2 =
-       { "Wacom Intuos3 9x12", 60960, 45720, 1023, 63,
-         INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0xB3 =
-       { "Wacom Intuos3 12x12", 60960, 60960, 1023, 63,
-         INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0xB4 =
-       { "Wacom Intuos3 12x19", 97536, 60960, 1023, 63,
-         INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0xB5 =
-       { "Wacom Intuos3 6x11", 54204, 31750, 1023, 63,
-         INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0xB7 =
-       { "Wacom Intuos3 4x6", 31496, 19685, 1023, 63,
-         INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0xB8 =
-       { "Wacom Intuos4 4x6", 31496, 19685, 2047, 63,
-         INTUOS4S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0xB9 =
-       { "Wacom Intuos4 6x9", 44704, 27940, 2047, 63,
-         INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0xBA =
-       { "Wacom Intuos4 8x13", 65024, 40640, 2047, 63,
-         INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0xBB =
-       { "Wacom Intuos4 12x19", 97536, 60960, 2047, 63,
-         INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0xBC =
-       { "Wacom Intuos4 WL", 40640, 25400, 2047, 63,
-         INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0x26 =
-       { "Wacom Intuos5 touch S", 31496, 19685, 2047, 63,
-         INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 };
-static const struct wacom_features wacom_features_0x27 =
-       { "Wacom Intuos5 touch M", 44704, 27940, 2047, 63,
-         INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 };
-static const struct wacom_features wacom_features_0x28 =
-       { "Wacom Intuos5 touch L", 65024, 40640, 2047, 63,
-         INTUOS5L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 };
-static const struct wacom_features wacom_features_0x29 =
-       { "Wacom Intuos5 S", 31496, 19685, 2047, 63,
-         INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0x2A =
-       { "Wacom Intuos5 M", 44704, 27940, 2047, 63,
-         INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0x314 =
-       { "Wacom Intuos Pro S", 31496, 19685, 2047, 63,
-         INTUOSPS, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16,
-         .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
-static const struct wacom_features wacom_features_0x315 =
-       { "Wacom Intuos Pro M", 44704, 27940, 2047, 63,
-         INTUOSPM, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16,
-         .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
-static const struct wacom_features wacom_features_0x317 =
-       { "Wacom Intuos Pro L", 65024, 40640, 2047, 63,
-         INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16,
-         .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
-static const struct wacom_features wacom_features_0xF4 =
-       { "Wacom Cintiq 24HD", 104280, 65400, 2047, 63,
-         WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
-static const struct wacom_features wacom_features_0xF8 =
-       { "Wacom Cintiq 24HD touch", 104280, 65400, 2047, 63, /* Pen */
-         WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
-         .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_0x3F =
-       { "Wacom Cintiq 21UX", 87200, 65600, 1023, 63,
-         CINTIQ, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0xC5 =
-       { "Wacom Cintiq 20WSX", 86680, 54180, 1023, 63,
-         WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0xC6 =
-       { "Wacom Cintiq 12WX", 53020, 33440, 1023, 63,
-         WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
-static const struct wacom_features wacom_features_0x304 =
-       { "Wacom Cintiq 13HD", 59352, 33648, 1023, 63,
-         WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
-static const struct wacom_features wacom_features_0xC7 =
-       { "Wacom DTU1931", 37832, 30305, 511, 0,
-         PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0xCE =
-       { "Wacom DTU2231", 47864, 27011, 511, 0,
-         DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-         .check_for_hid_type = true, .hid_type = HID_TYPE_USBMOUSE };
-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", 22096, 13960, 511, 0,
-         DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x57 =
-       { "Wacom DTK2241", 95640, 54060, 2047, 63,
-         DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
-static const struct wacom_features wacom_features_0x59 = /* Pen */
-       { "Wacom DTH2242", 95640, 54060, 2047, 63,
-         DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
-         .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", 87000, 65400, 2047, 63,
-         WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
-static const struct wacom_features wacom_features_0xFA =
-       { "Wacom Cintiq 22HD", 95640, 54060, 2047, 63,
-         WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
-static const struct wacom_features wacom_features_0x5B =
-       { "Wacom Cintiq 22HDT", 95640, 54060, 2047, 63,
-         WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
-         .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5e };
-static const struct wacom_features wacom_features_0x5E =
-       { "Wacom Cintiq 22HDT", .type = WACOM_24HDT,
-         .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5b, .touch_max = 10,
-         .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
-static const struct wacom_features wacom_features_0x90 =
-       { "Wacom ISDv4 90", 26202, 16325, 255, 0,
-         TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x93 =
-       { "Wacom ISDv4 93", 26202, 16325, 255, 0,
-         TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x97 =
-       { "Wacom ISDv4 97", 26202, 16325, 511, 0,
-         TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x9A =
-       { "Wacom ISDv4 9A", 26202, 16325, 255, 0,
-         TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x9F =
-       { "Wacom ISDv4 9F", 26202, 16325, 255, 0,
-         TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0xE2 =
-       { "Wacom ISDv4 E2", 26202, 16325, 255, 0,
-         TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
-static const struct wacom_features wacom_features_0xE3 =
-       { "Wacom ISDv4 E3", 26202, 16325, 255, 0,
-         TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
-static const struct wacom_features wacom_features_0xE5 =
-       { "Wacom ISDv4 E5", 26202, 16325, 255, 0,
-         MTSCREEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0xE6 =
-       { "Wacom ISDv4 E6", 27760, 15694, 255, 0,
-         TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
-static const struct wacom_features wacom_features_0xEC =
-       { "Wacom ISDv4 EC", 25710, 14500, 255, 0,
-         TABLETPC,    WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0xED =
-       { "Wacom ISDv4 ED", 26202, 16325, 255, 0,
-         TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0xEF =
-       { "Wacom ISDv4 EF", 26202, 16325, 255, 0,
-         TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x100 =
-       { "Wacom ISDv4 100", 26202, 16325, 255, 0,
-         MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x101 =
-       { "Wacom ISDv4 101", 26202, 16325, 255, 0,
-         MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x10D =
-       { "Wacom ISDv4 10D", 26202, 16325, 255, 0,
-         MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x10E =
-       { "Wacom ISDv4 10E", 27760, 15694, 255, 0,
-         MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x10F =
-       { "Wacom ISDv4 10F", 27760, 15694, 255, 0,
-         MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x116 =
-       { "Wacom ISDv4 116", 26202, 16325, 255, 0,
-         TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x4001 =
-       { "Wacom ISDv4 4001", 26202, 16325, 255, 0,
-         MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x4004 =
-       { "Wacom ISDv4 4004", 11060, 6220, 255, 0,
-         MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x5000 =
-       { "Wacom ISDv4 5000", 27848, 15752, 1023, 0,
-         MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x5002 =
-       { "Wacom ISDv4 5002", 29576, 16724, 1023, 0,
-         MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x47 =
-       { "Wacom Intuos2 6x8", 20320, 16240, 1023, 31,
-         INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x84 =
-       { "Wacom Wireless Receiver", 0, 0, 0, 0,
-         WIRELESS, 0, 0, .touch_max = 16 };
-static const struct wacom_features wacom_features_0xD0 =
-       { "Wacom Bamboo 2FG", 14720, 9200, 1023, 31,
-         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
-static const struct wacom_features wacom_features_0xD1 =
-       { "Wacom Bamboo 2FG 4x5", 14720, 9200, 1023, 31,
-         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
-static const struct wacom_features wacom_features_0xD2 =
-       { "Wacom Bamboo Craft", 14720, 9200, 1023, 31,
-         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
-static const struct wacom_features wacom_features_0xD3 =
-       { "Wacom Bamboo 2FG 6x8", 21648, 13700, 1023, 31,
-         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
-static const struct wacom_features wacom_features_0xD4 =
-       { "Wacom Bamboo Pen", 14720, 9200, 1023, 31,
-         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0xD5 =
-       { "Wacom Bamboo Pen 6x8", 21648, 13700, 1023, 31,
-         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0xD6 =
-       { "Wacom BambooPT 2FG 4x5", 14720, 9200, 1023, 31,
-         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
-static const struct wacom_features wacom_features_0xD7 =
-       { "Wacom BambooPT 2FG Small", 14720, 9200, 1023, 31,
-         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
-static const struct wacom_features wacom_features_0xD8 =
-       { "Wacom Bamboo Comic 2FG", 21648, 13700, 1023, 31,
-         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
-static const struct wacom_features wacom_features_0xDA =
-       { "Wacom Bamboo 2FG 4x5 SE", 14720, 9200, 1023, 31,
-         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
-static const struct wacom_features wacom_features_0xDB =
-       { "Wacom Bamboo 2FG 6x8 SE", 21648, 13700, 1023, 31,
-         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 2 };
-static const struct wacom_features wacom_features_0xDD =
-        { "Wacom Bamboo Connect", 14720, 9200, 1023, 31,
-          BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0xDE =
-        { "Wacom Bamboo 16FG 4x5", 14720, 9200, 1023, 31,
-         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16 };
-static const struct wacom_features wacom_features_0xDF =
-        { "Wacom Bamboo 16FG 6x8", 21648, 13700, 1023, 31,
-         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, .touch_max = 16 };
-static const struct wacom_features wacom_features_0x300 =
-       { "Wacom Bamboo One S", 14720, 9225, 1023, 31,
-         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x301 =
-       { "Wacom Bamboo One M", 21648, 13530, 1023, 31,
-         BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x302 =
-       { "Wacom Intuos PT S", 15200, 9500, 1023, 31,
-         INTUOSHT, 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_0x303 =
-       { "Wacom Intuos PT M", 21600, 13500, 1023, 31,
-         INTUOSHT, 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_0x30E =
-       { "Wacom Intuos S", 15200, 9500, 1023, 31,
-         INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-         .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
-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", 59352, 33648, 2047, 63,
-         CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
-         .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 };
-
-#define USB_DEVICE_WACOM(prod)                                         \
-       HID_DEVICE(BUS_USB, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\
-       .driver_data = (kernel_ulong_t)&wacom_features_##prod
-
-#define USB_DEVICE_LENOVO(prod)                                        \
-       HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, prod),                     \
-       .driver_data = (kernel_ulong_t)&wacom_features_##prod
-
-const struct hid_device_id wacom_ids[] = {
-       { USB_DEVICE_WACOM(0x00) },
-       { USB_DEVICE_WACOM(0x03) },
-       { USB_DEVICE_WACOM(0x10) },
-       { USB_DEVICE_WACOM(0x11) },
-       { USB_DEVICE_WACOM(0x12) },
-       { USB_DEVICE_WACOM(0x13) },
-       { USB_DEVICE_WACOM(0x14) },
-       { USB_DEVICE_WACOM(0x15) },
-       { USB_DEVICE_WACOM(0x16) },
-       { USB_DEVICE_WACOM(0x17) },
-       { USB_DEVICE_WACOM(0x18) },
-       { USB_DEVICE_WACOM(0x19) },
-       { USB_DEVICE_WACOM(0x20) },
-       { USB_DEVICE_WACOM(0x21) },
-       { USB_DEVICE_WACOM(0x22) },
-       { USB_DEVICE_WACOM(0x23) },
-       { USB_DEVICE_WACOM(0x24) },
-       { USB_DEVICE_WACOM(0x26) },
-       { USB_DEVICE_WACOM(0x27) },
-       { USB_DEVICE_WACOM(0x28) },
-       { USB_DEVICE_WACOM(0x29) },
-       { USB_DEVICE_WACOM(0x2A) },
-       { USB_DEVICE_WACOM(0x30) },
-       { USB_DEVICE_WACOM(0x31) },
-       { USB_DEVICE_WACOM(0x32) },
-       { USB_DEVICE_WACOM(0x33) },
-       { USB_DEVICE_WACOM(0x34) },
-       { USB_DEVICE_WACOM(0x35) },
-       { USB_DEVICE_WACOM(0x37) },
-       { USB_DEVICE_WACOM(0x38) },
-       { USB_DEVICE_WACOM(0x39) },
-       { USB_DEVICE_WACOM(0x3F) },
-       { USB_DEVICE_WACOM(0x41) },
-       { USB_DEVICE_WACOM(0x42) },
-       { USB_DEVICE_WACOM(0x43) },
-       { USB_DEVICE_WACOM(0x44) },
-       { USB_DEVICE_WACOM(0x45) },
-       { USB_DEVICE_WACOM(0x47) },
-       { USB_DEVICE_WACOM(0x57) },
-       { USB_DEVICE_WACOM(0x59) },
-       { USB_DEVICE_WACOM(0x5B) },
-       { USB_DEVICE_WACOM(0x5D) },
-       { USB_DEVICE_WACOM(0x5E) },
-       { USB_DEVICE_WACOM(0x60) },
-       { USB_DEVICE_WACOM(0x61) },
-       { USB_DEVICE_WACOM(0x62) },
-       { USB_DEVICE_WACOM(0x63) },
-       { USB_DEVICE_WACOM(0x64) },
-       { USB_DEVICE_WACOM(0x65) },
-       { USB_DEVICE_WACOM(0x69) },
-       { USB_DEVICE_WACOM(0x6A) },
-       { USB_DEVICE_WACOM(0x6B) },
-       { USB_DEVICE_WACOM(0x84) },
-       { USB_DEVICE_WACOM(0x90) },
-       { USB_DEVICE_WACOM(0x93) },
-       { USB_DEVICE_WACOM(0x97) },
-       { USB_DEVICE_WACOM(0x9A) },
-       { USB_DEVICE_WACOM(0x9F) },
-       { USB_DEVICE_WACOM(0xB0) },
-       { USB_DEVICE_WACOM(0xB1) },
-       { USB_DEVICE_WACOM(0xB2) },
-       { USB_DEVICE_WACOM(0xB3) },
-       { USB_DEVICE_WACOM(0xB4) },
-       { USB_DEVICE_WACOM(0xB5) },
-       { USB_DEVICE_WACOM(0xB7) },
-       { USB_DEVICE_WACOM(0xB8) },
-       { USB_DEVICE_WACOM(0xB9) },
-       { USB_DEVICE_WACOM(0xBA) },
-       { USB_DEVICE_WACOM(0xBB) },
-       { USB_DEVICE_WACOM(0xBC) },
-       { USB_DEVICE_WACOM(0xC0) },
-       { USB_DEVICE_WACOM(0xC2) },
-       { USB_DEVICE_WACOM(0xC4) },
-       { USB_DEVICE_WACOM(0xC5) },
-       { USB_DEVICE_WACOM(0xC6) },
-       { USB_DEVICE_WACOM(0xC7) },
-       { USB_DEVICE_WACOM(0xCC) },
-       { USB_DEVICE_WACOM(0xCE) },
-       { USB_DEVICE_WACOM(0xD0) },
-       { USB_DEVICE_WACOM(0xD1) },
-       { USB_DEVICE_WACOM(0xD2) },
-       { USB_DEVICE_WACOM(0xD3) },
-       { USB_DEVICE_WACOM(0xD4) },
-       { USB_DEVICE_WACOM(0xD5) },
-       { USB_DEVICE_WACOM(0xD6) },
-       { USB_DEVICE_WACOM(0xD7) },
-       { USB_DEVICE_WACOM(0xD8) },
-       { USB_DEVICE_WACOM(0xDA) },
-       { USB_DEVICE_WACOM(0xDB) },
-       { USB_DEVICE_WACOM(0xDD) },
-       { USB_DEVICE_WACOM(0xDE) },
-       { USB_DEVICE_WACOM(0xDF) },
-       { USB_DEVICE_WACOM(0xE2) },
-       { USB_DEVICE_WACOM(0xE3) },
-       { USB_DEVICE_WACOM(0xE5) },
-       { USB_DEVICE_WACOM(0xE6) },
-       { USB_DEVICE_WACOM(0xEC) },
-       { USB_DEVICE_WACOM(0xED) },
-       { USB_DEVICE_WACOM(0xEF) },
-       { USB_DEVICE_WACOM(0xF0) },
-       { USB_DEVICE_WACOM(0xF4) },
-       { USB_DEVICE_WACOM(0xF6) },
-       { USB_DEVICE_WACOM(0xF8) },
-       { USB_DEVICE_WACOM(0xFA) },
-       { USB_DEVICE_WACOM(0xFB) },
-       { USB_DEVICE_WACOM(0x100) },
-       { USB_DEVICE_WACOM(0x101) },
-       { USB_DEVICE_WACOM(0x10D) },
-       { USB_DEVICE_WACOM(0x10E) },
-       { USB_DEVICE_WACOM(0x10F) },
-       { USB_DEVICE_WACOM(0x116) },
-       { USB_DEVICE_WACOM(0x300) },
-       { USB_DEVICE_WACOM(0x301) },
-       { USB_DEVICE_WACOM(0x302) },
-       { USB_DEVICE_WACOM(0x303) },
-       { USB_DEVICE_WACOM(0x304) },
-       { USB_DEVICE_WACOM(0x307) },
-       { USB_DEVICE_WACOM(0x309) },
-       { USB_DEVICE_WACOM(0x30E) },
-       { USB_DEVICE_WACOM(0x314) },
-       { USB_DEVICE_WACOM(0x315) },
-       { USB_DEVICE_WACOM(0x317) },
-       { USB_DEVICE_WACOM(0x4001) },
-       { USB_DEVICE_WACOM(0x4004) },
-       { USB_DEVICE_WACOM(0x5000) },
-       { USB_DEVICE_WACOM(0x5002) },
-       { }
-};
-MODULE_DEVICE_TABLE(hid, wacom_ids);
diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h
deleted file mode 100644 (file)
index 4c59247..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * drivers/input/tablet/wacom_wac.h
- *
- * 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.
- */
-#ifndef WACOM_WAC_H
-#define WACOM_WAC_H
-
-#include <linux/types.h>
-
-/* maximum packet length for USB devices */
-#define WACOM_PKGLEN_MAX       68
-
-#define WACOM_NAME_MAX         64
-
-/* packet length for individual models */
-#define WACOM_PKGLEN_PENPRTN    7
-#define WACOM_PKGLEN_GRAPHIRE   8
-#define WACOM_PKGLEN_BBFUN      9
-#define WACOM_PKGLEN_INTUOS    10
-#define WACOM_PKGLEN_TPC1FG     5
-#define WACOM_PKGLEN_TPC1FG_B  10
-#define WACOM_PKGLEN_TPC2FG    14
-#define WACOM_PKGLEN_BBTOUCH   20
-#define WACOM_PKGLEN_BBTOUCH3  64
-#define WACOM_PKGLEN_BBPEN     10
-#define WACOM_PKGLEN_WIRELESS  32
-#define WACOM_PKGLEN_MTOUCH    62
-#define WACOM_PKGLEN_MTTPC     40
-#define WACOM_PKGLEN_DTUS      68
-#define WACOM_PKGLEN_PENABLED   8
-
-/* wacom data size per MT contact */
-#define WACOM_BYTES_PER_MT_PACKET      11
-#define WACOM_BYTES_PER_24HDT_PACKET   14
-
-/* device IDs */
-#define STYLUS_DEVICE_ID       0x02
-#define TOUCH_DEVICE_ID                0x03
-#define CURSOR_DEVICE_ID       0x06
-#define ERASER_DEVICE_ID       0x0A
-#define PAD_DEVICE_ID          0x0F
-
-/* wacom data packet report IDs */
-#define WACOM_REPORT_PENABLED          2
-#define WACOM_REPORT_INTUOSREAD                5
-#define WACOM_REPORT_INTUOSWRITE       6
-#define WACOM_REPORT_INTUOSPAD         12
-#define WACOM_REPORT_INTUOS5PAD                3
-#define WACOM_REPORT_DTUSPAD           21
-#define WACOM_REPORT_TPC1FG            6
-#define WACOM_REPORT_TPC2FG            13
-#define WACOM_REPORT_TPCMT             13
-#define WACOM_REPORT_TPCMT2            3
-#define WACOM_REPORT_TPCHID            15
-#define WACOM_REPORT_TPCST             16
-#define WACOM_REPORT_DTUS              17
-#define WACOM_REPORT_TPC1FGE           18
-#define WACOM_REPORT_24HDT             1
-#define WACOM_REPORT_WL                        128
-#define WACOM_REPORT_USB               192
-
-/* device quirks */
-#define WACOM_QUIRK_MULTI_INPUT                0x0001
-#define WACOM_QUIRK_BBTOUCH_LOWRES     0x0002
-#define WACOM_QUIRK_NO_INPUT           0x0004
-#define WACOM_QUIRK_MONITOR            0x0008
-
-enum {
-       PENPARTNER = 0,
-       GRAPHIRE,
-       WACOM_G4,
-       PTU,
-       PL,
-       DTU,
-       DTUS,
-       INTUOS,
-       INTUOS3S,
-       INTUOS3,
-       INTUOS3L,
-       INTUOS4S,
-       INTUOS4,
-       INTUOS4L,
-       INTUOS5S,
-       INTUOS5,
-       INTUOS5L,
-       INTUOSPS,
-       INTUOSPM,
-       INTUOSPL,
-       INTUOSHT,
-       WACOM_21UX2,
-       WACOM_22HD,
-       DTK,
-       WACOM_24HD,
-       CINTIQ_HYBRID,
-       CINTIQ,
-       WACOM_BEE,
-       WACOM_13HD,
-       WACOM_MO,
-       WIRELESS,
-       BAMBOO_PT,
-       WACOM_24HDT,
-       TABLETPC,   /* add new TPC below */
-       TABLETPCE,
-       TABLETPC2FG,
-       MTSCREEN,
-       MTTPC,
-       MTTPC_B,
-       MAX_TYPE
-};
-
-struct wacom_features {
-       const char *name;
-       int x_max;
-       int y_max;
-       int pressure_max;
-       int distance_max;
-       int type;
-       int x_resolution;
-       int y_resolution;
-       int x_min;
-       int y_min;
-       int device_type;
-       int x_phy;
-       int y_phy;
-       unsigned unit;
-       int unitExpo;
-       int x_fuzz;
-       int y_fuzz;
-       int pressure_fuzz;
-       int distance_fuzz;
-       unsigned quirks;
-       unsigned touch_max;
-       int oVid;
-       int oPid;
-       int pktlen;
-       bool check_for_hid_type;
-       int hid_type;
-};
-
-struct wacom_shared {
-       bool stylus_in_proximity;
-       bool touch_down;
-       /* for wireless device to access USB interfaces */
-       unsigned touch_max;
-       int type;
-       struct input_dev *touch_input;
-};
-
-struct wacom_wac {
-       char name[WACOM_NAME_MAX];
-       char pad_name[WACOM_NAME_MAX];
-       unsigned char data[WACOM_PKGLEN_MAX];
-       int tool[2];
-       int id[2];
-       __u32 serial[2];
-       struct wacom_features features;
-       struct wacom_shared *shared;
-       struct input_dev *input;
-       struct input_dev *pad_input;
-       int pid;
-       int battery_capacity;
-       int num_contacts_left;
-};
-
-#endif