Merge branch 'wacom' into next

Merge large update to Wacom driver, converting it from USB to a HID
driver and unifying wired and bluetooth support, from Benjamin
Tissoires.
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 5e79c6a..4ed682c 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -748,12 +748,17 @@
 	  Rumble Force or Force Feedback Wheel.
 
 config HID_WACOM
-	tristate "Wacom Bluetooth devices support"
+	tristate "Wacom Intuos/Graphire tablet support (USB)"
 	depends on HID
-	depends on LEDS_CLASS
 	select POWER_SUPPLY
-	---help---
-	Support for Wacom Graphire Bluetooth and Intuos4 WL tablets.
+	select NEW_LEDS
+	select LEDS_CLASS
+	help
+	  Say Y here if you want to use the USB or BT 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"
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index a6fa6ba..e38c772 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -115,7 +115,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_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/hid-core.c b/drivers/hid/hid-core.c
index 8ed66fd..b3181ea 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -787,6 +787,15 @@
 		/* hid-rmi should take care of them, not hid-generic */
 		hid->group = HID_GROUP_RMI;
 
+	/*
+	 * Vendor specific handlings
+	 */
+	switch (hid->vendor) {
+	case USB_VENDOR_ID_WACOM:
+		hid->group = HID_GROUP_WACOM;
+		break;
+	}
+
 	vfree(parser);
 	return 0;
 }
@@ -1933,8 +1942,6 @@
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) },
-	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
-	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_Q_PAD) },
@@ -2339,7 +2346,6 @@
 	{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) },
-	{ HID_USB_DEVICE(USB_VENDOR_ID_WACOM, HID_ANY_ID) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) },
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
deleted file mode 100644
index 902013e..0000000
--- a/drivers/hid/hid-wacom.c
+++ /dev/null
@@ -1,973 +0,0 @@
-/*
- *  Bluetooth Wacom Tablet support
- *
- *  Copyright (c) 1999 Andreas Gal
- *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
- *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
- *  Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
- *  Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
- *  Copyright (c) 2011 Przemysław Firszt <przemo@firszt.eu>
- */
-
-/*
- * 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.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/device.h>
-#include <linux/hid.h>
-#include <linux/module.h>
-#include <linux/leds.h>
-#include <linux/slab.h>
-#include <linux/power_supply.h>
-
-#include "hid-ids.h"
-
-#define PAD_DEVICE_ID	0x0F
-
-#define WAC_CMD_LED_CONTROL     0x20
-#define WAC_CMD_ICON_START_STOP     0x21
-#define WAC_CMD_ICON_TRANSFER       0x26
-
-struct wacom_data {
-	__u16 tool;
-	__u16 butstate;
-	__u8 whlstate;
-	__u8 features;
-	__u32 id;
-	__u32 serial;
-	unsigned char high_speed;
-	__u8 battery_capacity;
-	__u8 power_raw;
-	__u8 ps_connected;
-	__u8 bat_charging;
-	struct power_supply battery;
-	struct power_supply ac;
-	__u8 led_selector;
-	struct led_classdev *leds[4];
-};
-
-/*percent of battery capacity for Graphire
-  8th value means AC online and show 100% capacity */
-static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 };
-/*percent of battery capacity for Intuos4 WL, AC has a separate bit*/
-static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 };
-
-static enum power_supply_property wacom_battery_props[] = {
-	POWER_SUPPLY_PROP_PRESENT,
-	POWER_SUPPLY_PROP_CAPACITY,
-	POWER_SUPPLY_PROP_SCOPE,
-	POWER_SUPPLY_PROP_STATUS,
-};
-
-static enum power_supply_property wacom_ac_props[] = {
-	POWER_SUPPLY_PROP_PRESENT,
-	POWER_SUPPLY_PROP_ONLINE,
-	POWER_SUPPLY_PROP_SCOPE,
-};
-
-static void wacom_scramble(__u8 *image)
-{
-	__u16 mask;
-	__u16 s1;
-	__u16 s2;
-	__u16 r1 ;
-	__u16 r2 ;
-	__u16 r;
-	__u8 buf[256];
-	int i, w, x, y, z;
-
-	for (x = 0; x < 32; x++) {
-		for (y = 0; y < 8; y++)
-			buf[(8 * x) + (7 - y)] = image[(8 * x) + y];
-	}
-
-	/* Change 76543210 into GECA6420 as required by Intuos4 WL
-	 *        HGFEDCBA      HFDB7531
-	 */
-	for (x = 0; x < 4; x++) {
-		for (y = 0; y < 4; y++) {
-			for (z = 0; z < 8; z++) {
-				mask = 0x0001;
-				r1 = 0;
-				r2 = 0;
-				i = (x << 6) + (y << 4) + z;
-				s1 = buf[i];
-				s2 = buf[i+8];
-				for (w = 0; w < 8; w++) {
-					r1 |= (s1 & mask);
-					r2 |= (s2 & mask);
-					s1 <<= 1;
-					s2 <<= 1;
-					mask <<= 2;
-				}
-				r = r1 | (r2 << 1);
-				i = (x << 6) + (y << 4) + (z << 1);
-				image[i] = 0xFF & r;
-				image[i+1] = (0xFF00 & r) >> 8;
-			}
-		}
-	}
-}
-
-static void wacom_set_image(struct hid_device *hdev, const char *image,
-						__u8 icon_no)
-{
-	__u8 rep_data[68];
-	__u8 p[256];
-	int ret, i, j;
-
-	for (i = 0; i < 256; i++)
-		p[i] = image[i];
-
-	rep_data[0] = WAC_CMD_ICON_START_STOP;
-	rep_data[1] = 0;
-	ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
-				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
-	if (ret < 0)
-		goto err;
-
-	rep_data[0] = WAC_CMD_ICON_TRANSFER;
-	rep_data[1] = icon_no & 0x07;
-
-	wacom_scramble(p);
-
-	for (i = 0; i < 4; i++) {
-		for (j = 0; j < 64; j++)
-			rep_data[j + 3] = p[(i << 6) + j];
-
-		rep_data[2] = i;
-		ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 67,
-					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
-	}
-
-	rep_data[0] = WAC_CMD_ICON_START_STOP;
-	rep_data[1] = 0;
-
-	ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
-				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
-
-err:
-	return;
-}
-
-static void wacom_leds_set_brightness(struct led_classdev *led_dev,
-						enum led_brightness value)
-{
-	struct device *dev = led_dev->dev->parent;
-	struct hid_device *hdev;
-	struct wacom_data *wdata;
-	unsigned char *buf;
-	__u8 led = 0;
-	int i;
-
-	hdev = container_of(dev, struct hid_device, dev);
-	wdata = hid_get_drvdata(hdev);
-	for (i = 0; i < 4; ++i) {
-		if (wdata->leds[i] == led_dev)
-			wdata->led_selector = i;
-	}
-
-	led = wdata->led_selector | 0x04;
-	buf = kzalloc(9, GFP_KERNEL);
-	if (buf) {
-		buf[0] = WAC_CMD_LED_CONTROL;
-		buf[1] = led;
-		buf[2] = value >> 2;
-		buf[3] = value;
-		/* use fixed brightness for OLEDs */
-		buf[4] = 0x08;
-		hid_hw_raw_request(hdev, buf[0], buf, 9, HID_FEATURE_REPORT,
-				   HID_REQ_SET_REPORT);
-		kfree(buf);
-	}
-
-	return;
-}
-
-static enum led_brightness wacom_leds_get_brightness(struct led_classdev *led_dev)
-{
-	struct wacom_data *wdata;
-	struct device *dev = led_dev->dev->parent;
-	int value = 0;
-	int i;
-
-	wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
-
-	for (i = 0; i < 4; ++i) {
-		if (wdata->leds[i] == led_dev) {
-			value = wdata->leds[i]->brightness;
-			break;
-		}
-	}
-
-	return value;
-}
-
-
-static int wacom_initialize_leds(struct hid_device *hdev)
-{
-	struct wacom_data *wdata = hid_get_drvdata(hdev);
-	struct led_classdev *led;
-	struct device *dev = &hdev->dev;
-	size_t namesz = strlen(dev_name(dev)) + 12;
-	char *name;
-	int i, ret;
-
-	wdata->led_selector = 0;
-
-	for (i = 0; i < 4; i++) {
-		led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL);
-		if (!led) {
-			hid_warn(hdev,
-				 "can't allocate memory for LED selector\n");
-			ret = -ENOMEM;
-			goto err;
-		}
-
-		name = (void *)&led[1];
-		snprintf(name, namesz, "%s:selector:%d", dev_name(dev), i);
-		led->name = name;
-		led->brightness = 0;
-		led->max_brightness = 127;
-		led->brightness_get = wacom_leds_get_brightness;
-		led->brightness_set = wacom_leds_set_brightness;
-
-		wdata->leds[i] = led;
-
-		ret = led_classdev_register(dev, wdata->leds[i]);
-
-		if (ret) {
-			wdata->leds[i] = NULL;
-			kfree(led);
-			hid_warn(hdev, "can't register LED\n");
-			goto err;
-		}
-	}
-
-err:
-	return ret;
-}
-
-static void wacom_destroy_leds(struct hid_device *hdev)
-{
-	struct wacom_data *wdata = hid_get_drvdata(hdev);
-	struct led_classdev *led;
-	int i;
-
-	for (i = 0; i < 4; ++i) {
-		if (wdata->leds[i]) {
-			led = wdata->leds[i];
-			wdata->leds[i] = NULL;
-			led_classdev_unregister(led);
-			kfree(led);
-		}
-	}
-
-}
-
-static int wacom_battery_get_property(struct power_supply *psy,
-				enum power_supply_property psp,
-				union power_supply_propval *val)
-{
-	struct wacom_data *wdata = container_of(psy,
-					struct wacom_data, battery);
-	int ret = 0;
-
-	switch (psp) {
-	case POWER_SUPPLY_PROP_PRESENT:
-		val->intval = 1;
-		break;
-	case POWER_SUPPLY_PROP_SCOPE:
-		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
-		break;
-	case POWER_SUPPLY_PROP_CAPACITY:
-		val->intval = wdata->battery_capacity;
-		break;
-	case POWER_SUPPLY_PROP_STATUS:
-		if (wdata->bat_charging)
-			val->intval = POWER_SUPPLY_STATUS_CHARGING;
-		else
-			if (wdata->battery_capacity == 100 && wdata->ps_connected)
-				val->intval = POWER_SUPPLY_STATUS_FULL;
-			else
-				val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
-		break;
-	default:
-		ret = -EINVAL;
-		break;
-	}
-	return ret;
-}
-
-static int wacom_ac_get_property(struct power_supply *psy,
-				enum power_supply_property psp,
-				union power_supply_propval *val)
-{
-	struct wacom_data *wdata = container_of(psy, struct wacom_data, ac);
-	int ret = 0;
-
-	switch (psp) {
-	case POWER_SUPPLY_PROP_PRESENT:
-		/* fall through */
-	case POWER_SUPPLY_PROP_ONLINE:
-		val->intval = wdata->ps_connected;
-		break;
-	case POWER_SUPPLY_PROP_SCOPE:
-		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
-		break;
-	default:
-		ret = -EINVAL;
-		break;
-	}
-	return ret;
-}
-
-static void wacom_set_features(struct hid_device *hdev, u8 speed)
-{
-	struct wacom_data *wdata = hid_get_drvdata(hdev);
-	int limit, ret;
-	__u8 rep_data[2];
-
-	switch (hdev->product) {
-	case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
-		rep_data[0] = 0x03 ; rep_data[1] = 0x00;
-		limit = 3;
-		do {
-			ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
-					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
-		} while (ret < 0 && limit-- > 0);
-
-		if (ret >= 0) {
-			if (speed == 0)
-				rep_data[0] = 0x05;
-			else
-				rep_data[0] = 0x06;
-
-			rep_data[1] = 0x00;
-			limit = 3;
-			do {
-				ret = hid_hw_raw_request(hdev, rep_data[0],
-					rep_data, 2, HID_FEATURE_REPORT,
-					HID_REQ_SET_REPORT);
-			} while (ret < 0 && limit-- > 0);
-
-			if (ret >= 0) {
-				wdata->high_speed = speed;
-				return;
-			}
-		}
-
-		/*
-		 * Note that if the raw queries fail, it's not a hard failure
-		 * and it is safe to continue
-		 */
-		hid_warn(hdev, "failed to poke device, command %d, err %d\n",
-			 rep_data[0], ret);
-		break;
-	case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
-		if (speed == 1)
-			wdata->features &= ~0x20;
-		else
-			wdata->features |= 0x20;
-
-		rep_data[0] = 0x03;
-		rep_data[1] = wdata->features;
-
-		ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
-				HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
-		if (ret >= 0)
-			wdata->high_speed = speed;
-		break;
-	}
-
-	return;
-}
-
-static ssize_t wacom_show_speed(struct device *dev,
-				struct device_attribute
-				*attr, char *buf)
-{
-	struct wacom_data *wdata = dev_get_drvdata(dev);
-
-	return snprintf(buf, PAGE_SIZE, "%i\n", wdata->high_speed);
-}
-
-static ssize_t wacom_store_speed(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t count)
-{
-	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-	int new_speed;
-
-	if (sscanf(buf, "%1d", &new_speed ) != 1)
-		return -EINVAL;
-
-	if (new_speed == 0 || new_speed == 1) {
-		wacom_set_features(hdev, new_speed);
-		return strnlen(buf, PAGE_SIZE);
-	} else
-		return -EINVAL;
-}
-
-static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP,
-		wacom_show_speed, wacom_store_speed);
-
-#define WACOM_STORE(OLED_ID)						\
-static ssize_t wacom_oled##OLED_ID##_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);					\
-									\
-	if (count != 256)						\
-		return -EINVAL;						\
-									\
-	wacom_set_image(hdev, buf, OLED_ID);				\
-									\
-	return count;							\
-}									\
-									\
-static DEVICE_ATTR(oled##OLED_ID##_img, S_IWUSR | S_IWGRP, NULL,	\
-				wacom_oled##OLED_ID##_store)
-
-WACOM_STORE(0);
-WACOM_STORE(1);
-WACOM_STORE(2);
-WACOM_STORE(3);
-WACOM_STORE(4);
-WACOM_STORE(5);
-WACOM_STORE(6);
-WACOM_STORE(7);
-
-static int wacom_gr_parse_report(struct hid_device *hdev,
-			struct wacom_data *wdata,
-			struct input_dev *input, unsigned char *data)
-{
-	int tool, x, y, rw;
-
-	tool = 0;
-	/* Get X & Y positions */
-	x = le16_to_cpu(*(__le16 *) &data[2]);
-	y = le16_to_cpu(*(__le16 *) &data[4]);
-
-	/* Get current tool identifier */
-	if (data[1] & 0x90) { /* If pen is in the in/active area */
-		switch ((data[1] >> 5) & 3) {
-		case 0:	/* Pen */
-			tool = BTN_TOOL_PEN;
-			break;
-
-		case 1: /* Rubber */
-			tool = BTN_TOOL_RUBBER;
-			break;
-
-		case 2: /* Mouse with wheel */
-		case 3: /* Mouse without wheel */
-			tool = BTN_TOOL_MOUSE;
-			break;
-		}
-
-		/* Reset tool if out of active tablet area */
-		if (!(data[1] & 0x10))
-			tool = 0;
-	}
-
-	/* If tool changed, notify input subsystem */
-	if (wdata->tool != tool) {
-		if (wdata->tool) {
-			/* Completely reset old tool state */
-			if (wdata->tool == BTN_TOOL_MOUSE) {
-				input_report_key(input, BTN_LEFT, 0);
-				input_report_key(input, BTN_RIGHT, 0);
-				input_report_key(input, BTN_MIDDLE, 0);
-				input_report_abs(input, ABS_DISTANCE,
-					input_abs_get_max(input, ABS_DISTANCE));
-			} else {
-				input_report_key(input, BTN_TOUCH, 0);
-				input_report_key(input, BTN_STYLUS, 0);
-				input_report_key(input, BTN_STYLUS2, 0);
-				input_report_abs(input, ABS_PRESSURE, 0);
-			}
-			input_report_key(input, wdata->tool, 0);
-			input_sync(input);
-		}
-		wdata->tool = tool;
-		if (tool)
-			input_report_key(input, tool, 1);
-	}
-
-	if (tool) {
-		input_report_abs(input, ABS_X, x);
-		input_report_abs(input, ABS_Y, y);
-
-		switch ((data[1] >> 5) & 3) {
-		case 2: /* Mouse with wheel */
-			input_report_key(input, BTN_MIDDLE, data[1] & 0x04);
-			rw = (data[6] & 0x01) ? -1 :
-				(data[6] & 0x02) ? 1 : 0;
-			input_report_rel(input, REL_WHEEL, rw);
-			/* fall through */
-
-		case 3: /* Mouse without wheel */
-			input_report_key(input, BTN_LEFT, data[1] & 0x01);
-			input_report_key(input, BTN_RIGHT, data[1] & 0x02);
-			/* Compute distance between mouse and tablet */
-			rw = 44 - (data[6] >> 2);
-			if (rw < 0)
-				rw = 0;
-			else if (rw > 31)
-				rw = 31;
-			input_report_abs(input, ABS_DISTANCE, rw);
-			break;
-
-		default:
-			input_report_abs(input, ABS_PRESSURE,
-					data[6] | (((__u16) (data[1] & 0x08)) << 5));
-			input_report_key(input, BTN_TOUCH, data[1] & 0x01);
-			input_report_key(input, BTN_STYLUS, data[1] & 0x02);
-			input_report_key(input, BTN_STYLUS2, (tool == BTN_TOOL_PEN) && data[1] & 0x04);
-			break;
-		}
-
-		input_sync(input);
-	}
-
-	/* Report the state of the two buttons at the top of the tablet
-	 * as two extra fingerpad keys (buttons 4 & 5). */
-	rw = data[7] & 0x03;
-	if (rw != wdata->butstate) {
-		wdata->butstate = rw;
-		input_report_key(input, BTN_0, rw & 0x02);
-		input_report_key(input, BTN_1, rw & 0x01);
-		input_report_key(input, BTN_TOOL_FINGER, 0xf0);
-		input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
-		input_sync(input);
-	}
-
-	/* Store current battery capacity and power supply state*/
-	rw = (data[7] >> 2 & 0x07);
-	if (rw != wdata->power_raw) {
-		wdata->power_raw = rw;
-		wdata->battery_capacity = batcap_gr[rw];
-		if (rw == 7)
-			wdata->ps_connected = 1;
-		else
-			wdata->ps_connected = 0;
-	}
-	return 1;
-}
-
-static void wacom_i4_parse_button_report(struct wacom_data *wdata,
-			struct input_dev *input, unsigned char *data)
-{
-	__u16 new_butstate;
-	__u8 new_whlstate;
-	__u8 sync = 0;
-
-	new_whlstate = data[1];
-	if (new_whlstate != wdata->whlstate) {
-		wdata->whlstate = new_whlstate;
-		if (new_whlstate & 0x80) {
-			input_report_key(input, BTN_TOUCH, 1);
-			input_report_abs(input, ABS_WHEEL, (new_whlstate & 0x7f));
-			input_report_key(input, BTN_TOOL_FINGER, 1);
-		} else {
-			input_report_key(input, BTN_TOUCH, 0);
-			input_report_abs(input, ABS_WHEEL, 0);
-			input_report_key(input, BTN_TOOL_FINGER, 0);
-		}
-		sync = 1;
-	}
-
-	new_butstate = (data[3] << 1) | (data[2] & 0x01);
-	if (new_butstate != wdata->butstate) {
-		wdata->butstate = new_butstate;
-		input_report_key(input, BTN_0, new_butstate & 0x001);
-		input_report_key(input, BTN_1, new_butstate & 0x002);
-		input_report_key(input, BTN_2, new_butstate & 0x004);
-		input_report_key(input, BTN_3, new_butstate & 0x008);
-		input_report_key(input, BTN_4, new_butstate & 0x010);
-		input_report_key(input, BTN_5, new_butstate & 0x020);
-		input_report_key(input, BTN_6, new_butstate & 0x040);
-		input_report_key(input, BTN_7, new_butstate & 0x080);
-		input_report_key(input, BTN_8, new_butstate & 0x100);
-		input_report_key(input, BTN_TOOL_FINGER, 1);
-		sync = 1;
-	}
-
-	if (sync) {
-		input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
-		input_event(input, EV_MSC, MSC_SERIAL, 0xffffffff);
-		input_sync(input);
-	}
-}
-
-static void wacom_i4_parse_pen_report(struct wacom_data *wdata,
-			struct input_dev *input, unsigned char *data)
-{
-	__u16 x, y, pressure;
-	__u8 distance;
-	__u8 tilt_x, tilt_y;
-
-	switch (data[1]) {
-	case 0x80: /* Out of proximity report */
-		input_report_key(input, BTN_TOUCH, 0);
-		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, wdata->tool, 0);
-		input_report_abs(input, ABS_MISC, 0);
-		input_event(input, EV_MSC, MSC_SERIAL, wdata->serial);
-		wdata->tool = 0;
-		input_sync(input);
-		break;
-	case 0xC2: /* Tool report */
-		wdata->id = ((data[2] << 4) | (data[3] >> 4) |
-			((data[7] & 0x0f) << 20) |
-			((data[8] & 0xf0) << 12));
-		wdata->serial = ((data[3] & 0x0f) << 28) +
-				(data[4] << 20) + (data[5] << 12) +
-				(data[6] << 4) + (data[7] >> 4);
-
-		switch (wdata->id) {
-		case 0x100802:
-			wdata->tool = BTN_TOOL_PEN;
-			break;
-		case 0x10080A:
-			wdata->tool = BTN_TOOL_RUBBER;
-			break;
-		}
-		break;
-	default: /* Position/pressure report */
-		x = data[2] << 9 | data[3] << 1 | ((data[9] & 0x02) >> 1);
-		y = data[4] << 9 | data[5] << 1 | (data[9] & 0x01);
-		pressure = (data[6] << 3) | ((data[7] & 0xC0) >> 5)
-			| (data[1] & 0x01);
-		distance = (data[9] >> 2) & 0x3f;
-		tilt_x = ((data[7] << 1) & 0x7e) | (data[8] >> 7);
-		tilt_y = data[8] & 0x7f;
-
-		input_report_key(input, BTN_TOUCH, pressure > 1);
-
-		input_report_key(input, BTN_STYLUS, data[1] & 0x02);
-		input_report_key(input, BTN_STYLUS2, data[1] & 0x04);
-		input_report_key(input, wdata->tool, 1);
-		input_report_abs(input, ABS_X, x);
-		input_report_abs(input, ABS_Y, y);
-		input_report_abs(input, ABS_PRESSURE, pressure);
-		input_report_abs(input, ABS_DISTANCE, distance);
-		input_report_abs(input, ABS_TILT_X, tilt_x);
-		input_report_abs(input, ABS_TILT_Y, tilt_y);
-		input_report_abs(input, ABS_MISC, wdata->id);
-		input_event(input, EV_MSC, MSC_SERIAL, wdata->serial);
-		input_report_key(input, wdata->tool, 1);
-		input_sync(input);
-		break;
-	}
-
-	return;
-}
-
-static void wacom_i4_parse_report(struct hid_device *hdev,
-			struct wacom_data *wdata,
-			struct input_dev *input, unsigned char *data)
-{
-	switch (data[0]) {
-	case 0x00: /* Empty report */
-		break;
-	case 0x02: /* Pen report */
-		wacom_i4_parse_pen_report(wdata, input, data);
-		break;
-	case 0x03: /* Features Report */
-		wdata->features = data[2];
-		break;
-	case 0x0C: /* Button report */
-		wacom_i4_parse_button_report(wdata, input, data);
-		break;
-	default:
-		hid_err(hdev, "Unknown report: %d,%d\n", data[0], data[1]);
-		break;
-	}
-}
-
-static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
-		u8 *raw_data, int size)
-{
-	struct wacom_data *wdata = hid_get_drvdata(hdev);
-	struct hid_input *hidinput;
-	struct input_dev *input;
-	unsigned char *data = (unsigned char *) raw_data;
-	int i;
-	__u8 power_raw;
-
-	if (!(hdev->claimed & HID_CLAIMED_INPUT))
-		return 0;
-
-	hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
-	input = hidinput->input;
-
-	switch (hdev->product) {
-	case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
-		if (data[0] == 0x03) {
-			return wacom_gr_parse_report(hdev, wdata, input, data);
-		} else {
-			hid_err(hdev, "Unknown report: %d,%d size:%d\n",
-					data[0], data[1], size);
-			return 0;
-		}
-		break;
-	case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
-		i = 1;
-
-		switch (data[0]) {
-		case 0x04:
-			wacom_i4_parse_report(hdev, wdata, input, data + i);
-			i += 10;
-			/* fall through */
-		case 0x03:
-			wacom_i4_parse_report(hdev, wdata, input, data + i);
-			i += 10;
-			wacom_i4_parse_report(hdev, wdata, input, data + i);
-			power_raw = data[i+10];
-			if (power_raw != wdata->power_raw) {
-				wdata->power_raw = power_raw;
-				wdata->battery_capacity = batcap_i4[power_raw & 0x07];
-				wdata->bat_charging = (power_raw & 0x08) ? 1 : 0;
-				wdata->ps_connected = (power_raw & 0x10) ? 1 : 0;
-			}
-
-			break;
-		default:
-			hid_err(hdev, "Unknown report: %d,%d size:%d\n",
-					data[0], data[1], size);
-			return 0;
-		}
-	}
-	return 1;
-}
-
-static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi,
-	struct hid_field *field, struct hid_usage *usage, unsigned long **bit,
-								int *max)
-{
-	struct input_dev *input = hi->input;
-
-	__set_bit(INPUT_PROP_POINTER, input->propbit);
-
-	/* Basics */
-	input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
-
-	__set_bit(REL_WHEEL, input->relbit);
-
-	__set_bit(BTN_TOOL_PEN, input->keybit);
-	__set_bit(BTN_TOUCH, input->keybit);
-	__set_bit(BTN_STYLUS, input->keybit);
-	__set_bit(BTN_STYLUS2, input->keybit);
-	__set_bit(BTN_LEFT, input->keybit);
-	__set_bit(BTN_RIGHT, input->keybit);
-	__set_bit(BTN_MIDDLE, input->keybit);
-
-	/* Pad */
-	input_set_capability(input, EV_MSC, MSC_SERIAL);
-
-	__set_bit(BTN_0, input->keybit);
-	__set_bit(BTN_1, input->keybit);
-	__set_bit(BTN_TOOL_FINGER, input->keybit);
-
-	/* Distance, rubber and mouse */
-	__set_bit(BTN_TOOL_RUBBER, input->keybit);
-	__set_bit(BTN_TOOL_MOUSE, input->keybit);
-
-	switch (hdev->product) {
-	case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
-		input_set_abs_params(input, ABS_X, 0, 16704, 4, 0);
-		input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0);
-		input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0);
-		input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0);
-		break;
-	case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
-		__set_bit(ABS_WHEEL, input->absbit);
-		__set_bit(ABS_MISC, input->absbit);
-		__set_bit(BTN_2, input->keybit);
-		__set_bit(BTN_3, input->keybit);
-		__set_bit(BTN_4, input->keybit);
-		__set_bit(BTN_5, input->keybit);
-		__set_bit(BTN_6, input->keybit);
-		__set_bit(BTN_7, input->keybit);
-		__set_bit(BTN_8, input->keybit);
-		input_set_abs_params(input, ABS_WHEEL, 0, 71, 0, 0);
-		input_set_abs_params(input, ABS_X, 0, 40640, 4, 0);
-		input_set_abs_params(input, ABS_Y, 0, 25400, 4, 0);
-		input_set_abs_params(input, ABS_PRESSURE, 0, 2047, 0, 0);
-		input_set_abs_params(input, ABS_DISTANCE, 0, 63, 0, 0);
-		input_set_abs_params(input, ABS_TILT_X, 0, 127, 0, 0);
-		input_set_abs_params(input, ABS_TILT_Y, 0, 127, 0, 0);
-		break;
-	}
-
-	return 0;
-}
-
-static int wacom_probe(struct hid_device *hdev,
-		const struct hid_device_id *id)
-{
-	struct wacom_data *wdata;
-	int ret;
-
-	wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
-	if (wdata == NULL) {
-		hid_err(hdev, "can't alloc wacom descriptor\n");
-		return -ENOMEM;
-	}
-
-	hid_set_drvdata(hdev, wdata);
-
-	/* Parse the HID report now */
-	ret = hid_parse(hdev);
-	if (ret) {
-		hid_err(hdev, "parse failed\n");
-		goto err_free;
-	}
-
-	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
-	if (ret) {
-		hid_err(hdev, "hw start failed\n");
-		goto err_free;
-	}
-
-	ret = device_create_file(&hdev->dev, &dev_attr_speed);
-	if (ret)
-		hid_warn(hdev,
-			 "can't create sysfs speed attribute err: %d\n", ret);
-
-#define OLED_INIT(OLED_ID)						\
-	do {								\
-		ret = device_create_file(&hdev->dev,			\
-				&dev_attr_oled##OLED_ID##_img);		\
-		if (ret)						\
-			hid_warn(hdev,					\
-			 "can't create sysfs oled attribute, err: %d\n", ret);\
-	} while (0)
-
-OLED_INIT(0);
-OLED_INIT(1);
-OLED_INIT(2);
-OLED_INIT(3);
-OLED_INIT(4);
-OLED_INIT(5);
-OLED_INIT(6);
-OLED_INIT(7);
-
-	wdata->features = 0;
-	wacom_set_features(hdev, 1);
-
-	if (hdev->product == USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) {
-		sprintf(hdev->name, "%s", "Wacom Intuos4 WL");
-		ret = wacom_initialize_leds(hdev);
-		if (ret)
-			hid_warn(hdev,
-				 "can't create led attribute, err: %d\n", ret);
-	}
-
-	wdata->battery.properties = wacom_battery_props;
-	wdata->battery.num_properties = ARRAY_SIZE(wacom_battery_props);
-	wdata->battery.get_property = wacom_battery_get_property;
-	wdata->battery.name = "wacom_battery";
-	wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
-	wdata->battery.use_for_apm = 0;
-
-
-	ret = power_supply_register(&hdev->dev, &wdata->battery);
-	if (ret) {
-		hid_err(hdev, "can't create sysfs battery attribute, err: %d\n",
-			ret);
-		goto err_battery;
-	}
-
-	power_supply_powers(&wdata->battery, &hdev->dev);
-
-	wdata->ac.properties = wacom_ac_props;
-	wdata->ac.num_properties = ARRAY_SIZE(wacom_ac_props);
-	wdata->ac.get_property = wacom_ac_get_property;
-	wdata->ac.name = "wacom_ac";
-	wdata->ac.type = POWER_SUPPLY_TYPE_MAINS;
-	wdata->ac.use_for_apm = 0;
-
-	ret = power_supply_register(&hdev->dev, &wdata->ac);
-	if (ret) {
-		hid_err(hdev,
-			"can't create ac battery attribute, err: %d\n", ret);
-		goto err_ac;
-	}
-
-	power_supply_powers(&wdata->ac, &hdev->dev);
-	return 0;
-
-err_ac:
-	power_supply_unregister(&wdata->battery);
-err_battery:
-	wacom_destroy_leds(hdev);
-	device_remove_file(&hdev->dev, &dev_attr_oled0_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled1_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled2_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled3_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled4_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled5_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled6_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled7_img);
-	device_remove_file(&hdev->dev, &dev_attr_speed);
-	hid_hw_stop(hdev);
-err_free:
-	kfree(wdata);
-	return ret;
-}
-
-static void wacom_remove(struct hid_device *hdev)
-{
-	struct wacom_data *wdata = hid_get_drvdata(hdev);
-
-	wacom_destroy_leds(hdev);
-	device_remove_file(&hdev->dev, &dev_attr_oled0_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled1_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled2_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled3_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled4_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled5_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled6_img);
-	device_remove_file(&hdev->dev, &dev_attr_oled7_img);
-	device_remove_file(&hdev->dev, &dev_attr_speed);
-	hid_hw_stop(hdev);
-
-	power_supply_unregister(&wdata->battery);
-	power_supply_unregister(&wdata->ac);
-	kfree(hid_get_drvdata(hdev));
-}
-
-static const struct hid_device_id wacom_devices[] = {
-	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
-	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
-
-	{ }
-};
-MODULE_DEVICE_TABLE(hid, wacom_devices);
-
-static struct hid_driver wacom_driver = {
-	.name = "wacom",
-	.id_table = wacom_devices,
-	.probe = wacom_probe,
-	.remove = wacom_remove,
-	.raw_event = wacom_raw_event,
-	.input_mapped = wacom_input_mapped,
-};
-module_hid_driver(wacom_driver);
-
-MODULE_DESCRIPTION("Driver for Wacom Graphire Bluetooth and Wacom Intuos4 WL");
-MODULE_LICENSE("GPL");
diff --git a/drivers/input/tablet/wacom.h b/drivers/hid/wacom.h
similarity index 88%
rename from drivers/input/tablet/wacom.h
rename to drivers/hid/wacom.h
index 9ebf0ed..64bc1b2 100644
--- a/drivers/input/tablet/wacom.h
+++ b/drivers/hid/wacom.h
@@ -12,6 +12,7 @@
  *  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>
+ *  Copyright (c) 2014 Benjamin Tissoires	<benjamin.tissoires@redhat.com>
  *
  *  ChangeLog:
  *      v0.1 (vp)  - Initial release
@@ -72,6 +73,8 @@
  *      v1.52 (pc) - Query Wacom data upon system resume
  *                 - add defines for features->type
  *                 - add new devices (0x9F, 0xE2, and 0XE3)
+ *      v2.00 (bt) - conversion to a HID driver
+ *                 - integration of the Bluetooth devices
  */
 
 /*
@@ -93,35 +96,30 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.53"
+#define DRIVER_VERSION "v2.00"
 #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 {
-	dma_addr_t data_dma;
 	struct usb_device *usbdev;
 	struct usb_interface *intf;
-	struct urb *irq;
 	struct wacom_wac wacom_wac;
+	struct hid_device *hdev;
 	struct mutex lock;
 	struct work_struct work;
-	bool open;
-	char phys[32];
 	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;
+	bool led_initialized;
 	struct power_supply battery;
+	struct power_supply ac;
 };
 
 static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
@@ -130,10 +128,19 @@
 	schedule_work(&wacom->work);
 }
 
-extern const struct usb_device_id wacom_ids[];
+static inline void wacom_notify_battery(struct wacom_wac *wacom_wac)
+{
+	struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
+
+	power_supply_changed(&wacom->battery);
+}
+
+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
index 0000000..3e388ec
--- /dev/null
+++ b/drivers/hid/wacom_sys.c
@@ -0,0 +1,1456 @@
+/*
+ * 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_ICON_BT_XFER	0x26
+#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 *buf,
+			    size_t size, unsigned int retries)
+{
+	int retval;
+
+	do {
+		retval = hid_hw_raw_request(hdev, buf[0], 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, 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;
+}
+
+static int wacom_bt_query_tablet_data(struct hid_device *hdev, u8 speed,
+		struct wacom_features *features)
+{
+	struct wacom *wacom = hid_get_drvdata(hdev);
+	int ret;
+	u8 rep_data[2];
+
+	switch (features->type) {
+	case GRAPHIRE_BT:
+		rep_data[0] = 0x03;
+		rep_data[1] = 0x00;
+		ret = wacom_set_report(hdev, HID_FEATURE_REPORT, rep_data, 2,
+					3);
+
+		if (ret >= 0) {
+			rep_data[0] = speed == 0 ? 0x05 : 0x06;
+			rep_data[1] = 0x00;
+
+			ret = wacom_set_report(hdev, HID_FEATURE_REPORT,
+						rep_data, 2, 3);
+
+			if (ret >= 0) {
+				wacom->wacom_wac.bt_high_speed = speed;
+				return 0;
+			}
+		}
+
+		/*
+		 * Note that if the raw queries fail, it's not a hard failure
+		 * and it is safe to continue
+		 */
+		hid_warn(hdev, "failed to poke device, command %d, err %d\n",
+			 rep_data[0], ret);
+		break;
+	case INTUOS4WL:
+		if (speed == 1)
+			wacom->wacom_wac.bt_features &= ~0x20;
+		else
+			wacom->wacom_wac.bt_features |= 0x20;
+
+		rep_data[0] = 0x03;
+		rep_data[1] = wacom->wacom_wac.bt_features;
+
+		ret = wacom_set_report(hdev, HID_FEATURE_REPORT, rep_data, 2,
+					1);
+		if (ret >= 0)
+			wacom->wacom_wac.bt_high_speed = speed;
+		break;
+	}
+
+	return 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 (hdev->bus == BUS_BLUETOOTH)
+		return wacom_bt_query_tablet_data(hdev, 1, 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, buf, 9,
+				  WAC_CMD_RETRIES);
+	kfree(buf);
+
+	return retval;
+}
+
+static int wacom_led_putimage(struct wacom *wacom, int button_id, u8 xfer_id,
+		const unsigned len, const void *img)
+{
+	unsigned char *buf;
+	int i, retval;
+	const unsigned chunk_len = len / 4; /* 4 chunks are needed to be sent */
+
+	buf = kzalloc(chunk_len + 3 , 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, buf, 2,
+				  WAC_CMD_RETRIES);
+	if (retval < 0)
+		goto out;
+
+	buf[0] = xfer_id;
+	buf[1] = button_id & 0x07;
+	for (i = 0; i < 4; i++) {
+		buf[2] = i;
+		memcpy(buf + 3, img + i * chunk_len, chunk_len);
+
+		retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
+					  buf, chunk_len + 3, 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, 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;
+	unsigned len;
+	u8 xfer_id;
+
+	if (hdev->bus == BUS_BLUETOOTH) {
+		len = 256;
+		xfer_id = WAC_CMD_ICON_BT_XFER;
+	} else {
+		len = 1024;
+		xfer_id = WAC_CMD_ICON_XFER;
+	}
+
+	if (count != len)
+		return -EINVAL;
+
+	mutex_lock(&wacom->lock);
+
+	err = wacom_led_putimage(wacom, button_id, xfer_id, len, 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 INTUOS4WL:
+	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);
+	wacom->led_initialized = true;
+
+	return 0;
+}
+
+static void wacom_destroy_leds(struct wacom *wacom)
+{
+	if (!wacom->led_initialized)
+		return;
+
+	wacom->led_initialized = false;
+
+	switch (wacom->wacom_wac.features.type) {
+	case INTUOS4S:
+	case INTUOS4:
+	case INTUOS4WL:
+	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_STATUS,
+	POWER_SUPPLY_PROP_SCOPE,
+	POWER_SUPPLY_PROP_CAPACITY
+};
+
+static enum power_supply_property wacom_ac_props[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_SCOPE,
+};
+
+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;
+			break;
+		case POWER_SUPPLY_PROP_STATUS:
+			if (wacom->wacom_wac.bat_charging)
+				val->intval = POWER_SUPPLY_STATUS_CHARGING;
+			else if (wacom->wacom_wac.battery_capacity == 100 &&
+				    wacom->wacom_wac.ps_connected)
+				val->intval = POWER_SUPPLY_STATUS_FULL;
+			else
+				val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+			break;
+		default:
+			ret = -EINVAL;
+			break;
+	}
+
+	return ret;
+}
+
+static int wacom_ac_get_property(struct power_supply *psy,
+				enum power_supply_property psp,
+				union power_supply_propval *val)
+{
+	struct wacom *wacom = container_of(psy, struct wacom, ac);
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PRESENT:
+		/* fall through */
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = wacom->wacom_wac.ps_connected;
+		break;
+	case POWER_SUPPLY_PROP_SCOPE:
+		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int wacom_initialize_battery(struct wacom *wacom)
+{
+	static atomic_t battery_no = ATOMIC_INIT(0);
+	int error;
+	unsigned long n;
+
+	if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) {
+		n = atomic_inc_return(&battery_no) - 1;
+
+		wacom->battery.properties = wacom_battery_props;
+		wacom->battery.num_properties = ARRAY_SIZE(wacom_battery_props);
+		wacom->battery.get_property = wacom_battery_get_property;
+		sprintf(wacom->wacom_wac.bat_name, "wacom_battery_%ld", n);
+		wacom->battery.name = wacom->wacom_wac.bat_name;
+		wacom->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+		wacom->battery.use_for_apm = 0;
+
+		wacom->ac.properties = wacom_ac_props;
+		wacom->ac.num_properties = ARRAY_SIZE(wacom_ac_props);
+		wacom->ac.get_property = wacom_ac_get_property;
+		sprintf(wacom->wacom_wac.ac_name, "wacom_ac_%ld", n);
+		wacom->ac.name = wacom->wacom_wac.ac_name;
+		wacom->ac.type = POWER_SUPPLY_TYPE_MAINS;
+		wacom->ac.use_for_apm = 0;
+
+		error = power_supply_register(&wacom->hdev->dev,
+					      &wacom->battery);
+		if (error)
+			return error;
+
+		power_supply_powers(&wacom->battery, &wacom->hdev->dev);
+
+		error = power_supply_register(&wacom->hdev->dev, &wacom->ac);
+		if (error) {
+			power_supply_unregister(&wacom->battery);
+			return error;
+		}
+
+		power_supply_powers(&wacom->ac, &wacom->hdev->dev);
+	}
+
+	return 0;
+}
+
+static void wacom_destroy_battery(struct wacom *wacom)
+{
+	if ((wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) &&
+	     wacom->battery.dev) {
+		power_supply_unregister(&wacom->battery);
+		wacom->battery.dev = NULL;
+		power_supply_unregister(&wacom->ac);
+		wacom->ac.dev = NULL;
+	}
+}
+
+static ssize_t wacom_show_speed(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, PAGE_SIZE, "%i\n", wacom->wacom_wac.bt_high_speed);
+}
+
+static ssize_t wacom_store_speed(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);
+	u8 new_speed;
+
+	if (kstrtou8(buf, 0, &new_speed))
+		return -EINVAL;
+
+	if (new_speed != 0 && new_speed != 1)
+		return -EINVAL;
+
+	wacom_bt_query_tablet_data(hdev, new_speed, &wacom->wacom_wac.features);
+
+	return count;
+}
+
+static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP,
+		wacom_show_speed, wacom_store_speed);
+
+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");
+		wacom_wac1->shared->type = 0;
+	} else {
+		const struct hid_device_id *id = wacom_ids;
+
+		hid_info(wacom->hdev, "wireless tablet connected with PID %x\n",
+			 wacom_wac->pid);
+
+		while (id->bus) {
+			if (id->vendor == USB_VENDOR_ID_WACOM &&
+			    id->product == wacom_wac->pid)
+				break;
+			id++;
+		}
+
+		if (!id->bus) {
+			hid_info(wacom->hdev, "ignoring unknown PID.\n");
+			return;
+		}
+
+		/* Stylus interface */
+		wacom_wac1->features =
+			*((struct wacom_features *)id->driver_data);
+		wacom_wac1->features.device_type = 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;
+	}
+
+	if (hdev->bus == BUS_BLUETOOTH)
+		features->quirks |= WACOM_QUIRK_BATTERY;
+
+	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_MONITOR) &&
+	     (features->quirks & WACOM_QUIRK_BATTERY)) {
+		error = wacom_initialize_battery(wacom);
+		if (error)
+			goto fail3;
+	}
+
+	if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
+		error = wacom_register_inputs(wacom);
+		if (error)
+			goto fail4;
+	}
+
+	if (hdev->bus == BUS_BLUETOOTH) {
+		error = device_create_file(&hdev->dev, &dev_attr_speed);
+		if (error)
+			hid_warn(hdev,
+				 "can't create sysfs speed attribute err: %d\n",
+				 error);
+	}
+
+	/* 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 fail5;
+	}
+
+	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;
+
+ fail5:	if (hdev->bus == BUS_BLUETOOTH)
+		device_remove_file(&hdev->dev, &dev_attr_speed);
+	wacom_unregister_inputs(wacom);
+ fail4:	wacom_destroy_battery(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);
+	if (hdev->bus == BUS_BLUETOOTH)
+		device_remove_file(&hdev->dev, &dev_attr_speed);
+	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);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/hid/wacom_wac.c
similarity index 74%
rename from drivers/input/tablet/wacom_wac.c
rename to drivers/hid/wacom_wac.c
index 977d05c..d4a2d53 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -25,11 +25,23 @@
 #define WACOM_INTUOS_RES	100
 #define WACOM_INTUOS3_RES	200
 
-/* Scale factor relating reported contact size to logical contact area.
+/*
+ * 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
 
+/*
+ * Percent of battery capacity for Graphire.
+ * 8th value means AC online and show 100% capacity.
+ */
+static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 };
+
+/*
+ * Percent of battery capacity for Intuos4 WL, AC has a separate bit.
+ */
+static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 };
+
 static int wacom_penpartner_irq(struct wacom_wac *wacom)
 {
 	unsigned char *data = wacom->data;
@@ -217,17 +229,13 @@
 			"%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);
-		/*
-		 * Serial number is required when expresskeys are
-		 * reported through pen interface.
-		 */
-		input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
 		return 1;
 	} else {
 		prox = data[1] & 0x80;
@@ -257,7 +265,6 @@
 			wacom->id[0] = 0;
 		input_report_key(input, wacom->tool[0], prox);
 		input_report_abs(input, ABS_MISC, wacom->id[0]);
-		input_event(input, EV_MSC, MSC_SERIAL, 1);
 		return 1;
 	}
 }
@@ -267,11 +274,20 @@
 	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 battery_capacity, ps_connected;
 	int prox;
 	int rw = 0;
 	int retval = 0;
 
-	if (data[0] != WACOM_REPORT_PENABLED) {
+	if (features->type == GRAPHIRE_BT) {
+		if (data[0] != WACOM_REPORT_PENABLED_BT) {
+			dev_dbg(input->dev.parent,
+				"%s: received unknown report #%d\n", __func__,
+				data[0]);
+			goto exit;
+		}
+	} else if (data[0] != WACOM_REPORT_PENABLED) {
 		dev_dbg(input->dev.parent,
 			"%s: received unknown report #%d\n", __func__, data[0]);
 		goto exit;
@@ -305,7 +321,12 @@
 		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));
+			if (features->type == GRAPHIRE_BT)
+				input_report_abs(input, ABS_PRESSURE, data[6] |
+					(((__u16) (data[1] & 0x08)) << 5));
+			else
+				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);
@@ -316,6 +337,20 @@
 					features->type == WACOM_MO) {
 				input_report_abs(input, ABS_DISTANCE, data[6] & 0x3f);
 				rw = (data[7] & 0x04) - (data[7] & 0x03);
+			} else if (features->type == GRAPHIRE_BT) {
+				/* Compute distance between mouse and tablet */
+				rw = 44 - (data[6] >> 2);
+				rw = clamp_val(rw, 0, 31);
+				input_report_abs(input, ABS_DISTANCE, rw);
+				if (((data[1] >> 5) & 3) == 2) {
+					/* Mouse with wheel */
+					input_report_key(input, BTN_MIDDLE,
+							data[1] & 0x04);
+					rw = (data[6] & 0x01) ? -1 :
+						(data[6] & 0x02) ? 1 : 0;
+				} else {
+					rw = 0;
+				}
 			} else {
 				input_report_abs(input, ABS_DISTANCE, data[7] & 0x3f);
 				rw = -(signed char)data[6];
@@ -327,7 +362,6 @@
 			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_event(input, EV_MSC, MSC_SERIAL, 1);
 		input_sync(input); /* sync last event */
 	}
 
@@ -337,14 +371,13 @@
 		prox = data[7] & 0xf8;
 		if (prox || wacom->id[1]) {
 			wacom->id[1] = PAD_DEVICE_ID;
-			input_report_key(input, BTN_BACK, (data[7] & 0x40));
-			input_report_key(input, BTN_FORWARD, (data[7] & 0x80));
+			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(input, REL_WHEEL, rw);
+			input_report_rel(pad_input, REL_WHEEL, rw);
 			if (!prox)
 				wacom->id[1] = 0;
-			input_report_abs(input, ABS_MISC, wacom->id[1]);
-			input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
+			input_report_abs(pad_input, ABS_MISC, wacom->id[1]);
 			retval = 1;
 		}
 		break;
@@ -353,18 +386,42 @@
 		prox = (data[7] & 0xf8) || data[8];
 		if (prox || wacom->id[1]) {
 			wacom->id[1] = PAD_DEVICE_ID;
-			input_report_key(input, BTN_BACK, (data[7] & 0x08));
-			input_report_key(input, BTN_LEFT, (data[7] & 0x20));
-			input_report_key(input, BTN_FORWARD, (data[7] & 0x10));
-			input_report_key(input, BTN_RIGHT, (data[7] & 0x40));
-			input_report_abs(input, ABS_WHEEL, (data[8] & 0x7f));
+			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(input, ABS_MISC, wacom->id[1]);
-			input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
+			input_report_abs(pad_input, ABS_MISC, wacom->id[1]);
 			retval = 1;
 		}
 		break;
+	case GRAPHIRE_BT:
+		prox = data[7] & 0x03;
+		if (prox || wacom->id[1]) {
+			wacom->id[1] = PAD_DEVICE_ID;
+			input_report_key(pad_input, BTN_0, (data[7] & 0x02));
+			input_report_key(pad_input, BTN_1, (data[7] & 0x01));
+			if (!prox)
+				wacom->id[1] = 0;
+			input_report_abs(pad_input, ABS_MISC, wacom->id[1]);
+			retval = 1;
+		}
+		break;
+	}
+
+	/* Store current battery capacity and power supply state */
+	if (features->type == GRAPHIRE_BT) {
+		rw = (data[7] >> 2 & 0x07);
+		battery_capacity = batcap_gr[rw];
+		ps_connected = rw == 7;
+		if ((wacom->battery_capacity != battery_capacity) ||
+		    (wacom->ps_connected != ps_connected)) {
+			wacom->battery_capacity = battery_capacity;
+			wacom->ps_connected = ps_connected;
+			wacom_notify_battery(wacom);
+		}
 	}
 exit:
 	return retval;
@@ -584,6 +641,7 @@
 
 	/* 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));
@@ -773,7 +831,6 @@
 				input_report_abs(input, ABS_MISC, 0);
 			}
 		}
-		input_event(input, EV_MSC, MSC_SERIAL, 0xffffffff);
                 return 1;
 	}
 
@@ -901,6 +958,58 @@
 	return int_sqrt(x*x + y*y);
 }
 
+static void wacom_intuos_bt_process_data(struct wacom_wac *wacom,
+		unsigned char *data)
+{
+	memcpy(wacom->data, data, 10);
+	wacom_intuos_irq(wacom);
+
+	input_sync(wacom->input);
+	if (wacom->pad_input)
+		input_sync(wacom->pad_input);
+}
+
+static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len)
+{
+	unsigned char data[WACOM_PKGLEN_MAX];
+	int i = 1;
+	unsigned power_raw, battery_capacity, bat_charging, ps_connected;
+
+	memcpy(data, wacom->data, len);
+
+	switch (data[0]) {
+	case 0x04:
+		wacom_intuos_bt_process_data(wacom, data + i);
+		i += 10;
+		/* fall through */
+	case 0x03:
+		wacom_intuos_bt_process_data(wacom, data + i);
+		i += 10;
+		wacom_intuos_bt_process_data(wacom, data + i);
+		i += 10;
+		power_raw = data[i];
+		bat_charging = (power_raw & 0x08) ? 1 : 0;
+		ps_connected = (power_raw & 0x10) ? 1 : 0;
+		battery_capacity = batcap_i4[power_raw & 0x07];
+		if ((wacom->battery_capacity != battery_capacity) ||
+		    (wacom->bat_charging != bat_charging) ||
+		    (wacom->ps_connected != ps_connected)) {
+			wacom->battery_capacity = battery_capacity;
+			wacom->bat_charging = bat_charging;
+			wacom->ps_connected = ps_connected;
+			wacom_notify_battery(wacom);
+		}
+
+		break;
+	default:
+		dev_dbg(wacom->input->dev.parent,
+				"Unknown report: %d,%d size:%zu\n",
+				data[0], data[1], len);
+		return 0;
+	}
+	return 0;
+}
+
 static int wacom_24hdt_irq(struct wacom_wac *wacom)
 {
 	struct input_dev *input = wacom->input;
@@ -1093,7 +1202,7 @@
 		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_abs(input, ABS_PRESSURE, ((data[7] & 0x07) << 8) | data[6]);
 		input_report_key(input, BTN_TOUCH, data[1] & 0x05);
 		input_report_key(input, wacom->tool[0], prox);
 		return 1;
@@ -1143,6 +1252,7 @@
 {
 	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;
 
@@ -1177,14 +1287,12 @@
 
 	input_mt_report_pointer_emulation(input, true);
 
-	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_BACK, (data[1] & 0x02) != 0);
-	input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0);
+	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);
 
-	input_sync(input);
-
-	return 0;
+	return 1;
 }
 
 static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
@@ -1232,7 +1340,7 @@
 
 static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data)
 {
-	struct input_dev *input = wacom->input;
+	struct input_dev *input = wacom->pad_input;
 	struct wacom_features *features = &wacom->features;
 
 	if (features->type == INTUOSHT) {
@@ -1269,9 +1377,7 @@
 	}
 	input_mt_report_pointer_emulation(input, true);
 
-	input_sync(input);
-
-	return 0;
+	return 1;
 }
 
 static int wacom_bpt_pen(struct wacom_wac *wacom)
@@ -1375,7 +1481,7 @@
 
 	connected = data[1] & 0x01;
 	if (connected) {
-		int pid, battery;
+		int pid, battery, ps_connected;
 
 		if ((wacom->shared->type == INTUOSHT) &&
 				wacom->shared->touch_max) {
@@ -1385,17 +1491,29 @@
 		}
 
 		pid = get_unaligned_be16(&data[6]);
-		battery = data[5] & 0x3f;
+		battery = (data[5] & 0x3f) * 100 / 31;
+		ps_connected = !!(data[5] & 0x80);
 		if (wacom->pid != pid) {
 			wacom->pid = pid;
 			wacom_schedule_work(wacom);
 		}
-		wacom->battery_capacity = battery;
+
+		if (wacom->shared->type &&
+		    (battery != wacom->battery_capacity ||
+		     ps_connected != wacom->ps_connected)) {
+			wacom->battery_capacity = battery;
+			wacom->ps_connected = ps_connected;
+			wacom->bat_charging = ps_connected &&
+						wacom->battery_capacity < 100;
+			wacom_notify_battery(wacom);
+		}
 	} else if (wacom->pid != 0) {
 		/* disconnected while previously connected */
 		wacom->pid = 0;
 		wacom_schedule_work(wacom);
 		wacom->battery_capacity = 0;
+		wacom->bat_charging = 0;
+		wacom->ps_connected = 0;
 	}
 
 	return 0;
@@ -1416,6 +1534,7 @@
 
 	case WACOM_G4:
 	case GRAPHIRE:
+	case GRAPHIRE_BT:
 	case WACOM_MO:
 		sync = wacom_graphire_irq(wacom_wac);
 		break;
@@ -1450,6 +1569,10 @@
 		sync = wacom_intuos_irq(wacom_wac);
 		break;
 
+	case INTUOS4WL:
+		sync = wacom_intuos_bt_irq(wacom_wac, len);
+		break;
+
 	case WACOM_24HDT:
 		sync = wacom_24hdt_irq(wacom_wac);
 		break;
@@ -1489,8 +1612,11 @@
 		break;
 	}
 
-	if (sync)
+	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)
@@ -1565,8 +1691,10 @@
 		features->quirks |= WACOM_QUIRK_NO_INPUT;
 
 		/* must be monitor interface if no device_type set */
-		if (!features->device_type)
+		if (!features->device_type) {
 			features->quirks |= WACOM_QUIRK_MONITOR;
+			features->quirks |= WACOM_QUIRK_BATTERY;
+		}
 	}
 }
 
@@ -1615,7 +1743,6 @@
 				   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);
 
@@ -1630,10 +1757,6 @@
 		/* fall through */
 
 	case WACOM_G4:
-		input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
-
-		__set_bit(BTN_BACK, input_dev->keybit);
-		__set_bit(BTN_FORWARD, input_dev->keybit);
 		/* fall through */
 
 	case GRAPHIRE:
@@ -1652,62 +1775,42 @@
 		__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
 		break;
 
+	case GRAPHIRE_BT:
+		__clear_bit(ABS_MISC, input_dev->absbit);
+		input_set_abs_params(input_dev, ABS_DISTANCE, 0,
+					      features->distance_max,
+					      0, 0);
+
+		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:
-		__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 = 6; 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_Z, -900, 899, 0, 0);
 		input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0);
 		/* fall through */
 
 	case DTK:
-		for (i = 0; i < 6; i++)
-			__set_bit(BTN_0 + i, input_dev->keybit);
-
 		__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
 
 		wacom_setup_cintiq(wacom_wac);
 		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);
 		input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
 
 		__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
@@ -1716,9 +1819,6 @@
 		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_Z, -900, 899, 0, 0);
 		__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
 		wacom_setup_cintiq(wacom_wac);
@@ -1726,21 +1826,7 @@
 
 	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);
 		input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
 		/* fall through */
 
@@ -1754,20 +1840,11 @@
 	case INTUOS5L:
 	case INTUOSPM:
 	case INTUOSPL:
-		if (features->device_type == BTN_TOOL_PEN) {
-			__set_bit(BTN_7, input_dev->keybit);
-			__set_bit(BTN_8, input_dev->keybit);
-		}
-		/* fall through */
-
 	case INTUOS5S:
 	case INTUOSPS:
 		__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
 
 		if (features->device_type == BTN_TOOL_PEN) {
-			for (i = 0; i < 7; i++)
-				__set_bit(BTN_0 + i, input_dev->keybit);
-
 			input_set_abs_params(input_dev, ABS_DISTANCE, 0,
 					      features->distance_max,
 					      0, 0);
@@ -1787,15 +1864,9 @@
 		break;
 
 	case INTUOS4:
+	case INTUOS4WL:
 	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_Z, -900, 899, 0, 0);
 		wacom_setup_intuos(wacom_wac);
 
@@ -1839,11 +1910,6 @@
 	case DTUS:
 	case PL:
 	case DTU:
-		if (features->type == DTUS) {
-			input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
-			for (i = 0; i < 4; i++)
-				__set_bit(BTN_0 + i, input_dev->keybit);
-		}
 		__set_bit(BTN_TOOL_PEN, input_dev->keybit);
 		__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
 		__set_bit(BTN_STYLUS, input_dev->keybit);
@@ -1877,11 +1943,6 @@
 
 		if (features->device_type == BTN_TOOL_FINGER) {
 
-			__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);
-
 			if (features->touch_max) {
 				/* touch interface */
 				unsigned int flags = INPUT_MT_POINTER;
@@ -1919,17 +1980,6 @@
 		break;
 
 	case CINTIQ_HYBRID:
-		__set_bit(BTN_1, input_dev->keybit);
-		__set_bit(BTN_2, input_dev->keybit);
-		__set_bit(BTN_3, input_dev->keybit);
-		__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);
-		__set_bit(BTN_8, input_dev->keybit);
-		__set_bit(BTN_0, input_dev->keybit);
-
 		input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
 		__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
 
@@ -1939,429 +1989,620 @@
 	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 GRAPHIRE_BT:
+		__set_bit(BTN_0, input_dev->keybit);
+		__set_bit(BTN_1, input_dev->keybit);
+		break;
+
+	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 INTUOS4WL:
+		/*
+		 * For Bluetooth devices, the udev rule does not work correctly
+		 * for pads unless we add a stylus capability, which forces
+		 * ID_INPUT_TABLET to be set.
+		 */
+		__set_bit(BTN_STYLUS, input_dev->keybit);
+		/* fall through */
+
+	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",     WACOM_PKGLEN_PENPRTN,    5040,  3780,  255,
-	  0, PENPARTNER, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES };
+	{ "Wacom Penpartner", 5040, 3780, 255, 0,
+	  PENPARTNER, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES };
 static const struct wacom_features wacom_features_0x10 =
-	{ "Wacom Graphire",       WACOM_PKGLEN_GRAPHIRE,  10206,  7422,  511,
-	  63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+	{ "Wacom Graphire", 10206, 7422, 511, 63,
+	  GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+static const struct wacom_features wacom_features_0x81 =
+	{ "Wacom Graphire BT", 16704, 12064, 511, 32,
+	  GRAPHIRE_BT, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
 static const struct wacom_features wacom_features_0x11 =
-	{ "Wacom Graphire2 4x5",  WACOM_PKGLEN_GRAPHIRE,  10206,  7422,  511,
-	  63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+	{ "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",  WACOM_PKGLEN_GRAPHIRE,  13918, 10206,  511,
-	  63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+	{ "Wacom Graphire2 5x7", 13918, 10206, 511, 63,
+	  GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
 static const struct wacom_features wacom_features_0x13 =
-	{ "Wacom Graphire3",      WACOM_PKGLEN_GRAPHIRE,  10208,  7424,  511,
-	  63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+	{ "Wacom Graphire3", 10208, 7424, 511, 63,
+	  GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
 static const struct wacom_features wacom_features_0x14 =
-	{ "Wacom Graphire3 6x8",  WACOM_PKGLEN_GRAPHIRE,  16704, 12064,  511,
-	  63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+	{ "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",  WACOM_PKGLEN_GRAPHIRE,  10208,  7424,  511,
-	  63, WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+	{ "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",  WACOM_PKGLEN_GRAPHIRE,  16704, 12064,  511,
-	  63, WACOM_G4, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+	{ "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",  WACOM_PKGLEN_BBFUN,     14760,  9225,  511,
-	  63, WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",  WACOM_PKGLEN_BBFUN,     21648, 13530,  511,
-	  63, WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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", WACOM_PKGLEN_GRAPHIRE,  16704, 12064,  511,
-	  63, GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
+	{ "Wacom Bamboo1 Medium", 16704, 12064, 511, 63,
+	  GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
 static const struct wacom_features wacom_features_0x60 =
-	{ "Wacom Volito",         WACOM_PKGLEN_GRAPHIRE,   5104,  3712,  511,
-	  63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
+	{ "Wacom Volito", 5104, 3712, 511, 63,
+	  GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
 static const struct wacom_features wacom_features_0x61 =
-	{ "Wacom PenStation2",    WACOM_PKGLEN_GRAPHIRE,   3250,  2320,  255,
-	  63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
+	{ "Wacom PenStation2", 3250, 2320, 255, 63,
+	  GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
 static const struct wacom_features wacom_features_0x62 =
-	{ "Wacom Volito2 4x5",    WACOM_PKGLEN_GRAPHIRE,   5104,  3712,  511,
-	  63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
+	{ "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",    WACOM_PKGLEN_GRAPHIRE,   3248,  2320,  511,
-	  63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
+	{ "Wacom Volito2 2x3", 3248, 2320, 511, 63,
+	  GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
 static const struct wacom_features wacom_features_0x64 =
-	{ "Wacom PenPartner2",    WACOM_PKGLEN_GRAPHIRE,   3250,  2320,  511,
-	  63, GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
+	{ "Wacom PenPartner2", 3250, 2320, 511, 63,
+	  GRAPHIRE, WACOM_VOLITO_RES, WACOM_VOLITO_RES };
 static const struct wacom_features wacom_features_0x65 =
-	{ "Wacom Bamboo",         WACOM_PKGLEN_BBFUN,     14760,  9225,  511,
-	  63, WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "Wacom Bamboo", 14760, 9225, 511, 63,
+	  WACOM_MO, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0x69 =
-	{ "Wacom Bamboo1",        WACOM_PKGLEN_GRAPHIRE,   5104,  3712,  511,
-	  63, GRAPHIRE, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES };
+	{ "Wacom Bamboo1", 5104, 3712, 511, 63,
+	  GRAPHIRE, WACOM_PENPRTN_RES, WACOM_PENPRTN_RES };
 static const struct wacom_features wacom_features_0x6A =
-	{ "Wacom Bamboo1 4x6",    WACOM_PKGLEN_GRAPHIRE,  14760,  9225, 1023,
-	  63, GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",    WACOM_PKGLEN_GRAPHIRE,  21648, 13530, 1023,
-	  63, GRAPHIRE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",     WACOM_PKGLEN_INTUOS,    12700, 10600, 1023,
-	  31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",     WACOM_PKGLEN_INTUOS,    20320, 16240, 1023,
-	  31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",    WACOM_PKGLEN_INTUOS,    30480, 24060, 1023,
-	  31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",   WACOM_PKGLEN_INTUOS,    30480, 31680, 1023,
-	  31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",   WACOM_PKGLEN_INTUOS,    45720, 31680, 1023,
-	  31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "Wacom Intuos 12x18", 45720, 31680, 1023, 31,
+	  INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0x30 =
-	{ "Wacom PL400",          WACOM_PKGLEN_GRAPHIRE,   5408,  4056,  255,
-	  0, PL, WACOM_PL_RES, WACOM_PL_RES };
+	{ "Wacom PL400", 5408, 4056, 255, 0,
+	  PL, WACOM_PL_RES, WACOM_PL_RES };
 static const struct wacom_features wacom_features_0x31 =
-	{ "Wacom PL500",          WACOM_PKGLEN_GRAPHIRE,   6144,  4608,  255,
-	  0, PL, WACOM_PL_RES, WACOM_PL_RES };
+	{ "Wacom PL500", 6144, 4608, 255, 0,
+	  PL, WACOM_PL_RES, WACOM_PL_RES };
 static const struct wacom_features wacom_features_0x32 =
-	{ "Wacom PL600",          WACOM_PKGLEN_GRAPHIRE,   6126,  4604,  255,
-	  0, PL, WACOM_PL_RES, WACOM_PL_RES };
+	{ "Wacom PL600", 6126, 4604, 255, 0,
+	  PL, WACOM_PL_RES, WACOM_PL_RES };
 static const struct wacom_features wacom_features_0x33 =
-	{ "Wacom PL600SX",        WACOM_PKGLEN_GRAPHIRE,   6260,  5016,  255,
-	  0, PL, WACOM_PL_RES, WACOM_PL_RES };
+	{ "Wacom PL600SX", 6260, 5016, 255, 0,
+	  PL, WACOM_PL_RES, WACOM_PL_RES };
 static const struct wacom_features wacom_features_0x34 =
-	{ "Wacom PL550",          WACOM_PKGLEN_GRAPHIRE,   6144,  4608,  511,
-	  0, PL, WACOM_PL_RES, WACOM_PL_RES };
+	{ "Wacom PL550", 6144, 4608, 511, 0,
+	  PL, WACOM_PL_RES, WACOM_PL_RES };
 static const struct wacom_features wacom_features_0x35 =
-	{ "Wacom PL800",          WACOM_PKGLEN_GRAPHIRE,   7220,  5780,  511,
-	  0, PL, WACOM_PL_RES, WACOM_PL_RES };
+	{ "Wacom PL800", 7220, 5780, 511, 0,
+	  PL, WACOM_PL_RES, WACOM_PL_RES };
 static const struct wacom_features wacom_features_0x37 =
-	{ "Wacom PL700",          WACOM_PKGLEN_GRAPHIRE,   6758,  5406,  511,
-	  0, PL, WACOM_PL_RES, WACOM_PL_RES };
+	{ "Wacom PL700", 6758, 5406, 511, 0,
+	  PL, WACOM_PL_RES, WACOM_PL_RES };
 static const struct wacom_features wacom_features_0x38 =
-	{ "Wacom PL510",          WACOM_PKGLEN_GRAPHIRE,   6282,  4762,  511,
-	  0, PL, WACOM_PL_RES, WACOM_PL_RES };
+	{ "Wacom PL510", 6282, 4762, 511, 0,
+	  PL, WACOM_PL_RES, WACOM_PL_RES };
 static const struct wacom_features wacom_features_0x39 =
-	{ "Wacom DTU710",         WACOM_PKGLEN_GRAPHIRE,  34080, 27660,  511,
-	  0, PL, WACOM_PL_RES, WACOM_PL_RES };
+	{ "Wacom DTU710", 34080, 27660, 511, 0,
+	  PL, WACOM_PL_RES, WACOM_PL_RES };
 static const struct wacom_features wacom_features_0xC4 =
-	{ "Wacom DTF521",         WACOM_PKGLEN_GRAPHIRE,   6282,  4762,  511,
-	  0, PL, WACOM_PL_RES, WACOM_PL_RES };
+	{ "Wacom DTF521", 6282, 4762, 511, 0,
+	  PL, WACOM_PL_RES, WACOM_PL_RES };
 static const struct wacom_features wacom_features_0xC0 =
-	{ "Wacom DTF720",         WACOM_PKGLEN_GRAPHIRE,   6858,  5506,  511,
-	  0, PL, WACOM_PL_RES, WACOM_PL_RES };
+	{ "Wacom DTF720", 6858, 5506, 511, 0,
+	  PL, WACOM_PL_RES, WACOM_PL_RES };
 static const struct wacom_features wacom_features_0xC2 =
-	{ "Wacom DTF720a",        WACOM_PKGLEN_GRAPHIRE,   6858,  5506,  511,
-	  0, PL, WACOM_PL_RES, WACOM_PL_RES };
+	{ "Wacom DTF720a", 6858, 5506, 511, 0,
+	  PL, WACOM_PL_RES, WACOM_PL_RES };
 static const struct wacom_features wacom_features_0x03 =
-	{ "Wacom Cintiq Partner", WACOM_PKGLEN_GRAPHIRE,  20480, 15360,  511,
-	  0, PTU, WACOM_PL_RES, WACOM_PL_RES };
+	{ "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",    WACOM_PKGLEN_INTUOS,    12700, 10600, 1023,
-	  31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",    WACOM_PKGLEN_INTUOS,    20320, 16240, 1023,
-	  31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",   WACOM_PKGLEN_INTUOS,    30480, 24060, 1023,
-	  31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",  WACOM_PKGLEN_INTUOS,    30480, 31680, 1023,
-	  31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",  WACOM_PKGLEN_INTUOS,    45720, 31680, 1023,
-	  31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",    WACOM_PKGLEN_INTUOS,    25400, 20320, 1023,
-	  63, INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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",    WACOM_PKGLEN_INTUOS,    40640, 30480, 1023,
-	  63, INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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",   WACOM_PKGLEN_INTUOS,    60960, 45720, 1023,
-	  63, INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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",  WACOM_PKGLEN_INTUOS,    60960, 60960, 1023,
-	  63, INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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",  WACOM_PKGLEN_INTUOS,    97536, 60960, 1023,
-	  63, INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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",   WACOM_PKGLEN_INTUOS,    54204, 31750, 1023,
-	  63, INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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",    WACOM_PKGLEN_INTUOS,    31496, 19685, 1023,
-	  63, INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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",    WACOM_PKGLEN_INTUOS,    31496, 19685, 2047,
-	  63, INTUOS4S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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",    WACOM_PKGLEN_INTUOS,    44704, 27940, 2047,
-	  63, INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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",   WACOM_PKGLEN_INTUOS,    65024, 40640, 2047,
-	  63, INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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",  WACOM_PKGLEN_INTUOS,    97536, 60960, 2047,
-	  63, INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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",     WACOM_PKGLEN_INTUOS,    40640, 25400, 2047,
-	  63, INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "Wacom Intuos4 WL", 40640, 25400, 2047, 63,
+	  INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xBD =
+	{ "Wacom Intuos4 WL", 40640, 25400, 2047, 63,
+	  INTUOS4WL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
 static const struct wacom_features wacom_features_0x26 =
-	{ "Wacom Intuos5 touch S", WACOM_PKGLEN_INTUOS,  31496, 19685, 2047,
-	  63, INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
-	  .touch_max = 16 };
+	{ "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", WACOM_PKGLEN_INTUOS,  44704, 27940, 2047,
-	  63, INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
-	  .touch_max = 16 };
+	{ "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", WACOM_PKGLEN_INTUOS, 65024, 40640, 2047,
-	  63, INTUOS5L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
-	  .touch_max = 16 };
+	{ "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", WACOM_PKGLEN_INTUOS,  31496, 19685, 2047,
-	  63, INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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", WACOM_PKGLEN_INTUOS,  44704, 27940, 2047,
-	  63, INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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", WACOM_PKGLEN_INTUOS,  31496, 19685, 2047,
-	  63, INTUOSPS, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
-	  .touch_max = 16 };
+	{ "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", WACOM_PKGLEN_INTUOS,  44704, 27940, 2047,
-	  63, INTUOSPM, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
-	  .touch_max = 16 };
+	{ "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", WACOM_PKGLEN_INTUOS,  65024, 40640, 2047,
-	  63, INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
-	  .touch_max = 16 };
+	{ "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",       WACOM_PKGLEN_INTUOS,   104280, 65400, 2047,
-	  63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
+	{ "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", WACOM_PKGLEN_INTUOS,   104280, 65400, 2047, /* Pen */
-	  63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
+	{ "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 };
+	  .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",    WACOM_PKGLEN_INTUOS,    87200, 65600, 1023,
-	  63, CINTIQ, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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",   WACOM_PKGLEN_INTUOS,    86680, 54180, 1023,
-	  63, WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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",    WACOM_PKGLEN_INTUOS,    53020, 33440, 1023,
-	  63, WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+	{ "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",    WACOM_PKGLEN_INTUOS,    59352, 33648, 1023,
-	  63, WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
+	{ "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",        WACOM_PKGLEN_GRAPHIRE,  37832, 30305,  511,
-	  0, PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "Wacom DTU1931", 37832, 30305, 511, 0,
+	  PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0xCE =
-	{ "Wacom DTU2231",        WACOM_PKGLEN_GRAPHIRE,  47864, 27011,  511,
-	  0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",        WACOM_PKGLEN_GRAPHIRE,  34623, 19553,  511,
-	  0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "Wacom DTU1631", 34623, 19553, 511, 0,
+	  DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0xFB =
-	{ "Wacom DTU1031",        WACOM_PKGLEN_DTUS,      22096, 13960,  511,
-	  0, DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "Wacom DTU1031", 22096, 13960, 511, 0,
+	  DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0x57 =
-	{ "Wacom DTK2241",        WACOM_PKGLEN_INTUOS,    95640, 54060, 2047,
-	  63, DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
+	{ "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",        WACOM_PKGLEN_INTUOS,    95640, 54060, 2047,
-	  63, DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
+	{ "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 };
+	  .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",   WACOM_PKGLEN_INTUOS,    87000, 65400, 2047,
-	  63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
+	{ "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",    WACOM_PKGLEN_INTUOS,    95640, 54060, 2047,
-	  63, WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200 };
+	{ "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", WACOM_PKGLEN_INTUOS,      95640, 54060, 2047,
-	  63, WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
+	{ "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 };
+	  .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",       WACOM_PKGLEN_GRAPHIRE,  26202, 16325,  255,
-	  0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",       WACOM_PKGLEN_GRAPHIRE,  26202, 16325,  255,
-	  0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",       WACOM_PKGLEN_GRAPHIRE,  26202, 16325,  511,
-	  0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",       WACOM_PKGLEN_GRAPHIRE,  26202, 16325,  255,
-	  0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",       WACOM_PKGLEN_GRAPHIRE,  26202, 16325,  255,
-	  0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",       WACOM_PKGLEN_TPC2FG,    26202, 16325,  255,
-	  0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 2 };
+	{ "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",       WACOM_PKGLEN_TPC2FG,    26202, 16325,  255,
-	  0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 2 };
+	{ "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",       WACOM_PKGLEN_MTOUCH,    26202, 16325,  255,
-	  0, MTSCREEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",       WACOM_PKGLEN_TPC2FG,    27760, 15694,  255,
-	  0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 2 };
+	{ "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",       WACOM_PKGLEN_GRAPHIRE,  25710, 14500,  255,
-	  0, TABLETPC,    WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",       WACOM_PKGLEN_GRAPHIRE,  26202, 16325,  255,
-	  0, TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",       WACOM_PKGLEN_GRAPHIRE,  26202, 16325,  255,
-	  0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",      WACOM_PKGLEN_MTTPC,     26202, 16325,  255,
-	  0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",      WACOM_PKGLEN_MTTPC,     26202, 16325,  255,
-	  0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",      WACOM_PKGLEN_MTTPC,     26202, 16325,  255,
-	  0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",      WACOM_PKGLEN_MTTPC,     27760, 15694,  255,
-	  0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",      WACOM_PKGLEN_MTTPC,     27760, 15694,  255,
-	  0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",      WACOM_PKGLEN_GRAPHIRE,  26202, 16325,  255,
-	  0, TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "Wacom ISDv4 116", 26202, 16325, 255, 0,
+	  TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x12C =
+	{ "Wacom ISDv4 12C", 27848, 15752, 2047, 0,
+	  TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0x4001 =
-	{ "Wacom ISDv4 4001",      WACOM_PKGLEN_MTTPC,     26202, 16325,  255,
-	  0, MTTPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",      WACOM_PKGLEN_MTTPC,     11060, 6220,  255,
-	  0, MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",      WACOM_PKGLEN_MTTPC,     27848, 15752,  1023,
-	  0, MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",      WACOM_PKGLEN_MTTPC,     29576, 16724,  1023,
-	  0, MTTPC_B, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",    WACOM_PKGLEN_INTUOS,    20320, 16240, 1023,
-	  31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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", WACOM_PKGLEN_WIRELESS, 0, 0, 0,
-	  0, WIRELESS, 0, 0, .touch_max = 16 };
+	{ "Wacom Wireless Receiver", 0, 0, 0, 0,
+	  WIRELESS, 0, 0, .touch_max = 16 };
 static const struct wacom_features wacom_features_0xD0 =
-	{ "Wacom Bamboo 2FG",     WACOM_PKGLEN_BBFUN,     14720,  9200, 1023,
-	  31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 2 };
+	{ "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", WACOM_PKGLEN_BBFUN,     14720,  9200, 1023,
-	  31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 2 };
+	{ "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",   WACOM_PKGLEN_BBFUN,     14720,  9200, 1023,
-	  31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 2 };
+	{ "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", WACOM_PKGLEN_BBFUN,     21648, 13700, 1023,
-	  31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 2 };
+	{ "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",     WACOM_PKGLEN_BBFUN,     14720,  9200, 1023,
-	  31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",     WACOM_PKGLEN_BBFUN, 21648, 13700, 1023,
-	  31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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", WACOM_PKGLEN_BBFUN,   14720,  9200, 1023,
-	  31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 2 };
+	{ "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", WACOM_PKGLEN_BBFUN, 14720,  9200, 1023,
-	  31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 2 };
+	{ "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", WACOM_PKGLEN_BBFUN,   21648, 13700, 1023,
-	  31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 2 };
+	{ "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", WACOM_PKGLEN_BBFUN,  14720,  9200, 1023,
-	  31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 2 };
+	{ "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", WACOM_PKGLEN_BBFUN,  21648, 13700, 1023,
-	  31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 2 };
+	{ "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", WACOM_PKGLEN_BBPEN,     14720,  9200, 1023,
-          31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+        { "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", WACOM_PKGLEN_BBPEN,    14720,  9200, 1023,
-	  31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 16 };
+        { "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", WACOM_PKGLEN_BBPEN,    21648, 13700, 1023,
-	  31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 16 };
+        { "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",    WACOM_PKGLEN_BBPEN,    14720,  9225, 1023,
-	  31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",    WACOM_PKGLEN_BBPEN,    21648, 13530, 1023,
-	  31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",     WACOM_PKGLEN_BBPEN,    15200,  9500, 1023,
-	  31, INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 16 };
+	{ "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",     WACOM_PKGLEN_BBPEN,    21600, 13500, 1023,
-	  31, INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
-	  .touch_max = 16 };
+	{ "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",        WACOM_PKGLEN_BBPEN,    15200,  9500, 1023,
-	  31, INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+	{ "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",               WACOM_PKGLEN_GRAPHIRE,  12800,  8000,  255,
-	  0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
-static const struct wacom_features wacom_features_0x0307 =
-	{ "Wacom ISDv5 307", WACOM_PKGLEN_INTUOS,  59352,  33648, 2047,
-	  63, CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 200, 200,
+	{ "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_0x0309 =
+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 };
+	  .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)					\
-	USB_DEVICE(USB_VENDOR_ID_WACOM, prod),			\
-	.driver_info = (kernel_ulong_t)&wacom_features_##prod
+#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_DETAILED(prod, class, sub, proto)			\
-	USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_WACOM, prod, class,	\
-				      sub, proto),			\
-	.driver_info = (kernel_ulong_t)&wacom_features_##prod
+#define BT_DEVICE_WACOM(prod)						\
+	HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\
+	.driver_data = (kernel_ulong_t)&wacom_features_##prod
 
 #define USB_DEVICE_LENOVO(prod)					\
-	USB_DEVICE(USB_VENDOR_ID_LENOVO, prod),			\
-	.driver_info = (kernel_ulong_t)&wacom_features_##prod
+	HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, prod),			\
+	.driver_data = (kernel_ulong_t)&wacom_features_##prod
 
-const struct usb_device_id wacom_ids[] = {
+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) },
@@ -2372,20 +2613,16 @@
 	{ USB_DEVICE_WACOM(0x17) },
 	{ USB_DEVICE_WACOM(0x18) },
 	{ USB_DEVICE_WACOM(0x19) },
-	{ 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(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) },
@@ -2395,20 +2632,34 @@
 	{ USB_DEVICE_WACOM(0x37) },
 	{ USB_DEVICE_WACOM(0x38) },
 	{ USB_DEVICE_WACOM(0x39) },
-	{ USB_DEVICE_WACOM(0xC4) },
-	{ USB_DEVICE_WACOM(0xC0) },
-	{ USB_DEVICE_WACOM(0xC2) },
-	{ USB_DEVICE_WACOM(0x03) },
+	{ 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_DETAILED(0x5D, USB_CLASS_HID, 0, 0) },
 	{ USB_DEVICE_WACOM(0x5B) },
-	{ USB_DEVICE_DETAILED(0x5E, USB_CLASS_HID, 0, 0) },
+	{ 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) },
+	{ BT_DEVICE_WACOM(0x81) },
+	{ 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) },
@@ -2421,23 +2672,15 @@
 	{ USB_DEVICE_WACOM(0xBA) },
 	{ USB_DEVICE_WACOM(0xBB) },
 	{ USB_DEVICE_WACOM(0xBC) },
-	{ USB_DEVICE_WACOM(0x26) },
-	{ USB_DEVICE_WACOM(0x27) },
-	{ USB_DEVICE_WACOM(0x28) },
-	{ USB_DEVICE_WACOM(0x29) },
-	{ USB_DEVICE_WACOM(0x2A) },
-	{ USB_DEVICE_WACOM(0x3F) },
+	{ BT_DEVICE_WACOM(0xBD) },
+	{ USB_DEVICE_WACOM(0xC0) },
+	{ USB_DEVICE_WACOM(0xC2) },
+	{ USB_DEVICE_WACOM(0xC4) },
 	{ USB_DEVICE_WACOM(0xC5) },
 	{ USB_DEVICE_WACOM(0xC6) },
 	{ USB_DEVICE_WACOM(0xC7) },
-	/*
-	 * DTU-2231 has two interfaces on the same configuration,
-	 * only one is used.
-	 */
-	{ USB_DEVICE_DETAILED(0xCE, USB_CLASS_HID,
-			      USB_INTERFACE_SUBCLASS_BOOT,
-			      USB_INTERFACE_PROTOCOL_MOUSE) },
-	{ USB_DEVICE_WACOM(0x84) },
+	{ USB_DEVICE_WACOM(0xCC) },
+	{ USB_DEVICE_WACOM(0xCE) },
 	{ USB_DEVICE_WACOM(0xD0) },
 	{ USB_DEVICE_WACOM(0xD1) },
 	{ USB_DEVICE_WACOM(0xD2) },
@@ -2452,13 +2695,6 @@
 	{ USB_DEVICE_WACOM(0xDD) },
 	{ USB_DEVICE_WACOM(0xDE) },
 	{ USB_DEVICE_WACOM(0xDF) },
-	{ USB_DEVICE_WACOM(0xF0) },
-	{ USB_DEVICE_WACOM(0xCC) },
-	{ USB_DEVICE_WACOM(0x90) },
-	{ USB_DEVICE_WACOM(0x93) },
-	{ USB_DEVICE_WACOM(0x97) },
-	{ USB_DEVICE_WACOM(0x9A) },
-	{ USB_DEVICE_WACOM(0x9F) },
 	{ USB_DEVICE_WACOM(0xE2) },
 	{ USB_DEVICE_WACOM(0xE3) },
 	{ USB_DEVICE_WACOM(0xE5) },
@@ -2466,34 +2702,34 @@
 	{ 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(0x12C) },
 	{ USB_DEVICE_WACOM(0x300) },
 	{ USB_DEVICE_WACOM(0x301) },
-	{ USB_DEVICE_DETAILED(0x302, USB_CLASS_HID, 0, 0) },
-	{ USB_DEVICE_DETAILED(0x303, USB_CLASS_HID, 0, 0) },
-	{ USB_DEVICE_DETAILED(0x30E, USB_CLASS_HID, 0, 0) },
+	{ USB_DEVICE_WACOM(0x302) },
+	{ USB_DEVICE_WACOM(0x303) },
 	{ USB_DEVICE_WACOM(0x304) },
-	{ USB_DEVICE_DETAILED(0x314, USB_CLASS_HID, 0, 0) },
-	{ USB_DEVICE_DETAILED(0x315, USB_CLASS_HID, 0, 0) },
-	{ USB_DEVICE_DETAILED(0x317, USB_CLASS_HID, 0, 0) },
+	{ 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) },
-	{ USB_DEVICE_WACOM(0x47) },
-	{ USB_DEVICE_WACOM(0xF4) },
-	{ USB_DEVICE_WACOM(0xF8) },
-	{ USB_DEVICE_DETAILED(0xF6, USB_CLASS_HID, 0, 0) },
-	{ USB_DEVICE_WACOM(0xFA) },
-	{ USB_DEVICE_WACOM(0xFB) },
-	{ USB_DEVICE_WACOM(0x0307) },
-	{ USB_DEVICE_DETAILED(0x0309, USB_CLASS_HID, 0, 0) },
-	{ USB_DEVICE_LENOVO(0x6004) },
 	{ }
 };
-MODULE_DEVICE_TABLE(usb, wacom_ids);
+MODULE_DEVICE_TABLE(hid, wacom_ids);
diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/hid/wacom_wac.h
similarity index 88%
rename from drivers/input/tablet/wacom_wac.h
rename to drivers/hid/wacom_wac.h
index b2c9a9c..339ab5d 100644
--- a/drivers/input/tablet/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -46,6 +46,7 @@
 
 /* wacom data packet report IDs */
 #define WACOM_REPORT_PENABLED		2
+#define WACOM_REPORT_PENABLED_BT	3
 #define WACOM_REPORT_INTUOSREAD		5
 #define WACOM_REPORT_INTUOSWRITE	6
 #define WACOM_REPORT_INTUOSPAD		12
@@ -68,10 +69,12 @@
 #define WACOM_QUIRK_BBTOUCH_LOWRES	0x0002
 #define WACOM_QUIRK_NO_INPUT		0x0004
 #define WACOM_QUIRK_MONITOR		0x0008
+#define WACOM_QUIRK_BATTERY		0x0010
 
 enum {
 	PENPARTNER = 0,
 	GRAPHIRE,
+	GRAPHIRE_BT,
 	WACOM_G4,
 	PTU,
 	PL,
@@ -83,6 +86,7 @@
 	INTUOS3L,
 	INTUOS4S,
 	INTUOS4,
+	INTUOS4WL,
 	INTUOS4L,
 	INTUOS5S,
 	INTUOS5,
@@ -114,7 +118,6 @@
 
 struct wacom_features {
 	const char *name;
-	int pktlen;
 	int x_max;
 	int y_max;
 	int pressure_max;
@@ -127,8 +130,8 @@
 	int device_type;
 	int x_phy;
 	int y_phy;
-	unsigned char unit;
-	unsigned char unitExpo;
+	unsigned unit;
+	int unitExpo;
 	int x_fuzz;
 	int y_fuzz;
 	int pressure_fuzz;
@@ -137,6 +140,9 @@
 	unsigned touch_max;
 	int oVid;
 	int oPid;
+	int pktlen;
+	bool check_for_hid_type;
+	int hid_type;
 };
 
 struct wacom_shared {
@@ -150,16 +156,24 @@
 
 struct wacom_wac {
 	char name[WACOM_NAME_MAX];
-	unsigned char *data;
+	char pad_name[WACOM_NAME_MAX];
+	char bat_name[WACOM_NAME_MAX];
+	char ac_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;
+	int bat_charging;
+	int ps_connected;
+	u8 bt_features;
+	u8 bt_high_speed;
 };
 
 #endif
diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig
index a5121b0..623bb9e 100644
--- a/drivers/input/tablet/Kconfig
+++ b/drivers/input/tablet/Kconfig
@@ -73,22 +73,6 @@
 	  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.
-
 config TABLET_SERIAL_WACOM4
 	tristate "Wacom protocol 4 serial tablet support"
 	select SERIO
diff --git a/drivers/input/tablet/Makefile b/drivers/input/tablet/Makefile
index 4d9339f..2e13010 100644
--- a/drivers/input/tablet/Makefile
+++ b/drivers/input/tablet/Makefile
@@ -2,13 +2,10 @@
 # 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
 obj-$(CONFIG_TABLET_SERIAL_WACOM4) += wacom_serial4.o
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
deleted file mode 100644
index 2c613cd..0000000
--- a/drivers/input/tablet/wacom_sys.c
+++ /dev/null
@@ -1,1497 +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"
-
-/* defines to get HID report descriptor */
-#define HID_DEVICET_HID		(USB_TYPE_CLASS | 0x01)
-#define HID_DEVICET_REPORT	(USB_TYPE_CLASS | 0x02)
-#define HID_USAGE_UNDEFINED		0x00
-#define HID_USAGE_PAGE			0x05
-#define HID_USAGE_PAGE_DIGITIZER	0x0d
-#define HID_USAGE_PAGE_DESKTOP		0x01
-#define HID_USAGE			0x09
-#define HID_USAGE_X			((HID_USAGE_PAGE_DESKTOP << 16) | 0x30)
-#define HID_USAGE_Y			((HID_USAGE_PAGE_DESKTOP << 16) | 0x31)
-#define HID_USAGE_PRESSURE		((HID_USAGE_PAGE_DIGITIZER << 16) | 0x30)
-#define HID_USAGE_X_TILT		((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3d)
-#define HID_USAGE_Y_TILT		((HID_USAGE_PAGE_DIGITIZER << 16) | 0x3e)
-#define HID_USAGE_FINGER		((HID_USAGE_PAGE_DIGITIZER << 16) | 0x22)
-#define HID_USAGE_STYLUS		((HID_USAGE_PAGE_DIGITIZER << 16) | 0x20)
-#define HID_USAGE_CONTACTMAX		((HID_USAGE_PAGE_DIGITIZER << 16) | 0x55)
-#define HID_COLLECTION			0xa1
-#define HID_COLLECTION_LOGICAL		0x02
-#define HID_COLLECTION_END		0xc0
-
-struct hid_descriptor {
-	struct usb_descriptor_header header;
-	__le16   bcdHID;
-	u8       bCountryCode;
-	u8       bNumDescriptors;
-	u8       bDescriptorType;
-	__le16   wDescriptorLength;
-} __attribute__ ((packed));
-
-/* defines to get/set USB message */
-#define USB_REQ_GET_REPORT	0x01
-#define USB_REQ_SET_REPORT	0x09
-
-#define WAC_HID_FEATURE_REPORT	0x03
-#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 usb_interface *intf, u8 type, u8 id,
-			    void *buf, size_t size, unsigned int retries)
-{
-	struct usb_device *dev = interface_to_usbdev(intf);
-	int retval;
-
-	do {
-		retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
-				USB_REQ_GET_REPORT,
-				USB_DIR_IN | USB_TYPE_CLASS |
-				USB_RECIP_INTERFACE,
-				(type << 8) + id,
-				intf->altsetting[0].desc.bInterfaceNumber,
-				buf, size, 100);
-	} while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
-
-	return retval;
-}
-
-static int wacom_set_report(struct usb_interface *intf, u8 type, u8 id,
-			    void *buf, size_t size, unsigned int retries)
-{
-	struct usb_device *dev = interface_to_usbdev(intf);
-	int retval;
-
-	do {
-		retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-				USB_REQ_SET_REPORT,
-				USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-				(type << 8) + id,
-				intf->altsetting[0].desc.bInterfaceNumber,
-				buf, size, 1000);
-	} while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
-
-	return retval;
-}
-
-static void wacom_sys_irq(struct urb *urb)
-{
-	struct wacom *wacom = urb->context;
-	struct device *dev = &wacom->intf->dev;
-	int retval;
-
-	switch (urb->status) {
-	case 0:
-		/* success */
-		break;
-	case -ECONNRESET:
-	case -ENOENT:
-	case -ESHUTDOWN:
-		/* this urb is terminated, clean up */
-		dev_dbg(dev, "%s - urb shutting down with status: %d\n",
-			__func__, urb->status);
-		return;
-	default:
-		dev_dbg(dev, "%s - nonzero urb status received: %d\n",
-			__func__, urb->status);
-		goto exit;
-	}
-
-	wacom_wac_irq(&wacom->wacom_wac, urb->actual_length);
-
- exit:
-	usb_mark_last_busy(wacom->usbdev);
-	retval = usb_submit_urb(urb, GFP_ATOMIC);
-	if (retval)
-		dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
-			__func__, retval);
-}
-
-static int wacom_open(struct input_dev *dev)
-{
-	struct wacom *wacom = input_get_drvdata(dev);
-	int retval = 0;
-
-	if (usb_autopm_get_interface(wacom->intf) < 0)
-		return -EIO;
-
-	mutex_lock(&wacom->lock);
-
-	if (usb_submit_urb(wacom->irq, GFP_KERNEL)) {
-		retval = -EIO;
-		goto out;
-	}
-
-	wacom->open = true;
-	wacom->intf->needs_remote_wakeup = 1;
-
-out:
-	mutex_unlock(&wacom->lock);
-	usb_autopm_put_interface(wacom->intf);
-	return retval;
-}
-
-static void wacom_close(struct input_dev *dev)
-{
-	struct wacom *wacom = input_get_drvdata(dev);
-	int autopm_error;
-
-	autopm_error = usb_autopm_get_interface(wacom->intf);
-
-	mutex_lock(&wacom->lock);
-	usb_kill_urb(wacom->irq);
-	wacom->open = false;
-	wacom->intf->needs_remote_wakeup = 0;
-	mutex_unlock(&wacom->lock);
-
-	if (!autopm_error)
-		usb_autopm_put_interface(wacom->intf);
-}
-
-/*
- * Calculate the resolution of the X or Y axis, given appropriate HID data.
- * This function is little more than hidinput_calc_abs_res stripped down.
- */
-static int wacom_calc_hid_res(int logical_extents, int physical_extents,
-                              unsigned char unit, unsigned char exponent)
-{
-	int prev, unit_exponent;
-
-	/* Check if the extents are sane */
-	if (logical_extents <= 0 || physical_extents <= 0)
-		return 0;
-
-	/* Get signed value of nybble-sized twos-compliment exponent */
-	unit_exponent = exponent;
-	if (unit_exponent > 7)
-		unit_exponent -= 16;
-
-	/* Convert physical_extents to millimeters */
-	if (unit == 0x11) {		/* If centimeters */
-		unit_exponent += 1;
-	} else if (unit == 0x13) {	/* If inches */
-		prev = physical_extents;
-		physical_extents *= 254;
-		if (physical_extents < prev)
-			return 0;
-		unit_exponent -= 1;
-	} else {
-		return 0;
-	}
-
-	/* Apply negative unit exponent */
-	for (; unit_exponent < 0; unit_exponent++) {
-		prev = logical_extents;
-		logical_extents *= 10;
-		if (logical_extents < prev)
-			return 0;
-	}
-	/* Apply positive unit exponent */
-	for (; unit_exponent > 0; unit_exponent--) {
-		prev = physical_extents;
-		physical_extents *= 10;
-		if (physical_extents < prev)
-			return 0;
-	}
-
-	/* Calculate resolution */
-	return logical_extents / physical_extents;
-}
-
-static int wacom_parse_logical_collection(unsigned char *report,
-					  struct wacom_features *features)
-{
-	int length = 0;
-
-	if (features->type == BAMBOO_PT) {
-
-		/* Logical collection is only used by 3rd gen Bamboo Touch */
-		features->pktlen = WACOM_PKGLEN_BBTOUCH3;
-		features->device_type = BTN_TOOL_FINGER;
-
-		features->x_max = features->y_max =
-			get_unaligned_le16(&report[10]);
-
-		length = 11;
-	}
-	return length;
-}
-
-static void wacom_retrieve_report_data(struct usb_interface *intf,
-				       struct wacom_features *features)
-{
-	int result = 0;
-	unsigned char *rep_data;
-
-	rep_data = kmalloc(2, GFP_KERNEL);
-	if (rep_data) {
-
-		rep_data[0] = 12;
-		result = wacom_get_report(intf, WAC_HID_FEATURE_REPORT,
-					  rep_data[0], rep_data, 2,
-					  WAC_MSG_RETRIES);
-
-		if (result >= 0 && rep_data[1] > 2)
-			features->touch_max = rep_data[1];
-
-		kfree(rep_data);
-	}
-}
-
-/*
- * 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.
- *
- * 3rd gen Bamboo Touch no longer define a Digitizer-Finger Pysical
- * Collection. Instead they define a Logical Collection with a single
- * Logical Maximum for both X and Y.
- *
- * Intuos5 touch interface does not contain useful data. We deal with
- * this after returning from this function.
- */
-static int wacom_parse_hid(struct usb_interface *intf,
-			   struct hid_descriptor *hid_desc,
-			   struct wacom_features *features)
-{
-	struct usb_device *dev = interface_to_usbdev(intf);
-	char limit = 0;
-	/* result has to be defined as int for some devices */
-	int result = 0, touch_max = 0;
-	int i = 0, page = 0, finger = 0, pen = 0;
-	unsigned char *report;
-
-	report = kzalloc(hid_desc->wDescriptorLength, GFP_KERNEL);
-	if (!report)
-		return -ENOMEM;
-
-	/* retrive report descriptors */
-	do {
-		result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
-			USB_REQ_GET_DESCRIPTOR,
-			USB_RECIP_INTERFACE | USB_DIR_IN,
-			HID_DEVICET_REPORT << 8,
-			intf->altsetting[0].desc.bInterfaceNumber, /* interface */
-			report,
-			hid_desc->wDescriptorLength,
-			5000); /* 5 secs */
-	} while (result < 0 && limit++ < WAC_MSG_RETRIES);
-
-	/* No need to parse the Descriptor. It isn't an error though */
-	if (result < 0)
-		goto out;
-
-	for (i = 0; i < hid_desc->wDescriptorLength; i++) {
-
-		switch (report[i]) {
-		case HID_USAGE_PAGE:
-			page = report[i + 1];
-			i++;
-			break;
-
-		case HID_USAGE:
-			switch (page << 16 | report[i + 1]) {
-			case HID_USAGE_X:
-				if (finger) {
-					features->device_type = BTN_TOOL_FINGER;
-					/* touch device at least supports one touch point */
-					touch_max = 1;
-					switch (features->type) {
-					case TABLETPC2FG:
-						features->pktlen = WACOM_PKGLEN_TPC2FG;
-						break;
-
-					case MTSCREEN:
-					case WACOM_24HDT:
-						features->pktlen = WACOM_PKGLEN_MTOUCH;
-						break;
-
-					case MTTPC:
-					case MTTPC_B:
-						features->pktlen = WACOM_PKGLEN_MTTPC;
-						break;
-
-					case BAMBOO_PT:
-						features->pktlen = WACOM_PKGLEN_BBTOUCH;
-						break;
-
-					default:
-						features->pktlen = WACOM_PKGLEN_GRAPHIRE;
-						break;
-					}
-
-					switch (features->type) {
-					case BAMBOO_PT:
-						features->x_phy =
-							get_unaligned_le16(&report[i + 5]);
-						features->x_max =
-							get_unaligned_le16(&report[i + 8]);
-						i += 15;
-						break;
-
-					case WACOM_24HDT:
-						features->x_max =
-							get_unaligned_le16(&report[i + 3]);
-						features->x_phy =
-							get_unaligned_le16(&report[i + 8]);
-						features->unit = report[i - 1];
-						features->unitExpo = report[i - 3];
-						i += 12;
-						break;
-
-					case MTTPC_B:
-						features->x_max =
-							get_unaligned_le16(&report[i + 3]);
-						features->x_phy =
-							get_unaligned_le16(&report[i + 6]);
-						features->unit = report[i - 5];
-						features->unitExpo = report[i - 3];
-						i += 9;
-						break;
-
-					default:
-						features->x_max =
-							get_unaligned_le16(&report[i + 3]);
-						features->x_phy =
-							get_unaligned_le16(&report[i + 6]);
-						features->unit = report[i + 9];
-						features->unitExpo = report[i + 11];
-						i += 12;
-						break;
-					}
-				} else if (pen) {
-					/* penabled only accepts exact bytes of data */
-					if (features->type >= TABLETPC)
-						features->pktlen = WACOM_PKGLEN_GRAPHIRE;
-					features->device_type = BTN_TOOL_PEN;
-					features->x_max =
-						get_unaligned_le16(&report[i + 3]);
-					i += 4;
-				}
-				break;
-
-			case HID_USAGE_Y:
-				if (finger) {
-					switch (features->type) {
-					case TABLETPC2FG:
-					case MTSCREEN:
-					case MTTPC:
-						features->y_max =
-							get_unaligned_le16(&report[i + 3]);
-						features->y_phy =
-							get_unaligned_le16(&report[i + 6]);
-						i += 7;
-						break;
-
-					case WACOM_24HDT:
-						features->y_max =
-							get_unaligned_le16(&report[i + 3]);
-						features->y_phy =
-							get_unaligned_le16(&report[i - 2]);
-						i += 7;
-						break;
-
-					case BAMBOO_PT:
-						features->y_phy =
-							get_unaligned_le16(&report[i + 3]);
-						features->y_max =
-							get_unaligned_le16(&report[i + 6]);
-						i += 12;
-						break;
-
-					case MTTPC_B:
-						features->y_max =
-							get_unaligned_le16(&report[i + 3]);
-						features->y_phy =
-							get_unaligned_le16(&report[i + 6]);
-						i += 9;
-						break;
-
-					default:
-						features->y_max =
-							features->x_max;
-						features->y_phy =
-							get_unaligned_le16(&report[i + 3]);
-						i += 4;
-						break;
-					}
-				} else if (pen) {
-					features->y_max =
-						get_unaligned_le16(&report[i + 3]);
-					i += 4;
-				}
-				break;
-
-			case HID_USAGE_FINGER:
-				finger = 1;
-				i++;
-				break;
-
-			/*
-			 * Requiring Stylus Usage will ignore boot mouse
-			 * X/Y values and some cases of invalid Digitizer X/Y
-			 * values commonly reported.
-			 */
-			case HID_USAGE_STYLUS:
-				pen = 1;
-				i++;
-				break;
-
-			case HID_USAGE_CONTACTMAX:
-				/* leave touch_max as is if predefined */
-				if (!features->touch_max)
-					wacom_retrieve_report_data(intf, features);
-				i++;
-				break;
-
-			case HID_USAGE_PRESSURE:
-				if (pen) {
-					features->pressure_max =
-						get_unaligned_le16(&report[i + 3]);
-					i += 4;
-				}
-				break;
-			}
-			break;
-
-		case HID_COLLECTION_END:
-			/* reset UsagePage and Finger */
-			finger = page = 0;
-			break;
-
-		case HID_COLLECTION:
-			i++;
-			switch (report[i]) {
-			case HID_COLLECTION_LOGICAL:
-				i += wacom_parse_logical_collection(&report[i],
-								    features);
-				break;
-			}
-			break;
-		}
-	}
-
- out:
-	if (!features->touch_max && touch_max)
-		features->touch_max = touch_max;
-	result = 0;
-	kfree(report);
-	return result;
-}
-
-static int wacom_set_device_mode(struct usb_interface *intf, 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(intf, WAC_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 usb_interface *intf, struct wacom_features *features)
-{
-	if (features->device_type == BTN_TOOL_FINGER) {
-		if (features->type > TABLETPC) {
-			/* MT Tablet PC touch */
-			return wacom_set_device_mode(intf, 3, 4, 4);
-		}
-		else if (features->type == WACOM_24HDT || features->type == CINTIQ_HYBRID) {
-			return wacom_set_device_mode(intf, 18, 3, 2);
-		}
-	} else if (features->device_type == BTN_TOOL_PEN) {
-		if (features->type <= BAMBOO_PT && features->type != WIRELESS) {
-			return wacom_set_device_mode(intf, 2, 2, 2);
-		}
-	}
-
-	return 0;
-}
-
-static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
-					 struct wacom_features *features)
-{
-	int error = 0;
-	struct usb_host_interface *interface = intf->cur_altsetting;
-	struct hid_descriptor *hid_desc;
-
-	/* 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) {
-		goto out;
-	}
-
-	error = usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc);
-	if (error) {
-		error = usb_get_extra_descriptor(&interface->endpoint[0],
-						 HID_DEVICET_REPORT, &hid_desc);
-		if (error) {
-			dev_err(&intf->dev,
-				"can not retrieve extra class descriptor\n");
-			goto out;
-		}
-	}
-	error = wacom_parse_hid(intf, hid_desc, features);
-
- out:
-	return error;
-}
-
-struct wacom_usbdev_data {
-	struct list_head list;
-	struct kref kref;
-	struct usb_device *dev;
-	struct wacom_shared shared;
-};
-
-static LIST_HEAD(wacom_udev_list);
-static DEFINE_MUTEX(wacom_udev_list_lock);
-
-static struct usb_device *wacom_get_sibling(struct usb_device *dev, int vendor, int product)
-{
-	int port1;
-	struct usb_device *sibling;
-
-	if (vendor == 0 && product == 0)
-		return dev;
-
-	if (dev->parent == NULL)
-		return NULL;
-
-	usb_hub_for_each_child(dev->parent, port1, sibling) {
-		struct usb_device_descriptor *d;
-		if (sibling == NULL)
-			continue;
-
-		d = &sibling->descriptor;
-		if (d->idVendor == vendor && d->idProduct == product)
-			return sibling;
-	}
-
-	return NULL;
-}
-
-static struct wacom_usbdev_data *wacom_get_usbdev_data(struct usb_device *dev)
-{
-	struct wacom_usbdev_data *data;
-
-	list_for_each_entry(data, &wacom_udev_list, list) {
-		if (data->dev == dev) {
-			kref_get(&data->kref);
-			return data;
-		}
-	}
-
-	return NULL;
-}
-
-static int wacom_add_shared_data(struct wacom_wac *wacom,
-				 struct usb_device *dev)
-{
-	struct wacom_usbdev_data *data;
-	int retval = 0;
-
-	mutex_lock(&wacom_udev_list_lock);
-
-	data = wacom_get_usbdev_data(dev);
-	if (!data) {
-		data = kzalloc(sizeof(struct wacom_usbdev_data), GFP_KERNEL);
-		if (!data) {
-			retval = -ENOMEM;
-			goto out;
-		}
-
-		kref_init(&data->kref);
-		data->dev = dev;
-		list_add_tail(&data->list, &wacom_udev_list);
-	}
-
-	wacom->shared = &data->shared;
-
-out:
-	mutex_unlock(&wacom_udev_list_lock);
-	return retval;
-}
-
-static void wacom_release_shared_data(struct kref *kref)
-{
-	struct wacom_usbdev_data *data =
-		container_of(kref, struct wacom_usbdev_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_usbdev_data *data;
-
-	if (wacom->shared) {
-		data = container_of(wacom->shared, struct wacom_usbdev_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->intf, 0x03, 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->intf, 0x03, 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->intf, 0x03, 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->intf, 0x03, 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 wacom *wacom = dev_get_drvdata(dev);
-	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 wacom *wacom = dev_get_drvdata(dev);			\
-	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 wacom *wacom = dev_get_drvdata(dev);			\
-									\
-	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 wacom *wacom = dev_get_drvdata(dev);
-	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->intf->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->intf->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->intf->dev.kobj,
-						  &intuos5_led_attr_group);
-		} else
-			return 0;
-		break;
-
-	default:
-		return 0;
-	}
-
-	if (error) {
-		dev_err(&wacom->intf->dev,
-			"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->intf->dev.kobj,
-				   &intuos4_led_attr_group);
-		break;
-
-	case WACOM_24HD:
-	case WACOM_21UX2:
-		sysfs_remove_group(&wacom->intf->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->intf->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->usbdev->dev,
-					      &wacom->battery);
-
-		if (!error)
-			power_supply_powers(&wacom->battery,
-					    &wacom->usbdev->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 int wacom_register_input(struct wacom *wacom)
-{
-	struct input_dev *input_dev;
-	struct usb_interface *intf = wacom->intf;
-	struct usb_device *dev = interface_to_usbdev(intf);
-	struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
-	int error;
-
-	input_dev = input_allocate_device();
-	if (!input_dev) {
-		error = -ENOMEM;
-		goto fail1;
-	}
-
-	input_dev->name = wacom_wac->name;
-	input_dev->dev.parent = &intf->dev;
-	input_dev->open = wacom_open;
-	input_dev->close = wacom_close;
-	usb_to_input_id(dev, &input_dev->id);
-	input_set_drvdata(input_dev, wacom);
-
-	wacom_wac->input = input_dev;
-	error = wacom_setup_input_capabilities(input_dev, wacom_wac);
-	if (error)
-		goto fail1;
-
-	error = input_register_device(input_dev);
-	if (error)
-		goto fail2;
-
-	return 0;
-
-fail2:
-	input_free_device(input_dev);
-	wacom_wac->input = NULL;
-fail1:
-	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 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 */
-	wacom1 = usb_get_intfdata(usbdev->config->interface[1]);
-	wacom_wac1 = &(wacom1->wacom_wac);
-	if (wacom_wac1->input)
-		input_unregister_device(wacom_wac1->input);
-	wacom_wac1->input = NULL;
-
-	/* Touch interface */
-	wacom2 = usb_get_intfdata(usbdev->config->interface[2]);
-	wacom_wac2 = &(wacom2->wacom_wac);
-	if (wacom_wac2->input)
-		input_unregister_device(wacom_wac2->input);
-	wacom_wac2->input = NULL;
-
-	if (wacom_wac->pid == 0) {
-		dev_info(&wacom->intf->dev, "wireless tablet disconnected\n");
-	} else {
-		const struct usb_device_id *id = wacom_ids;
-
-		dev_info(&wacom->intf->dev,
-			 "wireless tablet connected with PID %x\n",
-			 wacom_wac->pid);
-
-		while (id->match_flags) {
-			if (id->idVendor == USB_VENDOR_ID_WACOM &&
-			    id->idProduct == wacom_wac->pid)
-				break;
-			id++;
-		}
-
-		if (!id->match_flags) {
-			dev_info(&wacom->intf->dev,
-				 "ignoring unknown PID.\n");
-			return;
-		}
-
-		/* Stylus interface */
-		wacom_wac1->features =
-			*((struct wacom_features *)id->driver_info);
-		wacom_wac1->features.device_type = BTN_TOOL_PEN;
-		snprintf(wacom_wac1->name, WACOM_NAME_MAX, "%s (WL) Pen",
-			 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_input(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_info);
-			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);
-			error = wacom_register_input(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:
-	if (wacom_wac2->input) {
-		input_unregister_device(wacom_wac2->input);
-		wacom_wac2->input = NULL;
-	}
-
-	if (wacom_wac1->input) {
-		input_unregister_device(wacom_wac1->input);
-		wacom_wac1->input = NULL;
-	}
-	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_probe(struct usb_interface *intf, const struct usb_device_id *id)
-{
-	struct usb_device *dev = interface_to_usbdev(intf);
-	struct usb_endpoint_descriptor *endpoint;
-	struct wacom *wacom;
-	struct wacom_wac *wacom_wac;
-	struct wacom_features *features;
-	int error;
-
-	if (!id->driver_info)
-		return -EINVAL;
-
-	wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
-	if (!wacom)
-		return -ENOMEM;
-
-	wacom_wac = &wacom->wacom_wac;
-	wacom_wac->features = *((struct wacom_features *)id->driver_info);
-	features = &wacom_wac->features;
-	if (features->pktlen > WACOM_PKGLEN_MAX) {
-		error = -EINVAL;
-		goto fail1;
-	}
-
-	wacom_wac->data = usb_alloc_coherent(dev, WACOM_PKGLEN_MAX,
-					     GFP_KERNEL, &wacom->data_dma);
-	if (!wacom_wac->data) {
-		error = -ENOMEM;
-		goto fail1;
-	}
-
-	wacom->irq = usb_alloc_urb(0, GFP_KERNEL);
-	if (!wacom->irq) {
-		error = -ENOMEM;
-		goto fail2;
-	}
-
-	wacom->usbdev = dev;
-	wacom->intf = intf;
-	mutex_init(&wacom->lock);
-	INIT_WORK(&wacom->work, wacom_wireless_work);
-	usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
-	strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
-
-	endpoint = &intf->cur_altsetting->endpoint[0].desc;
-
-	/* 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 */
-	error = wacom_retrieve_hid_descriptor(intf, features);
-	if (error)
-		goto fail3;
-
-	/*
-	 * Intuos5 has no useful data about its touch interface in its
-	 * HID descriptor. If this is the touch interface (wMaxPacketSize
-	 * of WACOM_PKGLEN_BBTOUCH3), override the table values.
-	 */
-	if (features->type >= INTUOS5S && features->type <= INTUOSHT) {
-		if (endpoint->wMaxPacketSize == WACOM_PKGLEN_BBTOUCH3) {
-			features->device_type = BTN_TOOL_FINGER;
-			features->pktlen = WACOM_PKGLEN_BBTOUCH3;
-
-			features->x_max = 4096;
-			features->y_max = 4096;
-		} else {
-			features->device_type = BTN_TOOL_PEN;
-		}
-	}
-
-	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 = 16 - 3;
-	}
-	wacom_calculate_res(features);
-
-	strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name));
-
-	if (features->quirks & WACOM_QUIRK_MULTI_INPUT) {
-		struct usb_device *other_dev;
-
-		/* 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);
-
-		other_dev = wacom_get_sibling(dev, features->oVid, features->oPid);
-		if (other_dev == NULL || wacom_get_usbdev_data(other_dev) == NULL)
-			other_dev = dev;
-		error = wacom_add_shared_data(wacom_wac, other_dev);
-		if (error)
-			goto fail3;
-	}
-
-	usb_fill_int_urb(wacom->irq, dev,
-			 usb_rcvintpipe(dev, endpoint->bEndpointAddress),
-			 wacom_wac->data, features->pktlen,
-			 wacom_sys_irq, wacom, endpoint->bInterval);
-	wacom->irq->transfer_dma = wacom->data_dma;
-	wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
-	error = wacom_initialize_leds(wacom);
-	if (error)
-		goto fail4;
-
-	if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
-		error = wacom_register_input(wacom);
-		if (error)
-			goto fail5;
-	}
-
-	/* Note that if query fails it is not a hard failure */
-	wacom_query_tablet_data(intf, features);
-
-	usb_set_intfdata(intf, wacom);
-
-	if (features->quirks & WACOM_QUIRK_MONITOR) {
-		if (usb_submit_urb(wacom->irq, GFP_KERNEL)) {
-			error = -EIO;
-			goto fail5;
-		}
-	}
-
-	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;
-
- fail5: wacom_destroy_leds(wacom);
- fail4:	wacom_remove_shared_data(wacom_wac);
- fail3:	usb_free_urb(wacom->irq);
- fail2:	usb_free_coherent(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma);
- fail1:	kfree(wacom);
-	return error;
-}
-
-static void wacom_disconnect(struct usb_interface *intf)
-{
-	struct wacom *wacom = usb_get_intfdata(intf);
-
-	usb_set_intfdata(intf, NULL);
-
-	usb_kill_urb(wacom->irq);
-	cancel_work_sync(&wacom->work);
-	if (wacom->wacom_wac.input)
-		input_unregister_device(wacom->wacom_wac.input);
-	wacom_destroy_battery(wacom);
-	wacom_destroy_leds(wacom);
-	usb_free_urb(wacom->irq);
-	usb_free_coherent(interface_to_usbdev(intf), WACOM_PKGLEN_MAX,
-			wacom->wacom_wac.data, wacom->data_dma);
-	wacom_remove_shared_data(&wacom->wacom_wac);
-	kfree(wacom);
-}
-
-static int wacom_suspend(struct usb_interface *intf, pm_message_t message)
-{
-	struct wacom *wacom = usb_get_intfdata(intf);
-
-	mutex_lock(&wacom->lock);
-	usb_kill_urb(wacom->irq);
-	mutex_unlock(&wacom->lock);
-
-	return 0;
-}
-
-static int wacom_resume(struct usb_interface *intf)
-{
-	struct wacom *wacom = usb_get_intfdata(intf);
-	struct wacom_features *features = &wacom->wacom_wac.features;
-	int rv = 0;
-
-	mutex_lock(&wacom->lock);
-
-	/* switch to wacom mode first */
-	wacom_query_tablet_data(intf, features);
-	wacom_led_control(wacom);
-
-	if ((wacom->open || (features->quirks & WACOM_QUIRK_MONITOR)) &&
-	    usb_submit_urb(wacom->irq, GFP_NOIO) < 0)
-		rv = -EIO;
-
-	mutex_unlock(&wacom->lock);
-
-	return rv;
-}
-
-static int wacom_reset_resume(struct usb_interface *intf)
-{
-	return wacom_resume(intf);
-}
-
-static struct usb_driver wacom_driver = {
-	.name =		"wacom",
-	.id_table =	wacom_ids,
-	.probe =	wacom_probe,
-	.disconnect =	wacom_disconnect,
-	.suspend =	wacom_suspend,
-	.resume =	wacom_resume,
-	.reset_resume =	wacom_reset_resume,
-	.supports_autosuspend = 1,
-};
-
-module_usb_driver(wacom_driver);
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 77632cf..07fa806 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -311,6 +311,11 @@
 #define HID_GROUP_RMI				0x0100
 
 /*
+ * Vendor specific HID device groups
+ */
+#define HID_GROUP_WACOM				0x0101
+
+/*
  * This is the global environment of the parser. This information is
  * persistent for main-items. The global environment can be saved and
  * restored with PUSH/POP statements.