]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
HID: bpf: Add Huion Inspiroy Frego M button quirk
authorNikhil Chatterjee <nikhilc1527@gmail.com>
Sun, 26 Apr 2026 03:18:19 +0000 (20:18 -0700)
committerBenjamin Tissoires <bentiss@kernel.org>
Thu, 21 May 2026 15:31:14 +0000 (17:31 +0200)
The Huion Inspiroy Frego M pen report descriptor exposes the second
side button as Secondary Tip Switch instead of Secondary Barrel Switch.
This makes userspace see the control as the wrong pen button.

Add a HID-BPF report descriptor fixup for the Bluetooth 256c:8251
device and USB 256c:2012 L610 variant. The fixup matches the expected
pen descriptor and rewrites the offending usage from Secondary Tip
Switch to Secondary Barrel Switch.

Tested by building the HID-BPF object with:

  make -C drivers/hid/bpf/progs Huion__Inspiroy-Frego-M.bpf.o

Signed-off-by: Nikhil Chatterjee <nikhilc1527@gmail.com>
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
drivers/hid/bpf/progs/Huion__Inspiroy-Frego-M.bpf.c [new file with mode: 0644]

diff --git a/drivers/hid/bpf/progs/Huion__Inspiroy-Frego-M.bpf.c b/drivers/hid/bpf/progs/Huion__Inspiroy-Frego-M.bpf.c
new file mode 100644 (file)
index 0000000..e6ba229
--- /dev/null
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include "vmlinux.h"
+#include "hid_bpf.h"
+#include "hid_bpf_helpers.h"
+#include <bpf/bpf_tracing.h>
+
+/*
+ * Huion Inspiroy Frego M Pen Tablet
+ * Model L610
+ * 256c:8251 (Bluetooth)
+ * 256c:2012 (USB)
+ */
+#define VID_HUION                      0x256C
+#define PID_INSPIROY_FREGO_M           0x8251
+#define PID_L610                       0x2012
+
+#define PEN_RDESC_SIZE                 125
+#define SECONDARY_SWITCH_OFFSET                17
+
+HID_BPF_CONFIG(
+       HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_GENERIC, VID_HUION, PID_INSPIROY_FREGO_M),
+       HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HUION, PID_L610)
+);
+
+/*
+ * The pen descriptor reports the second side button as Secondary Tip Switch
+ * instead of Secondary Barrel Switch.
+ *
+ * Relevant part of the original pen report descriptor:
+ *
+ * 0x09, 0x42,       // Usage (Tip Switch)                  12
+ * 0x09, 0x44,       // Usage (Barrel Switch)               14
+ * 0x09, 0x43,       // Usage (Secondary Tip Switch)        16 <- change to 0x5a
+ * 0x09, 0x3c,       // Usage (Invert)                      18
+ * 0x09, 0x45,       // Usage (Eraser)                      20
+ * 0x15, 0x00,       // Logical Minimum (0)                 22
+ * 0x25, 0x01,       // Logical Maximum (1)                 24
+ */
+SEC(HID_BPF_RDESC_FIXUP)
+int BPF_PROG(fix_secondary_barrel_rdesc, struct hid_bpf_ctx *hctx)
+{
+       __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
+
+       if (!data)
+               return 0; /* EPERM check */
+
+       if (hctx->size != PEN_RDESC_SIZE)
+               return 0;
+
+       if (data[0] != 0x05 || data[1] != 0x0d || /* Usage Page (Digitizers) */
+           data[2] != 0x09 || data[3] != 0x02 || /* Usage (Pen) */
+           data[16] != 0x09 ||
+           data[SECONDARY_SWITCH_OFFSET] != 0x43) /* Secondary Tip Switch */
+               return 0;
+
+       data[SECONDARY_SWITCH_OFFSET] = 0x5a;
+
+       return 0;
+}
+
+HID_BPF_OPS(fix_secondary_barrel) = {
+       .hid_rdesc_fixup = (void *)fix_secondary_barrel_rdesc,
+};
+
+SEC("syscall")
+int probe(struct hid_bpf_probe_args *ctx)
+{
+       ctx->retval = ctx->rdesc_size != PEN_RDESC_SIZE;
+       if (ctx->retval) {
+               ctx->retval = -EINVAL;
+               return 0;
+       }
+
+       if (ctx->rdesc[0] != 0x05 || ctx->rdesc[1] != 0x0d || /* Usage Page (Digitizers) */
+           ctx->rdesc[2] != 0x09 || ctx->rdesc[3] != 0x02 || /* Usage (Pen) */
+           ctx->rdesc[16] != 0x09 ||
+           ctx->rdesc[SECONDARY_SWITCH_OFFSET] != 0x43) { /* Secondary Tip Switch */
+               ctx->retval = -EINVAL;
+               return 0;
+       }
+
+       ctx->retval = 0;
+
+       return 0;
+}
+
+char _license[] SEC("license") = "GPL";