* [PATCH] HID: add driver for EWEADN G5 mice
@ 2026-11-01 14:30 Liang Haowen
2026-11-01 15:04 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Liang Haowen @ 2026-11-01 14:30 UTC (permalink / raw)
To: linux-input; +Cc: Jiri Kosina, Benjamin Tissoires, linux-kernel
EWEADN G5 mice (YJX-CHIP receivers 0xa8a5:2255, replacement units
and the wired mouse 0xa8a4:2255) send motion and buttons in a
9-byte vendor report on interface 0. The report descriptor the
firmware ships does not describe that data: the 0xa8a5 receivers
declare vendor usages with no mouse mapping, and the 0xa8a4 units
declare a standard mouse report the firmware then does not send.
Either way the stock input path produces no pointer events, and
there is no Linux driver from the vendor.
The report itself is a plain mouse report: buttons, int16 dx and
dy, an int8 wheel. Replace the descriptor at parse time with one
that describes exactly that report, so hid-core can parse the
frames with no custom event handling. Interface 1 (keyboard) and
interface 2 (vendor configuration) keep hid-generic.
Both device IDs bind with the fixed-up descriptor, and motion
events flow through the stock input path. This also makes the
userspace relay used until now (github.com/NB-Group/g5moused)
unnecessary.
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <bentiss@kernel.org>
Cc: linux-input@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Liang Haowen <nbg2974@gmail.com>
---
drivers/hid/Kconfig | 9 +++
drivers/hid/Makefile | 1 +
drivers/hid/hid-eweadn.c | 120 +++++++++++++++++++++++++++++++++++++++
3 files changed, 130 insertions(+)
create mode 100644 drivers/hid/hid-eweadn.c
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 2481bc0..3946f75 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -380,6 +380,15 @@ config HID_EVISION
applying customization using userspace programs.
Support for some EVision devices requiring report descriptor fixups.
+config HID_EWEADN
+ tristate "EWEADN G5 mice support"
+ depends on USB_HID
+ help
+ Support for EWEADN G5 wireless mice (YJX-CHIP 0xa8a5:2255 receivers,
+ 0xa8a4 replacement units and the wired mouse). The stock report
+ descriptor does not describe the reports the firmware sends, so
+ the pointer is dead without this fixup.
+
config HID_EZKEY
tristate "Ezkey BTC 8193 keyboard"
help
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 43a8eb0..2db5b5d 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_HID_ELAN) += hid-elan.o
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
obj-$(CONFIG_HID_ELO) += hid-elo.o
obj-$(CONFIG_HID_EVISION) += hid-evision.o
+obj-$(CONFIG_HID_EWEADN) += hid-eweadn.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
obj-$(CONFIG_HID_FT260) += hid-ft260.o
obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o
diff --git a/drivers/hid/hid-eweadn.c b/drivers/hid/hid-eweadn.c
new file mode 100644
index 0000000..6aacfc5
--- /dev/null
+++ b/drivers/hid/hid-eweadn.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * HID driver for EWEADN G5 mice (YJX-CHIP a8a5:2255 / a8a4:2255).
+ *
+ * The mouse reports motion and buttons in a vendor-defined report
+ * (id 0x02) on interface 0. The report descriptor the firmware
+ * ships does not describe that data: the original 0xa8a5 receivers
+ * declare vendor usages with no mouse mapping, and the 0xa8a4 units
+ * declare a standard mouse report that the firmware then does not
+ * send. Either way the stock input path produces no pointer events.
+ * The report itself is a plain mouse report:
+ *
+ * [1] buttons, 5 bits
+ * [2] unused
+ * [3] dx, int16 LE
+ * [5] dy, int16 LE
+ * [7] wheel, int8
+ * [8] unused
+ *
+ * The descriptor below describes exactly that report, so hid-core
+ * can parse the frames without any custom event handling.
+ * Interface 1 (keyboard) and interface 2 (vendor configuration) are
+ * left to hid-generic.
+ *
+ * 0xa8a5 is the original receiver, 0xa8a4 the replacement units
+ * first seen in October 2026 and the mouse itself on its USB cable.
+ */
+
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+static __u8 eweadn_rdesc[] = {
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x02, /* Usage (Mouse) */
+ 0xA1, 0x01, /* Collection (Application) */
+ 0x85, 0x02, /* Report ID (2) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xA1, 0x00, /* Collection (Physical) */
+ 0x05, 0x09, /* Usage Page (Button) */
+ 0x19, 0x01, /* Usage Minimum (1) */
+ 0x29, 0x05, /* Usage Maximum (5) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x05, /* Report Count (5) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x95, 0x0B, /* Report Count (11), padding to byte */
+ 0x81, 0x03, /* Input (Cnst,Var,Abs) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x16, 0x00, 0x80, /* Logical Minimum (-32768) */
+ 0x26, 0xFF, 0x7F, /* Logical Maximum (32767) */
+ 0x75, 0x10, /* Report Size (16) */
+ 0x95, 0x02, /* Report Count (2) */
+ 0x81, 0x06, /* Input (Data,Var,Rel) */
+ 0x09, 0x38, /* Usage (Wheel) */
+ 0x15, 0x81, /* Logical Minimum (-127) */
+ 0x25, 0x7F, /* Logical Maximum (127) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x81, 0x06, /* Input (Data,Var,Rel) */
+ 0x95, 0x01, /* Report Count (1), trailing pad */
+ 0x81, 0x03, /* Input (Cnst,Var,Abs) */
+ 0xC0, /* End Collection */
+ 0xC0, /* End Collection */
+};
+
+static const __u8 *eweadn_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int *rsize)
+{
+ hid_info(hdev, "fixing up EWEADN G5 mouse report descriptor\n");
+ *rsize = sizeof(eweadn_rdesc);
+ return eweadn_rdesc;
+}
+
+static int eweadn_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ struct usb_interface *intf;
+ int ret;
+
+ intf = to_usb_interface(hdev->dev.parent);
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV; /* keyboard and config stay on hid-generic */
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "parse failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ if (ret) {
+ hid_err(hdev, "hw start failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct hid_device_id eweadn_devices[] = {
+ { HID_USB_DEVICE(0xa8a5, 0x2255) }, /* original receiver */
+ { HID_USB_DEVICE(0xa8a4, 0x2255) }, /* replacement unit, wired mouse */
+ { }
+};
+MODULE_DEVICE_TABLE(hid, eweadn_devices);
+
+static struct hid_driver eweadn_driver = {
+ .name = "eweadn",
+ .id_table = eweadn_devices,
+ .report_fixup = eweadn_report_fixup,
+ .probe = eweadn_probe,
+};
+module_hid_driver(eweadn_driver);
+
+MODULE_AUTHOR("Liang Haowen <nbg2974@gmail.com>");
+MODULE_DESCRIPTION("HID driver for EWEADN G5 mice");
+MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH] HID: add driver for EWEADN G5 mice
2026-11-01 14:30 [PATCH] HID: add driver for EWEADN G5 mice Liang Haowen
@ 2026-11-01 15:04 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-11-01 15:04 UTC (permalink / raw)
To: Liang Haowen, linux-input; +Cc: dmitry.torokhov
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] HID: hid-eweadn: returning -ENODEV in eweadn_probe() unbinds sibling interfaces
- [High] HID: hid-eweadn: unchecked to_usb_interface() cast in eweadn_probe()
- [Low] HID: hid-eweadn: hardcoded USB IDs instead of hid-ids.h macros in eweadn_devices
--
commit d81d5f8f2cf2bc9851adb951ad34ae0654a99eee
Author: Liang Haowen <nbg2974@gmail.com>
HID: add driver for EWEADN G5 mice
This patch adds a HID driver for EWEADN G5 mice (0xa8a5:2255 and
0xa8a4:2255) that replaces the broken report descriptor on USB interface 0
with a standard mouse report descriptor so hid-core can parse motion and
button events.
> diff --git a/drivers/hid/hid-eweadn.c b/drivers/hid/hid-eweadn.c
> new file mode 100644
> index 0000000000000..6aacfc5dbe8ac
> --- /dev/null
> +++ b/drivers/hid/hid-eweadn.c
> @@ -0,0 +1,120 @@
[ ... ]
> +static const __u8 *eweadn_report_fixup(struct hid_device *hdev, __u8 *rdesc,
> + unsigned int *rsize)
> +{
> + hid_info(hdev, "fixing up EWEADN G5 mouse report descriptor\n");
> + *rsize = sizeof(eweadn_rdesc);
> + return eweadn_rdesc;
> +}
> +
> +static int eweadn_probe(struct hid_device *hdev,
> + const struct hid_device_id *id)
> +{
> + struct usb_interface *intf;
> + int ret;
> +
> + intf = to_usb_interface(hdev->dev.parent);
[Severity: High]
Can to_usb_interface(hdev->dev.parent) be called here without first
checking hid_is_usb(hdev)?
Although eweadn_devices uses HID_USB_DEVICE(), that only matches
hdev->bus == BUS_USB, hdev->vendor, and hdev->product (or a device can be
bound via sysfs). A virtual HID device created via /dev/uhid in
uhid_dev_create2() (drivers/hid/uhid.c) can specify bus = BUS_USB and
matching vendor/product IDs while setting hdev->dev.parent to
uhid_misc.this_device rather than a struct usb_interface.
Without a hid_is_usb(hdev) check, container_of_const() on hdev->dev.parent
and dereferencing intf->cur_altsetting->desc.bInterfaceNumber will read out
of bounds and dereference an invalid pointer.
> + if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
> + return -ENODEV; /* keyboard and config stay on hid-generic */
[Severity: High]
Does returning -ENODEV in eweadn_probe() actually allow interfaces 1 and 2
to stay on hid-generic, or does it leave them completely unbound?
Each USB interface of the composite device creates a separate struct
hid_device with identical bus, vendor, and product IDs, so
hid_match_device(hdev, &eweadn_driver) matches eweadn_devices for all three
interfaces.
In drivers/hid/hid-generic.c, hid_generic_match() checks whether any other
registered HID driver's id_table matches the device:
drivers/hid/hid-generic.c:hid_generic_match() {
...
if (bus_for_each_drv(&hid_bus_type, NULL, hdev, __check_hid_generic))
return false;
return true;
}
where __check_hid_generic() returns 1 whenever hid_match_device(hdev, hdrv)
is non-NULL, regardless of whether hdrv->probe() later returns -ENODEV. In
addition, when eweadn_driver is registered, __hid_register_driver() calls
__hid_bus_reprobe_drivers(), which sees !hid_generic_match() and calls
device_reprobe(), detaching hid-generic from interfaces 1 and 2. Because
eweadn_probe() then returns -ENODEV for bInterfaceNumber != 0, interfaces 1
and 2 are left with no bound driver at all.
Instead of rejecting non-zero interfaces in a custom .probe callback,
should the bInterfaceNumber == 0 check (guarded by hid_is_usb(hdev)) be
done inside eweadn_report_fixup() so non-zero interfaces keep their
original report descriptor while binding normally?
> +
> + ret = hid_parse(hdev);
> + if (ret) {
> + hid_err(hdev, "parse failed: %d\n", ret);
> + return ret;
> + }
> +
> + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
> + if (ret) {
> + hid_err(hdev, "hw start failed: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static const struct hid_device_id eweadn_devices[] = {
> + { HID_USB_DEVICE(0xa8a5, 0x2255) }, /* original receiver */
> + { HID_USB_DEVICE(0xa8a4, 0x2255) }, /* replacement unit, wired mouse */
> + { }
> +};
[Severity: Low]
This isn't a bug, but drivers/hid/hid-ids.h already defines:
#define USB_VENDOR_ID_YJXCHIP 0xa8a5
#define USB_DEVICE_ID_YJXCHIP_MOUSE 0x2255
Should hid-eweadn.c include "hid-ids.h" and use those macros (adding a
corresponding macro for 0xa8a4) instead of raw hex constants, and mark
eweadn_rdesc[] as const?
--
Sashiko AI review · https://sashiko.dev/#/patchset/6ae7529d.10abdf86.7545b.3e9a@mx.google.com?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-11-01 15:04 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-01 14:30 [PATCH] HID: add driver for EWEADN G5 mice Liang Haowen
2026-11-01 15:04 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.