xref: /linux/drivers/hid/bpf/progs/XPPen__Deco01V3.bpf.c (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
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";
306