]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/hid/wacom_wac.c
Merge branches 'for-4.10/upstream-fixes', 'for-4.11/intel-ish', 'for-4.11/mayflash...
[mirror_ubuntu-artful-kernel.git] / drivers / hid / wacom_wac.c
index b1a9a3ca6d564c72d3f445e663b196af87743ed1..4aa3de9f1163b30eb64b4304f285a4167aef0cf0 100644 (file)
@@ -43,6 +43,8 @@ static void wacom_report_numbered_buttons(struct input_dev *input_dev,
 
 static int wacom_numbered_button_to_key(int n);
 
+static void wacom_update_led(struct wacom *wacom, int button_count, int mask,
+                            int group);
 /*
  * Percent of battery capacity for Graphire.
  * 8th value means AC online and show 100% capacity.
@@ -166,19 +168,21 @@ static int wacom_pl_irq(struct wacom_wac *wacom)
                wacom->id[0] = STYLUS_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 (prox) {
+               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_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));
+               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;
@@ -1190,6 +1194,166 @@ static int wacom_wac_finger_count_touches(struct wacom_wac *wacom)
        return count;
 }
 
+static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
+{
+       const int pen_frame_len = 14;
+       const int pen_frames = 7;
+
+       struct input_dev *pen_input = wacom->pen_input;
+       unsigned char *data = wacom->data;
+       int i;
+
+       wacom->serial[0] = get_unaligned_le64(&data[99]);
+       wacom->id[0]     = get_unaligned_le16(&data[107]);
+       if (wacom->serial[0] >> 52 == 1) {
+               /* 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;
+
+               if (!valid)
+                       continue;
+
+               if (range) {
+                       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, frame[7]);
+                       input_report_abs(pen_input, ABS_TILT_Y, frame[8]);
+                       input_report_abs(pen_input, ABS_Z, get_unaligned_le16(&frame[9]));
+                       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 */
+
+               wacom->shared->stylus_in_proximity = prox;
+
+               input_sync(pen_input);
+       }
+}
+
+static void wacom_intuos_pro2_bt_touch(struct wacom_wac *wacom)
+{
+       const int finger_touch_len = 8;
+       const int finger_frames = 4;
+       const int finger_frame_len = 43;
+
+       struct input_dev *touch_input = wacom->touch_input;
+       unsigned char *data = wacom->data;
+       int num_contacts_left = 5;
+       int i, j;
+
+       for (i = 0; i < finger_frames; i++) {
+               unsigned char *frame = &data[i*finger_frame_len + 109];
+               int current_num_contacts = frame[0] & 0x7F;
+               int contacts_to_send;
+
+               if (!(frame[0] & 0x80))
+                       continue;
+
+               /*
+                * First packet resets the counter since only the first
+                * packet in series will have non-zero current_num_contacts.
+                */
+               if (current_num_contacts)
+                       wacom->num_contacts_left = current_num_contacts;
+
+               contacts_to_send = min(num_contacts_left, wacom->num_contacts_left);
+
+               for (j = 0; j < contacts_to_send; j++) {
+                       unsigned char *touch = &frame[j*finger_touch_len + 1];
+                       int slot = input_mt_get_slot_by_key(touch_input, touch[0]);
+                       int x = get_unaligned_le16(&touch[2]);
+                       int y = get_unaligned_le16(&touch[4]);
+                       int w = touch[6] * input_abs_get_res(touch_input, ABS_MT_POSITION_X);
+                       int h = touch[7] * input_abs_get_res(touch_input, ABS_MT_POSITION_Y);
+
+                       if (slot < 0)
+                               continue;
+
+                       input_mt_slot(touch_input, slot);
+                       input_mt_report_slot_state(touch_input, MT_TOOL_FINGER, touch[1] & 0x01);
+                       input_report_abs(touch_input, ABS_MT_POSITION_X, x);
+                       input_report_abs(touch_input, ABS_MT_POSITION_Y, y);
+                       input_report_abs(touch_input, ABS_MT_TOUCH_MAJOR, max(w, h));
+                       input_report_abs(touch_input, ABS_MT_TOUCH_MINOR, min(w, h));
+                       input_report_abs(touch_input, ABS_MT_ORIENTATION, w > h);
+               }
+
+               input_mt_sync_frame(touch_input);
+
+               wacom->num_contacts_left -= contacts_to_send;
+               if (wacom->num_contacts_left <= 0) {
+                       wacom->num_contacts_left = 0;
+                       wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom);
+               }
+       }
+
+       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)
+{
+       struct input_dev *pad_input = wacom->pad_input;
+       unsigned char *data = wacom->data;
+
+       int buttons = (data[282] << 1) | ((data[281] >> 6) & 0x01);
+       int ring = data[285];
+       int prox = buttons | (ring & 0x80);
+
+       wacom_report_numbered_buttons(pad_input, 9, buttons);
+
+       input_report_abs(pad_input, ABS_WHEEL, (ring & 0x80) ? (ring & 0x7f) : 0);
+
+       input_report_key(pad_input, wacom->tool[1], prox ? 1 : 0);
+       input_report_abs(pad_input, ABS_MISC, prox ? PAD_DEVICE_ID : 0);
+       input_event(pad_input, EV_MSC, MSC_SERIAL, 0xffffffff);
+
+       input_sync(pad_input);
+}
+
+static void wacom_intuos_pro2_bt_battery(struct wacom_wac *wacom)
+{
+       unsigned char *data = wacom->data;
+
+       bool chg = data[284] & 0x80;
+       int battery_status = data[284] & 0x7F;
+
+       wacom_notify_battery(wacom, battery_status, chg, 1, chg);
+}
+
+static int wacom_intuos_pro2_bt_irq(struct wacom_wac *wacom, size_t len)
+{
+       unsigned char *data = wacom->data;
+
+       if (data[0] != 0x80) {
+               dev_dbg(wacom->pen_input->dev.parent,
+                       "%s: received unknown report #%d\n", __func__, data[0]);
+               return 0;
+       }
+
+       wacom_intuos_pro2_bt_pen(wacom);
+       wacom_intuos_pro2_bt_touch(wacom);
+       wacom_intuos_pro2_bt_pad(wacom);
+       wacom_intuos_pro2_bt_battery(wacom);
+       return 0;
+}
+
 static int wacom_24hdt_irq(struct wacom_wac *wacom)
 {
        struct input_dev *input = wacom->touch_input;
@@ -1444,7 +1608,7 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
        return 0;
 }
 
-static int wacom_equivalent_usage(int usage)
+int wacom_equivalent_usage(int usage)
 {
        if ((usage & HID_USAGE_PAGE) == WACOM_HID_UP_WACOMDIGITIZER) {
                int subpage = (usage & 0xFF00) << 8;
@@ -1471,6 +1635,16 @@ static int wacom_equivalent_usage(int usage)
                return subpage | subusage;
        }
 
+       if ((usage & HID_USAGE_PAGE) == WACOM_HID_UP_WACOMTOUCH) {
+               int subpage = (usage & 0xFF00) << 8;
+               int subusage = (usage & 0xFF);
+
+               if (subpage == HID_UP_UNDEFINED)
+                       subpage = WACOM_HID_SP_DIGITIZER;
+
+               return subpage | subusage;
+       }
+
        return usage;
 }
 
@@ -1550,12 +1724,14 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev,
                wacom_map_usage(input, usage, field, EV_ABS, ABS_Z, 0);
                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:
        case WACOM_HID_WD_BUTTONLEFT:
        case WACOM_HID_WD_BUTTONRIGHT:
-       case WACOM_HID_WD_BUTTONCENTER:
                wacom_map_usage(input, usage, field, EV_KEY,
                                wacom_numbered_button_to_key(features->numbered_buttons),
                                0);
@@ -1563,7 +1739,17 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev,
                features->device_type |= WACOM_DEVICETYPE_PAD;
                break;
        case WACOM_HID_WD_TOUCHONOFF:
-               wacom_map_usage(input, usage, field, EV_SW, SW_MUTE_DEVICE, 0);
+               /*
+                * This usage, which is used to mute touch events, comes
+                * from the pad packet, but is reported on the touch
+                * interface. Because the touch interface may not have
+                * been created yet, we cannot call wacom_map_usage(). In
+                * order to process this usage when we receive it, we set
+                * the usage type and code directly.
+                */
+               wacom_wac->has_mute_touch_switch = true;
+               usage->type = EV_SW;
+               usage->code = SW_MUTE_DEVICE;
                features->device_type |= WACOM_DEVICETYPE_PAD;
                break;
        case WACOM_HID_WD_TOUCHSTRIP:
@@ -1578,6 +1764,10 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev,
                wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0);
                features->device_type |= WACOM_DEVICETYPE_PAD;
                break;
+       case WACOM_HID_WD_TOUCHRINGSTATUS:
+               wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0);
+               features->device_type |= WACOM_DEVICETYPE_PAD;
+               break;
        }
 
        switch (equivalent_usage & 0xfffffff0) {
@@ -1620,17 +1810,40 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field
        struct input_dev *input = wacom_wac->pad_input;
        struct wacom_features *features = &wacom_wac->features;
        unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
+       int i;
+
+       /*
+        * Avoid reporting this event and setting inrange_state if this usage
+        * hasn't been mapped.
+        */
+       if (!usage->type)
+               return;
 
        if (wacom_equivalent_usage(field->physical) == HID_DG_TABLETFUNCTIONKEY) {
-               wacom_wac->hid_data.inrange_state |= value;
+               if (usage->hid != WACOM_HID_WD_TOUCHRING)
+                       wacom_wac->hid_data.inrange_state |= value;
        }
 
        switch (equivalent_usage) {
        case WACOM_HID_WD_TOUCHRINGSTATUS:
+               if (!value)
+                       input_event(input, usage->type, usage->code, 0);
+               break;
+
+       case WACOM_HID_WD_TOUCHONOFF:
+               if (wacom_wac->shared->touch_input) {
+                       input_report_switch(wacom_wac->shared->touch_input,
+                                           SW_MUTE_DEVICE, !value);
+                       input_sync(wacom_wac->shared->touch_input);
+               }
                break;
 
+       case WACOM_HID_WD_BUTTONCENTER:
+               for (i = 0; i < wacom->led.count; i++)
+                       wacom_update_led(wacom, features->numbered_buttons,
+                                        value, i);
+                /* fall through*/
        default:
-               features->input_event_flag = true;
                input_event(input, usage->type, usage->code, value);
                break;
        }
@@ -1668,20 +1881,15 @@ static void wacom_wac_pad_report(struct hid_device *hdev,
 {
        struct wacom *wacom = hid_get_drvdata(hdev);
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
-       struct wacom_features *features = &wacom_wac->features;
        struct input_dev *input = wacom_wac->pad_input;
        bool active = wacom_wac->hid_data.inrange_state != 0;
 
        /* report prox for expresskey events */
        if (wacom_equivalent_usage(report->field[0]->physical) == HID_DG_TABLETFUNCTIONKEY) {
-               features->input_event_flag = true;
                input_event(input, EV_ABS, ABS_MISC, active ? PAD_DEVICE_ID : 0);
-       }
-
-       if (features->input_event_flag) {
-               features->input_event_flag = false;
                input_sync(input);
        }
+
 }
 
 static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
@@ -2056,8 +2264,10 @@ static void wacom_wac_finger_pre_report(struct hid_device *hdev,
 
                for (j = 0; j < field->maxusage; j++) {
                        struct hid_usage *usage = &field->usage[j];
+                       unsigned int equivalent_usage =
+                               wacom_equivalent_usage(usage->hid);
 
-                       switch (usage->hid) {
+                       switch (equivalent_usage) {
                        case HID_GD_X:
                        case HID_GD_Y:
                        case HID_DG_WIDTH:
@@ -2066,7 +2276,7 @@ static void wacom_wac_finger_pre_report(struct hid_device *hdev,
                        case HID_DG_INRANGE:
                        case HID_DG_INVERT:
                        case HID_DG_TIPSWITCH:
-                               hid_data->last_slot_field = usage->hid;
+                               hid_data->last_slot_field = equivalent_usage;
                                break;
                        case HID_DG_CONTACTCOUNT:
                                hid_data->cc_report = report->id;
@@ -2121,8 +2331,8 @@ void wacom_wac_usage_mapping(struct hid_device *hdev,
        struct wacom_wac *wacom_wac = &wacom->wacom_wac;
        struct wacom_features *features = &wacom_wac->features;
 
-       /* currently, only direct devices have proper hid report descriptors */
-       features->device_type |= WACOM_DEVICETYPE_DIRECT;
+       if (WACOM_DIRECT_DEVICE(field))
+               features->device_type |= WACOM_DEVICETYPE_DIRECT;
 
        if (WACOM_PAD_FIELD(field))
                wacom_wac_pad_usage_mapping(hdev, field, usage);
@@ -2140,6 +2350,9 @@ void wacom_wac_event(struct hid_device *hdev, struct hid_field *field,
        if (wacom->wacom_wac.features.type != HID_GENERIC)
                return;
 
+       if (value > field->logical_maximum || value < field->logical_minimum)
+               return;
+
        if (WACOM_PAD_FIELD(field)) {
                wacom_wac_pad_battery_event(hdev, field, usage, value);
                if (wacom->wacom_wac.pad_input)
@@ -2187,6 +2400,16 @@ void wacom_wac_report(struct hid_device *hdev, struct hid_report *report)
 
        wacom_report_events(hdev, report);
 
+       /*
+        * Non-input reports may be sent prior to the device being
+        * completely initialized. Since only their events need
+        * to be processed, exit after 'wacom_report_events' has
+        * been called to prevent potential crashes in the report-
+        * processing functions.
+        */
+       if (report->type != HID_INPUT_REPORT)
+               return;
+
        if (WACOM_PAD_FIELD(field)) {
                wacom_wac_pad_battery_report(hdev, report);
                if (wacom->wacom_wac.pad_input)
@@ -2657,6 +2880,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
                        sync = wacom_intuos_irq(wacom_wac);
                break;
 
+       case INTUOSP2_BT:
+               sync = wacom_intuos_pro2_bt_irq(wacom_wac, len);
+               break;
+
        case TABLETPC:
        case TABLETPCE:
        case TABLETPC2FG:
@@ -2767,8 +2994,6 @@ void wacom_setup_device_quirks(struct wacom *wacom)
        struct wacom_features *features = &wacom->wacom_wac.features;
 
        /* The pen and pad share the same interface on most devices */
-       if (features->numbered_buttons > 0)
-               features->device_type |= WACOM_DEVICETYPE_PAD;
        if (features->type == GRAPHIRE_BT || features->type == WACOM_G4 ||
            features->type == DTUS ||
            (features->type >= INTUOS3S && features->type <= WACOM_MO)) {
@@ -2828,6 +3053,13 @@ void wacom_setup_device_quirks(struct wacom *wacom)
        if (features->type == REMOTE)
                features->device_type = WACOM_DEVICETYPE_PAD;
 
+       if (features->type == INTUOSP2_BT) {
+               features->device_type |= WACOM_DEVICETYPE_PEN |
+                                        WACOM_DEVICETYPE_PAD |
+                                        WACOM_DEVICETYPE_TOUCH;
+               features->quirks |= WACOM_QUIRK_BATTERY;
+       }
+
        switch (features->type) {
        case PL:
        case DTU:
@@ -2974,6 +3206,7 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
        case INTUOSPL:
        case INTUOS5S:
        case INTUOSPS:
+       case INTUOSP2_BT:
                input_set_abs_params(input_dev, ABS_DISTANCE, 0,
                                      features->distance_max,
                                      features->distance_fuzz, 0);
@@ -3082,6 +3315,27 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
        }
 
        switch (features->type) {
+       case INTUOSP2_BT:
+               input_dev->evbit[0] |= BIT_MASK(EV_SW);
+               __set_bit(SW_MUTE_DEVICE, input_dev->swbit);
+
+               if (wacom_wac->shared->touch->product == 0x361) {
+                       input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+                                            0, 12440, 4, 0);
+                       input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+                                            0, 8640, 4, 0);
+               }
+               else if (wacom_wac->shared->touch->product == 0x360) {
+                       input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+                                            0, 8960, 4, 0);
+                       input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+                                            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);
+
+               /* fall through */
+
        case INTUOS5:
        case INTUOS5L:
        case INTUOSPM:
@@ -3278,6 +3532,9 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
 {
        struct wacom_features *features = &wacom_wac->features;
 
+       if ((features->type == HID_GENERIC) && features->numbered_buttons > 0)
+               features->device_type |= WACOM_DEVICETYPE_PAD;
+
        if (!(features->device_type & WACOM_DEVICETYPE_PAD))
                return -ENODEV;
 
@@ -3379,6 +3636,7 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
        case INTUOSPL:
        case INTUOS5S:
        case INTUOSPS:
+       case INTUOSP2_BT:
                input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0);
                break;
 
@@ -3937,6 +4195,12 @@ static const struct wacom_features wacom_features_0x343 =
          DTUS, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 4,
          WACOM_DTU_OFFSET, WACOM_DTU_OFFSET,
          WACOM_DTU_OFFSET, WACOM_DTU_OFFSET };
+static const struct wacom_features wacom_features_0x360 =
+       { "Wacom Intuos Pro M", 44800, 29600, 8191, 63,
+         INTUOSP2_BT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 9, .touch_max = 10 };
+static const struct wacom_features wacom_features_0x361 =
+       { "Wacom Intuos Pro L", 62200, 43200, 8191, 63,
+         INTUOSP2_BT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, 9, .touch_max = 10 };
 
 static const struct wacom_features wacom_features_HID_ANY_ID =
        { "Wacom HID", .type = HID_GENERIC, .oVid = HID_ANY_ID, .oPid = HID_ANY_ID };
@@ -4103,6 +4367,8 @@ const struct hid_device_id wacom_ids[] = {
        { USB_DEVICE_WACOM(0x33D) },
        { USB_DEVICE_WACOM(0x33E) },
        { USB_DEVICE_WACOM(0x343) },
+       { BT_DEVICE_WACOM(0x360) },
+       { BT_DEVICE_WACOM(0x361) },
        { USB_DEVICE_WACOM(0x4001) },
        { USB_DEVICE_WACOM(0x4004) },
        { USB_DEVICE_WACOM(0x5000) },
@@ -4111,6 +4377,7 @@ const struct hid_device_id wacom_ids[] = {
 
        { USB_DEVICE_WACOM(HID_ANY_ID) },
        { I2C_DEVICE_WACOM(HID_ANY_ID) },
+       { BT_DEVICE_WACOM(HID_ANY_ID) },
        { }
 };
 MODULE_DEVICE_TABLE(hid, wacom_ids);