blob: ff84e934e13c50f443c102b01a9419ad265bd0fb [file] [log] [blame]
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