diff options
author | Nicolas Boichat <drinkcat@chromium.org> | 2017-08-10 14:24:40 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2017-08-16 06:03:49 -0700 |
commit | 58374f7d266e7bbfaf52b33f497c408a718cf81e (patch) | |
tree | 53f1dfa72773793f289df8838b8fa803e979b320 | |
parent | fb58920c9e6c301d2e1931e3c783230af992c647 (diff) | |
download | chrome-ec-58374f7d266e7bbfaf52b33f497c408a718cf81e.tar.gz |
usb_hid_touchpad: Add touch event to FIFO during suspend
Similarly to what we have done with keyboard events, we put touch
events in a FIFO. The AP will need to interpret the timestamp
in the events to be able to process the events correctly tough.
Resume should typically take about 50ms, so a 8-event long FIFO
should be good enough. Also, we bypass the FIFO altogether in most
cases, when the USB interface is not suspended.
BRANCH=none
BUG=b:35775048
TEST=Connect hammer, force autosuspend using:
DEVICE=$(dirname $(grep 5022 /sys/bus/usb/devices/*/idProduct))
echo 500 > $DEVICE/power/autosuspend_delay_ms
echo auto > $DEVICE/power/control
Look at evtest output.
Wait a second, make a swipe, see that events are received in
a very short amount of time after resume (every EP interval/2ms),
but the event timestamps show that some of them are older.
Change-Id: If6ab56396f7d564b19e6c3c528847196ffa4d849
Signed-off-by: Nicolas Boichat <drinkcat@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/612221
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r-- | chip/stm32/usb_hid_touchpad.c | 145 |
1 files changed, 128 insertions, 17 deletions
diff --git a/chip/stm32/usb_hid_touchpad.c b/chip/stm32/usb_hid_touchpad.c index a16988f03c..f6624c24c7 100644 --- a/chip/stm32/usb_hid_touchpad.c +++ b/chip/stm32/usb_hid_touchpad.c @@ -9,7 +9,9 @@ #include "console.h" #include "gpio.h" #include "hooks.h" +#include "hwtimer.h" #include "link_defs.h" +#include "queue.h" #include "registers.h" #include "task.h" #include "timer.h" @@ -23,6 +25,13 @@ /* Console output macro */ #define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) +#define CPRINTS(format, args...) cprints(CC_USB, format, ## args) + +static const int touchpad_debug; + +static struct queue const report_queue = QUEUE_NULL(8, + struct usb_hid_touchpad_report); +static struct mutex report_queue_mutex; #define HID_TOUCHPAD_REPORT_SIZE sizeof(struct usb_hid_touchpad_report) @@ -32,6 +41,9 @@ */ #define HID_TOUCHPAD_EP_INTERVAL_MS 2 /* ms */ +/* Discard TP events older than this time */ +#define EVENT_DISCARD_MAX_TIME (1 * SECOND) + /* HID descriptors */ const struct usb_interface_descriptor USB_IFACE_DESC(USB_IFACE_HID_TOUCHPAD) = { .bLength = USB_DT_INTERFACE_SIZE, @@ -167,35 +179,131 @@ const struct usb_hid_descriptor USB_CUSTOM_DESC_VAR(USB_IFACE_HID_TOUCHPAD, static usb_uint hid_ep_buf[DIV_ROUND_UP(HID_TOUCHPAD_REPORT_SIZE, 2)] __usb_ram; -void set_touchpad_report(struct usb_hid_touchpad_report *report) +/* + * Write a report to EP, must be called with queue mutex held, and caller + * must first check that EP is not busy. + */ +static void write_touchpad_report(struct usb_hid_touchpad_report *report) { - /* - * Endpoint is busy. This should rarely happen as we make sure that - * the trackpad interrupt period >= USB interrupt period. - * - * TODO(crosbug.com/p/59083): Figure out how to handle USB suspend. - */ - int timeout = 20; /* Wait up to 5 EP intervals. */ - - while ((STM32_USB_EP(USB_EP_HID_TOUCHPAD) & EP_TX_MASK) - == EP_TX_VALID) { - msleep(DIV_ROUND_UP(HID_TOUCHPAD_EP_INTERVAL_MS, 4)); - if (!--timeout) - return; - } - memcpy_to_usbram((void *) usb_sram_addr(hid_ep_buf), report, sizeof(*report)); /* enable TX */ STM32_TOGGLE_EP(USB_EP_HID_TOUCHPAD, EP_TX_MASK, EP_TX_VALID, 0); - /* Wake up host, if required. */ + /* + * Wake the host. This is required to prevent a race between EP getting + * reloaded and host suspending the device, as, ideally, we never want + * to have EP loaded during suspend, to avoid reporting stale data. + */ usb_wake(); } +static void hid_touchpad_process_queue(void); +DECLARE_DEFERRED(hid_touchpad_process_queue); + +static void hid_touchpad_process_queue(void) +{ + struct usb_hid_touchpad_report report; + uint16_t now; + int trimming = 0; + + mutex_lock(&report_queue_mutex); + + /* EP is busy, or nothing in queue: do nothing. */ + if (queue_count(&report_queue) == 0) + goto unlock; + + now = __hw_clock_source_read() / USB_HID_TOUCHPAD_TIMESTAMP_UNIT; + + if (usb_is_suspended() || + (STM32_USB_EP(USB_EP_HID_TOUCHPAD) & EP_TX_MASK) + == EP_TX_VALID) { + usb_wake(); + + /* Let's trim old events from the queue, if any. */ + trimming = 1; + } else { + hook_call_deferred(&hid_touchpad_process_queue_data, -1); + } + + if (touchpad_debug) + CPRINTS("TPQ t=%d (%d)", trimming, queue_count(&report_queue)); + + while (queue_count(&report_queue) > 0) { + int delta; + + queue_peek_units(&report_queue, &report, 0, 1); + + delta = (int)((uint16_t)(now - report.timestamp)) + * USB_HID_TOUCHPAD_TIMESTAMP_UNIT; + + if (touchpad_debug) + CPRINTS("evt t=%d d=%d", report.timestamp, delta); + + /* Drop old events */ + if (delta > EVENT_DISCARD_MAX_TIME) { + queue_advance_head(&report_queue, 1); + continue; + } + + if (trimming) { + /* + * If we stil fail to resume, this will discard the + * event after the timeout expires. + */ + hook_call_deferred(&hid_touchpad_process_queue_data, + EVENT_DISCARD_MAX_TIME - delta); + } else { + queue_advance_head(&report_queue, 1); + write_touchpad_report(&report); + } + break; + } + +unlock: + mutex_unlock(&report_queue_mutex); +} + +void set_touchpad_report(struct usb_hid_touchpad_report *report) +{ + static int print_full = 1; + + mutex_lock(&report_queue_mutex); + + /* USB/EP ready and nothing in queue, just write the report. */ + if (!usb_is_suspended() && + (STM32_USB_EP(USB_EP_HID_TOUCHPAD) & EP_TX_MASK) != EP_TX_VALID + && queue_count(&report_queue) == 0) { + write_touchpad_report(report); + mutex_unlock(&report_queue_mutex); + return; + } + + /* Else add to queue, dropping oldest event if needed. */ + if (touchpad_debug) + CPRINTS("sTP t=%d", report->timestamp); + if (queue_is_full(&report_queue)) { + if (print_full) + CPRINTF("TP queue full\n"); + print_full = 0; + + queue_advance_head(&report_queue, 1); + } else { + print_full = 1; + } + queue_add_unit(&report_queue, report); + + mutex_unlock(&report_queue_mutex); + + hid_touchpad_process_queue(); +} + static void hid_touchpad_tx(void) { hid_tx(USB_EP_HID_TOUCHPAD); + + if (queue_count(&report_queue) > 0) + hook_call_deferred(&hid_touchpad_process_queue_data, 0); } static void hid_touchpad_event(enum usb_ep_event evt) @@ -203,6 +311,9 @@ static void hid_touchpad_event(enum usb_ep_event evt) if (evt == USB_EVENT_RESET) hid_reset(USB_EP_HID_TOUCHPAD, hid_ep_buf, HID_TOUCHPAD_REPORT_SIZE); + else if (evt == USB_EVENT_DEVICE_RESUME && + queue_count(&report_queue) > 0) + hook_call_deferred(&hid_touchpad_process_queue_data, 0); } USB_DECLARE_EP(USB_EP_HID_TOUCHPAD, hid_touchpad_tx, hid_touchpad_tx, |