Input: wacom - add support for new LCD tablets
authorPing Cheng <pingc@wacom.com>
Tue, 15 Dec 2009 08:35:24 +0000 (00:35 -0800)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Tue, 15 Dec 2009 08:36:08 +0000 (00:36 -0800)
This adds support for the foolowing Wacom devices:

 - 0x9F - a single touch only LCD tablet;
 - 0xE2 - a two finger touch only LCD tablet;
 - 0xE3 -  a two finger touch, penabled LCD tablet.

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 d71da97..16310f3 100644 (file)
@@ -71,6 +71,7 @@
  *      v1.51 (pc) - Added support for Intuos4
  *      v1.52 (pc) - Query Wacom data upon system resume
  *                 - add defines for features->type
+ *                 - add new devices (0x9F, 0xE2, and 0XE3)
  */
 
 /*
@@ -135,6 +136,8 @@ extern void input_dev_i4s(struct input_dev *input_dev, struct wacom_wac *wacom_w
 extern void input_dev_i4(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
 extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
 extern void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_tpc(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_tpc2fg(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
 extern void input_dev_mo(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
 extern void input_dev_bee(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
 extern __u16 wacom_le16_to_cpu(unsigned char *data);
index b5b69cc..072f33b 100644 (file)
@@ -209,6 +209,7 @@ void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
        input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_LEFT) |
                BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
        input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_RUBBER) |
+               BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_STYLUS) |
                BIT_MASK(BTN_TOOL_MOUSE) | BIT_MASK(BTN_STYLUS2);
        input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0);
 }
@@ -256,6 +257,7 @@ void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
                BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE) |
                BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
        input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_RUBBER) |
+               BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_STYLUS) |
                BIT_MASK(BTN_TOOL_MOUSE) | BIT_MASK(BTN_TOOL_BRUSH) |
                BIT_MASK(BTN_TOOL_PENCIL) | BIT_MASK(BTN_TOOL_AIRBRUSH) |
                BIT_MASK(BTN_TOOL_LENS) | BIT_MASK(BTN_STYLUS2);
@@ -269,7 +271,8 @@ void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
 
 void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
 {
-       input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_STYLUS2);
+       input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_PEN) |
+               BIT_MASK(BTN_STYLUS) | BIT_MASK(BTN_STYLUS2);
 }
 
 void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
@@ -277,12 +280,32 @@ void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
        input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_RUBBER);
 }
 
+void input_dev_tpc(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+       if (wacom_wac->features->device_type == BTN_TOOL_DOUBLETAP ||
+           wacom_wac->features->device_type == BTN_TOOL_TRIPLETAP) {
+               input_set_abs_params(input_dev, ABS_RX, 0, wacom_wac->features->x_phy, 0, 0);
+               input_set_abs_params(input_dev, ABS_RY, 0, wacom_wac->features->y_phy, 0, 0);
+               input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_DOUBLETAP);
+       }
+}
+
+void input_dev_tpc2fg(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+       if (wacom_wac->features->device_type == BTN_TOOL_TRIPLETAP) {
+               input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_TRIPLETAP);
+               input_dev->evbit[0] |= BIT_MASK(EV_MSC);
+               input_dev->mscbit[0] |= BIT_MASK(MSC_SERIAL);
+       }
+}
+
 static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc,
-                          struct wacom_wac *wacom_wac)
+                          struct wacom_features *features)
 {
        struct usb_device *dev = interface_to_usbdev(intf);
-       struct wacom_features *features = wacom_wac->features;
-       char limit = 0, result = 0;
+       char limit = 0;
+       /* result has to be defined as int for some devices */
+       int result = 0;
        int i = 0, usage = WCM_UNDEFINED, finger = 0, pen = 0;
        unsigned char *report;
 
@@ -328,13 +351,24 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
                        case HID_USAGE_X:
                                if (usage == WCM_DESKTOP) {
                                        if (finger) {
-                                               features->touch_x_max =
-                                                       features->touch_y_max =
-                                                       wacom_le16_to_cpu(&report[i + 3]);
+                                               features->device_type = BTN_TOOL_DOUBLETAP;
+                                               if (features->type == TABLETPC2FG) {
+                                                       /* need to reset back */
+                                                       features->pktlen = WACOM_PKGLEN_TPC2FG;
+                                                       features->device_type = BTN_TOOL_TRIPLETAP;
+                                               }
                                                features->x_max =
+                                                       wacom_le16_to_cpu(&report[i + 3]);
+                                               features->x_phy =
                                                        wacom_le16_to_cpu(&report[i + 6]);
-                                               i += 7;
+                                               features->unit = report[i + 9];
+                                               features->unitExpo = report[i + 11];
+                                               i += 12;
                                        } else if (pen) {
+                                               /* penabled only accepts exact bytes of data */
+                                               if (features->type == TABLETPC2FG)
+                                                       features->pktlen = WACOM_PKGLEN_PENABLED;
+                                               features->device_type = BTN_TOOL_PEN;
                                                features->x_max =
                                                        wacom_le16_to_cpu(&report[i + 3]);
                                                i += 4;
@@ -350,10 +384,35 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
                                break;
 
                        case HID_USAGE_Y:
-                               if (usage == WCM_DESKTOP)
-                                       features->y_max =
-                                               wacom_le16_to_cpu(&report[i + 3]);
-                               i += 4;
+                               if (usage == WCM_DESKTOP) {
+                                       if (finger) {
+                                               features->device_type = BTN_TOOL_DOUBLETAP;
+                                               if (features->type == TABLETPC2FG) {
+                                                       /* need to reset back */
+                                                       features->pktlen = WACOM_PKGLEN_TPC2FG;
+                                                       features->device_type = BTN_TOOL_TRIPLETAP;
+                                                       features->y_max =
+                                                               wacom_le16_to_cpu(&report[i + 3]);
+                                                       features->y_phy =
+                                                               wacom_le16_to_cpu(&report[i + 6]);
+                                                       i += 7;
+                                               } else {
+                                                       features->y_max =
+                                                               features->x_max;
+                                                       features->y_phy =
+                                                               wacom_le16_to_cpu(&report[i + 3]);
+                                                       i += 4;
+                                               }
+                                       } else if (pen) {
+                                               /* penabled only accepts exact bytes of data */
+                                               if (features->type == TABLETPC2FG)
+                                                       features->pktlen = WACOM_PKGLEN_PENABLED;
+                                               features->device_type = BTN_TOOL_PEN;
+                                               features->y_max =
+                                                       wacom_le16_to_cpu(&report[i + 3]);
+                                               i += 4;
+                                       }
+                               }
                                break;
 
                        case HID_USAGE_FINGER:
@@ -376,7 +435,7 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
                        break;
 
                case HID_COLLECTION:
-                       /* reset UsagePage ans Finger */
+                       /* reset UsagePage and Finger */
                        finger = usage = 0;
                        break;
                }
@@ -388,43 +447,92 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
        return result;
 }
 
-static int wacom_query_tablet_data(struct usb_interface *intf)
+static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_features *features)
 {
        unsigned char *rep_data;
-       int limit = 0;
-       int error;
+       int limit = 0, report_id = 2;
+       int error = -ENOMEM;
 
        rep_data = kmalloc(2, GFP_KERNEL);
        if (!rep_data)
-               return -ENOMEM;
-
-       do {
-               rep_data[0] = 2;
-               rep_data[1] = 2;
-               error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,
-                                       2, rep_data, 2);
-               if (error >= 0)
-                       error = usb_get_report(intf,
-                                               WAC_HID_FEATURE_REPORT, 2,
-                                               rep_data, 2);
-       } while ((error < 0 || rep_data[1] != 2) && limit++ < 5);
+               return error;
+
+       /* ask to report tablet data if it is 2FGT or not a Tablet PC */
+       if (features->device_type == BTN_TOOL_TRIPLETAP) {
+               do {
+                       rep_data[0] = 3;
+                       rep_data[1] = 4;
+                       report_id = 3;
+                       error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,
+                               report_id, rep_data, 2);
+                       if (error >= 0)
+                               error = usb_get_report(intf,
+                                       WAC_HID_FEATURE_REPORT, report_id,
+                                       rep_data, 3);
+               } while ((error < 0 || rep_data[1] != 4) && limit++ < 5);
+       } else if (features->type != TABLETPC && features->type != TABLETPC2FG) {
+               do {
+                       rep_data[0] = 2;
+                       rep_data[1] = 2;
+                       error = usb_set_report(intf, WAC_HID_FEATURE_REPORT,
+                               report_id, rep_data, 2);
+                       if (error >= 0)
+                               error = usb_get_report(intf,
+                                       WAC_HID_FEATURE_REPORT, report_id,
+                                       rep_data, 2);
+               } while ((error < 0 || rep_data[1] != 2) && limit++ < 5);
+       }
 
        kfree(rep_data);
 
        return error < 0 ? error : 0;
 }
 
+static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
+               struct wacom_features *features)
+{
+       int error = 0;
+       struct usb_host_interface *interface = intf->cur_altsetting;
+       struct hid_descriptor *hid_desc;
+
+       /* default device to penabled */
+       features->device_type = BTN_TOOL_PEN;
+
+       /* only Tablet PCs need to retrieve the info */
+       if ((features->type != TABLETPC) && (features->type != TABLETPC2FG))
+               goto out;
+
+       if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) {
+               if (usb_get_extra_descriptor(&interface->endpoint[0],
+                               HID_DEVICET_REPORT, &hid_desc)) {
+                       printk("wacom: can not retrieve extra class descriptor\n");
+                       error = 1;
+                       goto out;
+               }
+       }
+       error = wacom_parse_hid(intf, hid_desc, features);
+       if (error)
+               goto out;
+
+       /* touch device found but size is not defined. use default */
+       if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) {
+               features->x_max = 1023;
+               features->y_max = 1023;
+       }
+
+ out:
+       return error;
+}
+
 static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
        struct usb_device *dev = interface_to_usbdev(intf);
-       struct usb_host_interface *interface = intf->cur_altsetting;
        struct usb_endpoint_descriptor *endpoint;
        struct wacom *wacom;
        struct wacom_wac *wacom_wac;
        struct wacom_features *features;
        struct input_dev *input_dev;
        int error = -ENOMEM;
-       struct hid_descriptor *hid_desc;
 
        wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
        wacom_wac = kzalloc(sizeof(struct wacom_wac), GFP_KERNEL);
@@ -432,7 +540,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
        if (!wacom || !input_dev || !wacom_wac)
                goto fail1;
 
-       wacom_wac->data = usb_buffer_alloc(dev, 10, GFP_KERNEL, &wacom->data_dma);
+       wacom_wac->data = usb_buffer_alloc(dev, WACOM_PKGLEN_MAX, GFP_KERNEL, &wacom->data_dma);
        if (!wacom_wac->data)
                goto fail1;
 
@@ -448,7 +556,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
        strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
 
        wacom_wac->features = features = get_wacom_feature(id);
-       BUG_ON(features->pktlen > 10);
+       BUG_ON(features->pktlen > WACOM_PKGLEN_MAX);
 
        input_dev->name = wacom_wac->features->name;
        wacom->wacom_wac = wacom_wac;
@@ -463,47 +571,24 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
 
        endpoint = &intf->cur_altsetting->endpoint[0].desc;
 
-       /* Initialize touch_x_max and touch_y_max in case it is not defined */
-       if (wacom_wac->features->type == TABLETPC) {
-               features->touch_x_max = 1023;
-               features->touch_y_max = 1023;
-       } else {
-               features->touch_x_max = 0;
-               features->touch_y_max = 0;
-       }
-
-       /* TabletPC need to retrieve the physical and logical maximum from report descriptor */
-       if (wacom_wac->features->type == TABLETPC) {
-               if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) {
-                       if (usb_get_extra_descriptor(&interface->endpoint[0],
-                                                    HID_DEVICET_REPORT, &hid_desc)) {
-                               printk("wacom: can not retrive extra class descriptor\n");
-                               goto fail2;
-                       }
-               }
-               error = wacom_parse_hid(intf, hid_desc, wacom_wac);
-               if (error)
-                       goto fail2;
-       }
+       /* Retrieve the physical and logical size for OEM devices */
+       error = wacom_retrieve_hid_descriptor(intf, features);
+       if (error)
+               goto fail2;
 
        input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
-       input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_PEN) |
-               BIT_MASK(BTN_TOUCH) | BIT_MASK(BTN_STYLUS);
+       input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOUCH);
+
        input_set_abs_params(input_dev, ABS_X, 0, features->x_max, 4, 0);
        input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, 4, 0);
        input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, 0, 0);
-       if (features->type == TABLETPC) {
-               input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_DOUBLETAP);
-               input_set_abs_params(input_dev, ABS_RX, 0, features->touch_x_max, 4, 0);
-               input_set_abs_params(input_dev, ABS_RY, 0, features->touch_y_max, 4, 0);
-       }
        input_dev->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC);
 
        wacom_init_input_dev(input_dev, wacom_wac);
 
        usb_fill_int_urb(wacom->irq, dev,
                         usb_rcvintpipe(dev, endpoint->bEndpointAddress),
-                        wacom_wac->data, wacom_wac->features->pktlen,
+                        wacom_wac->data, features->pktlen,
                         wacom_sys_irq, wacom, endpoint->bInterval);
        wacom->irq->transfer_dma = wacom->data_dma;
        wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
@@ -512,18 +597,14 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
        if (error)
                goto fail3;
 
-       /*
-        * Ask the tablet to report tablet data if it is not a Tablet PC.
-        * Note that if query fails it is not a hard failure.
-        */
-       if (wacom_wac->features->type != TABLETPC)
-               wacom_query_tablet_data(intf);
+       /* Note that if query fails it is not a hard failure */
+       wacom_query_tablet_data(intf, features);
 
        usb_set_intfdata(intf, wacom);
        return 0;
 
  fail3:        usb_free_urb(wacom->irq);
- fail2:        usb_buffer_free(dev, 10, wacom_wac->data, wacom->data_dma);
+ fail2:        usb_buffer_free(dev, WACOM_PKGLEN_MAX, wacom_wac->data, wacom->data_dma);
  fail1:        input_free_device(input_dev);
        kfree(wacom);
        kfree(wacom_wac);
@@ -539,7 +620,7 @@ static void wacom_disconnect(struct usb_interface *intf)
        usb_kill_urb(wacom->irq);
        input_unregister_device(wacom->dev);
        usb_free_urb(wacom->irq);
-       usb_buffer_free(interface_to_usbdev(intf), 10,
+       usb_buffer_free(interface_to_usbdev(intf), WACOM_PKGLEN_MAX,
                        wacom->wacom_wac->data, wacom->data_dma);
        kfree(wacom->wacom_wac);
        kfree(wacom);
@@ -559,12 +640,15 @@ static int wacom_suspend(struct usb_interface *intf, pm_message_t message)
 static int wacom_resume(struct usb_interface *intf)
 {
        struct wacom *wacom = usb_get_intfdata(intf);
+       struct wacom_features *features = wacom->wacom_wac->features;
        int rv;
 
        mutex_lock(&wacom->lock);
        if (wacom->open) {
                rv = usb_submit_urb(wacom->irq, GFP_NOIO);
-               wacom_query_tablet_data(intf);
+               /* switch to wacom mode if needed */
+               if (!wacom_retrieve_hid_descriptor(intf, features))
+                       wacom_query_tablet_data(intf, features);
        } else
                rv = 0;
        mutex_unlock(&wacom->lock);
index d5fc97d..4672589 100644 (file)
@@ -65,9 +65,8 @@ static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo)
 
        prox = data[1] & 0x40;
 
-       wacom->id[0] = ERASER_DEVICE_ID;
        if (prox) {
-
+               wacom->id[0] = ERASER_DEVICE_ID;
                pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
                if (wacom->features->pressure_max > 255)
                        pressure = (pressure << 1) | ((data[4] >> 6) & 1);
@@ -608,54 +607,146 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
        return 1;
 }
 
+
+static void wacom_tpc_finger_in(struct wacom_wac *wacom, void *wcombo, char *data, int idx)
+{
+       wacom_report_abs(wcombo, ABS_X,
+               (data[2 + idx * 2] & 0xff) | ((data[3 + idx * 2] & 0x7f) << 8));
+       wacom_report_abs(wcombo, ABS_Y,
+               (data[6 + idx * 2] & 0xff) | ((data[7 + idx * 2] & 0x7f) << 8));
+       wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]);
+       wacom_report_key(wcombo, wacom->tool[idx], 1);
+       if (idx)
+               wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
+       else
+               wacom_report_key(wcombo, BTN_TOUCH, 1);
+}
+
+static void wacom_tpc_touch_out(struct wacom_wac *wacom, void *wcombo, int idx)
+{
+       wacom_report_abs(wcombo, ABS_X, 0);
+       wacom_report_abs(wcombo, ABS_Y, 0);
+       wacom_report_abs(wcombo, ABS_MISC, 0);
+       wacom_report_key(wcombo, wacom->tool[idx], 0);
+       if (idx)
+               wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
+       else
+               wacom_report_key(wcombo, BTN_TOUCH, 0);
+       return;
+}
+
+static void wacom_tpc_touch_in(struct wacom_wac *wacom, void *wcombo)
+{
+       char *data = wacom->data;
+       struct urb *urb = ((struct wacom_combo *)wcombo)->urb;
+       static int firstFinger = 0;
+       static int secondFinger = 0;
+
+       wacom->tool[0] = BTN_TOOL_DOUBLETAP;
+       wacom->id[0] = TOUCH_DEVICE_ID;
+       wacom->tool[1] = BTN_TOOL_TRIPLETAP;
+
+       if (urb->actual_length != WACOM_PKGLEN_TPC1FG) {
+               switch (data[0]) {
+                       case 6:
+                               wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2]));
+                               wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4]));
+                               wacom_report_abs(wcombo, ABS_PRESSURE, wacom_le16_to_cpu(&data[6]));
+                               wacom_report_key(wcombo, BTN_TOUCH, wacom_le16_to_cpu(&data[6]));
+                               wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]);
+                               wacom_report_key(wcombo, wacom->tool[0], 1);
+                               break;
+                       case 13:
+                               /* keep this byte to send proper out-prox event */
+                               wacom->id[1] = data[1] & 0x03;
+
+                               if (data[1] & 0x01) {
+                                       wacom_tpc_finger_in(wacom, wcombo, data, 0);
+                                       firstFinger = 1;
+                               } else if (firstFinger) {
+                                       wacom_tpc_touch_out(wacom, wcombo, 0);
+                               }
+
+                               if (data[1] & 0x02) {
+                                       /* sync first finger data */
+                                       if (firstFinger)
+                                               wacom_input_sync(wcombo);
+
+                                       wacom_tpc_finger_in(wacom, wcombo, data, 1);
+                                       secondFinger = 1;
+                               } else if (secondFinger) {
+                                       /* sync first finger data */
+                                       if (firstFinger)
+                                               wacom_input_sync(wcombo);
+
+                                       wacom_tpc_touch_out(wacom, wcombo, 1);
+                                       secondFinger = 0;
+                               }
+                               if (!(data[1] & 0x01))
+                                       firstFinger = 0;
+                               break;
+               }
+       } else {
+               wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1]));
+               wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3]));
+               wacom_report_key(wcombo, BTN_TOUCH, 1);
+               wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]);
+               wacom_report_key(wcombo, wacom->tool[0], 1);
+       }
+       return;
+}
+
 static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
 {
        char *data = wacom->data;
-       int prox = 0, pressure;
+       int prox = 0, pressure, idx = -1;
        static int stylusInProx, touchInProx = 1, touchOut;
        struct urb *urb = ((struct wacom_combo *)wcombo)->urb;
 
        dbg("wacom_tpc_irq: received report #%d", data[0]);
 
-       if (urb->actual_length == WACOM_PKGLEN_TPC1FG || data[0] == 6) { /* Touch data */
+       if (urb->actual_length == WACOM_PKGLEN_TPC1FG ||
+           data[0] == 6 || /* single touch */
+           data[0] == 13) { /* 2FG touch */
                if (urb->actual_length == WACOM_PKGLEN_TPC1FG) {  /* with touch */
-                       prox = data[0] & 0x03;
+                       prox = data[0] & 0x01;
                } else {  /* with capacity */
-                       prox = data[1] & 0x03;
+                       if (data[0] == 6)
+                               /* single touch */
+                               prox = data[1] & 0x01;
+                       else
+                               /* 2FG touch data */
+                               prox = data[1] & 0x03;
                }
 
                if (!stylusInProx) { /* stylus not in prox */
                        if (prox) {
                                if (touchInProx) {
-                                       wacom->tool[1] = BTN_TOOL_DOUBLETAP;
-                                       wacom->id[0] = TOUCH_DEVICE_ID;
-                                       if (urb->actual_length != WACOM_PKGLEN_TPC1FG) {
-                                               wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2]));
-                                               wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4]));
-                                               wacom_report_abs(wcombo, ABS_PRESSURE, wacom_le16_to_cpu(&data[6]));
-                                               wacom_report_key(wcombo, BTN_TOUCH, wacom_le16_to_cpu(&data[6]));
-                                       } else {
-                                               wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1]));
-                                               wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3]));
-                                               wacom_report_key(wcombo, BTN_TOUCH, 1);
-                                       }
-                                       wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]);
-                                       wacom_report_key(wcombo, wacom->tool[1], prox & 0x01);
+                                       wacom_tpc_touch_in(wacom, wcombo);
                                        touchOut = 1;
                                        return 1;
                                }
                        } else {
-                               wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]);
-                               wacom_report_key(wcombo, wacom->tool[1], prox & 0x01);
-                               wacom_report_key(wcombo, BTN_TOUCH, 0);
+                               /* 2FGT out-prox */
+                               if ((data[0] & 0xff) == 13) {
+                                       idx = (wacom->id[1] & 0x01) - 1;
+                                       if (idx == 0) {
+                                               wacom_tpc_touch_out(wacom, wcombo, idx);
+                                               /* sync first finger event */
+                                               if (wacom->id[1] & 0x02)
+                                                       wacom_input_sync(wcombo);
+                                       }
+                                       idx = (wacom->id[1] & 0x02) - 1;
+                                       if (idx == 1)
+                                               wacom_tpc_touch_out(wacom, wcombo, idx);
+                               } else /* one finger touch */
+                                       wacom_tpc_touch_out(wacom, wcombo, 0);
                                touchOut = 0;
                                touchInProx = 1;
                                return 1;
                        }
                } else if (touchOut || !prox) { /* force touch out-prox */
-                       wacom_report_abs(wcombo, ABS_MISC, TOUCH_DEVICE_ID);
-                       wacom_report_key(wcombo, wacom->tool[1], 0);
-                       wacom_report_key(wcombo, BTN_TOUCH, 0);
+                       wacom_tpc_touch_out(wacom, wcombo, 0);
                        touchOut = 0;
                        touchInProx = 1;
                        return 1;
@@ -665,38 +756,14 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
 
                touchInProx = 0;
 
-               wacom->id[0] = ERASER_DEVICE_ID;
-
-               /*
-                * 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 (prox) { /* in prox */
-                       if (!wacom->tool[0]) {
+                       if (!wacom->id[0]) {
                                /* Going into proximity select tool */
-                               wacom->tool[1] = (data[1] & 0x08) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
-                               if (wacom->tool[1] == BTN_TOOL_PEN)
+                               wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+                               if (wacom->tool[0] == BTN_TOOL_PEN)
                                        wacom->id[0] = STYLUS_DEVICE_ID;
-                       } else if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[1] & 0x08)) {
-                               /*
-                                * was entered with stylus2 pressed
-                                * report out proximity for previous tool
-                               */
-                               wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]);
-                               wacom_report_key(wcombo, wacom->tool[1], 0);
-                               wacom_input_sync(wcombo);
-
-                               /* set new tool */
-                               wacom->tool[1] = BTN_TOOL_PEN;
-                               wacom->id[0] = STYLUS_DEVICE_ID;
-                               return 0;
-                       }
-                       if (wacom->tool[1] != BTN_TOOL_RUBBER) {
-                               /* Unknown tool selected default to pen tool */
-                               wacom->tool[1] = BTN_TOOL_PEN;
-                               wacom->id[0] = STYLUS_DEVICE_ID;
+                               else
+                                       wacom->id[0] = ERASER_DEVICE_ID;
                        }
                        wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
                        wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x10);
@@ -706,17 +773,21 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
                        if (pressure < 0)
                                pressure = wacom->features->pressure_max + pressure + 1;
                        wacom_report_abs(wcombo, ABS_PRESSURE, pressure);
-                       wacom_report_key(wcombo, BTN_TOUCH, pressure);
+                       wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x05);
                } else {
+                       wacom_report_abs(wcombo, ABS_X, 0);
+                       wacom_report_abs(wcombo, ABS_Y, 0);
                        wacom_report_abs(wcombo, ABS_PRESSURE, 0);
                        wacom_report_key(wcombo, BTN_STYLUS, 0);
                        wacom_report_key(wcombo, BTN_STYLUS2, 0);
                        wacom_report_key(wcombo, BTN_TOUCH, 0);
+                       wacom->id[0] = 0;
+                       /* pen is out so touch can be enabled now */
+                       touchInProx = 1;
                }
-               wacom_report_key(wcombo, wacom->tool[1], prox);
+               wacom_report_key(wcombo, wacom->tool[0], prox);
                wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]);
                stylusInProx = prox;
-               wacom->tool[0] = prox;
                return 1;
        }
        return 0;
@@ -751,6 +822,7 @@ int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo)
                        return wacom_intuos_irq(wacom_wac, wcombo);
 
                case TABLETPC:
+               case TABLETPC2FG:
                        return wacom_tpc_irq(wacom_wac, wcombo);
 
                default:
@@ -791,9 +863,17 @@ void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_w
                        input_dev_i4s(input_dev, wacom_wac);
                        input_dev_i(input_dev, wacom_wac);
                        break;
+               case TABLETPC2FG:
+                       input_dev_tpc2fg(input_dev, wacom_wac);
+                       /* fall through */
+               case TABLETPC:
+                       input_dev_tpc(input_dev, wacom_wac);
+                       if (wacom_wac->features->device_type != BTN_TOOL_PEN)
+                               break;  /* no need to process stylus stuff */
+
+                       /* fall through */
                case PL:
                case PTU:
-               case TABLETPC:
                        input_dev_pl(input_dev, wacom_wac);
                        /* fall through */
                case PENPARTNER:
@@ -863,6 +943,9 @@ static struct wacom_features wacom_features[] = {
        { "Wacom ISDv4 90",       WACOM_PKGLEN_GRAPHIRE,  26202, 16325,  255,  0, TABLETPC },
        { "Wacom ISDv4 93",       WACOM_PKGLEN_GRAPHIRE,  26202, 16325,  255,  0, TABLETPC },
        { "Wacom ISDv4 9A",       WACOM_PKGLEN_GRAPHIRE,  26202, 16325,  255,  0, TABLETPC },
+       { "Wacom ISDv4 9F",       WACOM_PKGLEN_PENABLED,  26202, 16325,  255,  0, TABLETPC },
+       { "Wacom ISDv4 E2",       WACOM_PKGLEN_TPC2FG,    26202, 16325,  255,  0, TABLETPC2FG },
+       { "Wacom ISDv4 E3",       WACOM_PKGLEN_TPC2FG,    26202, 16325,  255,  0, TABLETPC2FG },
        { "Wacom Intuos2 6x8",    WACOM_PKGLEN_INTUOS,    20320, 16240, 1023, 31, INTUOS },
        { }
 };
@@ -927,6 +1010,9 @@ static struct usb_device_id wacom_ids[] = {
        { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x90) },
        { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x93) },
        { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x9A) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x9F) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xE2) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xE3) },
        { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
        { }
 };
index 1bfe9a3..39c2516 100644 (file)
@@ -19,7 +19,9 @@
 #define WACOM_PKGLEN_INTUOS    10
 #define WACOM_PKGLEN_PENABLED   8
 #define WACOM_PKGLEN_TPC1FG     5
+#define WACOM_PKGLEN_TPC2FG    14
 
+/* device IDs */
 #define STYLUS_DEVICE_ID       0x02
 #define TOUCH_DEVICE_ID                0x03
 #define CURSOR_DEVICE_ID       0x06
@@ -43,6 +45,7 @@ enum {
        WACOM_BEE,
        WACOM_MO,
        TABLETPC,
+       TABLETPC2FG,
        MAX_TYPE
 };
 
@@ -54,8 +57,11 @@ struct wacom_features {
        int pressure_max;
        int distance_max;
        int type;
-       int touch_x_max;
-       int touch_y_max;
+       int device_type;
+       int x_phy;
+       int y_phy;
+       unsigned char unit;
+       unsigned char unitExpo;
 };
 
 struct wacom_wac {