diff options
Diffstat (limited to 'drivers/input/tablet/wacom_sys.c')
-rw-r--r-- | drivers/input/tablet/wacom_sys.c | 257 |
1 files changed, 189 insertions, 68 deletions
diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 0d269212931e..8b31473a81fe 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -28,6 +28,7 @@ #define HID_USAGE_Y_TILT 0x3e #define HID_USAGE_FINGER 0x22 #define HID_USAGE_STYLUS 0x20 +#define HID_USAGE_CONTACTMAX 0x55 #define HID_COLLECTION 0xa1 #define HID_COLLECTION_LOGICAL 0x02 #define HID_COLLECTION_END 0xc0 @@ -99,6 +100,7 @@ static int wacom_set_report(struct usb_interface *intf, u8 type, u8 id, static void wacom_sys_irq(struct urb *urb) { struct wacom *wacom = urb->context; + struct device *dev = &wacom->intf->dev; int retval; switch (urb->status) { @@ -109,10 +111,12 @@ static void wacom_sys_irq(struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __func__, urb->status); + dev_dbg(dev, "%s - urb shutting down with status: %d\n", + __func__, urb->status); return; default: - dbg("%s - nonzero urb status received: %d", __func__, urb->status); + dev_dbg(dev, "%s - nonzero urb status received: %d\n", + __func__, urb->status); goto exit; } @@ -122,8 +126,8 @@ static void wacom_sys_irq(struct urb *urb) usb_mark_last_busy(wacom->usbdev); retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) - err ("%s - usb_submit_urb failed with result %d", - __func__, retval); + dev_err(dev, "%s - usb_submit_urb failed with result %d\n", + __func__, retval); } static int wacom_open(struct input_dev *dev) @@ -201,6 +205,27 @@ static int wacom_parse_logical_collection(unsigned char *report, return length; } +static void wacom_retrieve_report_data(struct usb_interface *intf, + struct wacom_features *features) +{ + int result = 0; + unsigned char *rep_data; + + rep_data = kmalloc(2, GFP_KERNEL); + if (rep_data) { + + rep_data[0] = 12; + result = wacom_get_report(intf, WAC_HID_FEATURE_REPORT, + rep_data[0], rep_data, 2, + WAC_MSG_RETRIES); + + if (result >= 0 && rep_data[1] > 2) + features->touch_max = rep_data[1]; + + kfree(rep_data); + } +} + /* * Interface Descriptor of wacom devices can be incomplete and * inconsistent so wacom_features table is used to store stylus @@ -233,6 +258,9 @@ static int wacom_parse_logical_collection(unsigned char *report, * 3rd gen Bamboo Touch no longer define a Digitizer-Finger Pysical * Collection. Instead they define a Logical Collection with a single * Logical Maximum for both X and Y. + * + * Intuos5 touch interface does not contain useful data. We deal with + * this after returning from this function. */ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc, @@ -292,6 +320,10 @@ static int wacom_parse_hid(struct usb_interface *intf, /* need to reset back */ features->pktlen = WACOM_PKGLEN_TPC2FG; } + + if (features->type == MTSCREEN) + features->pktlen = WACOM_PKGLEN_MTOUCH; + if (features->type == BAMBOO_PT) { /* need to reset back */ features->pktlen = WACOM_PKGLEN_BBTOUCH; @@ -324,18 +356,15 @@ static int wacom_parse_hid(struct usb_interface *intf, case HID_USAGE_Y: if (usage == WCM_DESKTOP) { if (finger) { - features->device_type = BTN_TOOL_FINGER; - if (features->type == TABLETPC2FG) { - /* need to reset back */ - features->pktlen = WACOM_PKGLEN_TPC2FG; + int type = features->type; + + if (type == TABLETPC2FG || type == MTSCREEN) { features->y_max = get_unaligned_le16(&report[i + 3]); features->y_phy = get_unaligned_le16(&report[i + 6]); i += 7; - } else if (features->type == BAMBOO_PT) { - /* need to reset back */ - features->pktlen = WACOM_PKGLEN_BBTOUCH; + } else if (type == BAMBOO_PT) { features->y_phy = get_unaligned_le16(&report[i + 3]); features->y_max = @@ -349,10 +378,6 @@ static int wacom_parse_hid(struct usb_interface *intf, i += 4; } } else if (pen) { - /* penabled only accepts exact bytes of data */ - if (features->type == TABLETPC2FG) - features->pktlen = WACOM_PKGLEN_GRAPHIRE; - features->device_type = BTN_TOOL_PEN; features->y_max = get_unaligned_le16(&report[i + 3]); i += 4; @@ -374,6 +399,13 @@ static int wacom_parse_hid(struct usb_interface *intf, pen = 1; i++; break; + + case HID_USAGE_CONTACTMAX: + /* leave touch_max as is if predefined */ + if (!features->touch_max) + wacom_retrieve_report_data(intf, features); + i++; + break; } break; @@ -410,22 +442,29 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat if (!rep_data) return error; - /* ask to report tablet data if it is MT Tablet PC or - * not a Tablet PC */ - if (features->type == TABLETPC2FG) { - do { - rep_data[0] = 3; - rep_data[1] = 4; - rep_data[2] = 0; - rep_data[3] = 0; - report_id = 3; - error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT, - report_id, rep_data, 4, 1); - if (error >= 0) - error = wacom_get_report(intf, - WAC_HID_FEATURE_REPORT, - report_id, rep_data, 4, 1); - } while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES); + /* ask to report Wacom data */ + if (features->device_type == BTN_TOOL_FINGER) { + /* if it is an MT Tablet PC touch */ + if (features->type == TABLETPC2FG || + features->type == MTSCREEN) { + do { + rep_data[0] = 3; + rep_data[1] = 4; + rep_data[2] = 0; + rep_data[3] = 0; + report_id = 3; + error = wacom_set_report(intf, + WAC_HID_FEATURE_REPORT, + report_id, + rep_data, 4, 1); + if (error >= 0) + error = wacom_get_report(intf, + WAC_HID_FEATURE_REPORT, + report_id, + rep_data, 4, 1); + } while ((error < 0 || rep_data[1] != 4) && + limit++ < WAC_MSG_RETRIES); + } } else if (features->type != TABLETPC && features->type != WIRELESS && features->device_type == BTN_TOOL_PEN) { @@ -447,7 +486,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat } static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, - struct wacom_features *features) + struct wacom_features *features) { int error = 0; struct usb_host_interface *interface = intf->cur_altsetting; @@ -475,16 +514,21 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, } } - /* only Tablet PCs and Bamboo P&T need to retrieve the info */ - if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) && - (features->type != BAMBOO_PT)) + /* only devices that support touch need to retrieve the info */ + if (features->type != TABLETPC && + features->type != TABLETPC2FG && + features->type != BAMBOO_PT && + features->type != MTSCREEN) { goto out; + } - if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) { - if (usb_get_extra_descriptor(&interface->endpoint[0], - HID_DEVICET_REPORT, &hid_desc)) { - printk("wacom: can not retrieve extra class descriptor\n"); - error = 1; + error = usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc); + if (error) { + error = usb_get_extra_descriptor(&interface->endpoint[0], + HID_DEVICET_REPORT, &hid_desc); + if (error) { + dev_err(&intf->dev, + "can not retrieve extra class descriptor\n"); goto out; } } @@ -574,23 +618,39 @@ static void wacom_remove_shared_data(struct wacom_wac *wacom) static int wacom_led_control(struct wacom *wacom) { unsigned char *buf; - int retval, led = 0; + int retval; buf = kzalloc(9, GFP_KERNEL); if (!buf) return -ENOMEM; - if (wacom->wacom_wac.features.type == WACOM_21UX2 || - wacom->wacom_wac.features.type == WACOM_24HD) - led = (wacom->led.select[1] << 4) | 0x40; - - led |= wacom->led.select[0] | 0x4; - - buf[0] = WAC_CMD_LED_CONTROL; - buf[1] = led; - buf[2] = wacom->led.llv; - buf[3] = wacom->led.hlv; - buf[4] = wacom->led.img_lum; + if (wacom->wacom_wac.features.type >= INTUOS5S && + wacom->wacom_wac.features.type <= INTUOS5L) { + /* + * Touch Ring and crop mark LED luminance may take on + * one of four values: + * 0 = Low; 1 = Medium; 2 = High; 3 = Off + */ + int ring_led = wacom->led.select[0] & 0x03; + int ring_lum = (((wacom->led.llv & 0x60) >> 5) - 1) & 0x03; + int crop_lum = 0; + + buf[0] = WAC_CMD_LED_CONTROL; + buf[1] = (crop_lum << 4) | (ring_lum << 2) | (ring_led); + } + else { + int led = wacom->led.select[0] | 0x4; + + if (wacom->wacom_wac.features.type == WACOM_21UX2 || + wacom->wacom_wac.features.type == WACOM_24HD) + led |= (wacom->led.select[1] << 4) | 0x40; + + buf[0] = WAC_CMD_LED_CONTROL; + buf[1] = led; + buf[2] = wacom->led.llv; + buf[3] = wacom->led.hlv; + buf[4] = wacom->led.img_lum; + } retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_LED_CONTROL, buf, 9, WAC_CMD_RETRIES); @@ -783,6 +843,17 @@ static struct attribute_group intuos4_led_attr_group = { .attrs = intuos4_led_attrs, }; +static struct attribute *intuos5_led_attrs[] = { + &dev_attr_status0_luminance.attr, + &dev_attr_status_led0_select.attr, + NULL +}; + +static struct attribute_group intuos5_led_attr_group = { + .name = "wacom_led", + .attrs = intuos5_led_attrs, +}; + static int wacom_initialize_leds(struct wacom *wacom) { int error; @@ -812,6 +883,19 @@ static int wacom_initialize_leds(struct wacom *wacom) &cintiq_led_attr_group); break; + case INTUOS5S: + case INTUOS5: + case INTUOS5L: + wacom->led.select[0] = 0; + wacom->led.select[1] = 0; + wacom->led.llv = 32; + wacom->led.hlv = 0; + wacom->led.img_lum = 0; + + error = sysfs_create_group(&wacom->intf->dev.kobj, + &intuos5_led_attr_group); + break; + default: return 0; } @@ -840,6 +924,13 @@ static void wacom_destroy_leds(struct wacom *wacom) sysfs_remove_group(&wacom->intf->dev.kobj, &cintiq_led_attr_group); break; + + case INTUOS5S: + case INTUOS5: + case INTUOS5L: + sysfs_remove_group(&wacom->intf->dev.kobj, + &intuos5_led_attr_group); + break; } } @@ -901,8 +992,10 @@ static int wacom_register_input(struct wacom *wacom) int error; input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; + if (!input_dev) { + error = -ENOMEM; + goto fail1; + } input_dev->name = wacom_wac->name; input_dev->dev.parent = &intf->dev; @@ -912,14 +1005,20 @@ static int wacom_register_input(struct wacom *wacom) input_set_drvdata(input_dev, wacom); wacom_wac->input = input_dev; - wacom_setup_input_capabilities(input_dev, wacom_wac); + error = wacom_setup_input_capabilities(input_dev, wacom_wac); + if (error) + goto fail1; error = input_register_device(input_dev); - if (error) { - input_free_device(input_dev); - wacom_wac->input = NULL; - } + if (error) + goto fail2; + return 0; + +fail2: + input_free_device(input_dev); + wacom_wac->input = NULL; +fail1: return error; } @@ -938,22 +1037,22 @@ static void wacom_wireless_work(struct work_struct *work) wacom = usb_get_intfdata(usbdev->config->interface[1]); if (wacom->wacom_wac.input) input_unregister_device(wacom->wacom_wac.input); - wacom->wacom_wac.input = 0; + wacom->wacom_wac.input = NULL; /* Touch interface */ wacom = usb_get_intfdata(usbdev->config->interface[2]); if (wacom->wacom_wac.input) input_unregister_device(wacom->wacom_wac.input); - wacom->wacom_wac.input = 0; + wacom->wacom_wac.input = NULL; if (wacom_wac->pid == 0) { - printk(KERN_INFO "wacom: wireless tablet disconnected\n"); + dev_info(&wacom->intf->dev, "wireless tablet disconnected\n"); } else { const struct usb_device_id *id = wacom_ids; - printk(KERN_INFO - "wacom: wireless tablet connected with PID %x\n", - wacom_wac->pid); + dev_info(&wacom->intf->dev, + "wireless tablet connected with PID %x\n", + wacom_wac->pid); while (id->match_flags) { if (id->idVendor == USB_VENDOR_ID_WACOM && @@ -963,8 +1062,8 @@ static void wacom_wireless_work(struct work_struct *work) } if (!id->match_flags) { - printk(KERN_INFO - "wacom: ignorning unknown PID.\n"); + dev_info(&wacom->intf->dev, + "ignoring unknown PID.\n"); return; } @@ -1035,11 +1134,33 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i endpoint = &intf->cur_altsetting->endpoint[0].desc; - /* Retrieve the physical and logical size for OEM devices */ + /* Retrieve the physical and logical size for touch devices */ error = wacom_retrieve_hid_descriptor(intf, features); if (error) goto fail3; + /* + * Intuos5 has no useful data about its touch interface in its + * HID descriptor. If this is the touch interface (wMaxPacketSize + * of WACOM_PKGLEN_BBTOUCH3), override the table values. + */ + if (features->type >= INTUOS5S && features->type <= INTUOS5L) { + if (endpoint->wMaxPacketSize == WACOM_PKGLEN_BBTOUCH3) { + features->device_type = BTN_TOOL_FINGER; + features->pktlen = WACOM_PKGLEN_BBTOUCH3; + + features->x_phy = + (features->x_max * 100) / features->x_resolution; + features->y_phy = + (features->y_max * 100) / features->y_resolution; + + features->x_max = 4096; + features->y_max = 4096; + } else { + features->device_type = BTN_TOOL_PEN; + } + } + wacom_setup_device_quirks(features); strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name)); |