[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