Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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@who-t.net>
Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests/185
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
Signed-off-by: Jiri Kosina <jkosina@suse.com>

authored by

Benjamin Tissoires and committed by
Jiri Kosina
8ba327d5 040adbe8

+305
+305
drivers/hid/bpf/progs/XPPen__Deco01V3.bpf.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* Copyright (c) 2025 Red Hat 3 + */ 4 + 5 + #include "vmlinux.h" 6 + #include "hid_bpf.h" 7 + #include "hid_bpf_helpers.h" 8 + #include "hid_report_helpers.h" 9 + #include <bpf/bpf_tracing.h> 10 + 11 + #define VID_UGEE 0x28BD /* VID is shared with SinoWealth and Glorious and prob others */ 12 + #define PID_DECO_01_V3 0x0947 13 + 14 + HID_BPF_CONFIG( 15 + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_DECO_01_V3), 16 + ); 17 + 18 + /* 19 + * Default report descriptor reports: 20 + * - a report descriptor for the pad buttons, reported as key sequences 21 + * - a report descriptor for the pen 22 + * - a vendor-specific report descriptor 23 + * 24 + * The Pad report descriptor, see 25 + * https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/issues/54 26 + * 27 + * # Report descriptor length: 102 bytes 28 + * 0x05, 0x01, // Usage Page (Generic Desktop) 0 29 + * 0x09, 0x02, // Usage (Mouse) 2 30 + * 0xa1, 0x01, // Collection (Application) 4 31 + * 0x85, 0x09, // Report ID (9) 6 32 + * 0x09, 0x01, // Usage (Pointer) 8 33 + * 0xa1, 0x00, // Collection (Physical) 10 34 + * 0x05, 0x09, // Usage Page (Button) 12 35 + * 0x19, 0x01, // UsageMinimum (1) 14 36 + * 0x29, 0x03, // UsageMaximum (3) 16 37 + * 0x15, 0x00, // Logical Minimum (0) 18 38 + * 0x25, 0x01, // Logical Maximum (1) 20 39 + * 0x95, 0x03, // Report Count (3) 22 40 + * 0x75, 0x01, // Report Size (1) 24 41 + * 0x81, 0x02, // Input (Data,Var,Abs) 26 42 + * 0x95, 0x05, // Report Count (5) 28 43 + * 0x81, 0x01, // Input (Cnst,Arr,Abs) 30 44 + * 0x05, 0x01, // Usage Page (Generic Desktop) 32 45 + * 0x09, 0x30, // Usage (X) 34 46 + * 0x09, 0x31, // Usage (Y) 36 47 + * 0x26, 0xff, 0x7f, // Logical Maximum (32767) 38 48 + * 0x95, 0x02, // Report Count (2) 41 49 + * 0x75, 0x10, // Report Size (16) 43 50 + * 0x81, 0x02, // Input (Data,Var,Abs) 45 51 + * 0x05, 0x0d, // Usage Page (Digitizers) 47 52 + * 0x09, 0x30, // Usage (Tip Pressure) 49 53 + * 0x26, 0xff, 0x07, // Logical Maximum (2047) 51 54 + * 0x95, 0x01, // Report Count (1) 54 55 + * 0x75, 0x10, // Report Size (16) 56 56 + * 0x81, 0x02, // Input (Data,Var,Abs) 58 57 + * 0xc0, // End Collection 60 58 + * 0xc0, // End Collection 61 59 + * 0x05, 0x01, // Usage Page (Generic Desktop) 62 60 + * 0x09, 0x06, // Usage (Keyboard) 64 61 + * 0xa1, 0x01, // Collection (Application) 66 62 + * 0x85, 0x06, // Report ID (6) 68 63 + * 0x05, 0x07, // Usage Page (Keyboard/Keypad) 70 64 + * 0x19, 0xe0, // UsageMinimum (224) 72 65 + * 0x29, 0xe7, // UsageMaximum (231) 74 66 + * 0x15, 0x00, // Logical Minimum (0) 76 67 + * 0x25, 0x01, // Logical Maximum (1) 78 68 + * 0x75, 0x01, // Report Size (1) 80 69 + * 0x95, 0x08, // Report Count (8) 82 70 + * 0x81, 0x02, // Input (Data,Var,Abs) 84 71 + * 0x05, 0x07, // Usage Page (Keyboard/Keypad) 86 72 + * 0x19, 0x00, // UsageMinimum (0) 88 73 + * 0x29, 0xff, // UsageMaximum (255) 90 74 + * 0x26, 0xff, 0x00, // Logical Maximum (255) 92 75 + * 0x75, 0x08, // Report Size (8) 95 76 + * 0x95, 0x06, // Report Count (6) 97 77 + * 0x81, 0x00, // Input (Data,Arr,Abs) 99 78 + * 0xc0, // End Collection 101 79 + * 80 + * And key events for buttons top->bottom are: 81 + * Buttons released: 06 00 00 00 00 00 00 00 82 + * Button1: 06 00 05 00 00 00 00 00 -> b 83 + * Button2: 06 00 08 00 00 00 00 00 -> e 84 + * Button3: 06 04 00 00 00 00 00 00 -> LAlt 85 + * Button4: 06 00 2c 00 00 00 00 00 -> Space 86 + * Button5: 06 01 16 00 00 00 00 00 -> LControl + s 87 + * Button6: 06 01 1d 00 00 00 00 00 -> LControl + z 88 + * Button7: 06 01 57 00 00 00 00 00 -> LControl + Keypad Plus 89 + * Button8: 06 01 56 00 00 00 00 00 -> LControl + Keypad Dash 90 + * 91 + * When multiple buttons are pressed at the same time, the values used to 92 + * identify the buttons are identical, but they appear in different bytes of the 93 + * record. For example, when button 2 (0x08) and button 1 (0x05) are pressed, 94 + * this is the report: 95 + * 96 + * Buttons 2 and 1: 06 00 08 05 00 00 00 00 -> e + b 97 + * 98 + * Buttons 1, 2, 4, 5 and 6 can be matched by finding their values in the 99 + * report. 100 + * 101 + * Button 3 is pressed when the 3rd bit is 1. For example, pressing buttons 3 102 + * and 5 generates this report: 103 + * 104 + * Buttons 3 and 5: 06 05 16 00 00 00 00 00 -> LControl + LAlt + s 105 + * -- -- 106 + * | | 107 + * | `- Button 5 (0x16) 108 + * `- 0x05 = 0101. Button 3 is pressed 109 + * ^ 110 + * 111 + * pad_buttons contains a list of buttons that can be matched in 112 + * HID_BPF_DEVICE_EVENT. Button 3 as it has a dedicated bit. 113 + * 114 + * 115 + * The Pen report descriptor announces a wrong tilt range: 116 + * 117 + * Report descriptor length: 109 bytes 118 + * 0x05, 0x0d, // Usage Page (Digitizers) 0 119 + * 0x09, 0x02, // Usage (Pen) 2 120 + * 0xa1, 0x01, // Collection (Application) 4 121 + * 0x85, 0x07, // Report ID (7) 6 122 + * 0x09, 0x20, // Usage (Stylus) 8 123 + * 0xa1, 0x01, // Collection (Application) 10 124 + * 0x09, 0x42, // Usage (Tip Switch) 12 125 + * 0x09, 0x44, // Usage (Barrel Switch) 14 126 + * 0x09, 0x45, // Usage (Eraser) 16 127 + * 0x09, 0x3c, // Usage (Invert) 18 128 + * 0x15, 0x00, // Logical Minimum (0) 20 129 + * 0x25, 0x01, // Logical Maximum (1) 22 130 + * 0x75, 0x01, // Report Size (1) 24 131 + * 0x95, 0x04, // Report Count (4) 26 132 + * 0x81, 0x02, // Input (Data,Var,Abs) 28 133 + * 0x95, 0x01, // Report Count (1) 30 134 + * 0x81, 0x03, // Input (Cnst,Var,Abs) 32 135 + * 0x09, 0x32, // Usage (In Range) 34 136 + * 0x95, 0x01, // Report Count (1) 36 137 + * 0x81, 0x02, // Input (Data,Var,Abs) 38 138 + * 0x95, 0x02, // Report Count (2) 40 139 + * 0x81, 0x03, // Input (Cnst,Var,Abs) 42 140 + * 0x75, 0x10, // Report Size (16) 44 141 + * 0x95, 0x01, // Report Count (1) 46 142 + * 0x35, 0x00, // Physical Minimum (0) 48 143 + * 0xa4, // Push 50 144 + * 0x05, 0x01, // Usage Page (Generic Desktop) 51 145 + * 0x09, 0x30, // Usage (X) 53 146 + * 0x65, 0x13, // Unit (EnglishLinear: in) 55 147 + * 0x55, 0x0d, // Unit Exponent (-3) 57 148 + * 0x46, 0x10, 0x27, // Physical Maximum (10000) 59 149 + * 0x26, 0xff, 0x7f, // Logical Maximum (32767) 62 150 + * 0x81, 0x02, // Input (Data,Var,Abs) 65 151 + * 0x09, 0x31, // Usage (Y) 67 152 + * 0x46, 0x6a, 0x18, // Physical Maximum (6250) 69 153 + * 0x26, 0xff, 0x7f, // Logical Maximum (32767) 72 154 + * 0x81, 0x02, // Input (Data,Var,Abs) 75 155 + * 0xb4, // Pop 77 156 + * 0x09, 0x30, // Usage (X) 78 157 + * 0x45, 0x00, // Physical Maximum (0) 80 158 + * 0x26, 0xff, 0x3f, // Logical Maximum (16383) 82 159 + * 0x81, 0x42, // Input (Data,Var,Abs,Null) 85 160 + * 0x09, 0x3d, // Usage (Start) 87 161 + * 0x15, 0x81, // Logical Minimum (-127) 89 <- Change from -127 to -60 162 + * 0x25, 0x7f, // Logical Maximum (127) 91 <- Change from 127 to 60 163 + * 0x75, 0x08, // Report Size (8) 93 164 + * 0x95, 0x01, // Report Count (1) 95 165 + * 0x81, 0x02, // Input (Data,Var,Abs) 97 166 + * 0x09, 0x3e, // Usage (Select) 99 167 + * 0x15, 0x81, // Logical Minimum (-127) 101 <- Change from -127 to -60 168 + * 0x25, 0x7f, // Logical Maximum (127) 103 <- Change from 127 to 60 169 + * 0x81, 0x02, // Input (Data,Var,Abs) 105 170 + * 0xc0, // End Collection 107 171 + * 0xc0, // End Collection 108 172 + */ 173 + 174 + #define PEN_REPORT_DESCRIPTOR_LENGTH 109 175 + #define PAD_REPORT_DESCRIPTOR_LENGTH 102 176 + #define PAD_REPORT_LENGTH 8 177 + #define PAD_REPORT_ID 6 178 + #define PAD_NUM_BUTTONS 8 179 + 180 + static const __u8 fixed_rdesc_pad[] = { 181 + UsagePage_GenericDesktop 182 + Usage_GD_Keypad 183 + CollectionApplication( 184 + // Byte 0 in report is the report ID 185 + ReportId(PAD_REPORT_ID) 186 + ReportCount(1) 187 + ReportSize(8) 188 + UsagePage_Digitizers 189 + Usage_Dig_TabletFunctionKeys 190 + CollectionPhysical( 191 + // Byte 1 is the button state 192 + UsagePage_Button 193 + UsageMinimum_i8(0x01) 194 + UsageMaximum_i8(PAD_NUM_BUTTONS) 195 + LogicalMinimum_i8(0x0) 196 + LogicalMaximum_i8(0x1) 197 + ReportCount(PAD_NUM_BUTTONS) 198 + ReportSize(1) 199 + Input(Var|Abs) 200 + // Byte 2 in report - just exists so we get to be a tablet pad 201 + UsagePage_Digitizers 202 + Usage_Dig_BarrelSwitch // BTN_STYLUS 203 + ReportCount(1) 204 + ReportSize(1) 205 + Input(Var|Abs) 206 + ReportCount(7) // padding 207 + Input(Const) 208 + // Bytes 3/4 in report - just exists so we get to be a tablet pad 209 + UsagePage_GenericDesktop 210 + Usage_GD_X 211 + Usage_GD_Y 212 + ReportCount(2) 213 + ReportSize(8) 214 + Input(Var|Abs) 215 + // Byte 5-7 are padding so we match the original report lengtth 216 + ReportCount(3) 217 + ReportSize(8) 218 + Input(Const) 219 + ) 220 + ) 221 + }; 222 + 223 + SEC(HID_BPF_RDESC_FIXUP) 224 + int BPF_PROG(xppen_deco01v3_rdesc_fixup, struct hid_bpf_ctx *hctx) 225 + { 226 + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */); 227 + 228 + const __u8 wrong_logical_range[] = {0x15, 0x81, 0x25, 0x7f}; 229 + const __u8 correct_logical_range[] = {0x15, 0xc4, 0x25, 0x3c}; 230 + 231 + if (!data) 232 + return 0; /* EPERM check */ 233 + 234 + switch (hctx->size) { 235 + case PAD_REPORT_DESCRIPTOR_LENGTH: 236 + __builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad)); 237 + return sizeof(fixed_rdesc_pad); 238 + case PEN_REPORT_DESCRIPTOR_LENGTH: 239 + if (__builtin_memcmp(&data[89], wrong_logical_range, 240 + sizeof(wrong_logical_range)) == 0) 241 + __builtin_memcpy(&data[89], correct_logical_range, 242 + sizeof(correct_logical_range)); 243 + if (__builtin_memcmp(&data[101], wrong_logical_range, 244 + sizeof(wrong_logical_range)) == 0) 245 + __builtin_memcpy(&data[101], correct_logical_range, 246 + sizeof(correct_logical_range)); 247 + break; 248 + } 249 + 250 + return 0; 251 + } 252 + 253 + SEC(HID_BPF_DEVICE_EVENT) 254 + int BPF_PROG(xppen_deco01v3_device_event, struct hid_bpf_ctx *hctx) 255 + { 256 + static const __u8 pad_buttons[] = { 0x05, 0x08, 0x00, 0x2c, 0x16, 0x1d, 0x57, 0x56 }; 257 + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, PAD_REPORT_LENGTH /* size */); 258 + 259 + if (!data) 260 + return 0; /* EPERM check */ 261 + 262 + if (data[0] == PAD_REPORT_ID) { 263 + __u8 button_mask = 0; 264 + size_t d, b; 265 + 266 + /* data[1] stores the status of BTN_2 in the 3rd bit*/ 267 + if (data[1] & BIT(2)) 268 + button_mask |= BIT(2); 269 + 270 + /* The rest of the descriptor stores the buttons as in pad_buttons */ 271 + for (d = 2; d < 8; d++) { 272 + for (b = 0; b < sizeof(pad_buttons); b++) { 273 + if (data[d] != 0 && data[d] == pad_buttons[b]) 274 + button_mask |= BIT(b); 275 + } 276 + } 277 + 278 + __u8 report[8] = {PAD_REPORT_ID, button_mask, 0x00}; 279 + 280 + __builtin_memcpy(data, report, sizeof(report)); 281 + } 282 + return 0; 283 + } 284 + 285 + HID_BPF_OPS(xppen_deco01v3) = { 286 + .hid_rdesc_fixup = (void *)xppen_deco01v3_rdesc_fixup, 287 + .hid_device_event = (void *)xppen_deco01v3_device_event, 288 + }; 289 + 290 + SEC("syscall") 291 + int probe(struct hid_bpf_probe_args *ctx) 292 + { 293 + switch (ctx->rdesc_size) { 294 + case PAD_REPORT_DESCRIPTOR_LENGTH: 295 + case PEN_REPORT_DESCRIPTOR_LENGTH: 296 + ctx->retval = 0; 297 + break; 298 + default: 299 + ctx->retval = -EINVAL; 300 + } 301 + 302 + return 0; 303 + } 304 + 305 + char _license[] SEC("license") = "GPL";