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