Add Google vivaldi HID driver. This driver allows us to read and report
the top row layout of keyboards which provide a vendor-defined HID
usage.
Signed-off-by: Sean O'Brien
---
drivers/hid/Kconfig | 9 ++
drivers/hid/Makefile | 1 +
drivers/hid/hid-core.c | 7 ++
drivers/hid/hid-google-vivaldi.c | 144 +++
include/linux/hid.h | 2 +
5 files changed, 163 insertions(+)
create mode 100644 drivers/hid/hid-google-vivaldi.c
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 05315b434276..b608a5b1d753 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -397,6 +397,15 @@ config HID_GOOGLE_HAMMER
help
Say Y here if you have a Google Hammer device.
+config HID_GOOGLE_VIVALDI
+ tristate "Google Vivaldi Keyboard"
+ depends on HID
+ help
+ Say Y here if you want to enable support for Google Vivaldi keyboards.
+
+ Vivaldi keyboards use a vendor-specific HID usage to report how the
+ keys in the top row are physically ordered.
+
config HID_GT683R
tristate "MSI GT68xR LED support"
depends on LEDS_CLASS && USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index d8ea4b8c95af..35ca714c7ee2 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o
obj-$(CONFIG_HID_GFRM) += hid-gfrm.o
obj-$(CONFIG_HID_GLORIOUS) += hid-glorious.o
obj-$(CONFIG_HID_GOOGLE_HAMMER)+= hid-google-hammer.o
+obj-$(CONFIG_HID_GOOGLE_VIVALDI) += hid-google-vivaldi.o
obj-$(CONFIG_HID_GT683R) += hid-gt683r.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 359616e3efbb..4df05b35b4d0 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -814,6 +814,13 @@ static void hid_scan_collection(struct hid_parser *parser,
unsigned type)
if ((parser->global.usage_page << 16) >= HID_UP_MSVENDOR)
parser->scan_flags |= HID_SCAN_FLAG_VENDOR_SPECIFIC;
+
+ if ((parser->global.usage_page << 16) == HID_UP_GOOGLEVENDOR)
+ for (i = 0; i < parser->local.usage_index; i++)
+ if (parser->local.usage[i] ==
+ (HID_UP_GOOGLEVENDOR | 0x0001))
+ parser->device->group =
+ HID_GROUP_GOOGLE_VIVALDI;
}
static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
diff --git a/drivers/hid/hid-google-vivaldi.c b/drivers/hid/hid-google-vivaldi.c
new file mode 100644
index ..9165ef5e1542
--- /dev/null
+++ b/drivers/hid/hid-google-vivaldi.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * HID support for Google Vivaldi Keyboard
+ *
+ * Copyright 2020 Google LLC.
+ * Author: Sean O'Brien
+ */
+
+#include
+#include
+
+#define MIN_FN_ROW_KEY 1
+#define MAX_FN_ROW_KEY 24
+#define HID_VD_FN_ROW_PHYSMAP 0x0001
+#define HID_USAGE_FN_ROW_PHYSMAP (HID_UP_GOOGLEVENDOR | HID_VD_FN_ROW_PHYSMAP)
+
+static struct hid_driver hid_vivaldi;
+
+struct vivaldi_data {
+ u32 function_row_physmap[MAX_FN_ROW_KEY - MIN_FN_ROW_KEY + 1];
+ int max_function_row_key;
+};
+
+static ssize_t function_row_physmap_show(struct device *dev,
+struct device_attribute *attr,
+char *buf)
+{
+ struct hid_device *hdev = to_hid_device(dev);
+ struct vivaldi_data *drvdata = hid_get_drvdata(hdev);
+ ssize_t size = 0;
+ int i;
+
+ if (!drvdata->max_function_row_key)
+ return 0;
+
+ for (i = 0; i < drvdata->max_function_row_key; i++)
+ size += sprintf(buf + size, "%02X ",
+ drvdata->function_row_physmap[i]);
+ size += sprintf(buf + size, "\n");
+ return size;
+}
+
+DEVICE_ATTR_RO(function_row_physmap);
+static struct attribute *sysfs_attrs[] = {
+ &dev_attr_function_row_physmap.attr,
+ NULL
+};
+
+static const struct attribute_group input_attribute_group = {
+ .attrs = sysfs_attrs
+};
+
+static int vivaldi_probe(struct hid_device *hdev,
+const struct hid_device_id *id)
+{
+ struct vivaldi_data *drvdata;
+ int ret;
+
+ drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
+ hid_set_drvdata(hdev, drvdata);
+
+ ret = hid_parse(hdev);
+ if (ret)
+ return ret;
+
+ return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+}
+
+static void vivaldi_feature_mapping(struct hid_device *hdev,
+ struct hid_field *field,
+ struct hid_usage *usage)
+{
+ struct vivaldi_data *drvdata = hid_get_drvdata(hdev);
+ int fn_key;
+ int re