From 486b908d4412510d66ee348ba765de8d93441345 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Fri, 20 Feb 2015 14:25:58 -0800 Subject: [PATCH 1/5] HID: wacom: do not send pen events before touch is up/forced out If pen comes in proximity while touch is down, we force touch up before sending pen events. Otherwise, there can be unfinished touch events compete with pen events. This idea has been fully implemented for Tablet PCs. But other tablets that support both pen and touch are not fully considered. Signed-off-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 046351cf17f3..69827c928e50 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -554,6 +554,9 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) if (features->quirks & WACOM_QUIRK_MULTI_INPUT) wacom->shared->stylus_in_proximity = true; + if (wacom->shared->touch_down) + return 1; + /* in Range while exiting */ if (((data[1] & 0xfe) == 0x20) && wacom->reporting_data) { input_report_key(input, BTN_TOUCH, 0); @@ -1759,6 +1762,9 @@ static int wacom_bpt_pen(struct wacom_wac *wacom) return 0; } + if (wacom->shared->touch_down) + return 0; + prox = (data[1] & 0x20) == 0x20; /* From e0d41fd435ad71b86380f27195aa117400439f37 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Fri, 20 Feb 2015 14:27:30 -0800 Subject: [PATCH 2/5] HID: wacom: rely on actual touch down count to decide touch_down touch_down is a flag to indicate if there are touches on tablet or not. Since one set of touch events may be posted over more than one data packet/touch frame, and pen may come in proximity while touch events are partially sent, counting all touch events for the set reflects the actual status of touch_down. Signed-off-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 75 +++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 69827c928e50..cf767419cdc4 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1046,27 +1046,28 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom) struct input_dev *input = wacom->input; unsigned char *data = wacom->data; int i; - int current_num_contacts = 0; + int current_num_contacts = data[61]; int contacts_to_send = 0; int num_contacts_left = 4; /* maximum contacts per packet */ int byte_per_packet = WACOM_BYTES_PER_24HDT_PACKET; int y_offset = 2; + static int contact_with_no_pen_down_count = 0; if (wacom->features.type == WACOM_27QHDT) { current_num_contacts = data[63]; num_contacts_left = 10; byte_per_packet = WACOM_BYTES_PER_QHDTHID_PACKET; y_offset = 0; - } else { - current_num_contacts = data[61]; } /* * First packet resets the counter since only the first * packet in series will have non-zero current_num_contacts. */ - if (current_num_contacts) + if (current_num_contacts) { wacom->num_contacts_left = current_num_contacts; + contact_with_no_pen_down_count = 0; + } contacts_to_send = min(num_contacts_left, wacom->num_contacts_left); @@ -1099,15 +1100,16 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom) input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h)); input_report_abs(input, ABS_MT_ORIENTATION, w > h); } + contact_with_no_pen_down_count++; } } input_mt_report_pointer_emulation(input, true); wacom->num_contacts_left -= contacts_to_send; - if (wacom->num_contacts_left <= 0) + if (wacom->num_contacts_left <= 0) { wacom->num_contacts_left = 0; - - wacom->shared->touch_down = (wacom->num_contacts_left > 0); + wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); + } return 1; } @@ -1119,6 +1121,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom) int current_num_contacts = data[2]; int contacts_to_send = 0; int x_offset = 0; + static int contact_with_no_pen_down_count = 0; /* MTTPC does not support Height and Width */ if (wacom->features.type == MTTPC || wacom->features.type == MTTPC_B) @@ -1128,8 +1131,10 @@ static int wacom_mt_touch(struct wacom_wac *wacom) * First packet resets the counter since only the first * packet in series will have non-zero current_num_contacts. */ - if (current_num_contacts) + if (current_num_contacts) { wacom->num_contacts_left = current_num_contacts; + contact_with_no_pen_down_count = 0; + } /* There are at most 5 contacts per packet */ contacts_to_send = min(5, wacom->num_contacts_left); @@ -1150,15 +1155,16 @@ static int wacom_mt_touch(struct wacom_wac *wacom) int y = get_unaligned_le16(&data[offset + x_offset + 9]); input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); + contact_with_no_pen_down_count++; } } input_mt_report_pointer_emulation(input, true); wacom->num_contacts_left -= contacts_to_send; - if (wacom->num_contacts_left < 0) + if (wacom->num_contacts_left <= 0) { wacom->num_contacts_left = 0; - - wacom->shared->touch_down = (wacom->num_contacts_left > 0); + wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); + } return 1; } @@ -1196,29 +1202,25 @@ static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len) { unsigned char *data = wacom->data; struct input_dev *input = wacom->input; - bool prox; + bool prox = !wacom->shared->stylus_in_proximity; int x = 0, y = 0; if (wacom->features.touch_max > 1 || len > WACOM_PKGLEN_TPC2FG) return 0; - if (!wacom->shared->stylus_in_proximity) { - if (len == WACOM_PKGLEN_TPC1FG) { - prox = data[0] & 0x01; - x = get_unaligned_le16(&data[1]); - y = get_unaligned_le16(&data[3]); - } else if (len == WACOM_PKGLEN_TPC1FG_B) { - prox = data[2] & 0x01; - x = get_unaligned_le16(&data[3]); - y = get_unaligned_le16(&data[5]); - } else { - prox = data[1] & 0x01; - x = le16_to_cpup((__le16 *)&data[2]); - y = le16_to_cpup((__le16 *)&data[4]); - } - } else - /* force touch out when pen is in prox */ - prox = 0; + if (len == WACOM_PKGLEN_TPC1FG) { + prox = prox && (data[0] & 0x01); + x = get_unaligned_le16(&data[1]); + y = get_unaligned_le16(&data[3]); + } else if (len == WACOM_PKGLEN_TPC1FG_B) { + prox = prox && (data[2] & 0x01); + x = get_unaligned_le16(&data[3]); + y = get_unaligned_le16(&data[5]); + } else { + prox = prox && (data[1] & 0x01); + x = le16_to_cpup((__le16 *)&data[2]); + y = le16_to_cpup((__le16 *)&data[4]); + } if (prox) { input_report_abs(input, ABS_X, x); @@ -1616,6 +1618,7 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) struct input_dev *pad_input = wacom->pad_input; unsigned char *data = wacom->data; int i; + int contact_with_no_pen_down_count = 0; if (data[0] != 0x02) return 0; @@ -1643,6 +1646,7 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) } input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); + contact_with_no_pen_down_count++; } } @@ -1652,11 +1656,12 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) input_report_key(pad_input, BTN_FORWARD, (data[1] & 0x04) != 0); input_report_key(pad_input, BTN_BACK, (data[1] & 0x02) != 0); input_report_key(pad_input, BTN_RIGHT, (data[1] & 0x01) != 0); + wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); return 1; } -static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) +static int wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data, int last_touch_count) { struct wacom_features *features = &wacom->features; struct input_dev *input = wacom->input; @@ -1664,7 +1669,7 @@ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) int slot = input_mt_get_slot_by_key(input, data[0]); if (slot < 0) - return; + return 0; touch = touch && !wacom->shared->stylus_in_proximity; @@ -1696,7 +1701,9 @@ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) input_report_abs(input, ABS_MT_POSITION_Y, y); input_report_abs(input, ABS_MT_TOUCH_MAJOR, width); input_report_abs(input, ABS_MT_TOUCH_MINOR, height); + last_touch_count++; } + return last_touch_count; } static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data) @@ -1721,6 +1728,7 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom) unsigned char *data = wacom->data; int count = data[1] & 0x07; int i; + int contact_with_no_pen_down_count = 0; if (data[0] != 0x02) return 0; @@ -1731,12 +1739,15 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom) int msg_id = data[offset]; if (msg_id >= 2 && msg_id <= 17) - wacom_bpt3_touch_msg(wacom, data + offset); + contact_with_no_pen_down_count = + wacom_bpt3_touch_msg(wacom, data + offset, + contact_with_no_pen_down_count); else if (msg_id == 128) wacom_bpt3_button_msg(wacom, data + offset); } input_mt_report_pointer_emulation(input, true); + wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); return 1; } From a415457733b5fa40bc996bf1f4df471cd98d3608 Mon Sep 17 00:00:00 2001 From: "oliver@neukum.org" Date: Tue, 10 Mar 2015 16:36:22 +0100 Subject: [PATCH 3/5] HID: add ALWAYS_POLL quirk for a Logitech 0xc007 This device disconnects every 60s without X Signed-off-by: Oliver Neukum Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/usbhid/hid-quirks.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 204312bfab2c..8a7c347e8080 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -586,6 +586,7 @@ #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e #define USB_DEVICE_ID_LOGITECH_T651 0xb00c +#define USB_DEVICE_ID_LOGITECH_C077 0xc007 #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 9be99a67bfe2..a82127753461 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -78,6 +78,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C077, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP, HID_QUIRK_NO_INIT_REPORTS }, From 9b028649b9d0ae72090904629dad06b022f4ddc7 Mon Sep 17 00:00:00 2001 From: Forest Wilkinson Date: Thu, 12 Mar 2015 23:58:16 -0700 Subject: [PATCH 4/5] HID: tivo: enable all buttons on the TiVo Slide Pro remote The linux kernel has supported the TiVo Slide remote control for some time, but does not recognize the USB ID of the newer Slide Pro. This patch adds the missing data structures so the newer remote will be recognized by the driver, thereby allowing the TiVo, LiveTV, and Thumbs Up/Down buttons to be mapped with a hwdb file. Signed-off-by: Forest Wilkinson Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-tivo.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 7c669c328c4c..56ce8c2b5530 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1959,6 +1959,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb65a) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_BT) }, { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_PRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) }, { HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 8a7c347e8080..9c4786759f16 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -899,6 +899,7 @@ #define USB_VENDOR_ID_TIVO 0x150a #define USB_DEVICE_ID_TIVO_SLIDE_BT 0x1200 #define USB_DEVICE_ID_TIVO_SLIDE 0x1201 +#define USB_DEVICE_ID_TIVO_SLIDE_PRO 0x1203 #define USB_VENDOR_ID_TOPSEED 0x0766 #define USB_DEVICE_ID_TOPSEED_CYBERLINK 0x0204 diff --git a/drivers/hid/hid-tivo.c b/drivers/hid/hid-tivo.c index d790d8d71f7f..d98696927453 100644 --- a/drivers/hid/hid-tivo.c +++ b/drivers/hid/hid-tivo.c @@ -64,6 +64,7 @@ static const struct hid_device_id tivo_devices[] = { /* TiVo Slide Bluetooth remote, pairs with a Broadcom dongle */ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_BT) }, { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_PRO) }, { } }; MODULE_DEVICE_TABLE(hid, tivo_devices); From e2c7d8877e5caa2356b5bc8207535e83b126f653 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 5 Mar 2015 17:36:35 -0500 Subject: [PATCH 5/5] HID: wacom: check for wacom->shared before following the pointer 486b908 (HID: wacom: do not send pen events before touch is up/forced out) introduces a kernel oops when plugging a tablet without touch. wacom->shared is null for these devices so this leads to a null pointer exception. Change the condition to make it clear that what we need is wacom->shared not NULL. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index cf767419cdc4..bbe32d66e500 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -551,11 +551,12 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) (features->type == CINTIQ && !(data[1] & 0x40))) return 1; - if (features->quirks & WACOM_QUIRK_MULTI_INPUT) + if (wacom->shared) { wacom->shared->stylus_in_proximity = true; - if (wacom->shared->touch_down) - return 1; + if (wacom->shared->touch_down) + return 1; + } /* in Range while exiting */ if (((data[1] & 0xfe) == 0x20) && wacom->reporting_data) {