[PATCH v4] HID: chuyuen: add driver for vendor-specific brightness keys

Uddhav Swami <[email protected]>
Newsgroups org.kernel.vger.linux-input
Message-ID <[email protected]>
The Gigabyte Aero 15 XB keyboard (Chu Yuen Enterprise Co., Ltd,
USB ID 1044:7a3f) sends brightness up/down keypresses as vendor-defined
HID reports (Usage Page 0xFF02, Report ID 4) rather than standard HID
Consumer Control usages, causing KEY_BRIGHTNESSUP and KEY_BRIGHTNESSDOWN
to never reach the input subsystem.

Add a minimal HID driver that intercepts Report ID 4 and maps values
0x7D and 0x7E to KEY_BRIGHTNESSDOWN and KEY_BRIGHTNESSUP respectively.

Tested on: Gigabyte Aero 15 XB (USB ID 1044:7a3f)

Signed-off-by: Uddhav Swami <[email protected]>
---
v4:
- Rename driver from hid-gigabyte to hid-chuyuen (and Kconfig symbol
  HID_GIGABYTE_AERO to HID_CHUYUEN, module name to hid_chuyuen) to
  follow the hid-<vendor> naming convention, since Chu Yuen Enterprise
  is the actual USB vendor, not Gigabyte. (Jiri)
v3:
- Fix a NULL pointer dereference: hi->report can be NULL for some
  hid_input instances. Iterate hi->reports via list_for_each_entry()
  on hidinput_list to find report ID 0x04 before setting capabilities.
- Rename local variable ret to error in probe for clarity.
v2:
- Fix a use-after-free: check HID_CLAIMED_INPUT after hid_hw_start()
  to catch a failed input_register_device().

 drivers/hid/Kconfig       |  10 +++
 drivers/hid/Makefile      |   1 +
 drivers/hid/hid-chuyuen.c | 145 ++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-ids.h     |   3 +
 4 files changed, 159 insertions(+)
 create mode 100644 drivers/hid/hid-chuyuen.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 03f36899e458..6647434d5adf 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -243,6 +243,16 @@ config HID_CHICONY
 	help
 	Support for Chicony Tactical pad and special keys on Chicony keyboards.
 
+config HID_CHUYUEN
+	tristate "Chu Yuen Aero laptop vendor-specific keys"
+	depends on USB_HID
+	help
+	  Support for vendor-specific keyboard keys on Chu Yuen keyboards
+	  found in Gigabyte Aero laptops.
+
+	  Currently the following device is known to be supported:
+	   - Gigabyte Aero 15 XB
+
 config HID_CORSAIR
 	tristate "Corsair devices"
 	depends on USB_HID && LEDS_CLASS
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 23e6e3dd0c56..7f7503261b4f 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_HID_BETOP_FF)	+= hid-betopff.o
 obj-$(CONFIG_HID_BIGBEN_FF)	+= hid-bigbenff.o
 obj-$(CONFIG_HID_CHERRY)	+= hid-cherry.o
 obj-$(CONFIG_HID_CHICONY)	+= hid-chicony.o
+obj-$(CONFIG_HID_CHUYUEN)	+= hid-chuyuen.o
 obj-$(CONFIG_HID_CMEDIA)	+= hid-cmedia.o
 obj-$(CONFIG_HID_CORSAIR)	+= hid-corsair.o hid-corsair-void.o
 obj-$(CONFIG_HID_COUGAR)	+= hid-cougar.o
diff --git a/drivers/hid/hid-chuyuen.c b/drivers/hid/hid-chuyuen.c
new file mode 100644
index 000000000000..9bc0ab509083
--- /dev/null
+++ b/drivers/hid/hid-chuyuen.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  HID driver for Chu Yuen Enterprise keyboards, as found on Gigabyte
+ *  Aero laptops, with vendor-specific brightness keys.
+ *
+ *  The keyboard sends brightness up/down presses as a vendor-defined usage page
+ *  report instead of standard HID Consumer Control usages.
+ *
+ *  This driver intercepts them and emits the correct KEY_BRIGHTNESSUP /
+ *  KEY_BRIGHTNESSDOWN events.
+ *
+ *  Currently supported devices are:
+ *	Gigabyte Aero 15 XB
+ *
+ *  Copyright (c) 2026 Uddhav Swami <[email protected]>
+ *
+ *  This module based on hid-asus by
+ *  Copyright (c) 2016 Yusuke Fujimaki <[email protected]>
+ *  Copyright (c) 2016 Brendan McGrath <[email protected]>
+ *  Copyright (c) 2016 Victor Vlasenko <[email protected]>
+ *  Copyright (c) 2016 Frederik Wenigwieser <[email protected]>
+ */
+
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/input.h>
+
+#include "hid-ids.h"
+
+MODULE_AUTHOR("Uddhav Swami <[email protected]>");
+MODULE_DESCRIPTION("HID driver for Chu Yuen Aero keyboards");
+
+#define CHUYUEN_AERO_REPORT_ID 0x04
+#define CHUYUEN_AERO_BRIGHTNESS_DOWN 0x7D
+#define CHUYUEN_AERO_BRIGHTNESS_UP 0x7E
+
+struct chuyuen_drvdata {
+	struct input_dev *input;
+};
+
+static int chuyuen_aero_raw_event(struct hid_device *hdev,
+				   struct hid_report *report, u8 *data,
+				   int size)
+{
+	struct chuyuen_drvdata *drvdata = hid_get_drvdata(hdev);
+
+	if (!drvdata->input)
+		return 0;
+
+	if (report->id != CHUYUEN_AERO_REPORT_ID || size < 4)
+		return 0;
+
+	switch (data[3]) {
+	case CHUYUEN_AERO_BRIGHTNESS_DOWN:
+		input_report_key(drvdata->input, KEY_BRIGHTNESSDOWN, 1);
+		input_sync(drvdata->input);
+		input_report_key(drvdata->input, KEY_BRIGHTNESSDOWN, 0);
+		input_sync(drvdata->input);
+		return 1;
+	case CHUYUEN_AERO_BRIGHTNESS_UP:
+		input_report_key(drvdata->input, KEY_BRIGHTNESSUP, 1);
+		input_sync(drvdata->input);
+		input_report_key(drvdata->input, KEY_BRIGHTNESSUP, 0);
+		input_sync(drvdata->input);
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static int chuyuen_aero_input_configured(struct hid_device *hdev,
+					  struct hid_input *hi)
+{
+	struct chuyuen_drvdata *drvdata = hid_get_drvdata(hdev);
+	struct hid_report *report;
+	bool has_report = false;
+
+	list_for_each_entry(report, &hi->reports, hidinput_list) {
+		if (report->id == CHUYUEN_AERO_REPORT_ID) {
+			has_report = true;
+			break;
+		}
+	}
+
+	if (!has_report)
+		return 0;
+
+	input_set_capability(hi->input, EV_KEY, KEY_BRIGHTNESSUP);
+	input_set_capability(hi->input, EV_KEY, KEY_BRIGHTNESSDOWN);
+
+	drvdata->input = hi->input;
+
+	return 0;
+}
+
+static int chuyuen_aero_probe(struct hid_device *hdev,
+			       const struct hid_device_id *id)
+{
+	struct chuyuen_drvdata *drvdata;
+	int error;
+
+	drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+
+	hid_set_drvdata(hdev, drvdata);
+
+	error = hid_parse(hdev);
+	if (error) {
+		hid_err(hdev, "chuyuen_aero: parse failed: %d\n", error);
+		return error;
+	}
+
+	error = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+	if (error) {
+		hid_err(hdev, "chuyuen_aero: hw start failed: %d\n", error);
+		return error;
+	}
+
+	if (!(hdev->claimed & HID_CLAIMED_INPUT)) {
+		hid_err(hdev, "chuyuen_aero: no input device claimed\n");
+		hid_hw_stop(hdev);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static const struct hid_device_id chuyuen_aero_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_CHU_YUEN,
+			 USB_DEVICE_ID_CHU_YUEN_AERO_KBD) },
+	{}
+};
+MODULE_DEVICE_TABLE(hid, chuyuen_aero_devices);
+
+static struct hid_driver chuyuen_aero_driver = {
+	.name = "hid_chuyuen",
+	.id_table = chuyuen_aero_devices,
+	.probe = chuyuen_aero_probe,
+	.raw_event = chuyuen_aero_raw_event,
+	.input_configured = chuyuen_aero_input_configured,
+};
+module_hid_driver(chuyuen_aero_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index b70f719b3b07..b6bca8d8d94d 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -330,6 +330,9 @@
 #define USB_VENDOR_ID_CHUNGHWAT		0x2247
 #define USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH	0x0001
 
+#define USB_VENDOR_ID_CHU_YUEN			0x1044
+#define USB_DEVICE_ID_CHU_YUEN_AERO_KBD		0x7a3f
+
 #define USB_VENDOR_ID_CIDC		0x1677
 
 #define I2C_VENDOR_ID_CIRQUE           0x0488
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.