| From 547d08e200bccee36889d53da06e70d6df80d453 Mon Sep 17 00:00:00 2001 |
| From: Charlie Mooney <charliemooney@chromium.org> |
| Date: Tue, 24 Oct 2017 11:34:58 -0600 |
| Subject: [PATCH] CHROMIUM: hid: quickstep: Initial add of driver |
| |
| The ChomeOS Touch team uses a usb device built in-house to measure the |
| drag latency on touchpads. This device communicates with the OS over |
| USB using a custom driver. This CL adds that driver so that by default |
| Chromebooks will have the driver ready. Previously, the kernel had to |
| be rebuilt for every device we wanted to test every time, so it will |
| save a lot of time on our end to already have it in there. |
| |
| The device (named Quickstep) consists of a laser that lays across the |
| touchpad and hits a phototransistor on the other side. The device |
| reports over USB HIDRAW a 1 or a 0 immediates each time the laser is |
| broken/unbroken by a finger inbetween the sensor and the laser. This |
| driver simply listens for these events and records the time at which |
| they occur. These events are stored in memory and can be accessed via |
| sysfs entries for later analysis by the latency-measuring scripts. |
| |
| BUG=chromium:356342 |
| TEST=built and used with x86-generic which uses the 3.10 kernel |
| |
| Change-Id: I72b0cc8fe009c2a83ab4a8b03747749693fbf03a |
| Signed-off-by: Charlie Mooney <charliemooney@chromium.org> |
| Reviewed-on: https://chromium-review.googlesource.com/191514 |
| Reviewed-by: Olof Johansson <olofj@chromium.org> |
| |
| [benzh: 3.14 rebase. Resolved trivial conflicts] |
| Signed-off-by: Ben Zhang <benzh@chromium.org> |
| |
| [dtor: 4.4 rebase, resolved trivial conflicts in drivers/hid/hid-core.c |
| and squashed: |
| - CHROMIUM: HID: hid-quickstep - Change get kernel time function |
| - CHROMIUM: hid: quickstep: Remove world-writable access from "clear" device |
| ] |
| Signed-off-by: Filipe Brandenburger <filbranden@chromium.org> |
| Signed-off-by: Dmitry Torokhov <dtor@chromium.org> |
| Conflicts: |
| drivers/hid/hid-core.c |
| [rebase412(groeck): Context conflicts] |
| Signed-off-by: Guenter Roeck <groeck@chromium.org> |
| [rebase414(lannm): Trivial conflicts] |
| Signed-off-by: Lann Martin <lannm@chromium.org> |
| Conflicts: |
| drivers/hid/Makefile |
| drivers/hid/hid-core.c |
| drivers/hid/hid-ids.h |
| [rebase419(groeck): Context conflicts] |
| Signed-off-by: Guenter Roeck <groeck@chromium.org> |
| [rebase510(groeck): |
| Upstream Kconfig no longer likes "---help---" |
| use timespec64 and ktime_get_real_ts64() |
| (from Tomasz Nowicki <tn@semihalf.com>) |
| ] |
| Signed-off-by: Guenter Roeck <groeck@chromium.org> |
| --- |
| drivers/hid/Kconfig | 7 ++ |
| drivers/hid/Makefile | 1 + |
| drivers/hid/hid-ids.h | 1 + |
| drivers/hid/hid-quickstep.c | 173 ++++++++++++++++++++++++++++++++++++ |
| drivers/hid/hid-quirks.c | 3 + |
| 5 files changed, 185 insertions(+) |
| create mode 100644 drivers/hid/hid-quickstep.c |
| |
| diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig |
| index a95a7cbc4a59..84942cf01186 100644 |
| --- a/drivers/hid/Kconfig |
| +++ b/drivers/hid/Kconfig |
| @@ -956,6 +956,13 @@ config HID_PRIMAX |
| Support for Primax devices that are not fully compliant with the |
| HID standard. |
| |
| +config HID_QUICKSTEP |
| + tristate "ChromeOS Touch Latency Measurement Device -- Quickstep" |
| + depends on USB_HID |
| + help |
| + This module is the driver for the ChromeOS Touch Latency Measurement |
| + Device known as Quickstep. |
| + |
| config HID_RETRODE |
| tristate "Retrode 2 USB adapter for vintage video games" |
| depends on USB_HID |
| diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile |
| index 345ac5581bd8..b33b93390f74 100644 |
| --- a/drivers/hid/Makefile |
| +++ b/drivers/hid/Makefile |
| @@ -101,6 +101,7 @@ obj-$(CONFIG_HID_PLANTRONICS) += hid-plantronics.o |
| obj-$(CONFIG_HID_PLAYSTATION) += hid-playstation.o |
| obj-$(CONFIG_HID_PRIMAX) += hid-primax.o |
| obj-$(CONFIG_HID_RAZER) += hid-razer.o |
| +obj-$(CONFIG_HID_QUICKSTEP) += hid-quickstep.o |
| obj-$(CONFIG_HID_REDRAGON) += hid-redragon.o |
| obj-$(CONFIG_HID_RETRODE) += hid-retrode.o |
| obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ |
| diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h |
| index 053853a891c5..15ce5165621f 100644 |
| --- a/drivers/hid/hid-ids.h |
| +++ b/drivers/hid/hid-ids.h |
| @@ -504,6 +504,7 @@ |
| #define USB_DEVICE_ID_GOODTOUCH_000f 0x000f |
| |
| #define USB_VENDOR_ID_GOOGLE 0x18d1 |
| +#define USB_DEVICE_ID_GOOGLE_QUICKSTEP 0x0477 |
| #define USB_DEVICE_ID_GOOGLE_HAMMER 0x5022 |
| #define USB_DEVICE_ID_GOOGLE_TOUCH_ROSE 0x5028 |
| #define USB_DEVICE_ID_GOOGLE_STAFF 0x502b |
| diff --git a/drivers/hid/hid-quickstep.c b/drivers/hid/hid-quickstep.c |
| new file mode 100644 |
| index 000000000000..b637725ef153 |
| --- /dev/null |
| +++ b/drivers/hid/hid-quickstep.c |
| @@ -0,0 +1,173 @@ |
| +/* |
| + * HID driver for Quickstep, ChromeOS's Latency Measurement Gadget |
| + * |
| + * The device is connected via USB and transmits a byte each time a |
| + * laster is crossed. The job of the driver is to record when those events |
| + * happen and then make that information availible to the user via sysfs |
| + * entries. |
| + */ |
| + |
| +#include <linux/device.h> |
| +#include <linux/hid.h> |
| +#include <linux/module.h> |
| +#include <linux/string.h> |
| +#include <linux/time.h> |
| + |
| +#include "hid-ids.h" |
| + |
| +#define MAX_CROSSINGS 64 |
| + |
| +enum change_type { OFF, ON }; |
| + |
| +struct qs_event { |
| + struct timespec64 time; |
| + enum change_type direction; |
| +}; |
| + |
| +struct qs_data { |
| + unsigned int head; |
| + struct qs_event events[MAX_CROSSINGS]; |
| +}; |
| + |
| +static ssize_t append_event(struct qs_event *event, char *buf, ssize_t len) |
| +{ |
| + return snprintf(buf, len, "%010lld.%09ld\t%d\n", event->time.tv_sec, |
| + event->time.tv_nsec, event->direction); |
| + |
| +} |
| + |
| +static ssize_t show_log(struct device *dev, struct device_attribute *attr, |
| + char *buf) |
| +{ |
| + int i, str_len; |
| + struct qs_data *data = dev_get_drvdata(dev); |
| + |
| + str_len = snprintf(buf, PAGE_SIZE, |
| + "Laser Crossings:\ntime\t\t\tdirection\n"); |
| + |
| + if (data->head >= MAX_CROSSINGS) { |
| + for (i = data->head % MAX_CROSSINGS; i < MAX_CROSSINGS; i++) { |
| + str_len += append_event(&data->events[i], buf + str_len, |
| + PAGE_SIZE - str_len); |
| + } |
| + } |
| + |
| + for (i = 0; i < data->head % MAX_CROSSINGS; i++) { |
| + str_len += append_event(&data->events[i], buf + str_len, |
| + PAGE_SIZE - str_len); |
| + } |
| + |
| + return str_len; |
| +} |
| + |
| +static void empty_quickstep_data(struct qs_data *data) |
| +{ |
| + if (data == NULL) |
| + return; |
| + data->head = 0; |
| +} |
| + |
| +static ssize_t clear_log(struct device *dev, struct device_attribute *attr, |
| + const char *buf, size_t len) |
| +{ |
| + empty_quickstep_data(dev_get_drvdata(dev)); |
| + return len; |
| +} |
| + |
| +static DEVICE_ATTR(laser, 0444, show_log, NULL); |
| +static DEVICE_ATTR(clear, 0220, NULL, clear_log); |
| +static struct attribute *dev_attrs[] = { |
| + &dev_attr_laser.attr, |
| + &dev_attr_clear.attr, |
| + NULL, |
| +}; |
| +static struct attribute_group dev_attr_group = {.attrs = dev_attrs}; |
| + |
| +static int quickstep_probe(struct hid_device *hdev, |
| + const struct hid_device_id *id) |
| +{ |
| + int ret; |
| + struct qs_data *data; |
| + |
| + ret = hid_parse(hdev); |
| + if (ret) { |
| + hid_err(hdev, "parse failed\n"); |
| + return ret; |
| + } |
| + |
| + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); |
| + if (ret) { |
| + hid_err(hdev, "hw start failed\n"); |
| + return ret; |
| + } |
| + |
| + ret = hid_hw_open(hdev); |
| + if (ret) { |
| + hid_err(hdev, "hw open failed\n"); |
| + hid_hw_stop(hdev); |
| + return ret; |
| + } |
| + |
| + data = kmalloc(sizeof(struct qs_data), GFP_KERNEL); |
| + empty_quickstep_data(data); |
| + hid_set_drvdata(hdev, data); |
| + |
| + ret = sysfs_create_group(&hdev->dev.kobj, &dev_attr_group); |
| + |
| + return ret; |
| +} |
| + |
| +static void quickstep_remove(struct hid_device *hdev) |
| +{ |
| + sysfs_remove_group(&hdev->dev.kobj, &dev_attr_group); |
| + hid_hw_stop(hdev); |
| + kfree(hid_get_drvdata(hdev)); |
| +} |
| + |
| +static int quickstep_raw_event(struct hid_device *hdev, |
| + struct hid_report *report, u8 *msg, int size) |
| +{ |
| + struct timespec64 time; |
| + struct qs_data *data = hid_get_drvdata(hdev); |
| + |
| + ktime_get_real_ts64(&time); |
| + |
| + data->events[data->head % MAX_CROSSINGS].time = time; |
| + data->events[data->head % MAX_CROSSINGS].direction = msg[0] ? ON : OFF; |
| + |
| + data->head++; |
| + if (data->head >= MAX_CROSSINGS * 2) |
| + data->head = MAX_CROSSINGS + data->head % MAX_CROSSINGS; |
| + |
| + return 0; |
| +} |
| + |
| +static const struct hid_device_id quickstep_devices[] = { |
| + { HID_USB_DEVICE(USB_VENDOR_ID_GOOGLE, |
| + USB_DEVICE_ID_GOOGLE_QUICKSTEP) }, |
| + { } |
| +}; |
| +MODULE_DEVICE_TABLE(hid, quickstep_devices); |
| + |
| +static struct hid_driver quickstep_driver = { |
| + .name = "quickstep", |
| + .id_table = quickstep_devices, |
| + .probe = quickstep_probe, |
| + .remove = quickstep_remove, |
| + .raw_event = quickstep_raw_event, |
| +}; |
| + |
| +static int __init quickstep_init(void) |
| +{ |
| + return hid_register_driver(&quickstep_driver); |
| +} |
| + |
| +static void __exit quickstep_exit(void) |
| +{ |
| + hid_unregister_driver(&quickstep_driver); |
| +} |
| + |
| +module_init(quickstep_init); |
| +module_exit(quickstep_exit); |
| +MODULE_AUTHOR("Charlie Mooney <charliemooney@google.com>"); |
| +MODULE_LICENSE("GPL"); |
| diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c |
| index dc67717d2dab..e8026a3269d9 100644 |
| --- a/drivers/hid/hid-quirks.c |
| +++ b/drivers/hid/hid-quirks.c |
| @@ -584,6 +584,9 @@ static const struct hid_device_id hid_have_special_driver[] = { |
| #if IS_ENABLED(CONFIG_HID_PRODIKEYS) |
| { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) }, |
| #endif |
| +#if IS_ENABLED(CONFIG_HID_QUICKSTEP) |
| + { HID_USB_DEVICE(USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_QUICKSTEP) }, |
| +#endif |
| #if IS_ENABLED(CONFIG_HID_RETRODE) |
| { HID_USB_DEVICE(USB_VENDOR_ID_FUTURE_TECHNOLOGY, USB_DEVICE_ID_RETRODE2) }, |
| #endif |
| -- |
| 2.35.0 |
| |