Merge branches 'for-3.18/always-poll-quirk', 'for-3.18/logitech', 'for-3.18/picolcd...
[sfrench/cifs-2.6.git] / drivers / hid / wacom_sys.c
index f1c6d3dae248dc1aa2862480704404a3859dd621..8593047bb726cb1046ebbdba7a48a5802d1dc764 100644 (file)
 
 #include "wacom_wac.h"
 #include "wacom.h"
-#include <linux/hid.h>
 
 #define WAC_MSG_RETRIES                5
 
+#define WAC_CMD_WL_LED_CONTROL 0x03
 #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)
+#define DEV_ATTR_RW_PERM (S_IRUGO | S_IWUSR | S_IWGRP)
+#define DEV_ATTR_WO_PERM (S_IWUSR | S_IWGRP)
+
+static int wacom_get_report(struct hid_device *hdev, u8 type, u8 *buf,
+                           size_t size, unsigned int retries)
 {
        int retval;
 
        do {
-               retval = hid_hw_raw_request(hdev, id, buf, size, type,
+               retval = hid_hw_raw_request(hdev, buf[0], buf, size, type,
                                HID_REQ_GET_REPORT);
        } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
 
        return retval;
 }
 
-static int wacom_set_report(struct hid_device *hdev, u8 type, u8 id,
-                           void *buf, size_t size, unsigned int retries)
+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, id, buf, size, type,
+               retval = hid_hw_raw_request(hdev, buf[0], buf, size, type,
                                HID_REQ_SET_REPORT);
        } while ((retval == -ETIMEDOUT || retval == -EPIPE) && --retries);
 
@@ -105,12 +109,35 @@ static void wacom_feature_mapping(struct hid_device *hdev,
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_features *features = &wacom->wacom_wac.features;
+       struct hid_data *hid_data = &wacom->wacom_wac.hid_data;
+       u8 *data;
+       int ret;
 
        switch (usage->hid) {
        case HID_DG_CONTACTMAX:
                /* leave touch_max as is if predefined */
-               if (!features->touch_max)
-                       features->touch_max = field->value[0];
+               if (!features->touch_max) {
+                       /* read manually */
+                       data = kzalloc(2, GFP_KERNEL);
+                       if (!data)
+                               break;
+                       data[0] = field->report->id;
+                       ret = wacom_get_report(hdev, HID_FEATURE_REPORT,
+                                               data, 2, 0);
+                       if (ret == 2)
+                               features->touch_max = data[1];
+                       kfree(data);
+               }
+               break;
+       case HID_DG_INPUTMODE:
+               /* Ignore if value index is out of bounds. */
+               if (usage->usage_index >= field->report_count) {
+                       dev_err(&hdev->dev, "HID_DG_INPUTMODE out of range\n");
+                       break;
+               }
+
+               hid_data->inputmode = field->report->id;
+               hid_data->inputmode_index = usage->usage_index;
                break;
        }
 }
@@ -198,6 +225,9 @@ static void wacom_usage_mapping(struct hid_device *hdev,
                        features->pressure_max = field->logical_maximum;
                break;
        }
+
+       if (features->type == HID_GENERIC)
+               wacom_wac_usage_mapping(hdev, field, usage);
 }
 
 static void wacom_parse_hid(struct hid_device *hdev,
@@ -236,6 +266,25 @@ static void wacom_parse_hid(struct hid_device *hdev,
        }
 }
 
+static int wacom_hid_set_device_mode(struct hid_device *hdev)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct hid_data *hid_data = &wacom->wacom_wac.hid_data;
+       struct hid_report *r;
+       struct hid_report_enum *re;
+
+       if (hid_data->inputmode < 0)
+               return 0;
+
+       re = &(hdev->report_enum[HID_FEATURE_REPORT]);
+       r = re->report_id_hash[hid_data->inputmode];
+       if (r) {
+               r->field[0]->value[hid_data->inputmode_index] = 2;
+               hid_hw_request(hdev, r, HID_REQ_SET_REPORT);
+       }
+       return 0;
+}
+
 static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
                int length, int mode)
 {
@@ -250,11 +299,11 @@ static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
                rep_data[0] = report_id;
                rep_data[1] = mode;
 
-               error = wacom_set_report(hdev, HID_FEATURE_REPORT,
-                                        report_id, rep_data, length, 1);
+               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);
+                                                rep_data, length, 1);
        } while ((error < 0 || rep_data[1] != mode) && limit++ < WAC_MSG_RETRIES);
 
        kfree(rep_data);
@@ -262,6 +311,59 @@ static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
        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
@@ -272,6 +374,12 @@ static int wacom_set_device_mode(struct hid_device *hdev, int report_id,
 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->type == HID_GENERIC)
+               return wacom_hid_set_device_mode(hdev);
+
        if (features->device_type == BTN_TOOL_FINGER) {
                if (features->type > TABLETPC) {
                        /* MT Tablet PC touch */
@@ -430,8 +538,14 @@ static int wacom_led_control(struct wacom *wacom)
 {
        unsigned char *buf;
        int retval;
+       unsigned char report_id = WAC_CMD_LED_CONTROL;
+       int buf_size = 9;
 
-       buf = kzalloc(9, GFP_KERNEL);
+       if (wacom->wacom_wac.pid) { /* wireless connected */
+               report_id = WAC_CMD_WL_LED_CONTROL;
+               buf_size = 13;
+       }
+       buf = kzalloc(buf_size, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
 
@@ -445,9 +559,16 @@ static int wacom_led_control(struct wacom *wacom)
                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);
+               unsigned char led_bits = (crop_lum << 4) | (ring_lum << 2) | (ring_led);
+
+               buf[0] = report_id;
+               if (wacom->wacom_wac.pid) {
+                       wacom_get_report(wacom->hdev, HID_FEATURE_REPORT,
+                                        buf, buf_size, WAC_CMD_RETRIES);
+                       buf[0] = report_id;
+                       buf[4] = led_bits;
+               } else
+                       buf[1] = led_bits;
        }
        else {
                int led = wacom->led.select[0] | 0x4;
@@ -456,46 +577,47 @@ static int wacom_led_control(struct wacom *wacom)
                    wacom->wacom_wac.features.type == WACOM_24HD)
                        led |= (wacom->led.select[1] << 4) | 0x40;
 
-               buf[0] = WAC_CMD_LED_CONTROL;
+               buf[0] = report_id;
                buf[1] = led;
                buf[2] = wacom->led.llv;
                buf[3] = wacom->led.hlv;
                buf[4] = wacom->led.img_lum;
        }
 
-       retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
-                                 WAC_CMD_LED_CONTROL, buf, 9, WAC_CMD_RETRIES);
+       retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, buf_size,
+                                 WAC_CMD_RETRIES);
        kfree(buf);
 
        return retval;
 }
 
-static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *img)
+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(259, GFP_KERNEL);
+       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,
-                                 WAC_CMD_ICON_START, buf, 2, WAC_CMD_RETRIES);
+       retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, 2,
+                                 WAC_CMD_RETRIES);
        if (retval < 0)
                goto out;
 
-       buf[0] = WAC_CMD_ICON_XFER;
+       buf[0] = xfer_id;
        buf[1] = button_id & 0x07;
        for (i = 0; i < 4; i++) {
                buf[2] = i;
-               memcpy(buf + 3, img + i * 256, 256);
+               memcpy(buf + 3, img + i * chunk_len, chunk_len);
 
                retval = wacom_set_report(wacom->hdev, HID_FEATURE_REPORT,
-                                         WAC_CMD_ICON_XFER,
-                                         buf, 259, WAC_CMD_RETRIES);
+                                         buf, chunk_len + 3, WAC_CMD_RETRIES);
                if (retval < 0)
                        break;
        }
@@ -503,8 +625,8 @@ static int wacom_led_putimage(struct wacom *wacom, int button_id, const void *im
        /* Send 'stop' */
        buf[0] = WAC_CMD_ICON_START;
        buf[1] = 0;
-       wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, WAC_CMD_ICON_START,
-                        buf, 2, WAC_CMD_RETRIES);
+       wacom_set_report(wacom->hdev, HID_FEATURE_REPORT, buf, 2,
+                        WAC_CMD_RETRIES);
 
 out:
        kfree(buf);
@@ -544,9 +666,10 @@ static ssize_t wacom_led##SET_ID##_select_show(struct device *dev, \
 {                                                                      \
        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]);     \
+       return scnprintf(buf, PAGE_SIZE, "%d\n",                        \
+                        wacom->led.select[SET_ID]);                    \
 }                                                                      \
-static DEVICE_ATTR(status_led##SET_ID##_select, S_IWUSR | S_IRUSR,     \
+static DEVICE_ATTR(status_led##SET_ID##_select, DEV_ATTR_RW_PERM,      \
                    wacom_led##SET_ID##_select_show,                    \
                    wacom_led##SET_ID##_select_store)
 
@@ -583,8 +706,15 @@ static ssize_t wacom_##name##_luminance_store(struct device *dev,  \
        return wacom_luminance_store(wacom, &wacom->led.field,          \
                                     buf, count);                       \
 }                                                                      \
-static DEVICE_ATTR(name##_luminance, S_IWUSR,                          \
-                  NULL, wacom_##name##_luminance_store)
+static ssize_t wacom_##name##_luminance_show(struct device *dev,       \
+       struct device_attribute *attr, char *buf)                       \
+{                                                                      \
+       struct wacom *wacom = dev_get_drvdata(dev);                     \
+       return scnprintf(buf, PAGE_SIZE, "%d\n", wacom->led.field);     \
+}                                                                      \
+static DEVICE_ATTR(name##_luminance, DEV_ATTR_RW_PERM,                 \
+                  wacom_##name##_luminance_show,                       \
+                  wacom_##name##_luminance_store)
 
 DEVICE_LUMINANCE_ATTR(status0, llv);
 DEVICE_LUMINANCE_ATTR(status1, hlv);
@@ -596,13 +726,23 @@ static ssize_t wacom_button_image_store(struct device *dev, int button_id,
        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 (count != 1024)
+       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, buf);
+       err = wacom_led_putimage(wacom, button_id, xfer_id, len, buf);
 
        mutex_unlock(&wacom->lock);
 
@@ -615,7 +755,7 @@ static ssize_t wacom_btnimg##BUTTON_ID##_store(struct device *dev,  \
 {                                                                      \
        return wacom_button_image_store(dev, BUTTON_ID, buf, count);    \
 }                                                                      \
-static DEVICE_ATTR(button##BUTTON_ID##_rawimg, S_IWUSR,                        \
+static DEVICE_ATTR(button##BUTTON_ID##_rawimg, DEV_ATTR_WO_PERM,       \
                   NULL, wacom_btnimg##BUTTON_ID##_store)
 
 DEVICE_BTNIMG_ATTR(0);
@@ -678,6 +818,7 @@ static int wacom_initialize_leds(struct wacom *wacom)
        switch (wacom->wacom_wac.features.type) {
        case INTUOS4S:
        case INTUOS4:
+       case INTUOS4WL:
        case INTUOS4L:
                wacom->led.select[0] = 0;
                wacom->led.select[1] = 0;
@@ -744,6 +885,7 @@ static void wacom_destroy_leds(struct wacom *wacom)
        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);
@@ -769,10 +911,17 @@ static void wacom_destroy_leds(struct wacom *wacom)
 }
 
 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)
@@ -786,7 +935,16 @@ static int wacom_battery_get_property(struct power_supply *psy,
                        break;
                case POWER_SUPPLY_PROP_CAPACITY:
                        val->intval =
-                               wacom->wacom_wac.battery_capacity * 100 / 31;
+                               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;
@@ -796,38 +954,116 @@ static int wacom_battery_get_property(struct power_supply *psy,
        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)
 {
-       int error = 0;
+       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;
 
-       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";
+               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;
 
-               if (!error)
-                       power_supply_powers(&wacom->battery,
-                                           &wacom->hdev->dev);
+               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 error;
+       return 0;
 }
 
 static void wacom_destroy_battery(struct wacom *wacom)
 {
-       if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR &&
-           wacom->battery.dev) {
+       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, DEV_ATTR_RW_PERM,
+               wacom_show_speed, wacom_store_speed);
+
 static struct input_dev *wacom_allocate_input(struct wacom *wacom)
 {
        struct input_dev *input_dev;
@@ -846,47 +1082,82 @@ static struct input_dev *wacom_allocate_input(struct wacom *wacom)
        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.product = wacom_wac->pid ? wacom_wac->pid : 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)
+static void wacom_free_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;
+       struct wacom_wac *wacom_wac = &(wacom->wacom_wac);
+
+       if (wacom_wac->input)
+               input_free_device(wacom_wac->input);
+       if (wacom_wac->pad_input)
+               input_free_device(wacom_wac->pad_input);
+       wacom_wac->input = NULL;
+       wacom_wac->pad_input = NULL;
 }
 
-static int wacom_register_inputs(struct wacom *wacom)
+static int wacom_allocate_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_free_inputs(wacom);
+               return -ENOMEM;
        }
 
        wacom_wac->input = input_dev;
        wacom_wac->pad_input = pad_input_dev;
        wacom_wac->pad_input->name = wacom_wac->pad_name;
 
+       return 0;
+}
+
+static void wacom_clean_inputs(struct wacom *wacom)
+{
+       if (wacom->wacom_wac.input) {
+               if (wacom->wacom_wac.input_registered)
+                       input_unregister_device(wacom->wacom_wac.input);
+               else
+                       input_free_device(wacom->wacom_wac.input);
+       }
+       if (wacom->wacom_wac.pad_input) {
+               if (wacom->wacom_wac.input_registered)
+                       input_unregister_device(wacom->wacom_wac.pad_input);
+               else
+                       input_free_device(wacom->wacom_wac.pad_input);
+       }
+       wacom->wacom_wac.input = NULL;
+       wacom->wacom_wac.pad_input = NULL;
+       wacom_destroy_leds(wacom);
+}
+
+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_wac->input;
+       pad_input_dev = wacom_wac->pad_input;
+
+       if (!input_dev || !pad_input_dev)
+               return -EINVAL;
+
        error = wacom_setup_input_capabilities(input_dev, wacom_wac);
        if (error)
-               goto fail2;
+               return error;
 
        error = input_register_device(input_dev);
        if (error)
-               goto fail2;
+               return error;
 
        error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac);
        if (error) {
@@ -897,22 +1168,23 @@ static int wacom_register_inputs(struct wacom *wacom)
        } else {
                error = input_register_device(pad_input_dev);
                if (error)
-                       goto fail3;
+                       goto fail_register_pad_input;
+
+               error = wacom_initialize_leds(wacom);
+               if (error)
+                       goto fail_leds;
        }
 
+       wacom_wac->input_registered = true;
+
        return 0;
 
-fail3:
+fail_leds:
+       input_unregister_device(pad_input_dev);
+       pad_input_dev = NULL;
+fail_register_pad_input:
        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;
 }
 
@@ -937,16 +1209,17 @@ static void wacom_wireless_work(struct work_struct *work)
        hdev1 = usb_get_intfdata(usbdev->config->interface[1]);
        wacom1 = hid_get_drvdata(hdev1);
        wacom_wac1 = &(wacom1->wacom_wac);
-       wacom_unregister_inputs(wacom1);
+       wacom_clean_inputs(wacom1);
 
        /* Touch interface */
        hdev2 = usb_get_intfdata(usbdev->config->interface[2]);
        wacom2 = hid_get_drvdata(hdev2);
        wacom_wac2 = &(wacom2->wacom_wac);
-       wacom_unregister_inputs(wacom2);
+       wacom_clean_inputs(wacom2);
 
        if (wacom_wac->pid == 0) {
                hid_info(wacom->hdev, "wireless tablet disconnected\n");
+               wacom_wac1->shared->type = 0;
        } else {
                const struct hid_device_id *id = wacom_ids;
 
@@ -975,7 +1248,9 @@ static void wacom_wireless_work(struct work_struct *work)
                         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);
+               wacom_wac1->pid = wacom_wac->pid;
+               error = wacom_allocate_inputs(wacom1) ||
+                       wacom_register_inputs(wacom1);
                if (error)
                        goto fail;
 
@@ -995,7 +1270,9 @@ static void wacom_wireless_work(struct work_struct *work)
                                         "%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);
+                       wacom_wac2->pid = wacom_wac->pid;
+                       error = wacom_allocate_inputs(wacom2) ||
+                               wacom_register_inputs(wacom2);
                        if (error)
                                goto fail;
 
@@ -1012,8 +1289,8 @@ static void wacom_wireless_work(struct work_struct *work)
        return;
 
 fail:
-       wacom_unregister_inputs(wacom1);
-       wacom_unregister_inputs(wacom2);
+       wacom_clean_inputs(wacom1);
+       wacom_clean_inputs(wacom2);
        return;
 }
 
@@ -1076,10 +1353,13 @@ static int wacom_probe(struct hid_device *hdev,
        struct wacom_wac *wacom_wac;
        struct wacom_features *features;
        int error;
+       unsigned int connect_mask = HID_CONNECT_HIDRAW;
 
        if (!id->driver_data)
                return -EINVAL;
 
+       hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
+
        wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
        if (!wacom)
                return -ENOMEM;
@@ -1091,7 +1371,7 @@ static int wacom_probe(struct hid_device *hdev,
        error = hid_parse(hdev);
        if (error) {
                hid_err(hdev, "parse failed\n");
-               goto fail1;
+               goto fail_parse;
        }
 
        wacom_wac = &wacom->wacom_wac;
@@ -1100,12 +1380,12 @@ static int wacom_probe(struct hid_device *hdev,
        features->pktlen = wacom_compute_pktlen(hdev);
        if (features->pktlen > WACOM_PKGLEN_MAX) {
                error = -EINVAL;
-               goto fail1;
+               goto fail_pktlen;
        }
 
        if (features->check_for_hid_type && features->hid_type != hdev->type) {
                error = -ENODEV;
-               goto fail1;
+               goto fail_type;
        }
 
        wacom->usbdev = dev;
@@ -1113,6 +1393,12 @@ static int wacom_probe(struct hid_device *hdev,
        mutex_init(&wacom->lock);
        INIT_WORK(&wacom->work, wacom_wireless_work);
 
+       if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
+               error = wacom_allocate_inputs(wacom);
+               if (error)
+                       goto fail_allocate_inputs;
+       }
+
        /* set the default size in case we do not get them from hid */
        wacom_set_default_phy(features);
 
@@ -1147,6 +1433,9 @@ static int wacom_probe(struct hid_device *hdev,
                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 */
@@ -1171,29 +1460,43 @@ static int wacom_probe(struct hid_device *hdev,
 
                error = wacom_add_shared_data(hdev);
                if (error)
-                       goto fail1;
+                       goto fail_shared_data;
        }
 
-       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 fail_battery;
+       }
 
        if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
                error = wacom_register_inputs(wacom);
                if (error)
-                       goto fail3;
+                       goto fail_register_inputs;
        }
 
-       /* Note that if query fails it is not a hard failure */
-       wacom_query_tablet_data(hdev, features);
+       if (hdev->bus == BUS_BLUETOOTH) {
+               error = device_create_file(&hdev->dev, &dev_attr_speed);
+               if (error)
+                       hid_warn(hdev,
+                                "can't create sysfs speed attribute err: %d\n",
+                                error);
+       }
+
+       if (features->type == HID_GENERIC)
+               connect_mask |= HID_CONNECT_DRIVER;
 
        /* Regular HID work starts now */
-       error = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+       error = hid_hw_start(hdev, connect_mask);
        if (error) {
                hid_err(hdev, "hw start failed\n");
-               goto fail4;
+               goto fail_hw_start;
        }
 
+       /* Note that if query fails it is not a hard failure */
+       wacom_query_tablet_data(hdev, features);
+
        if (features->quirks & WACOM_QUIRK_MONITOR)
                error = hid_hw_open(hdev);
 
@@ -1204,10 +1507,21 @@ static int wacom_probe(struct hid_device *hdev,
 
        return 0;
 
- fail4:        wacom_unregister_inputs(wacom);
- fail3:        wacom_destroy_leds(wacom);
- fail2:        wacom_remove_shared_data(wacom_wac);
- fail1:        kfree(wacom);
+fail_hw_start:
+       if (hdev->bus == BUS_BLUETOOTH)
+               device_remove_file(&hdev->dev, &dev_attr_speed);
+fail_register_inputs:
+       wacom_clean_inputs(wacom);
+       wacom_destroy_battery(wacom);
+fail_battery:
+       wacom_remove_shared_data(wacom_wac);
+fail_shared_data:
+       wacom_clean_inputs(wacom);
+fail_allocate_inputs:
+fail_type:
+fail_pktlen:
+fail_parse:
+       kfree(wacom);
        hid_set_drvdata(hdev, NULL);
        return error;
 }
@@ -1219,15 +1533,17 @@ static void wacom_remove(struct hid_device *hdev)
        hid_hw_stop(hdev);
 
        cancel_work_sync(&wacom->work);
-       wacom_unregister_inputs(wacom);
+       wacom_clean_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);
 }
 
+#ifdef CONFIG_PM
 static int wacom_resume(struct hid_device *hdev)
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
@@ -1248,12 +1564,15 @@ static int wacom_reset_resume(struct hid_device *hdev)
 {
        return wacom_resume(hdev);
 }
+#endif /* CONFIG_PM */
 
 static struct hid_driver wacom_driver = {
        .name =         "wacom",
        .id_table =     wacom_ids,
        .probe =        wacom_probe,
        .remove =       wacom_remove,
+       .event =        wacom_wac_event,
+       .report =       wacom_wac_report,
 #ifdef CONFIG_PM
        .resume =       wacom_resume,
        .reset_resume = wacom_reset_resume,
@@ -1261,3 +1580,8 @@ static struct hid_driver wacom_driver = {
        .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);