]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/hid/wacom_wac.c
HID: wacom: generic: Treat serial number and related fields as unsigned
[mirror_ubuntu-bionic-kernel.git] / drivers / hid / wacom_wac.c
index 16af6886e82887982054cc8e75cc02de1a39ad08..bffb4ebe74b74b09e80642751f7767c142c2948c 100644 (file)
@@ -537,14 +537,14 @@ static int wacom_intuos_pad(struct wacom_wac *wacom)
                 */
                buttons = (data[4] << 1) | (data[3] & 0x01);
        } else if (features->type == CINTIQ_COMPANION_2) {
-               /* d-pad right  -> data[4] & 0x10
-                * d-pad up     -> data[4] & 0x20
-                * d-pad left   -> data[4] & 0x40
-                * d-pad down   -> data[4] & 0x80
-                * d-pad center -> data[3] & 0x01
+               /* d-pad right  -> data[2] & 0x10
+                * d-pad up     -> data[2] & 0x20
+                * d-pad left   -> data[2] & 0x40
+                * d-pad down   -> data[2] & 0x80
+                * d-pad center -> data[1] & 0x01
                 */
                buttons = ((data[2] >> 4) << 7) |
-                         ((data[1] & 0x04) << 6) |
+                         ((data[1] & 0x04) << 4) |
                          ((data[2] & 0x0F) << 2) |
                          (data[1] & 0x03);
        } else if (features->type >= INTUOS5S && features->type <= INTUOSPL) {
@@ -689,6 +689,45 @@ static int wacom_intuos_get_tool_type(int tool_id)
        return tool_type;
 }
 
+static void wacom_exit_report(struct wacom_wac *wacom)
+{
+       struct input_dev *input = wacom->pen_input;
+       struct wacom_features *features = &wacom->features;
+       unsigned char *data = wacom->data;
+       int idx = (features->type == INTUOS) ? (data[1] & 0x01) : 0;
+
+       /*
+        * Reset all states otherwise we lose the initial states
+        * when in-prox next time
+        */
+       input_report_abs(input, ABS_X, 0);
+       input_report_abs(input, ABS_Y, 0);
+       input_report_abs(input, ABS_DISTANCE, 0);
+       input_report_abs(input, ABS_TILT_X, 0);
+       input_report_abs(input, ABS_TILT_Y, 0);
+       if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
+               input_report_key(input, BTN_LEFT, 0);
+               input_report_key(input, BTN_MIDDLE, 0);
+               input_report_key(input, BTN_RIGHT, 0);
+               input_report_key(input, BTN_SIDE, 0);
+               input_report_key(input, BTN_EXTRA, 0);
+               input_report_abs(input, ABS_THROTTLE, 0);
+               input_report_abs(input, ABS_RZ, 0);
+       } else {
+               input_report_abs(input, ABS_PRESSURE, 0);
+               input_report_key(input, BTN_STYLUS, 0);
+               input_report_key(input, BTN_STYLUS2, 0);
+               input_report_key(input, BTN_TOUCH, 0);
+               input_report_abs(input, ABS_WHEEL, 0);
+               if (features->type >= INTUOS3S)
+                       input_report_abs(input, ABS_Z, 0);
+       }
+       input_report_key(input, wacom->tool[idx], 0);
+       input_report_abs(input, ABS_MISC, 0); /* reset tool id */
+       input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+       wacom->id[idx] = 0;
+}
+
 static int wacom_intuos_inout(struct wacom_wac *wacom)
 {
        struct wacom_features *features = &wacom->features;
@@ -741,36 +780,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
                if (!wacom->id[idx])
                        return 1;
 
-               /*
-                * Reset all states otherwise we lose the initial states
-                * when in-prox next time
-                */
-               input_report_abs(input, ABS_X, 0);
-               input_report_abs(input, ABS_Y, 0);
-               input_report_abs(input, ABS_DISTANCE, 0);
-               input_report_abs(input, ABS_TILT_X, 0);
-               input_report_abs(input, ABS_TILT_Y, 0);
-               if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
-                       input_report_key(input, BTN_LEFT, 0);
-                       input_report_key(input, BTN_MIDDLE, 0);
-                       input_report_key(input, BTN_RIGHT, 0);
-                       input_report_key(input, BTN_SIDE, 0);
-                       input_report_key(input, BTN_EXTRA, 0);
-                       input_report_abs(input, ABS_THROTTLE, 0);
-                       input_report_abs(input, ABS_RZ, 0);
-               } else {
-                       input_report_abs(input, ABS_PRESSURE, 0);
-                       input_report_key(input, BTN_STYLUS, 0);
-                       input_report_key(input, BTN_STYLUS2, 0);
-                       input_report_key(input, BTN_TOUCH, 0);
-                       input_report_abs(input, ABS_WHEEL, 0);
-                       if (features->type >= INTUOS3S)
-                               input_report_abs(input, ABS_Z, 0);
-               }
-               input_report_key(input, wacom->tool[idx], 0);
-               input_report_abs(input, ABS_MISC, 0); /* reset tool id */
-               input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
-               wacom->id[idx] = 0;
+               wacom_exit_report(wacom);
                return 2;
        }
 
@@ -838,6 +848,8 @@ static int wacom_intuos_general(struct wacom_wac *wacom)
                y >>= 1;
                distance >>= 1;
        }
+       if (features->type == INTUOSHT2)
+               distance = features->distance_max - distance;
        input_report_abs(input, ABS_X, x);
        input_report_abs(input, ABS_Y, y);
        input_report_abs(input, ABS_DISTANCE, distance);
@@ -1051,7 +1063,7 @@ static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len)
        input_report_key(input, BTN_BASE2, (data[11] & 0x02));
 
        if (data[12] & 0x80)
-               input_report_abs(input, ABS_WHEEL, (data[12] & 0x7f));
+               input_report_abs(input, ABS_WHEEL, (data[12] & 0x7f) - 1);
        else
                input_report_abs(input, ABS_WHEEL, 0);
 
@@ -1215,17 +1227,28 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
                /* Add back in missing bits of ID for non-USI pens */
                wacom->id[0] |= (wacom->serial[0] >> 32) & 0xFFFFF;
        }
-       wacom->tool[0]   = wacom_intuos_get_tool_type(wacom_intuos_id_mangle(wacom->id[0]));
 
        for (i = 0; i < pen_frames; i++) {
                unsigned char *frame = &data[i*pen_frame_len + 1];
                bool valid = frame[0] & 0x80;
                bool prox = frame[0] & 0x40;
                bool range = frame[0] & 0x20;
+               bool invert = frame[0] & 0x10;
 
                if (!valid)
                        continue;
 
+               if (!prox) {
+                       wacom->shared->stylus_in_proximity = false;
+                       wacom_exit_report(wacom);
+                       input_sync(pen_input);
+
+                       wacom->tool[0] = 0;
+                       wacom->id[0] = 0;
+                       wacom->serial[0] = 0;
+                       return;
+               }
+
                if (range) {
                        /* Fix rotation alignment: userspace expects zero at left */
                        int16_t rotation = (int16_t)get_unaligned_le16(&frame[9]);
@@ -1233,6 +1256,16 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
                        if (rotation > 899)
                                rotation -= 1800;
 
+                       if (!wacom->tool[0]) { /* first in range */
+                               /* Going into range select tool */
+                               if (invert)
+                                       wacom->tool[0] = BTN_TOOL_RUBBER;
+                               else if (wacom->id[0])
+                                       wacom->tool[0] = wacom_intuos_get_tool_type(wacom->id[0]);
+                               else
+                                       wacom->tool[0] = BTN_TOOL_PEN;
+                       }
+
                        input_report_abs(pen_input, ABS_X, get_unaligned_le16(&frame[1]));
                        input_report_abs(pen_input, ABS_Y, get_unaligned_le16(&frame[3]));
                        input_report_abs(pen_input, ABS_TILT_X, (char)frame[7]);
@@ -1240,17 +1273,19 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
                        input_report_abs(pen_input, ABS_Z, rotation);
                        input_report_abs(pen_input, ABS_WHEEL, get_unaligned_le16(&frame[11]));
                }
-               input_report_abs(pen_input, ABS_PRESSURE, get_unaligned_le16(&frame[5]));
-               input_report_abs(pen_input, ABS_DISTANCE, range ? frame[13] : wacom->features.distance_max);
-
-               input_report_key(pen_input, BTN_TOUCH, frame[0] & 0x01);
-               input_report_key(pen_input, BTN_STYLUS, frame[0] & 0x02);
-               input_report_key(pen_input, BTN_STYLUS2, frame[0] & 0x04);
-
-               input_report_key(pen_input, wacom->tool[0], prox);
-               input_event(pen_input, EV_MSC, MSC_SERIAL, wacom->serial[0]);
-               input_report_abs(pen_input, ABS_MISC,
-                                wacom_intuos_id_mangle(wacom->id[0])); /* report tool id */
+               if (wacom->tool[0]) {
+                       input_report_abs(pen_input, ABS_PRESSURE, get_unaligned_le16(&frame[5]));
+                       input_report_abs(pen_input, ABS_DISTANCE, range ? frame[13] : wacom->features.distance_max);
+
+                       input_report_key(pen_input, BTN_TOUCH, frame[0] & 0x09);
+                       input_report_key(pen_input, BTN_STYLUS, frame[0] & 0x02);
+                       input_report_key(pen_input, BTN_STYLUS2, frame[0] & 0x04);
+
+                       input_report_key(pen_input, wacom->tool[0], prox);
+                       input_event(pen_input, EV_MSC, MSC_SERIAL, wacom->serial[0]);
+                       input_report_abs(pen_input, ABS_MISC,
+                                        wacom_intuos_id_mangle(wacom->id[0])); /* report tool id */
+               }
 
                wacom->shared->stylus_in_proximity = prox;
 
@@ -1312,11 +1347,17 @@ static void wacom_intuos_pro2_bt_touch(struct wacom_wac *wacom)
                if (wacom->num_contacts_left <= 0) {
                        wacom->num_contacts_left = 0;
                        wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
+                       input_sync(touch_input);
                }
        }
 
-       input_report_switch(touch_input, SW_MUTE_DEVICE, !(data[281] >> 7));
-       input_sync(touch_input);
+       if (wacom->num_contacts_left == 0) {
+               // Be careful that we don't accidentally call input_sync with
+               // only a partial set of fingers of processed
+               input_report_switch(touch_input, SW_MUTE_DEVICE, !(data[281] >> 7));
+               input_sync(touch_input);
+       }
+
 }
 
 static void wacom_intuos_pro2_bt_pad(struct wacom_wac *wacom)
@@ -1324,7 +1365,7 @@ static void wacom_intuos_pro2_bt_pad(struct wacom_wac *wacom)
        struct input_dev *pad_input = wacom->pad_input;
        unsigned char *data = wacom->data;
 
-       int buttons = (data[282] << 1) | ((data[281] >> 6) & 0x01);
+       int buttons = data[282] | ((data[281] & 0x40) << 2);
        int ring = data[285] & 0x7F;
        bool ringstatus = data[285] & 0x80;
        bool prox = buttons || ringstatus;
@@ -1832,8 +1873,6 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev,
                features->device_type |= WACOM_DEVICETYPE_PAD;
                break;
        case WACOM_HID_WD_BUTTONCENTER:
-               wacom->generic_has_leds = true;
-               /* fall through */
        case WACOM_HID_WD_BUTTONHOME:
        case WACOM_HID_WD_BUTTONUP:
        case WACOM_HID_WD_BUTTONDOWN:
@@ -1924,7 +1963,6 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field
        struct wacom_features *features = &wacom_wac->features;
        unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
        int i;
-       bool is_touch_on = value;
        bool do_report = false;
 
        /*
@@ -1969,16 +2007,17 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field
                break;
 
        case WACOM_HID_WD_MUTE_DEVICE:
-               if (wacom_wac->shared->touch_input && value) {
-                       wacom_wac->shared->is_touch_on = !wacom_wac->shared->is_touch_on;
-                       is_touch_on = wacom_wac->shared->is_touch_on;
-               }
-
-               /* fall through*/
        case WACOM_HID_WD_TOUCHONOFF:
                if (wacom_wac->shared->touch_input) {
+                       bool *is_touch_on = &wacom_wac->shared->is_touch_on;
+
+                       if (equivalent_usage == WACOM_HID_WD_MUTE_DEVICE && value)
+                               *is_touch_on = !(*is_touch_on);
+                       else if (equivalent_usage == WACOM_HID_WD_TOUCHONOFF)
+                               *is_touch_on = value;
+
                        input_report_switch(wacom_wac->shared->touch_input,
-                                           SW_MUTE_DEVICE, !is_touch_on);
+                                           SW_MUTE_DEVICE, !(*is_touch_on));
                        input_sync(wacom_wac->shared->touch_input);
                }
                break;
@@ -2149,7 +2188,7 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field
        case HID_DG_TOOLSERIALNUMBER:
                if (value) {
                        wacom_wac->serial[0] = (wacom_wac->serial[0] & ~0xFFFFFFFFULL);
-                       wacom_wac->serial[0] |= (__u32)value;
+                       wacom_wac->serial[0] |= wacom_s32tou(value, field->report_size);
                }
                return;
        case HID_DG_TWIST:
@@ -2165,15 +2204,17 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field
                return;
        case WACOM_HID_WD_SERIALHI:
                if (value) {
+                       __u32 raw_value = wacom_s32tou(value, field->report_size);
+
                        wacom_wac->serial[0] = (wacom_wac->serial[0] & 0xFFFFFFFF);
-                       wacom_wac->serial[0] |= ((__u64)value) << 32;
+                       wacom_wac->serial[0] |= ((__u64)raw_value) << 32;
                        /*
                         * Non-USI EMR devices may contain additional tool type
                         * information here. See WACOM_HID_WD_TOOLTYPE case for
                         * more details.
                         */
                        if (value >> 20 == 1) {
-                               wacom_wac->id[0] |= value & 0xFFFFF;
+                               wacom_wac->id[0] |= raw_value & 0xFFFFF;
                        }
                }
                return;
@@ -2185,7 +2226,7 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field
                 * bitwise OR so the complete value can be built
                 * up over time :(
                 */
-               wacom_wac->id[0] |= value;
+               wacom_wac->id[0] |= wacom_s32tou(value, field->report_size);
                return;
        case WACOM_HID_WD_OFFSETLEFT:
                if (features->offset_left && value != features->offset_left)
@@ -2401,6 +2442,7 @@ static void wacom_wac_finger_event(struct hid_device *hdev,
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
        unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
+       struct wacom_features *features = &wacom->wacom_wac.features;
 
        switch (equivalent_usage) {
        case HID_GD_X:
@@ -2421,6 +2463,9 @@ static void wacom_wac_finger_event(struct hid_device *hdev,
        case HID_DG_TIPSWITCH:
                wacom_wac->hid_data.tipswitch = value;
                break;
+       case HID_DG_CONTACTMAX:
+               features->touch_max = value;
+               return;
        }
 
 
@@ -3208,8 +3253,14 @@ void wacom_setup_device_quirks(struct wacom *wacom)
                        if (features->type >= INTUOSHT && features->type <= BAMBOO_PT)
                                features->device_type |= WACOM_DEVICETYPE_PAD;
 
-                       features->x_max = 4096;
-                       features->y_max = 4096;
+                       if (features->type == INTUOSHT2) {
+                               features->x_max = features->x_max / 10;
+                               features->y_max = features->y_max / 10;
+                       }
+                       else {
+                               features->x_max = 4096;
+                               features->y_max = 4096;
+                       }
                }
                else if (features->pktlen == WACOM_PKGLEN_BBTOUCH) {
                        features->device_type |= WACOM_DEVICETYPE_PAD;
@@ -3521,7 +3572,7 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
                                             0, 5920, 4, 0);
                }
                input_abs_set_res(input_dev, ABS_MT_POSITION_X, 40);
-               input_abs_set_res(input_dev, ABS_MT_POSITION_X, 40);
+               input_abs_set_res(input_dev, ABS_MT_POSITION_Y, 40);
 
                /* fall through */
 
@@ -3642,7 +3693,7 @@ static void wacom_24hd_update_leds(struct wacom *wacom, int mask, int group)
 static bool wacom_is_led_toggled(struct wacom *wacom, int button_count,
                                 int mask, int group)
 {
-       int button_per_group;
+       int group_button;
 
        /*
         * 21UX2 has LED group 1 to the left and LED group 0
@@ -3652,9 +3703,12 @@ static bool wacom_is_led_toggled(struct wacom *wacom, int button_count,
        if (wacom->wacom_wac.features.type == WACOM_21UX2)
                group = 1 - group;
 
-       button_per_group = button_count/wacom->led.count;
+       group_button = group * (button_count/wacom->led.count);
+
+       if (wacom->wacom_wac.features.type == INTUOSP2_BT)
+               group_button = 8;
 
-       return mask & (1 << (group * button_per_group));
+       return mask & (1 << group_button);
 }
 
 static void wacom_update_led(struct wacom *wacom, int button_count, int mask,