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>
This commit is contained in:
Nicolas Boichat
2017-08-10 14:24:40 +08:00
committed by chrome-bot
parent fb58920c9e
commit 58374f7d26

View File

@@ -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,