xref: /linux/drivers/hid/bpf/progs/Huion__Dial-2.bpf.c (revision 6e7fd890f1d6ac83805409e9c346240de2705584)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2024 Red Hat, Inc
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_HUION 0x256C
12 #define PID_DIAL_2 0x0060
13 
14 
15 HID_BPF_CONFIG(
16 	HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HUION, PID_DIAL_2),
17 );
18 
19 /* Filled in by udev-hid-bpf */
20 char UDEV_PROP_HUION_FIRMWARE_ID[64];
21 
22 /* The prefix of the firmware ID we expect for this device. The full firmware
23  * string has a date suffix, e.g. HUION_T21j_221221
24  */
25 char EXPECTED_FIRMWARE_ID[] = "HUION_T216_";
26 
27 /* How this BPF program works: the tablet has two modes, firmware mode and
28  * tablet mode. In firmware mode (out of the box) the tablet sends button events
29  * and the dial as keyboard combinations. In tablet mode it uses a vendor specific
30  * hid report to report everything instead.
31  * Depending on the mode some hid reports are never sent and the corresponding
32  * devices are mute.
33  *
34  * To switch the tablet use e.g.  https://github.com/whot/huion-switcher
35  * or one of the tools from the digimend project
36  *
37  * This BPF works for both modes. The huion-switcher tool sets the
38  * HUION_FIRMWARE_ID udev property - if that is set then we disable the firmware
39  * pad and pen reports (by making them vendor collections that are ignored).
40  * If that property is not set we fix all hidraw nodes so the tablet works in
41  * either mode though the drawback is that the device will show up twice if
42  * you bind it to all event nodes
43  *
44  * Default report descriptor for the first exposed hidraw node:
45  *
46  * # HUION Huion Tablet_Q630M
47  * # 0x06, 0x00, 0xff,              // Usage Page (Vendor Defined Page 1)  0
48  * # 0x09, 0x01,                    // Usage (Vendor Usage 1)              3
49  * # 0xa1, 0x01,                    // Collection (Application)            5
50  * # 0x85, 0x08,                    //  Report ID (8)                      7
51  * # 0x75, 0x58,                    //  Report Size (88)                   9
52  * # 0x95, 0x01,                    //  Report Count (1)                   11
53  * # 0x09, 0x01,                    //  Usage (Vendor Usage 1)             13
54  * # 0x81, 0x02,                    //  Input (Data,Var,Abs)               15
55  * # 0xc0,                          // End Collection                      17
56  * R: 18 06 00 ff 09 01 a1 01 85 08 75 58 95 01 09 01 81 02 c0
57  *
58  * This rdesc does nothing until the tablet is switched to raw mode, see
59  * https://github.com/whot/huion-switcher
60  *
61  *
62  * Second hidraw node is the Pen. This one sends events until the tablet is
63  * switched to raw mode, then it's mute.
64  *
65  * # Report descriptor length: 93 bytes
66  * # HUION Huion Tablet_Q630M
67  * # 0x05, 0x0d,                    // Usage Page (Digitizers)             0
68  * # 0x09, 0x02,                    // Usage (Pen)                         2
69  * # 0xa1, 0x01,                    // Collection (Application)            4
70  * # 0x85, 0x0a,                    //  Report ID (10)                     6
71  * # 0x09, 0x20,                    //  Usage (Stylus)                     8
72  * # 0xa1, 0x01,                    //  Collection (Application)           10
73  * # 0x09, 0x42,                    //   Usage (Tip Switch)                12
74  * # 0x09, 0x44,                    //   Usage (Barrel Switch)             14
75  * # 0x09, 0x45,                    //   Usage (Eraser)                    16
76  * # 0x09, 0x3c,                    //   Usage (Invert)                    18
77  * # 0x15, 0x00,                    //   Logical Minimum (0)               20
78  * # 0x25, 0x01,                    //   Logical Maximum (1)               22
79  * # 0x75, 0x01,                    //   Report Size (1)                   24
80  * # 0x95, 0x06,                    //   Report Count (6)                  26
81  * # 0x81, 0x02,                    //   Input (Data,Var,Abs)              28
82  * # 0x09, 0x32,                    //   Usage (In Range)                  30
83  * # 0x75, 0x01,                    //   Report Size (1)                   32
84  * # 0x95, 0x01,                    //   Report Count (1)                  34
85  * # 0x81, 0x02,                    //   Input (Data,Var,Abs)              36
86  * # 0x81, 0x03,                    //   Input (Cnst,Var,Abs)              38
87  * # 0x05, 0x01,                    //   Usage Page (Generic Desktop)      40
88  * # 0x09, 0x30,                    //   Usage (X)                         42
89  * # 0x09, 0x31,                    //   Usage (Y)                         44
90  * # 0x55, 0x0d,                    //   Unit Exponent (-3)                46
91  * # 0x65, 0x33,                    //   Unit (EnglishLinear: in³)         48
92  * # 0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           50
93  * # 0x35, 0x00,                    //   Physical Minimum (0)              53
94  * # 0x46, 0x00, 0x08,              //   Physical Maximum (2048)           55
95  * # 0x75, 0x10,                    //   Report Size (16)                  58
96  * # 0x95, 0x02,                    //   Report Count (2)                  60
97  * # 0x81, 0x02,                    //   Input (Data,Var,Abs)              62
98  * # 0x05, 0x0d,                    //   Usage Page (Digitizers)           64
99  * # 0x09, 0x30,                    //   Usage (Tip Pressure)              66
100  * # 0x26, 0xff, 0x1f,              //   Logical Maximum (8191)            68
101  * # 0x75, 0x10,                    //   Report Size (16)                  71
102  * # 0x95, 0x01,                    //   Report Count (1)                  73
103  * # 0x81, 0x02,                    //   Input (Data,Var,Abs)              75
104  * # 0x09, 0x3d,                    //   Usage (X Tilt)                    77
105  * # 0x09, 0x3e,                    //   Usage (Y Tilt)                    79
106  * # 0x15, 0x81,                    //   Logical Minimum (-127)            81
107  * # 0x25, 0x7f,                    //   Logical Maximum (127)             83
108  * # 0x75, 0x08,                    //   Report Size (8)                   85
109  * # 0x95, 0x02,                    //   Report Count (2)                  87
110  * # 0x81, 0x02,                    //   Input (Data,Var,Abs)              89
111  * # 0xc0,                          //  End Collection                     91
112  * # 0xc0,                          // End Collection                      92
113  * R: 93 05 0d 09 02 a1 01 85 0a 09 20 a1 01 09 42 09 44 09 45 09 3c 15 00 25 01 75 01 95 06 81 02 09 32 75 01 95 01 81 02 81 03 05 01 09 30 09 31 55 0d 65 33 26 ff 7f 35 00 46 00 08 75 10 95 02 81 02 05 0d 09 30 26 ff 1f 75 10 95 01 81 02 09 3d 09 3e 15 81 25 7f 75 08 95 02 81 02 c0 c0
114  *
115  * Third hidraw node is the pad which sends a combination of keyboard shortcuts until
116  * the tablet is switched to raw mode, then it's mute:
117  *
118  * # Report descriptor length: 148 bytes
119  * # HUION Huion Tablet_Q630M
120  * # 0x05, 0x01,                    // Usage Page (Generic Desktop)        0
121  * # 0x09, 0x0e,                    // Usage (System Multi-Axis Controller) 2
122  * # 0xa1, 0x01,                    // Collection (Application)            4
123  * # 0x85, 0x11,                    //  Report ID (17)                     6
124  * # 0x05, 0x0d,                    //  Usage Page (Digitizers)            8
125  * # 0x09, 0x21,                    //  Usage (Puck)                       10
126  * # 0xa1, 0x02,                    //  Collection (Logical)               12
127  * # 0x15, 0x00,                    //   Logical Minimum (0)               14
128  * # 0x25, 0x01,                    //   Logical Maximum (1)               16
129  * # 0x75, 0x01,                    //   Report Size (1)                   18
130  * # 0x95, 0x01,                    //   Report Count (1)                  20
131  * # 0xa1, 0x00,                    //   Collection (Physical)             22
132  * # 0x05, 0x09,                    //    Usage Page (Button)              24
133  * # 0x09, 0x01,                    //    Usage (Vendor Usage 0x01)        26
134  * # 0x81, 0x02,                    //    Input (Data,Var,Abs)             28
135  * # 0x05, 0x0d,                    //    Usage Page (Digitizers)          30
136  * # 0x09, 0x33,                    //    Usage (Touch)                    32
137  * # 0x81, 0x02,                    //    Input (Data,Var,Abs)             34
138  * # 0x95, 0x06,                    //    Report Count (6)                 36
139  * # 0x81, 0x03,                    //    Input (Cnst,Var,Abs)             38
140  * # 0xa1, 0x02,                    //    Collection (Logical)             40
141  * # 0x05, 0x01,                    //     Usage Page (Generic Desktop)    42
142  * # 0x09, 0x37,                    //     Usage (Dial)                    44
143  * # 0x16, 0x00, 0x80,              //     Logical Minimum (-32768)        46
144  * # 0x26, 0xff, 0x7f,              //     Logical Maximum (32767)         49
145  * # 0x75, 0x10,                    //     Report Size (16)                52
146  * # 0x95, 0x01,                    //     Report Count (1)                54
147  * # 0x81, 0x06,                    //     Input (Data,Var,Rel)            56
148  * # 0x35, 0x00,                    //     Physical Minimum (0)            58
149  * # 0x46, 0x10, 0x0e,              //     Physical Maximum (3600)         60
150  * # 0x15, 0x00,                    //     Logical Minimum (0)             63
151  * # 0x26, 0x10, 0x0e,              //     Logical Maximum (3600)          65
152  * # 0x09, 0x48,                    //     Usage (Resolution Multiplier)   68
153  * # 0xb1, 0x02,                    //     Feature (Data,Var,Abs)          70
154  * # 0x45, 0x00,                    //     Physical Maximum (0)            72
155  * # 0xc0,                          //    End Collection                   74
156  * # 0x75, 0x08,                    //    Report Size (8)                  75
157  * # 0x95, 0x01,                    //    Report Count (1)                 77
158  * # 0x81, 0x01,                    //    Input (Cnst,Arr,Abs)             79
159  * # 0x75, 0x08,                    //    Report Size (8)                  81
160  * # 0x95, 0x01,                    //    Report Count (1)                 83
161  * # 0x81, 0x01,                    //    Input (Cnst,Arr,Abs)             85
162  * # 0x75, 0x08,                    //    Report Size (8)                  87
163  * # 0x95, 0x01,                    //    Report Count (1)                 89
164  * # 0x81, 0x01,                    //    Input (Cnst,Arr,Abs)             91
165  * # 0x75, 0x08,                    //    Report Size (8)                  93
166  * # 0x95, 0x01,                    //    Report Count (1)                 95
167  * # 0x81, 0x01,                    //    Input (Cnst,Arr,Abs)             97
168  * # 0x75, 0x08,                    //    Report Size (8)                  99
169  * # 0x95, 0x01,                    //    Report Count (1)                 101
170  * # 0x81, 0x01,                    //    Input (Cnst,Arr,Abs)             103
171  * # 0xc0,                          //   End Collection                    105
172  * # 0xc0,                          //  End Collection                     106
173  * # 0xc0,                          // End Collection                      107
174  * # 0x05, 0x01,                    // Usage Page (Generic Desktop)        108
175  * # 0x09, 0x06,                    // Usage (Keyboard)                    110
176  * # 0xa1, 0x01,                    // Collection (Application)            112
177  * # 0x85, 0x03,                    //  Report ID (3)                      114
178  * # 0x05, 0x07,                    //  Usage Page (Keyboard)              116
179  * # 0x19, 0xe0,                    //  Usage Minimum (224)                118
180  * # 0x29, 0xe7,                    //  Usage Maximum (231)                120
181  * # 0x15, 0x00,                    //  Logical Minimum (0)                122
182  * # 0x25, 0x01,                    //  Logical Maximum (1)                124
183  * # 0x75, 0x01,                    //  Report Size (1)                    126
184  * # 0x95, 0x08,                    //  Report Count (8)                   128
185  * # 0x81, 0x02,                    //  Input (Data,Var,Abs)               130
186  * # 0x05, 0x07,                    //  Usage Page (Keyboard)              132
187  * # 0x19, 0x00,                    //  Usage Minimum (0)                  134
188  * # 0x29, 0xff,                    //  Usage Maximum (255)                136
189  * # 0x26, 0xff, 0x00,              //  Logical Maximum (255)              138
190  * # 0x75, 0x08,                    //  Report Size (8)                    141
191  * # 0x95, 0x06,                    //  Report Count (6)                   143
192  * # 0x81, 0x00,                    //  Input (Data,Arr,Abs)               145
193  * # 0xc0,                          // End Collection                      147
194  * R: 148 05 01 09 0e a1 01 85 11 05 0d 09 21 a1 02 15 00 25 01 75 01 95 01 a1 00 05 09 09 01 81 02 05 0d 09 33 81 02 95 06 81 03 a1 02 05 01 09 37 16 00 80 26 ff 7f 75 10 95 01 81 06 35 00 46 10 0e 15 00 26 10 0e 09 48 b1 02 45 00 c0 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 c0 c0 c0 05 01 09 06 a1 01 85 03 05 07 19 e0 29 e7 15 00 25 01 75 01 95 08 81 02 05 07 19 00 29 ff 26 ff 00 75 08 95 06 81 00 c0
195  */
196 
197 #define PAD_REPORT_DESCRIPTOR_LENGTH 148
198 #define PEN_REPORT_DESCRIPTOR_LENGTH 93
199 #define VENDOR_REPORT_DESCRIPTOR_LENGTH 18
200 #define PAD_REPORT_ID 3
201 #define DIAL_REPORT_ID 17
202 #define PEN_REPORT_ID 10
203 #define VENDOR_REPORT_ID 8
204 #define PAD_REPORT_LENGTH 9
205 #define PEN_REPORT_LENGTH 10
206 #define VENDOR_REPORT_LENGTH 12
207 
208 
209 __u8 last_button_state;
210 
211 static const __u8 fixed_rdesc_pad[] = {
212 	UsagePage_GenericDesktop
213 	Usage_GD_Keypad
214 	CollectionApplication(
215 		// -- Byte 0 in report
216 		ReportId(PAD_REPORT_ID)
217 		LogicalRange_i8(0, 1)
218 		UsagePage_Digitizers
219 		Usage_Dig_TabletFunctionKeys
220 		CollectionPhysical(
221 			// Byte 1 in report - just exists so we get to be a tablet pad
222 			Usage_Dig_BarrelSwitch // BTN_STYLUS
223 			ReportCount(1)
224 			ReportSize(1)
225 			Input(Var|Abs)
226 			ReportCount(7) // padding
227 			Input(Const)
228 			// Bytes 2/3 in report - just exists so we get to be a tablet pad
229 			UsagePage_GenericDesktop
230 			Usage_GD_X
231 			Usage_GD_Y
232 			ReportCount(2)
233 			ReportSize(8)
234 			Input(Var|Abs)
235 			// Byte 4 in report is the dial
236 			Usage_GD_Wheel
237 			LogicalRange_i8(-1, 1)
238 			ReportCount(1)
239 			ReportSize(8)
240 			Input(Var|Rel)
241 			// Byte 5 is the button state
242 			UsagePage_Button
243 			UsageRange_i8(0x01, 0x8)
244 			LogicalRange_i8(0x0, 0x1)
245 			ReportCount(7)
246 			ReportSize(1)
247 			Input(Var|Abs)
248 			ReportCount(1) // padding
249 			Input(Const)
250 		)
251 		// Make sure we match our original report length
252 		FixedSizeVendorReport(PAD_REPORT_LENGTH)
253 	)
254 };
255 
256 static const __u8 fixed_rdesc_pen[] = {
257 	UsagePage_Digitizers
258 	Usage_Dig_Pen
259 	CollectionApplication(
260 		// -- Byte 0 in report
261 		ReportId(PEN_REPORT_ID)
262 		Usage_Dig_Pen
263 		CollectionPhysical(
264 			// -- Byte 1 in report
265 			Usage_Dig_TipSwitch
266 			Usage_Dig_BarrelSwitch
267 			Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2
268 			LogicalRange_i8(0, 1)
269 			ReportSize(1)
270 			ReportCount(3)
271 			Input(Var|Abs)
272 			ReportCount(4)  // Padding
273 			Input(Const)
274 			Usage_Dig_InRange
275 			ReportCount(1)
276 			Input(Var|Abs)
277 			ReportSize(16)
278 			ReportCount(1)
279 			PushPop(
280 				UsagePage_GenericDesktop
281 				Unit(cm)
282 				UnitExponent(-1)
283 				PhysicalRange_i16(0, 266)
284 				LogicalRange_i16(0, 32767)
285 				Usage_GD_X
286 				Input(Var|Abs) // Bytes 2+3
287 				PhysicalRange_i16(0, 166)
288 				LogicalRange_i16(0, 32767)
289 				Usage_GD_Y
290 				Input(Var|Abs) // Bytes 4+5
291 			)
292 			UsagePage_Digitizers
293 			Usage_Dig_TipPressure
294 			LogicalRange_i16(0, 8191)
295 			Input(Var|Abs) // Byte 6+7
296 			ReportSize(8)
297 			ReportCount(2)
298 			LogicalRange_i8(-60, 60)
299 			Usage_Dig_XTilt
300 			Usage_Dig_YTilt
301 			Input(Var|Abs) // Byte 8+9
302 		)
303 	)
304 };
305 
306 static const __u8 fixed_rdesc_vendor[] = {
307 	UsagePage_Digitizers
308 	Usage_Dig_Pen
309 	CollectionApplication(
310 		// Byte 0
311 		// We leave the pen on the vendor report ID
312 		ReportId(VENDOR_REPORT_ID)
313 		Usage_Dig_Pen
314 		CollectionPhysical(
315 			// Byte 1 are the buttons
316 			LogicalRange_i8(0, 1)
317 			ReportSize(1)
318 			Usage_Dig_TipSwitch
319 			Usage_Dig_BarrelSwitch
320 			Usage_Dig_SecondaryBarrelSwitch
321 			ReportCount(3)
322 			Input(Var|Abs)
323 			ReportCount(4) // Padding
324 			Input(Const)
325 			Usage_Dig_InRange
326 			ReportCount(1)
327 			Input(Var|Abs)
328 			ReportSize(16)
329 			ReportCount(1)
330 			PushPop(
331 				UsagePage_GenericDesktop
332 				Unit(cm)
333 				UnitExponent(-1)
334 				// Note: reported logical range differs
335 				// from the pen report ID for x and y
336 				LogicalRange_i16(0, 53340)
337 				PhysicalRange_i16(0, 266)
338 				// Bytes 2/3 in report
339 				Usage_GD_X
340 				Input(Var|Abs)
341 				LogicalRange_i16(0, 33340)
342 				PhysicalRange_i16(0, 166)
343 				// Bytes 4/5 in report
344 				Usage_GD_Y
345 				Input(Var|Abs)
346 			)
347 			// Bytes 6/7 in report
348 			LogicalRange_i16(0, 8191)
349 			Usage_Dig_TipPressure
350 			Input(Var|Abs)
351 			// Bytes 8/9 in report
352 			ReportCount(1) // Padding
353 			Input(Const)
354 			LogicalRange_i8(-60, 60)
355 			// Byte 10 in report
356 			Usage_Dig_XTilt
357 			// Byte 11 in report
358 			Usage_Dig_YTilt
359 			ReportSize(8)
360 			ReportCount(2)
361 			Input(Var|Abs)
362 		)
363 	)
364 	UsagePage_GenericDesktop
365 	Usage_GD_Keypad
366 	CollectionApplication(
367 		// Byte 0
368 		ReportId(PAD_REPORT_ID)
369 		LogicalRange_i8(0, 1)
370 		UsagePage_Digitizers
371 		Usage_Dig_TabletFunctionKeys
372 		CollectionPhysical(
373 			// Byte 1 are the buttons
374 			Usage_Dig_BarrelSwitch	 // BTN_STYLUS, needed so we get to be a tablet pad
375 			ReportCount(1)
376 			ReportSize(1)
377 			Input(Var|Abs)
378 			ReportCount(7) // Padding
379 			Input(Const)
380 			// Bytes 2/3 - x/y just exist so we get to be a tablet pad
381 			UsagePage_GenericDesktop
382 			Usage_GD_X
383 			Usage_GD_Y
384 			ReportCount(2)
385 			ReportSize(8)
386 			Input(Var|Abs)
387 			// Byte 4 is the button state
388 			UsagePage_Button
389 			UsageRange_i8(0x01, 0x8)
390 			LogicalRange_i8(0x0, 0x1)
391 			ReportCount(8)
392 			ReportSize(1)
393 			Input(Var|Abs)
394 			// Byte 5 is the top dial
395 			UsagePage_GenericDesktop
396 			Usage_GD_Wheel
397 			LogicalRange_i8(-1, 1)
398 			ReportCount(1)
399 			ReportSize(8)
400 			Input(Var|Rel)
401 			// Byte 6 is the bottom dial
402 			UsagePage_Consumer
403 			Usage_Con_ACPan
404 			Input(Var|Rel)
405 		)
406 		// Make sure we match our original report length
407 		FixedSizeVendorReport(VENDOR_REPORT_LENGTH)
408 	)
409 };
410 
411 static const __u8 disabled_rdesc_pen[] = {
412 	FixedSizeVendorReport(PEN_REPORT_LENGTH)
413 };
414 
415 static const __u8 disabled_rdesc_pad[] = {
416 	FixedSizeVendorReport(PAD_REPORT_LENGTH)
417 };
418 
419 SEC(HID_BPF_RDESC_FIXUP)
420 int BPF_PROG(dial_2_fix_rdesc, struct hid_bpf_ctx *hctx)
421 {
422 	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
423 	__s32 rdesc_size = hctx->size;
424 	__u8 have_fw_id;
425 
426 	if (!data)
427 		return 0; /* EPERM check */
428 
429 	/* If we have a firmware ID and it matches our expected prefix, we
430 	 * disable the default pad/pen nodes. They won't send events
431 	 * but cause duplicate devices.
432 	 */
433 	have_fw_id = __builtin_memcmp(UDEV_PROP_HUION_FIRMWARE_ID,
434 				      EXPECTED_FIRMWARE_ID,
435 				      sizeof(EXPECTED_FIRMWARE_ID) - 1) == 0;
436 	if (rdesc_size == PAD_REPORT_DESCRIPTOR_LENGTH) {
437 		if (have_fw_id) {
438 			__builtin_memcpy(data, disabled_rdesc_pad, sizeof(disabled_rdesc_pad));
439 			return sizeof(disabled_rdesc_pad);
440 		}
441 
442 		__builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad));
443 		return sizeof(fixed_rdesc_pad);
444 	}
445 	if (rdesc_size == PEN_REPORT_DESCRIPTOR_LENGTH) {
446 		if (have_fw_id) {
447 			__builtin_memcpy(data, disabled_rdesc_pen, sizeof(disabled_rdesc_pen));
448 			return sizeof(disabled_rdesc_pen);
449 		}
450 
451 		__builtin_memcpy(data, fixed_rdesc_pen, sizeof(fixed_rdesc_pen));
452 		return sizeof(fixed_rdesc_pen);
453 	}
454 	/* Always fix the vendor mode so the tablet will work even if nothing sets
455 	 * the udev property (e.g. huion-switcher run manually)
456 	 */
457 	if (rdesc_size == VENDOR_REPORT_DESCRIPTOR_LENGTH) {
458 		__builtin_memcpy(data, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor));
459 		return sizeof(fixed_rdesc_vendor);
460 
461 	}
462 	return 0;
463 }
464 
465 SEC(HID_BPF_DEVICE_EVENT)
466 int BPF_PROG(dial_2_fix_events, struct hid_bpf_ctx *hctx)
467 {
468 	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 16 /* size */);
469 	static __u8 button;
470 
471 	if (!data)
472 		return 0; /* EPERM check */
473 
474 	/* Only sent if tablet is in default mode */
475 	if (data[0] == PAD_REPORT_ID) {
476 		/* Nicely enough, this device only supports one button down at a time so
477 		 * the reports are easy to match. Buttons numbered from the top
478 		 *   Button released: 03 00 00 00 00 00 00 00
479 		 *   Button 1: 03 00 05 00 00 00 00 00 -> b
480 		 *   Button 2: 03 00 08 00 00 00 00 00 -> e
481 		 *   Button 3: 03 00 0c 00 00 00 00 00 -> i
482 		 *   Button 4: 03 00 e0 16 00 00 00 00 -> Ctrl S
483 		 *   Button 5: 03 00 2c 00 00 00 00 00 -> space
484 		 *   Button 6: 03 00 e0 e2 1d 00 00 00 -> Ctrl Alt Z
485 		 */
486 		button &= 0xc0;
487 
488 		switch ((data[2] << 16) | (data[3] << 8) | data[4]) {
489 		case 0x000000:
490 			break;
491 		case 0x050000:
492 			button |= BIT(0);
493 			break;
494 		case 0x080000:
495 			button |= BIT(1);
496 			break;
497 		case 0x0c0000:
498 			button |= BIT(2);
499 			break;
500 		case 0xe01600:
501 			button |= BIT(3);
502 			break;
503 		case 0x2c0000:
504 			button |= BIT(4);
505 			break;
506 		case 0xe0e21d:
507 			button |= BIT(5);
508 			break;
509 		}
510 
511 		__u8 report[8] = {PAD_REPORT_ID, 0x0, 0x0, 0x0, 0x00, button};
512 
513 		__builtin_memcpy(data, report, sizeof(report));
514 		return sizeof(report);
515 	}
516 
517 	/* Only sent if tablet is in default mode */
518 	if (data[0] == DIAL_REPORT_ID) {
519 		/*
520 		 * In default mode, both dials are merged together:
521 		 *
522 		 *   Dial down: 11 00 ff ff 00 00 00 00 00 -> Dial -1
523 		 *   Dial up:   11 00 01 00 00 00 00 00 00 -> Dial 1
524 		 */
525 		__u16 dial = data[3] << 8 | data[2];
526 
527 		button &= 0x3f;
528 		button |= !!data[1] << 6;
529 
530 		__u8 report[] = {PAD_REPORT_ID, 0x0, 0x0, 0x0, dial, button};
531 
532 		__builtin_memcpy(data, report, sizeof(report));
533 		return sizeof(report);
534 	}
535 
536 	/* Nothing to do for the PEN_REPORT_ID, it's already mapped */
537 
538 	/* Only sent if tablet is in raw mode */
539 	if (data[0] == VENDOR_REPORT_ID) {
540 		/* Pad reports */
541 		if (data[1] & 0x20) {
542 			/* See fixed_rdesc_pad */
543 			struct pad_report {
544 				__u8 report_id;
545 				__u8 btn_stylus;
546 				__u8 x;
547 				__u8 y;
548 				__u8 buttons;
549 				__u8 dial_1;
550 				__u8 dial_2;
551 			} __attribute__((packed)) *pad_report;
552 			__u8 dial_1 = 0, dial_2 = 0;
553 
554 			/* Dial report */
555 			if (data[1] == 0xf1) {
556 				__u8 d = 0;
557 
558 				if (data[5] == 2)
559 					d = 0xff;
560 				else
561 					d = data[5];
562 
563 				if (data[3] == 1)
564 					dial_1 = d;
565 				else
566 					dial_2 = d;
567 			} else {
568 				/* data[4] are the buttons, mapped correctly */
569 				last_button_state = data[4];
570 				dial_1 = 0; // dial
571 				dial_2 = 0;
572 			}
573 
574 			pad_report = (struct pad_report *)data;
575 
576 			pad_report->report_id = PAD_REPORT_ID;
577 			pad_report->btn_stylus = 0;
578 			pad_report->x = 0;
579 			pad_report->y = 0;
580 			pad_report->buttons = last_button_state;
581 			pad_report->dial_1 = dial_1;
582 			pad_report->dial_2 = dial_2;
583 
584 			return sizeof(struct pad_report);
585 		}
586 
587 		/* Pen reports need nothing done */
588 	}
589 
590 	return 0;
591 }
592 
593 HID_BPF_OPS(inspiroy_dial2) = {
594 	.hid_device_event = (void *)dial_2_fix_events,
595 	.hid_rdesc_fixup = (void *)dial_2_fix_rdesc,
596 };
597 
598 SEC("syscall")
599 int probe(struct hid_bpf_probe_args *ctx)
600 {
601 	switch (ctx->rdesc_size) {
602 	case PAD_REPORT_DESCRIPTOR_LENGTH:
603 	case PEN_REPORT_DESCRIPTOR_LENGTH:
604 	case VENDOR_REPORT_DESCRIPTOR_LENGTH:
605 		ctx->retval = 0;
606 		break;
607 	default:
608 		ctx->retval = -EINVAL;
609 	}
610 
611 	return 0;
612 }
613 
614 char _license[] SEC("license") = "GPL";
615