linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] wacom: update to match upstream
@ 2009-12-16 22:58 Matthew Garrett
  2009-12-16 22:58 ` [PATCH 1/3] wacom: Add support for extra USB tablets Matthew Garrett
  0 siblings, 1 reply; 4+ messages in thread
From: Matthew Garrett @ 2009-12-16 22:58 UTC (permalink / raw)
  To: linux-input; +Cc: pingc

The upstream linuxwacom driver (http://linuxwacom.sourceforge.net) has
various updates that aren't present in the kernel. I've ported them to the
current state of things and tried to clean them up a little, but some of
the conditionals make >80 column lines pretty inevitable and I don't want
to do any real refactoring.


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/3] wacom: Add support for extra USB tablets
  2009-12-16 22:58 [PATCH 0/3] wacom: update to match upstream Matthew Garrett
@ 2009-12-16 22:58 ` Matthew Garrett
  2009-12-16 22:58   ` [PATCH 2/3] Wacom: Add support for two-finger touch Matthew Garrett
  0 siblings, 1 reply; 4+ messages in thread
From: Matthew Garrett @ 2009-12-16 22:58 UTC (permalink / raw)
  To: linux-input; +Cc: pingc, Matthew Garrett

From: Ping Cheng <pingc@wacom.com>

Add support for Tablet PCs with IDs 0xE2 and 0xE3 as well as touch-only
tablet 0x9F.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
---
 drivers/input/tablet/wacom.h     |    2 +
 drivers/input/tablet/wacom_sys.c |  138 +++++++++++-----
 drivers/input/tablet/wacom_wac.c |  327 +++++++++++++++++++++++++-------------
 drivers/input/tablet/wacom_wac.h |    9 +
 4 files changed, 328 insertions(+), 148 deletions(-)

diff --git a/drivers/input/tablet/wacom.h b/drivers/input/tablet/wacom.h
index 9114ae1..cdf4c0a 100644
--- a/drivers/input/tablet/wacom.h
+++ b/drivers/input/tablet/wacom.h
@@ -135,6 +135,8 @@ extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wa
 extern void input_dev_pt(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 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 __u16 wacom_le16_to_cpu(unsigned char *data);
 extern __u16 wacom_be16_to_cpu(unsigned char *data);
 extern struct wacom_features *get_wacom_feature(const struct usb_device_id *id);
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index ea30c98..de1562b 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -277,13 +277,31 @@ 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)
+{
+	input_set_abs_params(input_dev, ABS_RX, 0,
+			     wacom_wac->features->touch_x_max, 4, 0);
+	input_set_abs_params(input_dev, ABS_RY, 0,
+			     wacom_wac->features->touch_y_max, 4, 0);
+	input_set_abs_params(input_dev, ABS_Z, 0,
+			     wacom_wac->features->touch_x_res, 0, 0);
+	input_set_abs_params(input_dev, ABS_RZ, 0,
+			     wacom_wac->features->touch_y_res, 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)
+{
+	input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_TRIPLETAP);
+}
+
 static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc,
-			   struct wacom_wac *wacom_wac)
+		struct wacom_wac *wacom_wac)
 {
 	struct usb_device *dev = interface_to_usbdev(intf);
 	struct wacom_features *features = wacom_wac->features;
-	char limit = 0, result = 0;
-	int i = 0, usage = WCM_UNDEFINED, finger = 0, pen = 0;
+	char limit = 0;
+	int i = 0, usage = WCM_UNDEFINED, finger = 0, pen = 0, result = 0;
 	unsigned char *report;
 
 	report = kzalloc(hid_desc->wDescriptorLength, GFP_KERNEL);
@@ -329,11 +347,13 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
 				if (usage == WCM_DESKTOP) {
 					if (finger) {
 						features->touch_x_max =
-							features->touch_y_max =
 							wacom_le16_to_cpu(&report[i + 3]);
-						features->x_max =
+						features->touch_x_res =
 							wacom_le16_to_cpu(&report[i + 6]);
-						i += 7;
+						features->unit = report[i + 9];
+						features->unitExpo =
+							report[i + 11];
+						i += 12;
 					} else if (pen) {
 						features->x_max =
 							wacom_le16_to_cpu(&report[i + 3]);
@@ -350,10 +370,28 @@ 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) {
+						if (strstr(features->name,
+							   "Wacom ISDv4 E")) {
+							features->touch_y_max =
+								wacom_le16_to_cpu(&report[i + 3]);
+							features->touch_y_res =
+								wacom_le16_to_cpu(&report[i + 6]);
+							i += 7;
+						} else {
+							features->touch_y_max =
+								features->touch_x_max;
+							features->touch_y_res =
+								wacom_le16_to_cpu(&report[i + 3]);
+							i += 4;
+						}
+					} else if (pen) {
+						features->y_max =
+							wacom_le16_to_cpu(&report[i + 3]);
+						i += 4;
+					}
+				}
 				break;
 
 			case HID_USAGE_FINGER:
@@ -376,7 +414,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,26 +426,44 @@ 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 report_id = 2;
 
-	rep_data = kmalloc(2, GFP_KERNEL);
+	rep_data = kmalloc(4, 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);
+	if (features->type == TABLETPC2FG) {
+		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++ < 3);
+	} else if (features->type != TABLETPC) {
+		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++ < 3);
+	}
 
 	kfree(rep_data);
 
@@ -432,7 +488,8 @@ 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 +505,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,17 +520,14 @@ 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) {
+	/* Initialize touch_x_max and touch_y_max in case it is not defined in
+	   HID descriptor */
+	if (features->type == TABLETPC || features->type == TABLETPC2FG) {
 		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) {
+		/* TabletPC need to retrieve the physical and logical maximum
+		   from report descriptor */
 		if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) {
 			if (usb_get_extra_descriptor(&interface->endpoint[0],
 						     HID_DEVICET_REPORT, &hid_desc)) {
@@ -484,26 +538,28 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
 		error = wacom_parse_hid(intf, hid_desc, wacom_wac);
 		if (error)
 			goto fail2;
+	} else {
+		features->touch_x_max = 0;
+		features->touch_y_max = 0;
 	}
 
+
 	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_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;
@@ -517,7 +573,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
 	 * Note that if query fails it is not a hard failure.
 	 */
 	if (wacom_wac->features->type != TABLETPC)
-		wacom_query_tablet_data(intf);
+		wacom_query_tablet_data(intf, features);
 
 	usb_set_intfdata(intf, wacom);
 	return 0;
@@ -539,7 +595,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);
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index c896d6a..e3dfb14 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -14,11 +14,19 @@
 #include "wacom.h"
 #include "wacom_wac.h"
 
+/* packet length for individual models */
+#define WACOM_PKGLEN_PENPRTN	 7
+#define WACOM_PKGLEN_GRAPHIRE 	 8
+#define WACOM_PKGLEN_BBFUN 	 9
+#define WACOM_PKGLEN_INTUOS 	10
+#define WACOM_PKGLEN_TPC1FG	 5
+#define WACOM_PKGLEN_TPC2FG 	14
+
 static int wacom_penpartner_irq(struct wacom_wac *wacom, void *wcombo)
 {
 	unsigned char *data = wacom->data;
 
-	switch (data[0]) {
+	switch (data[0] & 0xff) {
 		case 1:
 			if (data[5] & 0x80) {
 				wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
@@ -58,7 +66,7 @@ static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo)
 	unsigned char *data = wacom->data;
 	int prox, pressure;
 
-	if (data[0] != 2) {
+	if ((data[0] & 0xff) != 2) {
 		dbg("wacom_pl_irq: received unknown report #%d", data[0]);
 		return 0;
 	}
@@ -103,8 +111,10 @@ static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo)
 		}
 		wacom_report_key(wcombo, wacom->tool[1], prox); /* report in proximity for tool */
 		wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */
-		wacom_report_abs(wcombo, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
-		wacom_report_abs(wcombo, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
+		wacom_report_abs(wcombo, ABS_X, (data[3] & 0xff) |
+				 (data[2] << 7) | ((data[1] & 0x03) << 14));
+		wacom_report_abs(wcombo, ABS_Y, (data[6] & 0xff) |
+				 (data[5] << 7) | ((data[4] & 0x03) << 14));
 		wacom_report_abs(wcombo, ABS_PRESSURE, pressure);
 
 		wacom_report_key(wcombo, BTN_TOUCH, data[4] & 0x08);
@@ -128,7 +138,7 @@ static int wacom_ptu_irq(struct wacom_wac *wacom, void *wcombo)
 {
 	unsigned char *data = wacom->data;
 
-	if (data[0] != 2) {
+	if ((data[0] & 0xff) != 2) {
 		printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]);
 		return 0;
 	}
@@ -156,7 +166,7 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
 	unsigned char *data = wacom->data;
 	int x, y, rw;
 
-	if (data[0] != 2) {
+	if ((data[0] & 0xff) != 2) {
 		dbg("wacom_graphire_irq: received unknown report #%d", data[0]);
 		return 0;
 	}
@@ -203,7 +213,9 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
 		wacom_report_abs(wcombo, ABS_X, x);
 		wacom_report_abs(wcombo, ABS_Y, y);
 		if (wacom->tool[0] != BTN_TOOL_MOUSE) {
-			wacom_report_abs(wcombo, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8));
+			wacom_report_abs(wcombo, ABS_PRESSURE,
+					 (data[6] & 0xff) |
+					 ((data[7] & 0x01) << 8));
 			wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01);
 			wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
 			wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x04);
@@ -432,7 +444,8 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
 	unsigned int t;
 	int idx = 0, result;
 
-	if (data[0] != 2 && data[0] != 5 && data[0] != 6 && data[0] != 12) {
+	if ((data[0] & 0xff) != 2 && (data[0] & 0xff) != 5 &&
+	    (data[0] & 0xff) != 6 && (data[0] & 0xff) != 12) {
 		dbg("wacom_intuos_irq: received unknown report #%d", data[0]);
                 return 0;
 	}
@@ -442,7 +455,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
 		idx = data[1] & 0x01;
 
 	/* pad packets. Works as a second tool and is always in prox */
-	if (data[0] == 12) {
+	if ((data[0] & 0xff) == 12) {
 		/* initiate the pad as a device */
 		if (wacom->tool[1] != BTN_TOOL_FINGER)
 			wacom->tool[1] = BTN_TOOL_FINGER;
@@ -486,10 +499,12 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
 			wacom_report_abs(wcombo, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
 			wacom_report_abs(wcombo, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
 
-			if ((data[5] & 0x1f) | (data[6] & 0x1f) | (data[1] & 0x1f) |
-				data[2] | (data[3] & 0x1f) | data[4]) {
+			if ((data[5] & 0x1f) | (data[6] & 0x1f) |
+			    (data[1] & 0x1f) | (data[2] & 0xff) |
+			    (data[3] & 0x1f) | (data[4] & 0xff)) {
 				wacom_report_key(wcombo, wacom->tool[1], 1);
-				wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID);
+				wacom_report_abs(wcombo, ABS_MISC,
+						 PAD_DEVICE_ID);
 			} else {
 				wacom_report_key(wcombo, wacom->tool[1], 0);
 				wacom_report_abs(wcombo, ABS_MISC, 0);
@@ -608,59 +623,136 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
 	return 1;
 }
 
+static void wacom_tpc_touch_in(struct wacom_wac *wacom, void *wcombo)
+{
+	char *data = wacom->data;
+	struct urb *urb = ((struct wacom_combo *)wcombo)->urb;
+
+	wacom->tool[0] = BTN_TOOL_DOUBLETAP;
+	wacom->id[0] = TOUCH_DEVICE_ID;
+	if (urb->actual_length != WACOM_PKGLEN_TPC1FG) {
+		switch (data[0] & 0xff) {
+		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]));
+			break;
+		case 13:
+			if (data[1] & 0x01) {
+				if (data[1] & 0x02) {
+					wacom->tool[1] = BTN_TOOL_TRIPLETAP;
+					wacom_report_abs(wcombo, ABS_X,
+							 (data[4] & 0xff) |
+							 ((data[5] & 0x7f) << 8));
+					wacom_report_abs(wcombo, ABS_Y,
+							 (data[8] & 0xff) |
+							 ((data[9] & 0x7f) << 8));
+					wacom_report_abs(wcombo, ABS_MISC,
+							 wacom->id[0]);
+					wacom_report_key(wcombo,
+							 wacom->tool[1], 1);
+					wacom_input_event(wcombo,
+							  EV_MSC, MSC_SERIAL,
+							  0xf0);
+
+					/* sync second finger data */
+					wacom_input_sync(wcombo);
+					}
+				wacom_report_abs(wcombo, ABS_X,
+						 (data[2] & 0xff) |
+						 ((data[3] & 0x7f) << 8));
+				wacom_report_abs(wcombo, ABS_Y,
+						 (data[6] & 0xff) |
+						 ((data[7] & 0x7f) << 8));
+				}
+				wacom_report_key(wcombo, BTN_TOUCH, 1);
+				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 void wacom_tpc_touch_out(struct wacom_wac *wacom, void *wcombo)
+{
+	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[0], 0);
+	wacom_report_key(wcombo, BTN_TOUCH, 0);
+	return;
+}
+
 static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
 {
 	char *data = wacom->data;
 	int prox = 0, pressure;
 	static int stylusInProx, touchInProx = 1, touchOut;
 	struct urb *urb = ((struct wacom_combo *)wcombo)->urb;
+	static int secondFingerIn;
 
 	dbg("wacom_tpc_irq: received report #%d", data[0]);
 
-	if (urb->actual_length == 5 || data[0] == 6) { /* Touch data */
-		if (urb->actual_length == 5) {  /* with touch */
+	if (urb->actual_length == WACOM_PKGLEN_TPC1FG ||
+	    ((data[0] & 0xff) == 6)  /* single touch */
+	    || (data[0] & 0xff) == 13) { /* 2FG touch */
+		/* with touch */
+		if (urb->actual_length == WACOM_PKGLEN_TPC1FG) {
 			prox = data[0] & 0x03;
 		} else {  /* with capacity */
-			prox = data[1] & 0x03;
+			if ((data[0] & 0xff) == 6)     /* single touch */
+				prox = data[1] & 0x01;
+			else			       /* 2FG touch data */
+				prox = data[1] & 0x03;
 		}
 
 		if (!stylusInProx) { /* stylus not in prox */
+			if ((data[0] & 0xff) == 13) {
+				if (data[1] & 0x02)
+					secondFingerIn = 1;
+				else if (secondFingerIn == 1) {
+					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[1], 0);
+					wacom_input_event(wcombo, EV_MSC,
+							  MSC_SERIAL, 0xf0);
+					/* sync second finger data */
+					wacom_input_sync(wcombo);
+					secondFingerIn = 0;
+				}
+			}
 			if (prox) {
 				if (touchInProx) {
-					wacom->tool[1] = BTN_TOOL_DOUBLETAP;
-					wacom->id[0] = TOUCH_DEVICE_ID;
-					if (urb->actual_length != 5) {
-						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);
+				wacom_tpc_touch_out(wacom, wcombo);
 				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);
 			touchOut = 0;
 			touchInProx = 1;
 			return 1;
 		}
-	} else if (data[0] == 2) { /* Penabled */
+	} else if ((data[0] & 0xff) == 2) { /* Penabled */
 		prox = data[1] & 0x20;
 
 		touchInProx = 0;
@@ -674,28 +766,30 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
 		 * an out of proximity for previous tool then a in for new tool.
 		 */
 		if (prox) { /* in prox */
-			if (!wacom->tool[0]) {
+			if (!wacom->id[1]) {
 				/* 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] & 0x08) ? 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)) {
+			} else if (wacom->tool[0] == 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_report_abs(wcombo, ABS_MISC,
+						 wacom->id[0]);
+				wacom_report_key(wcombo, wacom->tool[0], 0);
 				wacom_input_sync(wcombo);
 
 				/* set new tool */
-				wacom->tool[1] = BTN_TOOL_PEN;
+				wacom->tool[0] = 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->tool[0] = BTN_TOOL_PEN;
 				wacom->id[0] = STYLUS_DEVICE_ID;
 			}
 			wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
@@ -708,15 +802,21 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
 			wacom_report_abs(wcombo, ABS_PRESSURE, pressure);
 			wacom_report_key(wcombo, BTN_TOUCH, pressure);
 		} 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);
+			/* 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;
+		/* use the prox info to distinguish up-side switch from
+		   eraser */
+		wacom->id[1] = prox;
 		return 1;
 	}
 	return 0;
@@ -751,6 +851,8 @@ 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 +893,14 @@ 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);
+			/* fall through */
 		case PL:
 		case PTU:
-		case TABLETPC:
 			input_dev_pl(input_dev, wacom_wac);
 			/* fall through */
 		case PENPARTNER:
@@ -804,66 +911,69 @@ void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_w
 }
 
 static struct wacom_features wacom_features[] = {
-	{ "Wacom Penpartner",    7,   5040,  3780,  255,  0, PENPARTNER },
-        { "Wacom Graphire",      8,  10206,  7422,  511, 63, GRAPHIRE },
-	{ "Wacom Graphire2 4x5", 8,  10206,  7422,  511, 63, GRAPHIRE },
-	{ "Wacom Graphire2 5x7", 8,  13918, 10206,  511, 63, GRAPHIRE },
-	{ "Wacom Graphire3",     8,  10208,  7424,  511, 63, GRAPHIRE },
-	{ "Wacom Graphire3 6x8", 8,  16704, 12064,  511, 63, GRAPHIRE },
-	{ "Wacom Graphire4 4x5", 8,  10208,  7424,  511, 63, WACOM_G4 },
-	{ "Wacom Graphire4 6x8", 8,  16704, 12064,  511, 63, WACOM_G4 },
-	{ "Wacom BambooFun 4x5", 9,  14760,  9225,  511, 63, WACOM_MO },
-	{ "Wacom BambooFun 6x8", 9,  21648, 13530,  511, 63, WACOM_MO },
-	{ "Wacom Bamboo1 Medium",8,  16704, 12064,  511, 63, GRAPHIRE },
-	{ "Wacom Volito",        8,   5104,  3712,  511, 63, GRAPHIRE },
-	{ "Wacom PenStation2",   8,   3250,  2320,  255, 63, GRAPHIRE },
-	{ "Wacom Volito2 4x5",   8,   5104,  3712,  511, 63, GRAPHIRE },
-	{ "Wacom Volito2 2x3",   8,   3248,  2320,  511, 63, GRAPHIRE },
-	{ "Wacom PenPartner2",   8,   3250,  2320,  511, 63, GRAPHIRE },
-	{ "Wacom Bamboo",        9,  14760,  9225,  511, 63, WACOM_MO },
-	{ "Wacom Bamboo1",       8,   5104,  3712,  511, 63, GRAPHIRE },
-	{ "Wacom Intuos 4x5",   10,  12700, 10600, 1023, 31, INTUOS },
-	{ "Wacom Intuos 6x8",   10,  20320, 16240, 1023, 31, INTUOS },
-	{ "Wacom Intuos 9x12",  10,  30480, 24060, 1023, 31, INTUOS },
-	{ "Wacom Intuos 12x12", 10,  30480, 31680, 1023, 31, INTUOS },
-	{ "Wacom Intuos 12x18", 10,  45720, 31680, 1023, 31, INTUOS },
-	{ "Wacom PL400",         8,   5408,  4056,  255,  0, PL },
-	{ "Wacom PL500",         8,   6144,  4608,  255,  0, PL },
-	{ "Wacom PL600",         8,   6126,  4604,  255,  0, PL },
-	{ "Wacom PL600SX",       8,   6260,  5016,  255,  0, PL },
-	{ "Wacom PL550",         8,   6144,  4608,  511,  0, PL },
-	{ "Wacom PL800",         8,   7220,  5780,  511,  0, PL },
-	{ "Wacom PL700",         8,   6758,  5406,  511,  0, PL },
-	{ "Wacom PL510",         8,   6282,  4762,  511,  0, PL },
-	{ "Wacom DTU710",        8,  34080, 27660,  511,  0, PL },
-	{ "Wacom DTF521",        8,   6282,  4762,  511,  0, PL },
-	{ "Wacom DTF720",        8,   6858,  5506,  511,  0, PL },
-	{ "Wacom DTF720a",       8,   6858,  5506,  511,  0, PL },
-	{ "Wacom Cintiq Partner",8,  20480, 15360,  511,  0, PTU },
-	{ "Wacom Intuos2 4x5",   10, 12700, 10600, 1023, 31, INTUOS },
-	{ "Wacom Intuos2 6x8",   10, 20320, 16240, 1023, 31, INTUOS },
-	{ "Wacom Intuos2 9x12",  10, 30480, 24060, 1023, 31, INTUOS },
-	{ "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 31, INTUOS },
-	{ "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 31, INTUOS },
-	{ "Wacom Intuos3 4x5",   10, 25400, 20320, 1023, 63, INTUOS3S },
-	{ "Wacom Intuos3 6x8",   10, 40640, 30480, 1023, 63, INTUOS3 },
-	{ "Wacom Intuos3 9x12",  10, 60960, 45720, 1023, 63, INTUOS3 },
-	{ "Wacom Intuos3 12x12", 10, 60960, 60960, 1023, 63, INTUOS3L },
-	{ "Wacom Intuos3 12x19", 10, 97536, 60960, 1023, 63, INTUOS3L },
-	{ "Wacom Intuos3 6x11",  10, 54204, 31750, 1023, 63, INTUOS3 },
-	{ "Wacom Intuos3 4x6",   10, 31496, 19685, 1023, 63, INTUOS3S },
-	{ "Wacom Intuos4 4x6",   10, 31496, 19685, 2047, 63, INTUOS4S },
-	{ "Wacom Intuos4 6x9",   10, 44704, 27940, 2047, 63, INTUOS4 },
-	{ "Wacom Intuos4 8x13",  10, 65024, 40640, 2047, 63, INTUOS4L },
-	{ "Wacom Intuos4 12x19", 10, 97536, 60960, 2047, 63, INTUOS4L },
-	{ "Wacom Cintiq 21UX",   10, 87200, 65600, 1023, 63, CINTIQ },
-	{ "Wacom Cintiq 20WSX",  10, 86680, 54180, 1023, 63, WACOM_BEE },
-	{ "Wacom Cintiq 12WX",   10, 53020, 33440, 1023, 63, WACOM_BEE },
-	{ "Wacom DTU1931",        8, 37832, 30305,  511,  0, PL },
-	{ "Wacom ISDv4 90",       8, 26202, 16325,  255,  0, TABLETPC },
-	{ "Wacom ISDv4 93",       8, 26202, 16325,  255,  0, TABLETPC },
-	{ "Wacom ISDv4 9A",       8, 26202, 16325,  255,  0, TABLETPC },
-	{ "Wacom Intuos2 6x8",   10, 20320, 16240, 1023, 31, INTUOS },
+	{ "Wacom Penpartner",         WACOM_PKGLEN_PENPRTN,    5040,  3780,  255,  0, PENPARTNER },
+	{ "Wacom Graphire",           WACOM_PKGLEN_GRAPHIRE,  10206,  7422,  511, 63, GRAPHIRE },
+	{ "Wacom Graphire2 4x5",      WACOM_PKGLEN_GRAPHIRE,  10206,  7422,  511, 63, GRAPHIRE },
+	{ "Wacom Graphire2 5x7",      WACOM_PKGLEN_GRAPHIRE,  13918, 10206,  511, 63, GRAPHIRE },
+	{ "Wacom Graphire3",          WACOM_PKGLEN_GRAPHIRE,  10208,  7424,  511, 63, GRAPHIRE },
+	{ "Wacom Graphire3 6x8",      WACOM_PKGLEN_GRAPHIRE,  16704, 12064,  511, 63, GRAPHIRE },
+	{ "Wacom Graphire4 4x5",      WACOM_PKGLEN_GRAPHIRE,  10208,  7424,  511, 63, WACOM_G4 },
+	{ "Wacom Graphire4 6x8",      WACOM_PKGLEN_GRAPHIRE,  16704, 12064,  511, 63, WACOM_G4 },
+	{ "Wacom BambooFun 4x5",      WACOM_PKGLEN_BBFUN,     14760,  9225,  511, 63, WACOM_MO },
+	{ "Wacom BambooFun 6x8",      WACOM_PKGLEN_BBFUN,     21648, 13530,  511, 63, WACOM_MO },
+	{ "Wacom Bamboo1 Medium",     WACOM_PKGLEN_GRAPHIRE,  16704, 12064,  511, 63, GRAPHIRE },
+	{ "Wacom Volito",             WACOM_PKGLEN_GRAPHIRE,   5104,  3712,  511, 63, GRAPHIRE },
+	{ "Wacom PenStation2",        WACOM_PKGLEN_GRAPHIRE,   3250,  2320,  255, 63, GRAPHIRE },
+	{ "Wacom Volito2 4x5",        WACOM_PKGLEN_GRAPHIRE,   5104,  3712,  511, 63, GRAPHIRE },
+	{ "Wacom Volito2 2x3",        WACOM_PKGLEN_GRAPHIRE,   3248,  2320,  511, 63, GRAPHIRE },
+	{ "Wacom PenPartner2",        WACOM_PKGLEN_GRAPHIRE,   3250,  2320,  511, 63, GRAPHIRE },
+	{ "Wacom Bamboo",             WACOM_PKGLEN_BBFUN,     14760,  9225,  511, 63, WACOM_MO },
+	{ "Wacom Bamboo1",            WACOM_PKGLEN_GRAPHIRE,   5104,  3712,  511, 63, GRAPHIRE },
+	{ "Wacom Intuos 4x5",         WACOM_PKGLEN_INTUOS,    12700, 10600, 1023, 31, INTUOS },
+	{ "Wacom Intuos 6x8",         WACOM_PKGLEN_INTUOS,    20320, 16240, 1023, 31, INTUOS },
+	{ "Wacom Intuos 9x12",        WACOM_PKGLEN_INTUOS,    30480, 24060, 1023, 31, INTUOS },
+	{ "Wacom Intuos 12x12",       WACOM_PKGLEN_INTUOS,    30480, 31680, 1023, 31, INTUOS },
+	{ "Wacom Intuos 12x18",       WACOM_PKGLEN_INTUOS,    45720, 31680, 1023, 31, INTUOS },
+	{ "Wacom PL400",              WACOM_PKGLEN_GRAPHIRE,   5408,  4056,  255,  0, PL },
+	{ "Wacom PL500",              WACOM_PKGLEN_GRAPHIRE,   6144,  4608,  255,  0, PL },
+	{ "Wacom PL600",              WACOM_PKGLEN_GRAPHIRE,   6126,  4604,  255,  0, PL },
+	{ "Wacom PL600SX",            WACOM_PKGLEN_GRAPHIRE,   6260,  5016,  255,  0, PL },
+	{ "Wacom PL550",              WACOM_PKGLEN_GRAPHIRE,   6144,  4608,  511,  0, PL },
+	{ "Wacom PL800",              WACOM_PKGLEN_GRAPHIRE,   7220,  5780,  511,  0, PL },
+	{ "Wacom PL700",              WACOM_PKGLEN_GRAPHIRE,   6758,  5406,  511,  0, PL },
+	{ "Wacom PL510",              WACOM_PKGLEN_GRAPHIRE,   6282,  4762,  511,  0, PL },
+	{ "Wacom DTU710",             WACOM_PKGLEN_GRAPHIRE,  34080, 27660,  511,  0, PL },
+	{ "Wacom DTF521",             WACOM_PKGLEN_GRAPHIRE,   6282,  4762,  511,  0, PL },
+	{ "Wacom DTF720",             WACOM_PKGLEN_GRAPHIRE,   6858,  5506,  511,  0, PL },
+	{ "Wacom DTF720a",            WACOM_PKGLEN_GRAPHIRE,   6858,  5506,  511,  0, PL },
+	{ "Wacom Cintiq Partner",     WACOM_PKGLEN_GRAPHIRE,  20480, 15360,  511,  0, PTU },
+	{ "Wacom Intuos2 4x5",        WACOM_PKGLEN_INTUOS,    12700, 10600, 1023, 31, INTUOS },
+	{ "Wacom Intuos2 6x8",        WACOM_PKGLEN_INTUOS,    20320, 16240, 1023, 31, INTUOS },
+	{ "Wacom Intuos2 9x12",       WACOM_PKGLEN_INTUOS,    30480, 24060, 1023, 31, INTUOS },
+	{ "Wacom Intuos2 12x12",      WACOM_PKGLEN_INTUOS,    30480, 31680, 1023, 31, INTUOS },
+	{ "Wacom Intuos2 12x18",      WACOM_PKGLEN_INTUOS,    45720, 31680, 1023, 31, INTUOS },
+	{ "Wacom Intuos3 4x5",        WACOM_PKGLEN_INTUOS,    25400, 20320, 1023, 63, INTUOS3S },
+	{ "Wacom Intuos3 6x8",        WACOM_PKGLEN_INTUOS,    40640, 30480, 1023, 63, INTUOS3 },
+	{ "Wacom Intuos3 9x12",       WACOM_PKGLEN_INTUOS,    60960, 45720, 1023, 63, INTUOS3 },
+	{ "Wacom Intuos3 12x12",      WACOM_PKGLEN_INTUOS,    60960, 60960, 1023, 63, INTUOS3L },
+	{ "Wacom Intuos3 12x19",      WACOM_PKGLEN_INTUOS,    97536, 60960, 1023, 63, INTUOS3L },
+	{ "Wacom Intuos3 6x11",       WACOM_PKGLEN_INTUOS,    54204, 31750, 1023, 63, INTUOS3 },
+	{ "Wacom Intuos3 4x6",        WACOM_PKGLEN_INTUOS,    31496, 19685, 1023, 63, INTUOS3S },
+	{ "Wacom Intuos4 4x6",        WACOM_PKGLEN_INTUOS,    31496, 19685, 2047, 63, INTUOS4S },
+	{ "Wacom Intuos4 6x9",        WACOM_PKGLEN_INTUOS,    44704, 27940, 2047, 63, INTUOS4 },
+	{ "Wacom Intuos4 8x13",       WACOM_PKGLEN_INTUOS,    65024, 40640, 2047, 63, INTUOS4L },
+	{ "Wacom Intuos4 12x19",      WACOM_PKGLEN_INTUOS,    97536, 60960, 2047, 63, INTUOS4L },
+	{ "Wacom Cintiq 21UX",        WACOM_PKGLEN_INTUOS,    87200, 65600, 1023, 63, CINTIQ },
+	{ "Wacom Cintiq 20WSX",       WACOM_PKGLEN_INTUOS,    86680, 54180, 1023, 63, WACOM_BEE },
+	{ "Wacom Cintiq 12WX",        WACOM_PKGLEN_INTUOS,    53020, 33440, 1023, 63, WACOM_BEE },
+	{ "Wacom DTU1931",            WACOM_PKGLEN_GRAPHIRE,  37832, 30305,  511,  0, PL },
+	{ "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_GRAPHIRE,  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 +1037,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) },
 	{ }
 };
diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h
index c10235a..a6a86de 100644
--- a/drivers/input/tablet/wacom_wac.h
+++ b/drivers/input/tablet/wacom_wac.h
@@ -9,6 +9,10 @@
 #ifndef WACOM_WAC_H
 #define WACOM_WAC_H
 
+/* maximum packet length for USB devices */
+#define WACOM_PKGLEN_MAX	32
+
+/* device IDs */
 #define STYLUS_DEVICE_ID	0x02
 #define TOUCH_DEVICE_ID		0x03
 #define CURSOR_DEVICE_ID	0x06
@@ -32,6 +36,7 @@ enum {
 	WACOM_BEE,
 	WACOM_MO,
 	TABLETPC,
+	TABLETPC2FG,
 	MAX_TYPE
 };
 
@@ -43,8 +48,12 @@ struct wacom_features {
 	int pressure_max;
 	int distance_max;
 	int type;
+	int touch_x_res;
+	int touch_y_res;
 	int touch_x_max;
 	int touch_y_max;
+	unsigned char unit;
+	unsigned char unitExpo;
 };
 
 struct wacom_wac {
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 2/3] Wacom: Add support for two-finger touch
  2009-12-16 22:58 ` [PATCH 1/3] wacom: Add support for extra USB tablets Matthew Garrett
@ 2009-12-16 22:58   ` Matthew Garrett
  2009-12-16 22:58     ` [PATCH 3/3] wacom: Improve support for Penabled devices Matthew Garrett
  0 siblings, 1 reply; 4+ messages in thread
From: Matthew Garrett @ 2009-12-16 22:58 UTC (permalink / raw)
  To: linux-input; +Cc: pingc, Matthew Garrett

From: Ping Cheng <pingc@wacom.com>

Some wacom pads can report events for two-finger touches. Make sure that
these are reported.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
---
 drivers/input/tablet/wacom_sys.c |   78 +++++++-----
 drivers/input/tablet/wacom_wac.c |  254 +++++++++++++++++++++-----------------
 drivers/input/tablet/wacom_wac.h |    7 +-
 3 files changed, 188 insertions(+), 151 deletions(-)

diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index de1562b..02592b6 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -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)
@@ -279,20 +282,24 @@ void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
 
 void input_dev_tpc(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
 {
-	input_set_abs_params(input_dev, ABS_RX, 0,
-			     wacom_wac->features->touch_x_max, 4, 0);
-	input_set_abs_params(input_dev, ABS_RY, 0,
-			     wacom_wac->features->touch_y_max, 4, 0);
-	input_set_abs_params(input_dev, ABS_Z, 0,
-			     wacom_wac->features->touch_x_res, 0, 0);
-	input_set_abs_params(input_dev, ABS_RZ, 0,
-			     wacom_wac->features->touch_y_res, 0, 0);
-	input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_DOUBLETAP);
+	if (wacom_wac->features->device_type == BTN_TOOL_DOUBLETAP) {
+		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)
 {
-	input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_TRIPLETAP);
+	if (wacom_wac->features->device_type == BTN_TOOL_DOUBLETAP) {
+		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,
@@ -346,15 +353,19 @@ 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->device_type =
+							BTN_TOOL_DOUBLETAP;
+						features->x_max =
 							wacom_le16_to_cpu(&report[i + 3]);
-						features->touch_x_res =
+						features->x_phy =
 							wacom_le16_to_cpu(&report[i + 6]);
 						features->unit = report[i + 9];
 						features->unitExpo =
 							report[i + 11];
 						i += 12;
 					} else if (pen) {
+						features->device_type =
+							BTN_TOOL_PEN;
 						features->x_max =
 							wacom_le16_to_cpu(&report[i + 3]);
 						i += 4;
@@ -372,21 +383,25 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
 			case HID_USAGE_Y:
 				if (usage == WCM_DESKTOP) {
 					if (finger) {
+						features->device_type =
+							BTN_TOOL_DOUBLETAP;
 						if (strstr(features->name,
 							   "Wacom ISDv4 E")) {
-							features->touch_y_max =
+							features->y_max =
 								wacom_le16_to_cpu(&report[i + 3]);
-							features->touch_y_res =
+							features->y_phy =
 								wacom_le16_to_cpu(&report[i + 6]);
 							i += 7;
 						} else {
-							features->touch_y_max =
-								features->touch_x_max;
-							features->touch_y_res =
+							features->y_max =
+								features->x_max;
+							features->y_phy =
 								wacom_le16_to_cpu(&report[i + 3]);
 							i += 4;
 						}
 					} else if (pen) {
+						features->device_type =
+							BTN_TOOL_PEN;
 						features->y_max =
 							wacom_le16_to_cpu(&report[i + 3]);
 						i += 4;
@@ -507,6 +522,10 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
 	wacom_wac->features = features = get_wacom_feature(id);
 	BUG_ON(features->pktlen > WACOM_PKGLEN_MAX);
 
+	/* default device to penabled */
+	if (features->device_type)
+		features->device_type = BTN_TOOL_PEN;
+
 	input_dev->name = wacom_wac->features->name;
 	wacom->wacom_wac = wacom_wac;
 	usb_to_input_id(dev, &input_dev->id);
@@ -520,11 +539,7 @@ 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 in
-	   HID descriptor */
 	if (features->type == TABLETPC || features->type == TABLETPC2FG) {
-		features->touch_x_max = 1023;
-		features->touch_y_max = 1023;
 
 		/* TabletPC need to retrieve the physical and logical maximum
 		   from report descriptor */
@@ -538,21 +553,22 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
 		error = wacom_parse_hid(intf, hid_desc, wacom_wac);
 		if (error)
 			goto fail2;
-	} else {
-		features->touch_x_max = 0;
-		features->touch_y_max = 0;
-	}
 
+		/* 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;
+		}
+	}
 
 	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);
+	input_set_abs_params(input_dev, ABS_PRESSURE, 0,
+			     features->pressure_max, 0, 0);
 	input_dev->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC);
 
 	wacom_init_input_dev(input_dev, wacom_wac);
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index e3dfb14..1937cf1 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -73,9 +73,9 @@ 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);
@@ -165,6 +165,7 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
 {
 	unsigned char *data = wacom->data;
 	int x, y, rw;
+	static int penData;
 
 	if ((data[0] & 0xff) != 2) {
 		dbg("wacom_graphire_irq: received unknown report #%d", data[0]);
@@ -173,7 +174,7 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
 
 	if (data[1] & 0x80) {
 		/* in prox and not a pad data */
-
+		penData = 1;
 		switch ((data[1] >> 5) & 3) {
 
 			case 0:	/* Pen */
@@ -244,7 +245,11 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
 	switch (wacom->features->type) {
 	    case WACOM_G4:
 		if (data[7] & 0xf8) {
-			wacom_input_sync(wcombo); /* sync last event */
+			if (penData) {
+				wacom_input_sync(wcombo); /* sync last event */
+				if (!wacom->id[0])
+					penData = 0;
+			}
 			wacom->id[1] = PAD_DEVICE_ID;
 			wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
 			wacom_report_key(wcombo, BTN_4, (data[7] & 0x80));
@@ -254,10 +259,15 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
 			wacom_report_abs(wcombo, ABS_MISC, wacom->id[1]);
 			wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
 		} else if (wacom->id[1]) {
-			wacom_input_sync(wcombo); /* sync last event */
+			if (penData) {
+				wacom_input_sync(wcombo); /* sync last event */
+				if (!wacom->id[0])
+					penData = 0;
+			}
 			wacom->id[1] = 0;
 			wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
 			wacom_report_key(wcombo, BTN_4, (data[7] & 0x80));
+			wacom_report_rel(wcombo, REL_WHEEL, 0);
 			wacom_report_key(wcombo, BTN_TOOL_FINGER, 0);
 			wacom_report_abs(wcombo, ABS_MISC, 0);
 			wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
@@ -265,7 +275,11 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
 		break;
 	    case WACOM_MO:
 		if ((data[7] & 0xf8) || (data[8] & 0xff)) {
-			wacom_input_sync(wcombo); /* sync last event */
+			if (penData) {
+				wacom_input_sync(wcombo); /* sync last event */
+				if (!wacom->id[0])
+					penData = 0;
+			}
 			wacom->id[1] = PAD_DEVICE_ID;
 			wacom_report_key(wcombo, BTN_0, (data[7] & 0x08));
 			wacom_report_key(wcombo, BTN_1, (data[7] & 0x20));
@@ -276,7 +290,11 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
 			wacom_report_abs(wcombo, ABS_MISC, wacom->id[1]);
 			wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
 		} else if (wacom->id[1]) {
-			wacom_input_sync(wcombo); /* sync last event */
+			if (penData) {
+				wacom_input_sync(wcombo); /* sync last event */
+				if (!wacom->id[0])
+					penData = 0;
+			}
 			wacom->id[1] = 0;
 			wacom_report_key(wcombo, BTN_0, (data[7] & 0x08));
 			wacom_report_key(wcombo, BTN_1, (data[7] & 0x20));
@@ -623,117 +641,128 @@ 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;
+	static int secondFinger;
+
+	wacom->tool[0] = BTN_TOOL_DOUBLETAP;  
+	wacom->id[0] = TOUCH_DEVICE_ID; 
+	wacom->tool[1] = BTN_TOOL_TRIPLETAP;
 
-	wacom->tool[0] = BTN_TOOL_DOUBLETAP;
-	wacom->id[0] = TOUCH_DEVICE_ID;
 	if (urb->actual_length != WACOM_PKGLEN_TPC1FG) {
 		switch (data[0] & 0xff) {
-		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]));
-			break;
-		case 13:
-			if (data[1] & 0x01) {
-				if (data[1] & 0x02) {
-					wacom->tool[1] = BTN_TOOL_TRIPLETAP;
-					wacom_report_abs(wcombo, ABS_X,
-							 (data[4] & 0xff) |
-							 ((data[5] & 0x7f) << 8));
-					wacom_report_abs(wcombo, ABS_Y,
-							 (data[8] & 0xff) |
-							 ((data[9] & 0x7f) << 8));
-					wacom_report_abs(wcombo, ABS_MISC,
-							 wacom->id[0]);
-					wacom_report_key(wcombo,
-							 wacom->tool[1], 1);
-					wacom_input_event(wcombo,
-							  EV_MSC, MSC_SERIAL,
-							  0xf0);
-
-					/* sync second finger data */
-					wacom_input_sync(wcombo);
-					}
+			case 6:
 				wacom_report_abs(wcombo, ABS_X,
-						 (data[2] & 0xff) |
-						 ((data[3] & 0x7f) << 8));
+						 wacom_le16_to_cpu(&data[2]));
 				wacom_report_abs(wcombo, ABS_Y,
-						 (data[6] & 0xff) |
-						 ((data[7] & 0x7f) << 8));
+						 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;
 				}
-				wacom_report_key(wcombo, BTN_TOUCH, 1);
+				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);
 	}
-	wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]);
-	wacom_report_key(wcombo, wacom->tool[0], 1);
-
-	return;
-}
-
-static void wacom_tpc_touch_out(struct wacom_wac *wacom, void *wcombo)
-{
-	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[0], 0);
-	wacom_report_key(wcombo, BTN_TOUCH, 0);
 	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;
-	static int secondFingerIn;
 
 	dbg("wacom_tpc_irq: received report #%d", data[0]);
 
 	if (urb->actual_length == WACOM_PKGLEN_TPC1FG ||
 	    ((data[0] & 0xff) == 6)  /* single touch */
-	    || (data[0] & 0xff) == 13) { /* 2FG touch */
+	    || (data[0] & 0xff) == 13 ) { /* 2FG touch */
 		/* with touch */
 		if (urb->actual_length == WACOM_PKGLEN_TPC1FG) {
-			prox = data[0] & 0x03;
+			prox = data[0] & 0x01;
 		} else {  /* with capacity */
-			if ((data[0] & 0xff) == 6)     /* single touch */
+			if ((data[0] & 0xff) == 6)
+				/* single touch */
 				prox = data[1] & 0x01;
-			else			       /* 2FG touch data */
+			else
+				/* 2FG touch data */
 				prox = data[1] & 0x03;
 		}
 
 		if (!stylusInProx) { /* stylus not in prox */
-			if ((data[0] & 0xff) == 13) {
-				if (data[1] & 0x02)
-					secondFingerIn = 1;
-				else if (secondFingerIn == 1) {
-					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[1], 0);
-					wacom_input_event(wcombo, EV_MSC,
-							  MSC_SERIAL, 0xf0);
-					/* sync second finger data */
-					wacom_input_sync(wcombo);
-					secondFingerIn = 0;
-				}
-			}
 			if (prox) {
 				if (touchInProx) {
 					wacom_tpc_touch_in(wacom, wcombo);
@@ -741,13 +770,30 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
 					return 1;
 				}
 			} else {
-				wacom_tpc_touch_out(wacom, wcombo);
+				/* 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_tpc_touch_out(wacom, wcombo);
+			wacom_tpc_touch_out(wacom, wcombo, 0);
 			touchOut = 0;
 			touchInProx = 1;
 			return 1;
@@ -757,40 +803,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->id[1]) {
-				/* Going into proximity select tool */
-				wacom->tool[0] = (data[1] & 0x08) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+			if (!wacom->id[0]) {
+				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[0] == 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[0], 0);
-				wacom_input_sync(wcombo);
-
-				/* set new tool */
-				wacom->tool[0] = 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[0] = 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);
@@ -800,7 +820,7 @@ 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);
@@ -808,15 +828,13 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
 			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[0], prox);
 		wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]);
 		stylusInProx = prox;
-		/* use the prox info to distinguish up-side switch from
-		   eraser */
-		wacom->id[1] = prox;
 		return 1;
 	}
 	return 0;
@@ -898,6 +916,10 @@ void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_w
 			/* fall through */
 		case TABLETPC:
 			input_dev_tpc(input_dev, wacom_wac);
+			if (wacom_wac->features->device_type ==
+			    BTN_TOOL_DOUBLETAP)
+				break;  /* no need to process stylus stuff */
+
 			/* fall through */
 		case PL:
 		case PTU:
diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h
index a6a86de..37e0eb3 100644
--- a/drivers/input/tablet/wacom_wac.h
+++ b/drivers/input/tablet/wacom_wac.h
@@ -48,10 +48,9 @@ struct wacom_features {
 	int pressure_max;
 	int distance_max;
 	int type;
-	int touch_x_res;
-	int touch_y_res;
-	int touch_x_max;
-	int touch_y_max;
+	int device_type;
+	int x_phy;
+	int y_phy;
 	unsigned char unit;
 	unsigned char unitExpo;
 };
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH 3/3] wacom: Improve support for Penabled devices
  2009-12-16 22:58   ` [PATCH 2/3] Wacom: Add support for two-finger touch Matthew Garrett
@ 2009-12-16 22:58     ` Matthew Garrett
  0 siblings, 0 replies; 4+ messages in thread
From: Matthew Garrett @ 2009-12-16 22:58 UTC (permalink / raw)
  To: linux-input; +Cc: pingc, Matthew Garrett

From: Ping Cheng <pingc@wacom.com>

Refactor the code somewhat in order to provide better support for
Penabled devices.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
---
 drivers/input/tablet/wacom_sys.c |  144 ++++++++++++++++++++++++--------------
 drivers/input/tablet/wacom_wac.c |   24 ++-----
 drivers/input/tablet/wacom_wac.h |    9 +++
 3 files changed, 108 insertions(+), 69 deletions(-)

diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index 02592b6..9fc4244 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -1,7 +1,7 @@
 /*
  * drivers/input/tablet/wacom_sys.c
  *
- *  USB Wacom Graphire and Wacom Intuos tablet support - system specific code
+ *  USB Wacom tablet support - system specific code
  */
 
 /*
@@ -282,7 +282,8 @@ void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
 
 void input_dev_tpc(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
 {
-	if (wacom_wac->features->device_type == BTN_TOOL_DOUBLETAP) {
+	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,
@@ -294,7 +295,7 @@ void input_dev_tpc(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
 
 void input_dev_tpc2fg(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
 {
-	if (wacom_wac->features->device_type == BTN_TOOL_DOUBLETAP) {
+	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);
@@ -303,10 +304,9 @@ void input_dev_tpc2fg(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
 }
 
 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;
 	int i = 0, usage = WCM_UNDEFINED, finger = 0, pen = 0, result = 0;
 	unsigned char *report;
@@ -355,6 +355,14 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
 					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->x_max =
 							wacom_le16_to_cpu(&report[i + 3]);
 						features->x_phy =
@@ -364,6 +372,11 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
 							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 =
@@ -385,8 +398,13 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
 					if (finger) {
 						features->device_type =
 							BTN_TOOL_DOUBLETAP;
-						if (strstr(features->name,
-							   "Wacom ISDv4 E")) {
+						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 =
@@ -400,6 +418,11 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
 							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 =
@@ -441,19 +464,20 @@ 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,
-				   struct wacom_features *features)
+static int wacom_reset_report(struct usb_interface *intf,
+			      struct wacom_features *features)
 {
-	unsigned char *rep_data;
-	int limit = 0;
-	int error;
-	int report_id = 2;
+	char *rep_data, limit = 0, report_id = 2;
+	int error = -ENOMEM;
 
 	rep_data = kmalloc(4, GFP_KERNEL);
-	if (!rep_data)
-		return -ENOMEM;
 
-	if (features->type == TABLETPC2FG) {
+	/*
+	 * Ask to report tablet data if it is 2FGT or not a Tablet PC.
+	 * Repeat 3 times since on some systems the first 2 may fail.
+	 */
+
+	if (features->device_type == BTN_TOOL_TRIPLETAP) {
 		do {
 			rep_data[0] = 3;
 			rep_data[1] = 4;
@@ -466,7 +490,8 @@ static int wacom_query_tablet_data(struct usb_interface *intf,
 						       report_id,
 						       rep_data, 3);
 		} while ((error < 0 || rep_data[1] != 4) && limit++ < 3);
-	} else if (features->type != TABLETPC) {
+	} else if ((features->type != TABLETPC) &&
+		   (features->type != TABLETPC2FG)) {
 		do {
 			rep_data[0] = 2;
 			rep_data[1] = 2;
@@ -482,7 +507,44 @@ static int wacom_query_tablet_data(struct usb_interface *intf,
 
 	kfree(rep_data);
 
-	return error < 0 ? error : 0;
+	return error;
+}
+
+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(KERN_ERR
+			       "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)
@@ -522,10 +584,6 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
 	wacom_wac->features = features = get_wacom_feature(id);
 	BUG_ON(features->pktlen > WACOM_PKGLEN_MAX);
 
-	/* default device to penabled */
-	if (features->device_type)
-		features->device_type = BTN_TOOL_PEN;
-
 	input_dev->name = wacom_wac->features->name;
 	wacom->wacom_wac = wacom_wac;
 	usb_to_input_id(dev, &input_dev->id);
@@ -539,28 +597,10 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
 
 	endpoint = &intf->cur_altsetting->endpoint[0].desc;
 
-	if (features->type == TABLETPC || features->type == TABLETPC2FG) {
-
-		/* TabletPC need to retrieve the physical and logical maximum
-		   from report descriptor */
-		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;
-
-		/* 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;
-		}
-	}
+	/* 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_TOUCH);
@@ -584,12 +624,8 @@ 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, features);
+	/* switch to wacom mode if needed */
+	wacom_reset_report(intf, features);
 
 	usb_set_intfdata(intf, wacom);
 	return 0;
@@ -631,12 +667,16 @@ 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)
+	if (wacom->open) {
 		rv = usb_submit_urb(wacom->irq, GFP_NOIO);
-	else
+		/* switch to wacom mode if needed */
+		if (!wacom_retrieve_hid_descriptor(intf, features))
+			wacom_reset_report(intf, features);
+	} else
 		rv = 0;
 	mutex_unlock(&wacom->lock);
 
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 1937cf1..781b115 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -1,7 +1,7 @@
 /*
  * drivers/input/tablet/wacom_wac.c
  *
- *  USB Wacom Graphire and Wacom Intuos tablet support - Wacom specific code
+ *  USB Wacom tablet support - Wacom specific code
  *
  */
 
@@ -14,14 +14,6 @@
 #include "wacom.h"
 #include "wacom_wac.h"
 
-/* packet length for individual models */
-#define WACOM_PKGLEN_PENPRTN	 7
-#define WACOM_PKGLEN_GRAPHIRE 	 8
-#define WACOM_PKGLEN_BBFUN 	 9
-#define WACOM_PKGLEN_INTUOS 	10
-#define WACOM_PKGLEN_TPC1FG	 5
-#define WACOM_PKGLEN_TPC2FG 	14
-
 static int wacom_penpartner_irq(struct wacom_wac *wacom, void *wcombo)
 {
 	unsigned char *data = wacom->data;
@@ -869,7 +861,6 @@ 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);
 
@@ -916,8 +907,7 @@ void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_w
 			/* fall through */
 		case TABLETPC:
 			input_dev_tpc(input_dev, wacom_wac);
-			if (wacom_wac->features->device_type ==
-			    BTN_TOOL_DOUBLETAP)
+			if (wacom_wac->features->device_type != BTN_TOOL_PEN)
 				break;  /* no need to process stylus stuff */
 
 			/* fall through */
@@ -988,11 +978,11 @@ static struct wacom_features wacom_features[] = {
 	{ "Wacom Cintiq 21UX",        WACOM_PKGLEN_INTUOS,    87200, 65600, 1023, 63, CINTIQ },
 	{ "Wacom Cintiq 20WSX",       WACOM_PKGLEN_INTUOS,    86680, 54180, 1023, 63, WACOM_BEE },
 	{ "Wacom Cintiq 12WX",        WACOM_PKGLEN_INTUOS,    53020, 33440, 1023, 63, WACOM_BEE },
-	{ "Wacom DTU1931",            WACOM_PKGLEN_GRAPHIRE,  37832, 30305,  511,  0, PL },
-	{ "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_GRAPHIRE,  26202, 16325,  255,  0, TABLETPC },
+	{ "Wacom DTU1931",            WACOM_PKGLEN_PENABLED,  37832, 30305,  511,  0, PL },
+	{ "Wacom ISDv4 90",           WACOM_PKGLEN_PENABLED,  26202, 16325,  255,  0, TABLETPC },
+	{ "Wacom ISDv4 93",           WACOM_PKGLEN_PENABLED,  26202, 16325,  255,  0, TABLETPC },
+	{ "Wacom ISDv4 9A",           WACOM_PKGLEN_PENABLED,  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 },
diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h
index 37e0eb3..39c2516 100644
--- a/drivers/input/tablet/wacom_wac.h
+++ b/drivers/input/tablet/wacom_wac.h
@@ -12,6 +12,15 @@
 /* maximum packet length for USB devices */
 #define WACOM_PKGLEN_MAX	32
 
+/* packet length for individual models */
+#define WACOM_PKGLEN_PENPRTN	 7
+#define WACOM_PKGLEN_GRAPHIRE	 8
+#define WACOM_PKGLEN_BBFUN 	 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
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2009-12-16 22:59 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-12-16 22:58 [PATCH 0/3] wacom: update to match upstream Matthew Garrett
2009-12-16 22:58 ` [PATCH 1/3] wacom: Add support for extra USB tablets Matthew Garrett
2009-12-16 22:58   ` [PATCH 2/3] Wacom: Add support for two-finger touch Matthew Garrett
2009-12-16 22:58     ` [PATCH 3/3] wacom: Improve support for Penabled devices Matthew Garrett

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).