Merge branches 'for-4.3/chicony', 'for-4.3/cp2112', 'for-4.3/i2c-hid', 'for-4.3/lenov...
[linux-drm-fsl-dcu.git] / drivers / hid / wacom_wac.c
index 232da89f4e886fe02b82d452c1a0868f0b65b967..0215ab62bb93f1b3669ed02ba1087ab43ffa6456 100644 (file)
@@ -125,61 +125,47 @@ static int wacom_pl_irq(struct wacom_wac *wacom)
 
        prox = data[1] & 0x40;
 
-       if (prox) {
-               wacom->id[0] = ERASER_DEVICE_ID;
-               pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
-               if (features->pressure_max > 255)
-                       pressure = (pressure << 1) | ((data[4] >> 6) & 1);
-               pressure += (features->pressure_max + 1) / 2;
-
-               /*
-                * if going from out of proximity into proximity select between the eraser
-                * and the pen based on the state of the stylus2 button, choose eraser if
-                * pressed else choose pen. if not a proximity change from out to in, send
-                * an out of proximity for previous tool then a in for new tool.
-                */
-               if (!wacom->tool[0]) {
-                       /* Eraser bit set for DTF */
-                       if (data[1] & 0x10)
-                               wacom->tool[1] = BTN_TOOL_RUBBER;
-                       else
-                               /* Going into proximity select tool */
-                               wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
-               } else {
-                       /* was entered with stylus2 pressed */
-                       if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) {
-                               /* report out proximity for previous tool */
-                               input_report_key(input, wacom->tool[1], 0);
-                               input_sync(input);
-                               wacom->tool[1] = BTN_TOOL_PEN;
-                               return 0;
-                       }
+       if (!wacom->id[0]) {
+               if ((data[0] & 0x10) || (data[4] & 0x20)) {
+                       wacom->tool[0] = BTN_TOOL_RUBBER;
+                       wacom->id[0] = ERASER_DEVICE_ID;
                }
-               if (wacom->tool[1] != BTN_TOOL_RUBBER) {
-                       /* Unknown tool selected default to pen tool */
-                       wacom->tool[1] = BTN_TOOL_PEN;
+               else {
+                       wacom->tool[0] = BTN_TOOL_PEN;
                        wacom->id[0] = STYLUS_DEVICE_ID;
                }
-               input_report_key(input, wacom->tool[1], prox); /* report in proximity for tool */
-               input_report_abs(input, ABS_MISC, wacom->id[0]); /* report tool id */
-               input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
-               input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
-               input_report_abs(input, ABS_PRESSURE, pressure);
+       }
 
-               input_report_key(input, BTN_TOUCH, data[4] & 0x08);
-               input_report_key(input, BTN_STYLUS, data[4] & 0x10);
-               /* Only allow the stylus2 button to be reported for the pen tool. */
-               input_report_key(input, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
-       } else {
-               /* report proximity-out of a (valid) tool */
-               if (wacom->tool[1] != BTN_TOOL_RUBBER) {
-                       /* Unknown tool selected default to pen tool */
-                       wacom->tool[1] = BTN_TOOL_PEN;
-               }
-               input_report_key(input, wacom->tool[1], prox);
+       /* If the eraser is in prox, STYLUS2 is always set. If we
+        * mis-detected the type and notice that STYLUS2 isn't set
+        * then force the eraser out of prox and let the pen in.
+        */
+       if (wacom->tool[0] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) {
+               input_report_key(input, BTN_TOOL_RUBBER, 0);
+               input_report_abs(input, ABS_MISC, 0);
+               input_sync(input);
+               wacom->tool[0] = BTN_TOOL_PEN;
+               wacom->id[0] = STYLUS_DEVICE_ID;
        }
 
-       wacom->tool[0] = prox; /* Save proximity state */
+       pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
+       if (features->pressure_max > 255)
+               pressure = (pressure << 1) | ((data[4] >> 6) & 1);
+       pressure += (features->pressure_max + 1) / 2;
+
+       input_report_abs(input, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
+       input_report_abs(input, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
+       input_report_abs(input, ABS_PRESSURE, pressure);
+
+       input_report_key(input, BTN_TOUCH, data[4] & 0x08);
+       input_report_key(input, BTN_STYLUS, data[4] & 0x10);
+       /* Only allow the stylus2 button to be reported for the pen tool. */
+       input_report_key(input, BTN_STYLUS2, (wacom->tool[0] == BTN_TOOL_PEN) && (data[4] & 0x20));
+
+       if (!prox)
+               wacom->id[0] = 0;
+       input_report_key(input, wacom->tool[0], prox);
+       input_report_abs(input, ABS_MISC, wacom->id[0]);
        return 1;
 }
 
@@ -645,6 +631,130 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
        return 0;
 }
 
+static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len)
+{
+       unsigned char *data = wacom_wac->data;
+       struct input_dev *input = wacom_wac->pad_input;
+       struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
+       struct wacom_features *features = &wacom_wac->features;
+       int bat_charging, bat_percent, touch_ring_mode;
+       __u32 serial;
+       int i;
+
+       if (data[0] != WACOM_REPORT_REMOTE) {
+               dev_dbg(input->dev.parent,
+                       "%s: received unknown report #%d", __func__, data[0]);
+               return 0;
+       }
+
+       serial = data[3] + (data[4] << 8) + (data[5] << 16);
+       wacom_wac->id[0] = PAD_DEVICE_ID;
+
+       input_report_key(input, BTN_0, (data[9] & 0x01));
+       input_report_key(input, BTN_1, (data[9] & 0x02));
+       input_report_key(input, BTN_2, (data[9] & 0x04));
+       input_report_key(input, BTN_3, (data[9] & 0x08));
+       input_report_key(input, BTN_4, (data[9] & 0x10));
+       input_report_key(input, BTN_5, (data[9] & 0x20));
+       input_report_key(input, BTN_6, (data[9] & 0x40));
+       input_report_key(input, BTN_7, (data[9] & 0x80));
+
+       input_report_key(input, BTN_8, (data[10] & 0x01));
+       input_report_key(input, BTN_9, (data[10] & 0x02));
+       input_report_key(input, BTN_A, (data[10] & 0x04));
+       input_report_key(input, BTN_B, (data[10] & 0x08));
+       input_report_key(input, BTN_C, (data[10] & 0x10));
+       input_report_key(input, BTN_X, (data[10] & 0x20));
+       input_report_key(input, BTN_Y, (data[10] & 0x40));
+       input_report_key(input, BTN_Z, (data[10] & 0x80));
+
+       input_report_key(input, BTN_BASE, (data[11] & 0x01));
+       input_report_key(input, BTN_BASE2, (data[11] & 0x02));
+
+       if (data[12] & 0x80)
+               input_report_abs(input, ABS_WHEEL, (data[12] & 0x7f));
+       else
+               input_report_abs(input, ABS_WHEEL, 0);
+
+       bat_percent = data[7] & 0x7f;
+       bat_charging = !!(data[7] & 0x80);
+
+       if (data[9] | data[10] | (data[11] & 0x03) | data[12])
+               input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
+       else
+               input_report_abs(input, ABS_MISC, 0);
+
+       input_event(input, EV_MSC, MSC_SERIAL, serial);
+
+       /*Which mode select (LED light) is currently on?*/
+       touch_ring_mode = (data[11] & 0xC0) >> 6;
+
+       for (i = 0; i < WACOM_MAX_REMOTES; i++) {
+               if (wacom_wac->serial[i] == serial)
+                       wacom->led.select[i] = touch_ring_mode;
+       }
+
+       if (!wacom->battery &&
+           !(features->quirks & WACOM_QUIRK_BATTERY)) {
+               features->quirks |= WACOM_QUIRK_BATTERY;
+               INIT_WORK(&wacom->work, wacom_battery_work);
+               wacom_schedule_work(wacom_wac);
+       }
+
+       wacom_notify_battery(wacom_wac, bat_percent, bat_charging, 1,
+                            bat_charging);
+
+       return 1;
+}
+
+static int wacom_remote_status_irq(struct wacom_wac *wacom_wac, size_t len)
+{
+       struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
+       unsigned char *data = wacom_wac->data;
+       int i;
+
+       if (data[0] != WACOM_REPORT_DEVICE_LIST)
+               return 0;
+
+       for (i = 0; i < WACOM_MAX_REMOTES; i++) {
+               int j = i * 6;
+               int serial = (data[j+6] << 16) + (data[j+5] << 8) + data[j+4];
+               bool connected = data[j+2];
+
+               if (connected) {
+                       int k;
+
+                       if (wacom_wac->serial[i] == serial)
+                               continue;
+
+                       if (wacom_wac->serial[i]) {
+                               wacom_remote_destroy_attr_group(wacom,
+                                                       wacom_wac->serial[i]);
+                       }
+
+                       /* A remote can pair more than once with an EKR,
+                        * check to make sure this serial isn't already paired.
+                        */
+                       for (k = 0; k < WACOM_MAX_REMOTES; k++) {
+                               if (wacom_wac->serial[k] == serial)
+                                       break;
+                       }
+
+                       if (k < WACOM_MAX_REMOTES) {
+                               wacom_wac->serial[i] = serial;
+                               continue;
+                       }
+                       wacom_remote_create_attr_group(wacom, serial, i);
+
+               } else if (wacom_wac->serial[i]) {
+                       wacom_remote_destroy_attr_group(wacom,
+                                                       wacom_wac->serial[i]);
+               }
+       }
+
+       return 0;
+}
+
 static void wacom_intuos_general(struct wacom_wac *wacom)
 {
        struct wacom_features *features = &wacom->features;
@@ -1437,6 +1547,12 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field,
        return 0;
 }
 
+static void wacom_wac_pen_pre_report(struct hid_device *hdev,
+               struct hid_report *report)
+{
+       return;
+}
+
 static void wacom_wac_pen_report(struct hid_device *hdev,
                struct hid_report *report)
 {
@@ -1491,6 +1607,13 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
                        wacom_map_usage(input, usage, field, EV_ABS,
                                        ABS_MT_POSITION_Y, 4);
                break;
+       case HID_DG_WIDTH:
+       case HID_DG_HEIGHT:
+               features->last_slot_field = usage->hid;
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_MT_TOUCH_MAJOR, 0);
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_MT_TOUCH_MINOR, 0);
+               input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+               break;
        case HID_DG_CONTACTID:
                features->last_slot_field = usage->hid;
                break;
@@ -1504,6 +1627,10 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev,
                features->last_slot_field = usage->hid;
                wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0);
                break;
+       case HID_DG_CONTACTCOUNT:
+               wacom_wac->hid_data.cc_index = field->index;
+               wacom_wac->hid_data.cc_value_index = usage->usage_index;
+               break;
        }
 }
 
@@ -1515,6 +1642,10 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
        bool prox = hid_data->tipswitch &&
                    !wacom_wac->shared->stylus_in_proximity;
 
+       wacom_wac->hid_data.num_received++;
+       if (wacom_wac->hid_data.num_received > wacom_wac->hid_data.num_expected)
+               return;
+
        if (mt) {
                int slot;
 
@@ -1531,6 +1662,13 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
                                 hid_data->x);
                input_report_abs(input, mt ? ABS_MT_POSITION_Y : ABS_Y,
                                 hid_data->y);
+
+               if (test_bit(ABS_MT_TOUCH_MAJOR, input->absbit)) {
+                       input_report_abs(input, ABS_MT_TOUCH_MAJOR, max(hid_data->width, hid_data->height));
+                       input_report_abs(input, ABS_MT_TOUCH_MINOR, min(hid_data->width, hid_data->height));
+                       if (hid_data->width != hid_data->height)
+                               input_report_abs(input, ABS_MT_ORIENTATION, hid_data->width <= hid_data->height ? 0 : 1);
+               }
        }
 }
 
@@ -1547,6 +1685,12 @@ static int wacom_wac_finger_event(struct hid_device *hdev,
        case HID_GD_Y:
                wacom_wac->hid_data.y = value;
                break;
+       case HID_DG_WIDTH:
+               wacom_wac->hid_data.width = value;
+               break;
+       case HID_DG_HEIGHT:
+               wacom_wac->hid_data.height = value;
+               break;
        case HID_DG_CONTACTID:
                wacom_wac->hid_data.id = value;
                break;
@@ -1564,6 +1708,24 @@ static int wacom_wac_finger_event(struct hid_device *hdev,
        return 0;
 }
 
+static void wacom_wac_finger_pre_report(struct hid_device *hdev,
+               struct hid_report *report)
+{
+       struct wacom *wacom = hid_get_drvdata(hdev);
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct hid_data* hid_data = &wacom_wac->hid_data;
+
+       if (hid_data->cc_index >= 0) {
+               struct hid_field *field = report->field[hid_data->cc_index];
+               int value = field->value[hid_data->cc_value_index];
+               if (value)
+                       hid_data->num_expected = value;
+       }
+       else {
+               hid_data->num_expected = wacom_wac->features.touch_max;
+       }
+}
+
 static void wacom_wac_finger_report(struct hid_device *hdev,
                struct hid_report *report)
 {
@@ -1572,10 +1734,18 @@ static void wacom_wac_finger_report(struct hid_device *hdev,
        struct input_dev *input = wacom_wac->touch_input;
        unsigned touch_max = wacom_wac->features.touch_max;
 
+       /* If more packets of data are expected, give us a chance to
+        * process them rather than immediately syncing a partial
+        * update.
+        */
+       if (wacom_wac->hid_data.num_received < wacom_wac->hid_data.num_expected)
+               return;
+
        if (touch_max > 1)
                input_mt_sync_frame(input);
 
        input_sync(input);
+       wacom_wac->hid_data.num_received = 0;
 
        /* keep touch state for pen event */
        wacom_wac->shared->touch_down = wacom_wac_finger_count_touches(wacom_wac);
@@ -1615,6 +1785,25 @@ int wacom_wac_event(struct hid_device *hdev, struct hid_field *field,
        return 0;
 }
 
+static void wacom_report_events(struct hid_device *hdev, struct hid_report *report)
+{
+       int r;
+
+       for (r = 0; r < report->maxfield; r++) {
+               struct hid_field *field;
+               unsigned count, n;
+
+               field = report->field[r];
+               count = field->report_count;
+
+               if (!(HID_MAIN_ITEM_VARIABLE & field->flags))
+                       continue;
+
+               for (n = 0; n < count; n++)
+                       wacom_wac_event(hdev, field, &field->usage[n], field->value[n]);
+       }
+}
+
 void wacom_wac_report(struct hid_device *hdev, struct hid_report *report)
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
@@ -1624,6 +1813,14 @@ void wacom_wac_report(struct hid_device *hdev, struct hid_report *report)
        if (wacom_wac->features.type != HID_GENERIC)
                return;
 
+       if (WACOM_PEN_FIELD(field))
+               wacom_wac_pen_pre_report(hdev, report);
+
+       if (WACOM_FINGER_FIELD(field))
+               wacom_wac_finger_pre_report(hdev, report);
+
+       wacom_report_events(hdev, report);
+
        if (WACOM_PEN_FIELD(field))
                return wacom_wac_pen_report(hdev, report);
 
@@ -1699,7 +1896,7 @@ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
                int y = (data[3] << 4) | (data[4] & 0x0f);
                int width, height;
 
-               if (features->type >= INTUOSPS && features->type <= INTUOSPL) {
+               if (features->type >= INTUOSPS && features->type <= INTUOSHT) {
                        width  = data[5] * 100;
                        height = data[6] * 100;
                } else {
@@ -2118,6 +2315,13 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
                sync = wacom_wireless_irq(wacom_wac, len);
                break;
 
+       case REMOTE:
+               if (wacom_wac->data[0] == WACOM_REPORT_DEVICE_LIST)
+                       sync = wacom_remote_status_irq(wacom_wac, len);
+               else
+                       sync = wacom_remote_irq(wacom_wac, len);
+               break;
+
        default:
                sync = false;
                break;
@@ -2213,6 +2417,9 @@ void wacom_setup_device_quirks(struct wacom *wacom)
                        features->x_max = 4096;
                        features->y_max = 4096;
                }
+               else if (features->pktlen == WACOM_PKGLEN_BBTOUCH) {
+                       features->device_type |= WACOM_DEVICETYPE_PAD;
+               }
        }
 
        /*
@@ -2220,10 +2427,13 @@ void wacom_setup_device_quirks(struct wacom *wacom)
         * 0, whose HID descriptor has an application usage of 0xFF0D
         * (i.e., WACOM_VENDORDEFINED_PEN). We route pen packets back
         * out through the HID_GENERIC device created for interface 1,
-        * so rewrite this one to be of type BTN_TOOL_FINGER.
+        * so rewrite this one to be of type WACOM_DEVICETYPE_TOUCH.
         */
        if (features->type == BAMBOO_PAD)
-               features->device_type |= WACOM_DEVICETYPE_TOUCH;
+               features->device_type = WACOM_DEVICETYPE_TOUCH;
+
+       if (features->type == REMOTE)
+               features->device_type = WACOM_DEVICETYPE_PAD;
 
        if (wacom->hdev->bus == BUS_BLUETOOTH)
                features->quirks |= WACOM_QUIRK_BATTERY;
@@ -2239,13 +2449,7 @@ void wacom_setup_device_quirks(struct wacom *wacom)
        }
 
        if (features->type == WIRELESS) {
-
-               /* monitor never has input and pen/touch have delayed create */
-               features->quirks |= WACOM_QUIRK_NO_INPUT;
-
-               /* must be monitor interface if no device_type set */
-               if (features->device_type == WACOM_DEVICETYPE_NONE) {
-                       features->quirks |= WACOM_QUIRK_MONITOR;
+               if (features->device_type == WACOM_DEVICETYPE_WL_MONITOR) {
                        features->quirks |= WACOM_QUIRK_BATTERY;
                }
        }
@@ -2510,11 +2714,23 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
        return 0;
 }
 
+static void wacom_setup_numbered_buttons(struct input_dev *input_dev,
+                               int button_count)
+{
+       int i;
+
+       for (i = 0; i < button_count && i < 10; i++)
+               __set_bit(BTN_0 + i, input_dev->keybit);
+       for (i = 10; i < button_count && i < 16; i++)
+               __set_bit(BTN_A + (i-10), input_dev->keybit);
+       for (i = 16; i < button_count && i < 18; i++)
+               __set_bit(BTN_BASE + (i-16), input_dev->keybit);
+}
+
 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;
 
        if (!(features->device_type & WACOM_DEVICETYPE_PAD))
                return -ENODEV;
@@ -2531,10 +2747,14 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
        /* kept for making udev and libwacom accepting the pad */
        __set_bit(BTN_STYLUS, input_dev->keybit);
 
+       wacom_setup_numbered_buttons(input_dev, features->numbered_buttons);
+
        switch (features->type) {
+
+       case CINTIQ_HYBRID:
+       case DTK:
+       case DTUS:
        case GRAPHIRE_BT:
-               __set_bit(BTN_0, input_dev->keybit);
-               __set_bit(BTN_1, input_dev->keybit);
                break;
 
        case WACOM_MO:
@@ -2552,16 +2772,6 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
                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);
@@ -2583,12 +2793,6 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
                __set_bit(INPUT_PROP_ACCELEROMETER, input_dev->propbit);
                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);
@@ -2596,52 +2800,22 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
                /* 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;
 
@@ -2649,15 +2823,8 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
        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:
-               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;
 
@@ -2672,28 +2839,10 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
 
        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:
                __clear_bit(ABS_MISC, input_dev->absbit);
@@ -2705,6 +2854,11 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
 
                break;
 
+       case REMOTE:
+               input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
+               input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
+               break;
+
        default:
                /* no pad supported */
                return -ENODEV;
@@ -2720,7 +2874,7 @@ static const struct wacom_features wacom_features_0x10 =
          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 };
+         GRAPHIRE_BT, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES, 2 };
 static const struct wacom_features wacom_features_0x11 =
        { "Wacom Graphire2 4x5", 10206, 7422, 511, 63,
          GRAPHIRE, WACOM_GRAPHIRE_RES, WACOM_GRAPHIRE_RES };
@@ -2846,77 +3000,77 @@ static const struct wacom_features wacom_features_0x45 =
          INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0xB0 =
        { "Wacom Intuos3 4x5", 25400, 20320, 1023, 63,
-         INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 4 };
 static const struct wacom_features wacom_features_0xB1 =
        { "Wacom Intuos3 6x8", 40640, 30480, 1023, 63,
-         INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 8 };
 static const struct wacom_features wacom_features_0xB2 =
        { "Wacom Intuos3 9x12", 60960, 45720, 1023, 63,
-         INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 8 };
 static const struct wacom_features wacom_features_0xB3 =
        { "Wacom Intuos3 12x12", 60960, 60960, 1023, 63,
-         INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 8 };
 static const struct wacom_features wacom_features_0xB4 =
        { "Wacom Intuos3 12x19", 97536, 60960, 1023, 63,
-         INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         INTUOS3L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 8 };
 static const struct wacom_features wacom_features_0xB5 =
        { "Wacom Intuos3 6x11", 54204, 31750, 1023, 63,
-         INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         INTUOS3, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 8 };
 static const struct wacom_features wacom_features_0xB7 =
        { "Wacom Intuos3 4x6", 31496, 19685, 1023, 63,
-         INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         INTUOS3S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 4 };
 static const struct wacom_features wacom_features_0xB8 =
        { "Wacom Intuos4 4x6", 31496, 19685, 2047, 63,
-         INTUOS4S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         INTUOS4S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 7 };
 static const struct wacom_features wacom_features_0xB9 =
        { "Wacom Intuos4 6x9", 44704, 27940, 2047, 63,
-         INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9 };
 static const struct wacom_features wacom_features_0xBA =
        { "Wacom Intuos4 8x13", 65024, 40640, 2047, 63,
-         INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9 };
 static const struct wacom_features wacom_features_0xBB =
        { "Wacom Intuos4 12x19", 97536, 60960, 2047, 63,
-         INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         INTUOS4L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9 };
 static const struct wacom_features wacom_features_0xBC =
        { "Wacom Intuos4 WL", 40640, 25400, 2047, 63,
-         INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9 };
 static const struct wacom_features wacom_features_0xBD =
        { "Wacom Intuos4 WL", 40640, 25400, 2047, 63,
-         INTUOS4WL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         INTUOS4WL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9 };
 static const struct wacom_features wacom_features_0x26 =
        { "Wacom Intuos5 touch S", 31496, 19685, 2047, 63,
-         INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 };
+         INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 7, .touch_max = 16 };
 static const struct wacom_features wacom_features_0x27 =
        { "Wacom Intuos5 touch M", 44704, 27940, 2047, 63,
-         INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 };
+         INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, .touch_max = 16 };
 static const struct wacom_features wacom_features_0x28 =
        { "Wacom Intuos5 touch L", 65024, 40640, 2047, 63,
-         INTUOS5L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16 };
+         INTUOS5L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, .touch_max = 16 };
 static const struct wacom_features wacom_features_0x29 =
        { "Wacom Intuos5 S", 31496, 19685, 2047, 63,
-         INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 7 };
 static const struct wacom_features wacom_features_0x2A =
        { "Wacom Intuos5 M", 44704, 27940, 2047, 63,
-         INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9 };
 static const struct wacom_features wacom_features_0x314 =
        { "Wacom Intuos Pro S", 31496, 19685, 2047, 63,
-         INTUOSPS, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16,
+         INTUOSPS, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 7, .touch_max = 16,
          .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0x315 =
        { "Wacom Intuos Pro M", 44704, 27940, 2047, 63,
-         INTUOSPM, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16,
+         INTUOSPM, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, .touch_max = 16,
          .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0x317 =
        { "Wacom Intuos Pro L", 65024, 40640, 2047, 63,
-         INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, .touch_max = 16,
+         INTUOSPL, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, .touch_max = 16,
          .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0xF4 =
        { "Wacom Cintiq 24HD", 104080, 65200, 2047, 63,
-         WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 16,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0xF8 =
        { "Wacom Cintiq 24HD touch", 104080, 65200, 2047, 63, /* Pen */
-         WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 16,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf6 };
 static const struct wacom_features wacom_features_0xF6 =
@@ -2925,11 +3079,11 @@ static const struct wacom_features wacom_features_0xF6 =
          .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0x32A =
        { "Wacom Cintiq 27QHD", 119740, 67520, 2047, 63,
-         WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 0,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0x32B =
        { "Wacom Cintiq 27QHD touch", 119740, 67520, 2047, 63,
-         WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 0,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x32C };
 static const struct wacom_features wacom_features_0x32C =
@@ -2937,20 +3091,20 @@ static const struct wacom_features wacom_features_0x32C =
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x32B, .touch_max = 10 };
 static const struct wacom_features wacom_features_0x3F =
        { "Wacom Cintiq 21UX", 87200, 65600, 1023, 63,
-         CINTIQ, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         CINTIQ, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 8 };
 static const struct wacom_features wacom_features_0xC5 =
        { "Wacom Cintiq 20WSX", 86680, 54180, 1023, 63,
-         WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 10 };
 static const struct wacom_features wacom_features_0xC6 =
        { "Wacom Cintiq 12WX", 53020, 33440, 1023, 63,
-         WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+         WACOM_BEE, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 10 };
 static const struct wacom_features wacom_features_0x304 =
        { "Wacom Cintiq 13HD", 59152, 33448, 1023, 63,
-         WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0x333 =
        { "Wacom Cintiq 13HD touch", 59152, 33448, 2047, 63,
-         WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x335 };
 static const struct wacom_features wacom_features_0x335 =
@@ -2969,22 +3123,22 @@ static const struct wacom_features wacom_features_0xF0 =
          DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0xFB =
        { "Wacom DTU1031", 21896, 13760, 511, 0,
-         DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
+         DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4,
          WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
 static const struct wacom_features wacom_features_0x32F =
        { "Wacom DTU1031X", 22472, 12728, 511, 0,
-         DTUSX, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
+         DTUSX, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 0,
          WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
 static const struct wacom_features wacom_features_0x336 =
        { "Wacom DTU1141", 23472, 13203, 1023, 0,
-         DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+         DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4 };
 static const struct wacom_features wacom_features_0x57 =
        { "Wacom DTK2241", 95640, 54060, 2047, 63,
-         DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 6,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0x59 = /* Pen */
        { "Wacom DTH2242", 95640, 54060, 2047, 63,
-         DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 6,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5D };
 static const struct wacom_features wacom_features_0x5D = /* Touch */
@@ -2993,15 +3147,15 @@ static const struct wacom_features wacom_features_0x5D = /* Touch */
          .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0xCC =
        { "Wacom Cintiq 21UX2", 86800, 65200, 2047, 63,
-         WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 18,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0xFA =
        { "Wacom Cintiq 22HD", 95440, 53860, 2047, 63,
-         WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 18,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
 static const struct wacom_features wacom_features_0x5B =
        { "Wacom Cintiq 22HDT", 95440, 53860, 2047, 63,
-         WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         WACOM_22HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 18,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5e };
 static const struct wacom_features wacom_features_0x5E =
@@ -3148,7 +3302,7 @@ static const struct wacom_features wacom_features_0x6004 =
          TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0x307 =
        { "Wacom ISDv5 307", 59152, 33448, 2047, 63,
-         CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x309 };
 static const struct wacom_features wacom_features_0x309 =
@@ -3157,7 +3311,7 @@ static const struct wacom_features wacom_features_0x309 =
          .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
 static const struct wacom_features wacom_features_0x30A =
        { "Wacom ISDv5 30A", 59152, 33448, 2047, 63,
-         CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+         CINTIQ_HYBRID, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9,
          WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET,
          .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30C };
 static const struct wacom_features wacom_features_0x30C =
@@ -3174,6 +3328,10 @@ static const struct wacom_features wacom_features_0x323 =
        { "Wacom Intuos P M", 21600, 13500, 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_0x331 =
+       { "Wacom Express Key Remote", 0, 0, 0, 0,
+         REMOTE, 0, 0, 18, .check_for_hid_type = true,
+         .hid_type = HID_TYPE_USBNONE };
 
 static const struct wacom_features wacom_features_HID_ANY_ID =
        { "Wacom HID", .type = HID_GENERIC };
@@ -3329,6 +3487,7 @@ const struct hid_device_id wacom_ids[] = {
        { USB_DEVICE_WACOM(0x32B) },
        { USB_DEVICE_WACOM(0x32C) },
        { USB_DEVICE_WACOM(0x32F) },
+       { USB_DEVICE_WACOM(0x331) },
        { USB_DEVICE_WACOM(0x333) },
        { USB_DEVICE_WACOM(0x335) },
        { USB_DEVICE_WACOM(0x336) },