lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20251118-wip-sync-udev-hid-bpf-v1-6-0f8105c54835@kernel.org>
Date: Tue, 18 Nov 2025 18:16:27 +0100
From: Benjamin Tissoires <bentiss@...nel.org>
To: Jiri Kosina <jikos@...nel.org>
Cc: linux-kernel@...r.kernel.org, linux-input@...r.kernel.org, 
 Benjamin Tissoires <bentiss@...nel.org>, 
 Peter Hutterer <peter.hutterer@...-t.net>
Subject: [PATCH 06/10] HID: bpf: Add support for the XP-Pen Deco 01 V3

This device needs a fix for the tilt range on the pen report descriptor
and the usual conversion of the pad keys from the firmware's hardcoded
keyboard shortcuts to actual pad buttons.

Signed-off-by: Peter Hutterer <peter.hutterer@...-t.net>
Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests/185
Signed-off-by: Benjamin Tissoires <bentiss@...nel.org>
---
 drivers/hid/bpf/progs/XPPen__Deco01V3.bpf.c | 305 ++++++++++++++++++++++++++++
 1 file changed, 305 insertions(+)

diff --git a/drivers/hid/bpf/progs/XPPen__Deco01V3.bpf.c b/drivers/hid/bpf/progs/XPPen__Deco01V3.bpf.c
new file mode 100644
index 0000000000000000000000000000000000000000..2502fcc9ede69b856e2115a15307096951953571
--- /dev/null
+++ b/drivers/hid/bpf/progs/XPPen__Deco01V3.bpf.c
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2025 Red Hat
+ */
+
+#include "vmlinux.h"
+#include "hid_bpf.h"
+#include "hid_bpf_helpers.h"
+#include "hid_report_helpers.h"
+#include <bpf/bpf_tracing.h>
+
+#define VID_UGEE 0x28BD /* VID is shared with SinoWealth and Glorious and prob others */
+#define PID_DECO_01_V3 0x0947
+
+HID_BPF_CONFIG(
+	HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_DECO_01_V3),
+);
+
+/*
+ * Default report descriptor reports:
+ * - a report descriptor for the pad buttons, reported as key sequences
+ * - a report descriptor for the pen
+ * - a vendor-specific report descriptor
+ *
+ * The Pad report descriptor, see
+ * https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/issues/54
+ *
+ * # Report descriptor length: 102 bytes
+ * 0x05, 0x01,                    // Usage Page (Generic Desktop)              0
+ * 0x09, 0x02,                    // Usage (Mouse)                             2
+ * 0xa1, 0x01,                    // Collection (Application)                  4
+ * 0x85, 0x09,                    //   Report ID (9)                           6
+ * 0x09, 0x01,                    //   Usage (Pointer)                         8
+ * 0xa1, 0x00,                    //   Collection (Physical)                   10
+ * 0x05, 0x09,                    //     Usage Page (Button)                   12
+ * 0x19, 0x01,                    //     UsageMinimum (1)                      14
+ * 0x29, 0x03,                    //     UsageMaximum (3)                      16
+ * 0x15, 0x00,                    //     Logical Minimum (0)                   18
+ * 0x25, 0x01,                    //     Logical Maximum (1)                   20
+ * 0x95, 0x03,                    //     Report Count (3)                      22
+ * 0x75, 0x01,                    //     Report Size (1)                       24
+ * 0x81, 0x02,                    //     Input (Data,Var,Abs)                  26
+ * 0x95, 0x05,                    //     Report Count (5)                      28
+ * 0x81, 0x01,                    //     Input (Cnst,Arr,Abs)                  30
+ * 0x05, 0x01,                    //     Usage Page (Generic Desktop)          32
+ * 0x09, 0x30,                    //     Usage (X)                             34
+ * 0x09, 0x31,                    //     Usage (Y)                             36
+ * 0x26, 0xff, 0x7f,              //     Logical Maximum (32767)               38
+ * 0x95, 0x02,                    //     Report Count (2)                      41
+ * 0x75, 0x10,                    //     Report Size (16)                      43
+ * 0x81, 0x02,                    //     Input (Data,Var,Abs)                  45
+ * 0x05, 0x0d,                    //     Usage Page (Digitizers)               47
+ * 0x09, 0x30,                    //     Usage (Tip Pressure)                  49
+ * 0x26, 0xff, 0x07,              //     Logical Maximum (2047)                51
+ * 0x95, 0x01,                    //     Report Count (1)                      54
+ * 0x75, 0x10,                    //     Report Size (16)                      56
+ * 0x81, 0x02,                    //     Input (Data,Var,Abs)                  58
+ * 0xc0,                          //   End Collection                          60
+ * 0xc0,                          // End Collection                            61
+ * 0x05, 0x01,                    // Usage Page (Generic Desktop)              62
+ * 0x09, 0x06,                    // Usage (Keyboard)                          64
+ * 0xa1, 0x01,                    // Collection (Application)                  66
+ * 0x85, 0x06,                    //   Report ID (6)                           68
+ * 0x05, 0x07,                    //   Usage Page (Keyboard/Keypad)            70
+ * 0x19, 0xe0,                    //   UsageMinimum (224)                      72
+ * 0x29, 0xe7,                    //   UsageMaximum (231)                      74
+ * 0x15, 0x00,                    //   Logical Minimum (0)                     76
+ * 0x25, 0x01,                    //   Logical Maximum (1)                     78
+ * 0x75, 0x01,                    //   Report Size (1)                         80
+ * 0x95, 0x08,                    //   Report Count (8)                        82
+ * 0x81, 0x02,                    //   Input (Data,Var,Abs)                    84
+ * 0x05, 0x07,                    //   Usage Page (Keyboard/Keypad)            86
+ * 0x19, 0x00,                    //   UsageMinimum (0)                        88
+ * 0x29, 0xff,                    //   UsageMaximum (255)                      90
+ * 0x26, 0xff, 0x00,              //   Logical Maximum (255)                   92
+ * 0x75, 0x08,                    //   Report Size (8)                         95
+ * 0x95, 0x06,                    //   Report Count (6)                        97
+ * 0x81, 0x00,                    //   Input (Data,Arr,Abs)                    99
+ * 0xc0,                          // End Collection                            101
+ *
+ * And key events for buttons top->bottom are:
+ * Buttons released: 06 00 00 00 00 00 00 00
+ * Button1:          06 00 05 00 00 00 00 00 -> b
+ * Button2:          06 00 08 00 00 00 00 00 -> e
+ * Button3:          06 04 00 00 00 00 00 00 -> LAlt
+ * Button4:          06 00 2c 00 00 00 00 00 -> Space
+ * Button5:          06 01 16 00 00 00 00 00 -> LControl + s
+ * Button6:          06 01 1d 00 00 00 00 00 -> LControl + z
+ * Button7:          06 01 57 00 00 00 00 00 -> LControl + Keypad Plus
+ * Button8:          06 01 56 00 00 00 00 00 -> LControl + Keypad Dash
+ *
+ * When multiple buttons are pressed at the same time, the values used to
+ * identify the buttons are identical, but they appear in different bytes of the
+ * record. For example, when button 2 (0x08) and button 1 (0x05) are pressed,
+ * this is the report:
+ *
+ *   Buttons 2 and 1:  06 00 08 05 00 00 00 00 -> e + b
+ *
+ * Buttons 1, 2, 4, 5 and 6 can be matched by finding their values in the
+ * report.
+ *
+ * Button 3 is pressed when the 3rd bit is 1. For example, pressing buttons 3
+ * and 5 generates this report:
+ *
+ *   Buttons 3 and 5:  06 05 16 00 00 00 00 00 -> LControl + LAlt + s
+ *                        -- --
+ *                         |  |
+ *                         |  `- Button 5 (0x16)
+ *                         `- 0x05 = 0101. Button 3 is pressed
+ *                                    ^
+ *
+ * pad_buttons contains a list of buttons that can be matched in
+ * HID_BPF_DEVICE_EVENT. Button 3 as it has a dedicated bit.
+ *
+ *
+ * The Pen report descriptor announces a wrong tilt range:
+ *
+ * Report descriptor length: 109 bytes
+ * 0x05, 0x0d,                    // Usage Page (Digitizers)                   0
+ * 0x09, 0x02,                    // Usage (Pen)                               2
+ * 0xa1, 0x01,                    // Collection (Application)                  4
+ * 0x85, 0x07,                    //   Report ID (7)                           6
+ * 0x09, 0x20,                    //   Usage (Stylus)                          8
+ * 0xa1, 0x01,                    //   Collection (Application)                10
+ * 0x09, 0x42,                    //     Usage (Tip Switch)                    12
+ * 0x09, 0x44,                    //     Usage (Barrel Switch)                 14
+ * 0x09, 0x45,                    //     Usage (Eraser)                        16
+ * 0x09, 0x3c,                    //     Usage (Invert)                        18
+ * 0x15, 0x00,                    //     Logical Minimum (0)                   20
+ * 0x25, 0x01,                    //     Logical Maximum (1)                   22
+ * 0x75, 0x01,                    //     Report Size (1)                       24
+ * 0x95, 0x04,                    //     Report Count (4)                      26
+ * 0x81, 0x02,                    //     Input (Data,Var,Abs)                  28
+ * 0x95, 0x01,                    //     Report Count (1)                      30
+ * 0x81, 0x03,                    //     Input (Cnst,Var,Abs)                  32
+ * 0x09, 0x32,                    //     Usage (In Range)                      34
+ * 0x95, 0x01,                    //     Report Count (1)                      36
+ * 0x81, 0x02,                    //     Input (Data,Var,Abs)                  38
+ * 0x95, 0x02,                    //     Report Count (2)                      40
+ * 0x81, 0x03,                    //     Input (Cnst,Var,Abs)                  42
+ * 0x75, 0x10,                    //     Report Size (16)                      44
+ * 0x95, 0x01,                    //     Report Count (1)                      46
+ * 0x35, 0x00,                    //     Physical Minimum (0)                  48
+ * 0xa4,                          //     Push                                  50
+ * 0x05, 0x01,                    //     Usage Page (Generic Desktop)          51
+ * 0x09, 0x30,                    //     Usage (X)                             53
+ * 0x65, 0x13,                    //     Unit (EnglishLinear: in)              55
+ * 0x55, 0x0d,                    //     Unit Exponent (-3)                    57
+ * 0x46, 0x10, 0x27,              //     Physical Maximum (10000)              59
+ * 0x26, 0xff, 0x7f,              //     Logical Maximum (32767)               62
+ * 0x81, 0x02,                    //     Input (Data,Var,Abs)                  65
+ * 0x09, 0x31,                    //     Usage (Y)                             67
+ * 0x46, 0x6a, 0x18,              //     Physical Maximum (6250)               69
+ * 0x26, 0xff, 0x7f,              //     Logical Maximum (32767)               72
+ * 0x81, 0x02,                    //     Input (Data,Var,Abs)                  75
+ * 0xb4,                          //     Pop                                   77
+ * 0x09, 0x30,                    //     Usage (X)                             78
+ * 0x45, 0x00,                    //     Physical Maximum (0)                  80
+ * 0x26, 0xff, 0x3f,              //     Logical Maximum (16383)               82
+ * 0x81, 0x42,                    //     Input (Data,Var,Abs,Null)             85
+ * 0x09, 0x3d,                    //     Usage (Start)                         87
+ * 0x15, 0x81,                    //     Logical Minimum (-127)                89  <- Change from -127 to -60
+ * 0x25, 0x7f,                    //     Logical Maximum (127)                 91  <- Change from 127 to 60
+ * 0x75, 0x08,                    //     Report Size (8)                       93
+ * 0x95, 0x01,                    //     Report Count (1)                      95
+ * 0x81, 0x02,                    //     Input (Data,Var,Abs)                  97
+ * 0x09, 0x3e,                    //     Usage (Select)                        99
+ * 0x15, 0x81,                    //     Logical Minimum (-127)                101  <- Change from -127 to -60
+ * 0x25, 0x7f,                    //     Logical Maximum (127)                 103  <- Change from 127 to 60
+ * 0x81, 0x02,                    //     Input (Data,Var,Abs)                  105
+ * 0xc0,                          //   End Collection                          107
+ * 0xc0,                          // End Collection                            108
+ */
+
+#define PEN_REPORT_DESCRIPTOR_LENGTH 109
+#define PAD_REPORT_DESCRIPTOR_LENGTH 102
+#define PAD_REPORT_LENGTH 8
+#define PAD_REPORT_ID 6
+#define PAD_NUM_BUTTONS 8
+
+static const __u8 fixed_rdesc_pad[] = {
+	UsagePage_GenericDesktop
+	Usage_GD_Keypad
+	CollectionApplication(
+		// Byte 0 in report is the report ID
+		ReportId(PAD_REPORT_ID)
+		ReportCount(1)
+		ReportSize(8)
+		UsagePage_Digitizers
+		Usage_Dig_TabletFunctionKeys
+		CollectionPhysical(
+			// Byte 1 is the button state
+			UsagePage_Button
+			UsageMinimum_i8(0x01)
+			UsageMaximum_i8(PAD_NUM_BUTTONS)
+			LogicalMinimum_i8(0x0)
+			LogicalMaximum_i8(0x1)
+			ReportCount(PAD_NUM_BUTTONS)
+			ReportSize(1)
+			Input(Var|Abs)
+			// Byte 2 in report - just exists so we get to be a tablet pad
+			UsagePage_Digitizers
+			Usage_Dig_BarrelSwitch // BTN_STYLUS
+			ReportCount(1)
+			ReportSize(1)
+			Input(Var|Abs)
+			ReportCount(7) // padding
+			Input(Const)
+			// Bytes 3/4 in report - just exists so we get to be a tablet pad
+			UsagePage_GenericDesktop
+			Usage_GD_X
+			Usage_GD_Y
+			ReportCount(2)
+			ReportSize(8)
+			Input(Var|Abs)
+			// Byte 5-7 are padding so we match the original report lengtth
+			ReportCount(3)
+			ReportSize(8)
+			Input(Const)
+		)
+	)
+};
+
+SEC(HID_BPF_RDESC_FIXUP)
+int BPF_PROG(xppen_deco01v3_rdesc_fixup, struct hid_bpf_ctx *hctx)
+{
+	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
+
+	const __u8 wrong_logical_range[] = {0x15, 0x81, 0x25, 0x7f};
+	const __u8 correct_logical_range[] = {0x15, 0xc4, 0x25, 0x3c};
+
+	if (!data)
+		return 0; /* EPERM check */
+
+	switch (hctx->size) {
+	case PAD_REPORT_DESCRIPTOR_LENGTH:
+		__builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad));
+		return sizeof(fixed_rdesc_pad);
+	case PEN_REPORT_DESCRIPTOR_LENGTH:
+		if (__builtin_memcmp(&data[89], wrong_logical_range,
+				     sizeof(wrong_logical_range)) == 0)
+			__builtin_memcpy(&data[89], correct_logical_range,
+					 sizeof(correct_logical_range));
+		if (__builtin_memcmp(&data[101], wrong_logical_range,
+				     sizeof(wrong_logical_range)) == 0)
+			__builtin_memcpy(&data[101], correct_logical_range,
+					 sizeof(correct_logical_range));
+		break;
+	}
+
+	return 0;
+}
+
+SEC(HID_BPF_DEVICE_EVENT)
+int BPF_PROG(xppen_deco01v3_device_event, struct hid_bpf_ctx *hctx)
+{
+	static const __u8 pad_buttons[] = { 0x05, 0x08, 0x00, 0x2c, 0x16, 0x1d, 0x57, 0x56 };
+	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, PAD_REPORT_LENGTH /* size */);
+
+	if (!data)
+		return 0; /* EPERM check */
+
+	if (data[0] == PAD_REPORT_ID) {
+		__u8 button_mask = 0;
+		size_t d, b;
+
+		/* data[1] stores the status of BTN_2 in the 3rd bit*/
+		if (data[1] & BIT(2))
+			button_mask |= BIT(2);
+
+		/* The rest of the descriptor stores the buttons as in pad_buttons */
+		for (d = 2; d < 8; d++) {
+			for (b = 0; b < sizeof(pad_buttons); b++) {
+				if (data[d] != 0 && data[d] == pad_buttons[b])
+					button_mask |= BIT(b);
+			}
+		}
+
+		__u8 report[8] = {PAD_REPORT_ID, button_mask, 0x00};
+
+		__builtin_memcpy(data, report, sizeof(report));
+	}
+	return 0;
+}
+
+HID_BPF_OPS(xppen_deco01v3) = {
+	.hid_rdesc_fixup = (void *)xppen_deco01v3_rdesc_fixup,
+	.hid_device_event = (void *)xppen_deco01v3_device_event,
+};
+
+SEC("syscall")
+int probe(struct hid_bpf_probe_args *ctx)
+{
+	switch (ctx->rdesc_size) {
+	case PAD_REPORT_DESCRIPTOR_LENGTH:
+	case PEN_REPORT_DESCRIPTOR_LENGTH:
+		ctx->retval = 0;
+		break;
+	default:
+		ctx->retval = -EINVAL;
+	}
+
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";

-- 
2.51.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ