Input: wacom - add 0xE5 (MT device) support
authorPing Cheng <pinglinux@gmail.com>
Mon, 30 Apr 2012 04:09:18 +0000 (21:09 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Mon, 30 Apr 2012 04:13:53 +0000 (21:13 -0700)
Main part of patch is adding support for a new Wacom MT touch
packet and labels these devices using MTSCREEN type.

Other items of interest:

Delete some duplicate code in HID parsing for Y info since
its already done in X path.

In wacom_query_tablet_data(), only invoke the set report
that requests tablets to send Wacom Touch packets for
Finger interfaces.  Mostly, this is to make code intent clear.

Tested-by: Jason Gerecke <killertofu@gmail.com>
Signed-off-by: Chris Bagwell <chris@cnpbagwell.com>
Signed-off-by: Ping Cheng <pingc@wacom.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
drivers/input/tablet/wacom.h
drivers/input/tablet/wacom_sys.c
drivers/input/tablet/wacom_wac.c
drivers/input/tablet/wacom_wac.h

index b4842d0e61dd4bdd0e0c4aaa5b4305c61e82e63d..b79d45198d82801b89b58af39b14896f63b0d4c9 100644 (file)
@@ -135,6 +135,6 @@ extern const struct usb_device_id wacom_ids[];
 
 void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
 void wacom_setup_device_quirks(struct wacom_features *features);
-void wacom_setup_input_capabilities(struct input_dev *input_dev,
-                                   struct wacom_wac *wacom_wac);
+int wacom_setup_input_capabilities(struct input_dev *input_dev,
+                                  struct wacom_wac *wacom_wac);
 #endif
index be13223793fb6187b222728b5e9af4cdc2615fb0..86b96f94cd84f339abb73fe52dd6f6234d3c9448 100644 (file)
@@ -317,6 +317,10 @@ static int wacom_parse_hid(struct usb_interface *intf,
                                                        /* need to reset back */
                                                        features->pktlen = WACOM_PKGLEN_TPC2FG;
                                                }
+
+                                               if (features->type == MTSCREEN)
+                                                       features->pktlen = WACOM_PKGLEN_MTOUCH;
+
                                                if (features->type == BAMBOO_PT) {
                                                        /* need to reset back */
                                                        features->pktlen = WACOM_PKGLEN_BBTOUCH;
@@ -349,18 +353,15 @@ static int wacom_parse_hid(struct usb_interface *intf,
                        case HID_USAGE_Y:
                                if (usage == WCM_DESKTOP) {
                                        if (finger) {
-                                               features->device_type = BTN_TOOL_FINGER;
-                                               if (features->type == TABLETPC2FG) {
-                                                       /* need to reset back */
-                                                       features->pktlen = WACOM_PKGLEN_TPC2FG;
+                                               int type = features->type;
+
+                                               if (type == TABLETPC2FG || type == MTSCREEN) {
                                                        features->y_max =
                                                                get_unaligned_le16(&report[i + 3]);
                                                        features->y_phy =
                                                                get_unaligned_le16(&report[i + 6]);
                                                        i += 7;
-                                               } else if (features->type == BAMBOO_PT) {
-                                                       /* need to reset back */
-                                                       features->pktlen = WACOM_PKGLEN_BBTOUCH;
+                                               } else if (type == BAMBOO_PT) {
                                                        features->y_phy =
                                                                get_unaligned_le16(&report[i + 3]);
                                                        features->y_max =
@@ -374,10 +375,6 @@ static int wacom_parse_hid(struct usb_interface *intf,
                                                        i += 4;
                                                }
                                        } else if (pen) {
-                                               /* penabled only accepts exact bytes of data */
-                                               if (features->type == TABLETPC2FG)
-                                                       features->pktlen = WACOM_PKGLEN_GRAPHIRE;
-                                               features->device_type = BTN_TOOL_PEN;
                                                features->y_max =
                                                        get_unaligned_le16(&report[i + 3]);
                                                i += 4;
@@ -440,22 +437,29 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
        if (!rep_data)
                return error;
 
-       /* ask to report tablet data if it is MT Tablet PC or
-        * not a Tablet PC */
-       if (features->type == TABLETPC2FG) {
-               do {
-                       rep_data[0] = 3;
-                       rep_data[1] = 4;
-                       rep_data[2] = 0;
-                       rep_data[3] = 0;
-                       report_id = 3;
-                       error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT,
-                                                report_id, rep_data, 4, 1);
-                       if (error >= 0)
-                               error = wacom_get_report(intf,
-                                               WAC_HID_FEATURE_REPORT,
-                                               report_id, rep_data, 4, 1);
-               } while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES);
+       /* ask to report Wacom data */
+       if (features->device_type == BTN_TOOL_FINGER) {
+               /* if it is an MT Tablet PC touch */
+               if (features->type == TABLETPC2FG ||
+                   features->type == MTSCREEN) {
+                       do {
+                               rep_data[0] = 3;
+                               rep_data[1] = 4;
+                               rep_data[2] = 0;
+                               rep_data[3] = 0;
+                               report_id = 3;
+                               error = wacom_set_report(intf,
+                                                        WAC_HID_FEATURE_REPORT,
+                                                        report_id,
+                                                        rep_data, 4, 1);
+                               if (error >= 0)
+                                       error = wacom_get_report(intf,
+                                                       WAC_HID_FEATURE_REPORT,
+                                                       report_id,
+                                                       rep_data, 4, 1);
+                       } while ((error < 0 || rep_data[1] != 4) &&
+                                limit++ < WAC_MSG_RETRIES);
+               }
        } else if (features->type != TABLETPC &&
                   features->type != WIRELESS &&
                   features->device_type == BTN_TOOL_PEN) {
@@ -477,7 +481,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
 }
 
 static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
-               struct wacom_features *features)
+                                        struct wacom_features *features)
 {
        int error = 0;
        struct usb_host_interface *interface = intf->cur_altsetting;
@@ -505,10 +509,13 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
                }
        }
 
-       /* only Tablet PCs and Bamboo P&T need to retrieve the info */
-       if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) &&
-           (features->type != BAMBOO_PT))
+       /* only devices that support touch need to retrieve the info */
+       if (features->type != TABLETPC &&
+           features->type != TABLETPC2FG &&
+           features->type != BAMBOO_PT &&
+           features->type != MTSCREEN) {
                goto out;
+       }
 
        if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) {
                if (usb_get_extra_descriptor(&interface->endpoint[0],
@@ -978,8 +985,10 @@ static int wacom_register_input(struct wacom *wacom)
        int error;
 
        input_dev = input_allocate_device();
-       if (!input_dev)
-               return -ENOMEM;
+       if (!input_dev) {
+               error = -ENOMEM;
+               goto fail1;
+       }
 
        input_dev->name = wacom_wac->name;
        input_dev->dev.parent = &intf->dev;
@@ -989,14 +998,20 @@ static int wacom_register_input(struct wacom *wacom)
        input_set_drvdata(input_dev, wacom);
 
        wacom_wac->input = input_dev;
-       wacom_setup_input_capabilities(input_dev, wacom_wac);
+       error = wacom_setup_input_capabilities(input_dev, wacom_wac);
+       if (error)
+               goto fail1;
 
        error = input_register_device(input_dev);
-       if (error) {
-               input_free_device(input_dev);
-               wacom_wac->input = NULL;
-       }
+       if (error)
+               goto fail2;
+
+       return 0;
 
+fail2:
+       input_free_device(input_dev);
+       wacom_wac->input = NULL;
+fail1:
        return error;
 }
 
index e5cd0e57d1783f3287df2199ba9aef3d94c3106a..10e5cf8703593cf519c4d077b058749a00f5d50d 100644 (file)
@@ -768,6 +768,72 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
        return 1;
 }
 
+static int find_slot_from_contactid(struct wacom_wac *wacom, int contactid)
+{
+       int touch_max = wacom->features.touch_max;
+       int i;
+
+       if (!wacom->slots)
+               return -1;
+
+       for (i = 0; i < touch_max; ++i) {
+               if (wacom->slots[i] == contactid)
+                       return i;
+       }
+       for (i = 0; i < touch_max; ++i) {
+               if (wacom->slots[i] == -1)
+                       return i;
+       }
+       return -1;
+}
+
+static int wacom_mt_touch(struct wacom_wac *wacom)
+{
+       struct input_dev *input = wacom->input;
+       char *data = wacom->data;
+       int i;
+       int current_num_contacts = data[2];
+       int contacts_to_send = 0;
+
+       /*
+        * 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;
+
+       /* There are at most 5 contacts per packet */
+       contacts_to_send = min(5, wacom->num_contacts_left);
+
+       for (i = 0; i < contacts_to_send; i++) {
+               int offset = (WACOM_BYTES_PER_MT_PACKET * i) + 3;
+               bool touch = data[offset] & 0x1;
+               int id = le16_to_cpup((__le16 *)&data[offset + 1]);
+               int slot = find_slot_from_contactid(wacom, id);
+
+               if (slot < 0)
+                       continue;
+
+               input_mt_slot(input, slot);
+               input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+               if (touch) {
+                       int x = le16_to_cpup((__le16 *)&data[offset + 7]);
+                       int y = le16_to_cpup((__le16 *)&data[offset + 9]);
+                       input_report_abs(input, ABS_MT_POSITION_X, x);
+                       input_report_abs(input, ABS_MT_POSITION_Y, y);
+               }
+               wacom->slots[slot] = touch ? id : -1;
+       }
+
+       input_mt_report_pointer_emulation(input, true);
+
+       wacom->num_contacts_left -= contacts_to_send;
+       if (wacom->num_contacts_left < 0)
+               wacom->num_contacts_left = 0;
+
+       return 1;
+}
+
 static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
 {
        struct input_dev *input = wacom->input;
@@ -806,6 +872,9 @@ static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
        bool prox;
        int x = 0, y = 0;
 
+       if (wacom->features.touch_max > 1 || len > WACOM_PKGLEN_TPC2FG)
+               return 0;
+
        if (!wacom->shared->stylus_in_proximity) {
                if (len == WACOM_PKGLEN_TPC1FG) {
                        prox = data[0] & 0x01;
@@ -873,10 +942,10 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
 
        switch (len) {
        case WACOM_PKGLEN_TPC1FG:
-                return wacom_tpc_single_touch(wacom, len);
+               return wacom_tpc_single_touch(wacom, len);
 
        case WACOM_PKGLEN_TPC2FG:
-               return wacom_tpc_mt_touch(wacom);
+               return wacom_tpc_mt_touch(wacom);
 
        default:
                switch (data[0]) {
@@ -885,6 +954,9 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
                case WACOM_REPORT_TPCST:
                        return wacom_tpc_single_touch(wacom, len);
 
+               case WACOM_REPORT_TPCMT:
+                       return wacom_mt_touch(wacom);
+
                case WACOM_REPORT_PENABLED:
                        return wacom_tpc_pen(wacom);
                }
@@ -1164,6 +1236,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
 
        case TABLETPC:
        case TABLETPC2FG:
+       case MTSCREEN:
                sync = wacom_tpc_irq(wacom_wac, len);
                break;
 
@@ -1237,7 +1310,8 @@ void wacom_setup_device_quirks(struct wacom_features *features)
        /* these device have multiple inputs */
        if (features->type == TABLETPC || features->type == TABLETPC2FG ||
            features->type == BAMBOO_PT || features->type == WIRELESS ||
-           (features->type >= INTUOS5S && features->type <= INTUOS5L))
+           (features->type >= INTUOS5S && features->type <= INTUOS5L) ||
+           features->type == MTSCREEN)
                features->quirks |= WACOM_QUIRK_MULTI_INPUT;
 
        /* quirk for bamboo touch with 2 low res touches */
@@ -1268,8 +1342,8 @@ static unsigned int wacom_calculate_touch_res(unsigned int logical_max,
        return (logical_max * 100) / physical_max;
 }
 
-void wacom_setup_input_capabilities(struct input_dev *input_dev,
-                                   struct wacom_wac *wacom_wac)
+int wacom_setup_input_capabilities(struct input_dev *input_dev,
+                                  struct wacom_wac *wacom_wac)
 {
        struct wacom_features *features = &wacom_wac->features;
        int i;
@@ -1465,8 +1539,18 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
                break;
 
        case TABLETPC2FG:
+       case MTSCREEN:
                if (features->device_type == BTN_TOOL_FINGER) {
 
+                       wacom_wac->slots = kmalloc(features->touch_max *
+                                                       sizeof(int),
+                                                  GFP_KERNEL);
+                       if (!wacom_wac->slots)
+                               return -ENOMEM;
+
+                       for (i = 0; i < features->touch_max; i++)
+                               wacom_wac->slots[i] = -1;
+
                        input_mt_init_slots(input_dev, features->touch_max);
                        input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
                                        0, MT_TOOL_MAX, 0, 0);
@@ -1552,6 +1636,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
                }
                break;
        }
+       return 0;
 }
 
 static const struct wacom_features wacom_features_0x00 =
@@ -1784,6 +1869,9 @@ static const struct wacom_features wacom_features_0xE3 =
        { "Wacom ISDv4 E3",       WACOM_PKGLEN_TPC2FG,    26202, 16325,  255,
          0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
          .touch_max = 2 };
+static const struct wacom_features wacom_features_0xE5 =
+       { "Wacom ISDv4 E5",       WACOM_PKGLEN_MTOUCH,    26202, 16325,  255,
+         0, MTSCREEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
 static const struct wacom_features wacom_features_0xE6 =
        { "Wacom ISDv4 E6",       WACOM_PKGLEN_TPC2FG,    27760, 15694,  255,
          0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
@@ -1962,6 +2050,7 @@ const struct usb_device_id wacom_ids[] = {
        { USB_DEVICE_WACOM(0x9F) },
        { USB_DEVICE_WACOM(0xE2) },
        { USB_DEVICE_WACOM(0xE3) },
+       { USB_DEVICE_WACOM(0xE5) },
        { USB_DEVICE_WACOM(0xE6) },
        { USB_DEVICE_WACOM(0xEC) },
        { USB_DEVICE_WACOM(0x47) },
index 321269c1ac4ca37b3ec7adc9a6a627d0f7a35789..78fbd3f420095c6279644f3a11d789c87bb960cc 100644 (file)
 #define WACOM_PKGLEN_BBTOUCH3  64
 #define WACOM_PKGLEN_BBPEN     10
 #define WACOM_PKGLEN_WIRELESS  32
+#define WACOM_PKGLEN_MTOUCH    62
+
+/* wacom data size per MT contact */
+#define WACOM_BYTES_PER_MT_PACKET      11
 
 /* device IDs */
 #define STYLUS_DEVICE_ID       0x02
@@ -41,6 +45,7 @@
 #define WACOM_REPORT_INTUOS5PAD                3
 #define WACOM_REPORT_TPC1FG            6
 #define WACOM_REPORT_TPC2FG            13
+#define WACOM_REPORT_TPCMT             13
 #define WACOM_REPORT_TPCHID            15
 #define WACOM_REPORT_TPCST             16
 
@@ -76,6 +81,7 @@ enum {
        WACOM_MO,
        TABLETPC,
        TABLETPC2FG,
+       MTSCREEN,
        MAX_TYPE
 };
 
@@ -118,6 +124,8 @@ struct wacom_wac {
        struct input_dev *input;
        int pid;
        int battery_capacity;
+       int num_contacts_left;
+       int *slots;
 };
 
 #endif