diff mbox series

[v3] HID: add vivaldi HID driver

Message ID 20200909150254.1.I170489c0c2ac1538b3890abb5a92b95ad4f04d01@changeid
State Accepted
Commit 14c9c014babedc6098bf4cd83c622997867bc0df
Headers show
Series [v3] HID: add vivaldi HID driver | expand

Commit Message

Sean O'Brien Sept. 9, 2020, 10:03 p.m. UTC
Add vivaldi HID driver. This driver allows us to read and report the top
row layout of keyboards which provide a vendor-defined (Google) HID
usage.

Signed-off-by: Sean O'Brien <seobrien@chromium.org>
---

 drivers/hid/Kconfig       |   9 +++
 drivers/hid/Makefile      |   1 +
 drivers/hid/hid-core.c    |   7 ++
 drivers/hid/hid-vivaldi.c | 144 ++++++++++++++++++++++++++++++++++++++
 include/linux/hid.h       |   2 +
 5 files changed, 163 insertions(+)
 create mode 100644 drivers/hid/hid-vivaldi.c

Comments

Joe Perches Sept. 9, 2020, 10:11 p.m. UTC | #1
On Wed, 2020-09-09 at 15:03 -0700, Sean O'Brien wrote:
> Add vivaldi HID driver. This driver allows us to read and report the top
> row layout of keyboards which provide a vendor-defined (Google) HID
> usage.
[]
> diff --git 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_VIVALDI;

Can you please make this less indented code
so there are fewer line breaks?

Something like:

	if ((parser->global.usage_page << 16) != HID_UP_GOOGLEVENDOR)
		return;

	for (i = 0; i < parser->local.usage_index; i++) {
		if (parser->local.usage[i] != (HID_UP_GOOGLEVENDOR | 0x0001))
			continue;
		parser->device->group = HID_GROUP_VIVALDI;
		break;
	}
Sean O'Brien Sept. 21, 2020, 10:49 p.m. UTC | #2
Friendly ping.
Jiri Kosina Sept. 30, 2020, 8:45 p.m. UTC | #3
On Wed, 9 Sep 2020, Sean O'Brien wrote:

> Add vivaldi HID driver. This driver allows us to read and report the top
> row layout of keyboards which provide a vendor-defined (Google) HID
> usage.
> 
> Signed-off-by: Sean O'Brien <seobrien@chromium.org>

Queued in hid.git#for-5.10/vivaldi. Thanks,
diff mbox series

Patch

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 05315b434276..612629678c84 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_VIVALDI
+	tristate "Vivaldi Keyboard"
+	depends on HID
+	help
+	  Say Y here if you want to enable support for Vivaldi keyboards.
+
+	  Vivaldi keyboards use a vendor-specific (Google) 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..4acb583c92a6 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_VIVALDI)	+= hid-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 d2ecc9c45255..6dbd09254c44 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_VIVALDI;
 }
 
 static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
diff --git a/drivers/hid/hid-vivaldi.c b/drivers/hid/hid-vivaldi.c
new file mode 100644
index 000000000000..cd7ada48b1d9
--- /dev/null
+++ b/drivers/hid/hid-vivaldi.c
@@ -0,0 +1,144 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * HID support for Vivaldi Keyboard
+ *
+ * Copyright 2020 Google LLC.
+ * Author: Sean O'Brien <seobrien@chromium.org>
+ */
+
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#define MIN_FN_ROW_KEY	1
+#define MAX_FN_ROW_KEY	24
+#define HID_VD_FN_ROW_PHYSMAP 0x00000001
+#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 ret;
+	u32 report_len;
+	u8 *buf;
+
+	if (field->logical != HID_USAGE_FN_ROW_PHYSMAP ||
+	    (usage->hid & HID_USAGE_PAGE) != HID_UP_ORDINAL)
+		return;
+
+	fn_key = (usage->hid & HID_USAGE);
+	if (fn_key < MIN_FN_ROW_KEY || fn_key > MAX_FN_ROW_KEY)
+		return;
+	if (fn_key > drvdata->max_function_row_key)
+		drvdata->max_function_row_key = fn_key;
+
+	buf = hid_alloc_report_buf(field->report, GFP_KERNEL);
+	if (!buf)
+		return;
+
+	report_len = hid_report_len(field->report);
+	ret = hid_hw_raw_request(hdev, field->report->id, buf,
+				 report_len, HID_FEATURE_REPORT,
+				 HID_REQ_GET_REPORT);
+	if (ret < 0) {
+		dev_warn(&hdev->dev, "failed to fetch feature %d\n",
+			 field->report->id);
+		goto out;
+	}
+
+	ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf,
+				   report_len, 0);
+	if (ret) {
+		dev_warn(&hdev->dev, "failed to report feature %d\n",
+			 field->report->id);
+		goto out;
+	}
+
+	drvdata->function_row_physmap[fn_key - MIN_FN_ROW_KEY] =
+	    field->value[usage->usage_index];
+
+out:
+	kfree(buf);
+}
+
+static int vivaldi_input_configured(struct hid_device *hdev,
+				    struct hid_input *hidinput)
+{
+	return sysfs_create_group(&hdev->dev.kobj, &input_attribute_group);
+}
+
+static const struct hid_device_id vivaldi_table[] = {
+	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_VIVALDI, HID_ANY_ID,
+		     HID_ANY_ID) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(hid, vivaldi_table);
+
+static struct hid_driver hid_vivaldi = {
+	.name = "hid-vivaldi",
+	.id_table = vivaldi_table,
+	.probe = vivaldi_probe,
+	.feature_mapping = vivaldi_feature_mapping,
+	.input_configured = vivaldi_input_configured,
+};
+
+module_hid_driver(hid_vivaldi);
+
+MODULE_AUTHOR("Sean O'Brien");
+MODULE_DESCRIPTION("HID vivaldi driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/hid.h b/include/linux/hid.h
index c7044a14200e..58684657960b 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -163,6 +163,7 @@  struct hid_item {
 #define HID_UP_LNVENDOR		0xffa00000
 #define HID_UP_SENSOR		0x00200000
 #define HID_UP_ASUSVENDOR	0xff310000
+#define HID_UP_GOOGLEVENDOR	0xffd10000
 
 #define HID_USAGE		0x0000ffff
 
@@ -371,6 +372,7 @@  struct hid_item {
 #define HID_GROUP_LOGITECH_DJ_DEVICE		0x0102
 #define HID_GROUP_STEAM				0x0103
 #define HID_GROUP_LOGITECH_27MHZ_DEVICE		0x0104
+#define HID_GROUP_VIVALDI			0x0105
 
 /*
  * HID protocol status