From: Ping Cheng This patch adds support for a Wacom new tablet, Intuos3, and its associated tools. Signed-off-by: Andrew Morton --- 25-akpm/drivers/usb/input/wacom.c | 335 +++++++++++++++++++++++++++++--------- 1 files changed, 262 insertions(+), 73 deletions(-) diff -puN drivers/usb/input/wacom.c~wacom-tablet-driver drivers/usb/input/wacom.c --- 25/drivers/usb/input/wacom.c~wacom-tablet-driver 2004-11-16 23:46:34.092121656 -0800 +++ 25-akpm/drivers/usb/input/wacom.c 2004-11-16 23:46:34.099120592 -0800 @@ -72,7 +72,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.30" +#define DRIVER_VERSION "v1.40" #define DRIVER_AUTHOR "Vojtech Pavlik " #define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver" #define DRIVER_LICENSE "GPL" @@ -141,8 +141,10 @@ static void wacom_pl_irq(struct urb *urb goto exit; } - if (data[0] != 2) + if (data[0] != 2) { dbg("wacom_pl_irq: received unknown report #%d", data[0]); + goto exit; + } prox = data[1] & 0x40; @@ -233,6 +235,7 @@ static void wacom_ptu_irq(struct urb *ur if (data[0] != 2) { printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]); + goto exit; } input_regs(dev, regs); @@ -246,9 +249,9 @@ static void wacom_ptu_irq(struct urb *ur input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x20); input_report_key(dev, BTN_TOUCH, data[1] & 0x01); } - input_report_abs(dev, ABS_X, data[3] << 8 | data[2]); - input_report_abs(dev, ABS_Y, data[5] << 8 | data[4]); - input_report_abs(dev, ABS_PRESSURE, (data[6]|data[7] << 8)); + input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[2])); + input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[4])); + input_report_abs(dev, ABS_PRESSURE, le16_to_cpu(*(__le16 *) &data[6])); input_report_key(dev, BTN_STYLUS, data[1] & 0x02); input_report_key(dev, BTN_STYLUS2, data[1] & 0x10); @@ -283,10 +286,15 @@ static void wacom_penpartner_irq(struct goto exit; } + if (data[0] != 2) { + printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]); + goto exit; + } + input_regs(dev, regs); input_report_key(dev, BTN_TOOL_PEN, 1); - input_report_abs(dev, ABS_X, le16_to_cpu(get_unaligned((__le16 *) &data[1]))); - input_report_abs(dev, ABS_Y, le16_to_cpu(get_unaligned((__le16 *) &data[3]))); + input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[1])); + input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[3])); input_report_abs(dev, ABS_PRESSURE, (signed char)data[6] + 127); input_report_key(dev, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20)); input_report_key(dev, BTN_STYLUS, (data[5] & 0x40)); @@ -322,12 +330,10 @@ static void wacom_graphire_irq(struct ur goto exit; } - /* check if we can handle the data */ - if (data[0] == 99) - goto exit; - - if (data[0] != 2) + if (data[0] != 2) { dbg("wacom_graphire_irq: received unknown report #%d", data[0]); + goto exit; + } x = le16_to_cpu(*(__le16 *) &data[2]); y = le16_to_cpu(*(__le16 *) &data[4]); @@ -381,107 +387,171 @@ exit: __FUNCTION__, retval); } -static void wacom_intuos_irq(struct urb *urb, struct pt_regs *regs) +static int wacom_intuos_inout(struct urb *urb) { struct wacom *wacom = urb->context; unsigned char *data = wacom->data; struct input_dev *dev = &wacom->dev; - unsigned int t; int idx; - int retval; - - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); - goto exit; - } - - if (data[0] != 2) - dbg("wacom_intuos_irq: received unknown report #%d", data[0]); - - input_regs(dev, regs); /* tool number */ idx = data[1] & 0x01; - if ((data[1] & 0xfc) == 0xc0) { /* Enter report */ - - wacom->serial[idx] = ((__u32)(data[3] & 0x0f) << 28) + /* serial number of the tool */ + /* Enter report */ + if ((data[1] & 0xfc) == 0xc0) + { + /* serial number of the tool */ + wacom->serial[idx] = ((__u32)(data[3] & 0x0f) << 28) + ((__u32)data[4] << 20) + ((__u32)data[5] << 12) + ((__u32)data[6] << 4) + (data[7] >> 4); switch (((__u32)data[2] << 4) | (data[3] >> 4)) { - case 0x812: - case 0x012: wacom->tool[idx] = BTN_TOOL_PENCIL; break; /* Inking pen */ - case 0x822: + case 0x812: /* Inking pen */ + case 0x801: /* Intuos3 Inking pen */ + case 0x012: + wacom->tool[idx] = BTN_TOOL_PENCIL; + break; + case 0x822: /* Pen */ case 0x842: case 0x852: - case 0x022: wacom->tool[idx] = BTN_TOOL_PEN; break; /* Pen */ - case 0x832: - case 0x032: wacom->tool[idx] = BTN_TOOL_BRUSH; break; /* Stroke pen */ - case 0x007: + case 0x823: /* Intuos3 Grip Pen */ + case 0x813: /* Intuos3 Classic Pen */ + case 0x885: /* Intuos3 Marker Pen */ + case 0x022: + wacom->tool[idx] = BTN_TOOL_PEN; + break; + case 0x832: /* Stroke pen */ + case 0x032: + wacom->tool[idx] = BTN_TOOL_BRUSH; + break; + case 0x007: /* Mouse 4D and 2D */ case 0x09c: - case 0x094: wacom->tool[idx] = BTN_TOOL_MOUSE; break; /* Mouse 4D and 2D */ - case 0x096: wacom->tool[idx] = BTN_TOOL_LENS; break; /* Lens cursor */ - case 0x82a: + case 0x094: + case 0x017: /* Intuos3 2D Mouse */ + wacom->tool[idx] = BTN_TOOL_MOUSE; + break; + case 0x096: /* Lens cursor */ + case 0x097: /* Intuos3 Lens cursor */ + wacom->tool[idx] = BTN_TOOL_LENS; + break; + case 0x82a: /* Eraser */ case 0x85a: case 0x91a: case 0xd1a: - case 0x0fa: wacom->tool[idx] = BTN_TOOL_RUBBER; break; /* Eraser */ + case 0x0fa: + case 0x82b: /* Intuos3 Grip Pen Eraser */ + case 0x81b: /* Intuos3 Classic Pen Eraser */ + case 0x91b: /* Intuos3 Airbrush Eraser */ + wacom->tool[idx] = BTN_TOOL_RUBBER; + break; case 0xd12: case 0x912: - case 0x112: wacom->tool[idx] = BTN_TOOL_AIRBRUSH; break; /* Airbrush */ - default: wacom->tool[idx] = BTN_TOOL_PEN; break; /* Unknown tool */ + case 0x112: + case 0x913: /* Intuos3 Airbrush */ + wacom->tool[idx] = BTN_TOOL_AIRBRUSH; + break; /* Airbrush */ + default: /* Unknown tool */ + wacom->tool[idx] = BTN_TOOL_PEN; } - input_report_key(dev, wacom->tool[idx], 1); input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]); input_sync(dev); - goto exit; + return 1; } - if ((data[1] & 0xfe) == 0x80) { /* Exit report */ + /* Exit report */ + if ((data[1] & 0xfe) == 0x80) { input_report_key(dev, wacom->tool[idx], 0); input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]); input_sync(dev); - goto exit; + return 1; } - input_report_abs(dev, ABS_X, be16_to_cpu(*(__be16 *) &data[2])); - input_report_abs(dev, ABS_Y, be16_to_cpu(*(__be16 *) &data[4])); - input_report_abs(dev, ABS_DISTANCE, data[9]); + return 0; +} - if ((data[1] & 0xb8) == 0xa0) { /* general pen packet */ - input_report_abs(dev, ABS_PRESSURE, t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3)); - input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7)); +static void wacom_intuos_general(struct urb *urb) +{ + struct wacom *wacom = urb->context; + unsigned char *data = wacom->data; + struct input_dev *dev = &wacom->dev; + unsigned int t; + + /* general pen packet */ + if ((data[1] & 0xb8) == 0xa0) + { + t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3); + input_report_abs(dev, ABS_PRESSURE, t); + input_report_abs(dev, ABS_TILT_X, + ((data[7] << 1) & 0x7e) | (data[8] >> 7)); input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f); input_report_key(dev, BTN_STYLUS, data[1] & 2); input_report_key(dev, BTN_STYLUS2, data[1] & 4); input_report_key(dev, BTN_TOUCH, t > 10); } - if ((data[1] & 0xbc) == 0xb4) { /* airbrush second packet */ - input_report_abs(dev, ABS_WHEEL, ((__u32)data[6] << 2) | ((data[7] >> 6) & 3)); - input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7)); + /* airbrush second packet */ + if ((data[1] & 0xbc) == 0xb4) + { + input_report_abs(dev, ABS_WHEEL, + ((__u32)data[6] << 2) | ((data[7] >> 6) & 3)); + input_report_abs(dev, ABS_TILT_X, + ((data[7] << 1) & 0x7e) | (data[8] >> 7)); input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f); } + return; +} + +static void wacom_intuos_irq(struct urb *urb, struct pt_regs *regs) +{ + struct wacom *wacom = urb->context; + unsigned char *data = wacom->data; + struct input_dev *dev = &wacom->dev; + unsigned int t; + int idx; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + goto exit; + } + + if (data[0] != 2 && data[0] != 5 && data[0] != 6) { + dbg("wacom_intuos_irq: received unknown report #%d", data[0]); + goto exit; + } + + input_regs(dev, regs); + + /* tool number */ + idx = data[1] & 0x01; + + /* process in/out prox events */ + if (wacom_intuos_inout(urb)) goto exit; + + input_report_abs(dev, ABS_X, be16_to_cpu(*(__be16 *) &data[2])); + input_report_abs(dev, ABS_Y, be16_to_cpu(*(__be16 *) &data[4])); + input_report_abs(dev, ABS_DISTANCE, data[9]); + + /* process general packets */ + wacom_intuos_general(urb); if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) { /* 4D mouse or Lens cursor packets */ if (data[1] & 0x02) { /* Rotation packet */ - input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ? - ((__u32)data[6] << 3) | ((data[7] >> 5) & 7): - (-(((__u32)data[6] << 3) | ((data[7] >> 5) & 7))) - 1); + t = ((__u32)data[6] << 3) | ((data[7] >> 5) & 7); + input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ? ((t - 1) / 2) : -t / 2); } else { @@ -493,9 +563,8 @@ static void wacom_intuos_irq(struct urb input_report_key(dev, BTN_SIDE, data[8] & 0x20); input_report_key(dev, BTN_EXTRA, data[8] & 0x10); - input_report_abs(dev, ABS_THROTTLE, -((data[8] & 0x08) ? - ((__u32)data[6] << 2) | ((data[7] >> 6) & 3) : - -((__u32)data[6] << 2) | ((data[7] >> 6) & 3))); + t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3); + input_report_abs(dev, ABS_THROTTLE, (data[8] & 0x08) ? -t : t); } else { if (wacom->tool[idx] == BTN_TOOL_MOUSE) { /* 2D mouse packets */ @@ -527,6 +596,111 @@ exit: __FUNCTION__, retval); } +static void wacom_intuos3_irq(struct urb *urb, struct pt_regs *regs) +{ + struct wacom *wacom = urb->context; + unsigned char *data = wacom->data; + struct input_dev *dev = &wacom->dev; + unsigned int t; + int idx, retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + goto exit; + } + + /* check for valid report */ + if (data[0] != 2 && data[0] != 5 && data[0] != 12) + { + printk(KERN_INFO "wacom_intuos3_irq: received unknown report #%d\n", data[0]); + goto exit; + } + + input_regs(dev, regs); + + /* tool index is always 0 here since there is no dual input tool */ + idx = data[1] & 0x01; + + /* pad packets. Works as a second tool and is always in prox */ + if (data[0] == 12) + { + /* initiate the pad as a device */ + if (wacom->tool[1] != BTN_TOOL_FINGER) + { + wacom->tool[1] = BTN_TOOL_FINGER; + input_report_key(dev, wacom->tool[1], 1); + } + input_report_key(dev, BTN_0, (data[5] & 0x01)); + input_report_key(dev, BTN_1, (data[5] & 0x02)); + input_report_key(dev, BTN_2, (data[5] & 0x04)); + input_report_key(dev, BTN_3, (data[5] & 0x08)); + input_report_key(dev, BTN_4, (data[6] & 0x01)); + input_report_key(dev, BTN_5, (data[6] & 0x02)); + input_report_key(dev, BTN_6, (data[6] & 0x04)); + input_report_key(dev, BTN_7, (data[6] & 0x08)); + input_report_abs(dev, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]); + input_report_abs(dev, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]); + input_event(dev, EV_MSC, MSC_SERIAL, 0xffffffff); + input_sync(dev); + goto exit; + } + + /* process in/out prox events */ + if (wacom_intuos_inout(urb)) goto exit; + + input_report_abs(dev, ABS_X, ((__u32)data[2] << 9) | ((__u32)data[3] << 1) | ((data[9] >> 1) & 1)); + input_report_abs(dev, ABS_Y, ((__u32)data[4] << 9) | ((__u32)data[5] << 1) | (data[9] & 1)); + input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 2) & 0x3f)); + + /* process general packets */ + wacom_intuos_general(urb); + + if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) + { + /* Marker pen rotation packet. Reported as wheel due to valuator limitation */ + if (data[1] & 0x02) + { + t = ((__u32)data[6] << 3) | ((data[7] >> 5) & 7); + t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) : + ((t-1) / 2 + 450)) : (450 - t / 2) ; + input_report_abs(dev, ABS_WHEEL, t); + } + + /* 2D mouse packets */ + if (wacom->tool[idx] == BTN_TOOL_MOUSE) + { + input_report_key(dev, BTN_LEFT, data[8] & 0x04); + input_report_key(dev, BTN_MIDDLE, data[8] & 0x08); + input_report_key(dev, BTN_RIGHT, data[8] & 0x10); + input_report_key(dev, BTN_SIDE, data[8] & 0x40); + input_report_key(dev, BTN_EXTRA, data[8] & 0x20); + /* mouse wheel is positive when rolled backwards */ + input_report_rel(dev, REL_WHEEL, ((__u32)((data[8] & 0x02) >> 1) + - (__u32)(data[8] & 0x01))); + } + } + + input_report_key(dev, wacom->tool[idx], 1); + input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]); + input_sync(dev); + +exit: + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) + err ("%s - usb_submit_urb failed with result %d", + __FUNCTION__, retval); +} + static struct wacom_features wacom_features[] = { { "Wacom Penpartner", 7, 5040, 3780, 255, 32, 0, wacom_penpartner_irq }, { "Wacom Graphire", 8, 10206, 7422, 511, 32, 1, wacom_graphire_irq }, @@ -552,6 +726,9 @@ static struct wacom_features wacom_featu { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, 2, wacom_intuos_irq }, { "Wacom Volito", 8, 5104, 3712, 511, 32, 1, wacom_graphire_irq }, { "Wacom Cintiq Partner",8, 20480, 15360, 511, 32, 3, wacom_ptu_irq }, + { "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 15, 4, wacom_intuos3_irq }, + { "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 15, 4, wacom_intuos3_irq }, + { "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 15, 4, wacom_intuos3_irq }, { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, 2, wacom_intuos_irq }, { } }; @@ -581,6 +758,9 @@ static struct usb_device_id wacom_ids[] { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x45) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) }, { } }; @@ -651,6 +831,12 @@ static int wacom_probe(struct usb_interf wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2); break; + case 4: /* new functions for Intuos3 */ + wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER); + wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7); + wacom->dev.absbit[0] |= BIT(ABS_RX) | BIT(ABS_RY); + /* fall through */ + case 2: wacom->dev.evbit[0] |= BIT(EV_MSC) | BIT(EV_REL); wacom->dev.mscbit[0] |= BIT(MSC_SERIAL); @@ -662,7 +848,7 @@ static int wacom_probe(struct usb_interf break; case 3: - wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2); + wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER); break; } @@ -674,6 +860,8 @@ static int wacom_probe(struct usb_interf wacom->dev.absmax[ABS_TILT_Y] = 127; wacom->dev.absmax[ABS_WHEEL] = 1023; + wacom->dev.absmax[ABS_RX] = 4097; + wacom->dev.absmax[ABS_RY] = 4097; wacom->dev.absmin[ABS_RZ] = -900; wacom->dev.absmax[ABS_RZ] = 899; wacom->dev.absmin[ABS_THROTTLE] = -1023; @@ -712,9 +900,10 @@ static int wacom_probe(struct usb_interf input_register_device(&wacom->dev); + /* ask the tablet to report tablet data */ + usb_set_report(intf, 3, 2, rep_data, 2); + /* repeat once (not sure why the first call often fails) */ usb_set_report(intf, 3, 2, rep_data, 2); - usb_set_report(intf, 3, 5, rep_data, 0); - usb_set_report(intf, 3, 6, rep_data, 0); printk(KERN_INFO "input: %s on %s\n", wacom->features->name, path); _