From ec1f8d719fb028501f50e554ad7bf5279dd36252 Mon Sep 17 00:00:00 2001 From: Wei-Han Chen Date: Thu, 4 Oct 2018 17:24:01 +0800 Subject: touchpad_st: update touchpad hung detection logic When idle count does not change, reset the touchpad if there are no fingers on touchpad. BRANCH=nocturne BUG=b:113315759 TEST=tested on whiskers Signed-off-by: Wei-Han Chen Change-Id: I8336bac2b31963e4c2cb671ced8502e6b176424d Reviewed-on: https://chromium-review.googlesource.com/1261615 Commit-Ready: ChromeOS CL Exonerator Bot Reviewed-by: Nicolas Boichat --- driver/touchpad_st.c | 49 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 8 deletions(-) (limited to 'driver/touchpad_st.c') diff --git a/driver/touchpad_st.c b/driver/touchpad_st.c index 01920b3778..c06e825063 100644 --- a/driver/touchpad_st.c +++ b/driver/touchpad_st.c @@ -79,6 +79,11 @@ static int tp_reset_retry_count; static int dump_memory_on_error; +/* + * Bitmap to track if a finger exists. + */ +static int touch_slot; + /* * Timestamp of last interrupt (32 bits are enough as we divide the value by 100 * and then put it in a 16-bit field). @@ -176,14 +181,23 @@ static int st_tp_parse_finger(struct usb_hid_touchpad_report *report, struct st_tp_event_t *event, int i) { - /* We cannot report more fingers */ - if (i >= ARRAY_SIZE(report->finger)) - return i; + const int id = event->finger.touch_id; /* This is not a finger */ if (event->finger.touch_type == ST_TP_TOUCH_TYPE_INVALID) return i; + if (event->evt_id == ST_TP_EVENT_ID_ENTER_POINTER) + touch_slot |= 1 << id; + else if (event->evt_id == ST_TP_EVENT_ID_LEAVE_POINTER) + touch_slot &= ~(1 << id); + + /* We cannot report more fingers */ + if (i >= ARRAY_SIZE(report->finger)) { + CPRINTS("WARN: ST reports more than %d fingers", i); + return i; + } + switch (event->evt_id) { case ST_TP_EVENT_ID_ENTER_POINTER: case ST_TP_EVENT_ID_MOTION_POINTER: @@ -191,7 +205,7 @@ static int st_tp_parse_finger(struct usb_hid_touchpad_report *report, report->finger[i].confidence = (event->finger.z < 255); report->finger[i].tip = 1; report->finger[i].inrange = 1; - report->finger[i].id = event->finger.touch_id; + report->finger[i].id = id; report->finger[i].pressure = event->finger.z; report->finger[i].width = (event->finger.minor | (event->minor_high << 4)) << 5; @@ -204,7 +218,7 @@ static int st_tp_parse_finger(struct usb_hid_touchpad_report *report, event->finger.y); break; case ST_TP_EVENT_ID_LEAVE_POINTER: - report->finger[i].id = event->finger.touch_id; + report->finger[i].id = id; /* When a finger is leaving, it's not a palm */ report->finger[i].confidence = 1; break; @@ -672,6 +686,7 @@ static int st_tp_handle_error_report(struct st_tp_event_t *e, static void st_tp_handle_status_report(struct st_tp_event_t *e) { + static uint32_t prev_idle_count; uint32_t info = ((e->report.info[0] << 0) | (e->report.info[1] << 8) | (e->report.info[2] << 16) | @@ -684,11 +699,29 @@ static void st_tp_handle_status_report(struct st_tp_event_t *e) /* * Idle count might not change if ST FW is busy (for example, when the - * user puts a big palm on touchpad). Therefore we don't check if idle - * count is changed. + * user puts a big palm on touchpad). Therefore if idle count doesn't + * change, we need to double check with touch count. + * + * If touch count is 0, and idle count doesn't change, it means that: + * + * 1) ST doesn't think there are any fingers. + * 2) ST is busy on something, can't get into idle mode, and this + * might cause (1). + * + * Resetting touchpad should be the correct action. */ - if (e->report.report_type == ST_TP_STATUS_BEACON) + if (e->report.report_type == ST_TP_STATUS_BEACON) { + const uint8_t touch_count = e->report.reserved; + CPRINTS("BEACON: idle count=%08x", info); + CPRINTS(" touch count=%d touch slot=%04x", + touch_count, touch_slot); + if (prev_idle_count == info && touch_slot == 0) { + tp_control |= TP_CONTROL_SHALL_RESET; + return; + } + prev_idle_count = info; + } } /* -- cgit v1.2.1