xref: /linux/drivers/hid/bpf/progs/Huion__Dial-2.bpf.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
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 		LogicalMaximum_i8(0)
218 		LogicalMaximum_i8(1)
219 		UsagePage_Digitizers
220 		Usage_Dig_TabletFunctionKeys
221 		CollectionPhysical(
222 			// Byte 1 in report - just exists so we get to be a tablet pad
223 			Usage_Dig_BarrelSwitch // BTN_STYLUS
224 			ReportCount(1)
225 			ReportSize(1)
226 			Input(Var|Abs)
227 			ReportCount(7) // padding
228 			Input(Const)
229 			// Bytes 2/3 in report - just exists so we get to be a tablet pad
230 			UsagePage_GenericDesktop
231 			Usage_GD_X
232 			Usage_GD_Y
233 			ReportCount(2)
234 			ReportSize(8)
235 			Input(Var|Abs)
236 			// Byte 4 in report is the dial
237 			Usage_GD_Wheel
238 			LogicalMinimum_i8(-1)
239 			LogicalMaximum_i8(1)
240 			ReportCount(1)
241 			ReportSize(8)
242 			Input(Var|Rel)
243 			// Byte 5 is the button state
244 			UsagePage_Button
245 			UsageMinimum_i8(0x01)
246 			UsageMaximum_i8(0x08)
247 			LogicalMinimum_i8(0x0)
248 			LogicalMaximum_i8(0x1)
249 			ReportCount(7)
250 			ReportSize(1)
251 			Input(Var|Abs)
252 			ReportCount(1) // padding
253 			Input(Const)
254 		)
255 		// Make sure we match our original report length
256 		FixedSizeVendorReport(PAD_REPORT_LENGTH)
257 	)
258 };
259 
260 static const __u8 fixed_rdesc_pen[] = {
261 	UsagePage_Digitizers
262 	Usage_Dig_Pen
263 	CollectionApplication(
264 		// -- Byte 0 in report
265 		ReportId(PEN_REPORT_ID)
266 		Usage_Dig_Pen
267 		CollectionPhysical(
268 			// -- Byte 1 in report
269 			Usage_Dig_TipSwitch
270 			Usage_Dig_BarrelSwitch
271 			Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2
272 			LogicalMinimum_i8(0)
273 			LogicalMaximum_i8(1)
274 			ReportSize(1)
275 			ReportCount(3)
276 			Input(Var|Abs)
277 			ReportCount(4)  // Padding
278 			Input(Const)
279 			Usage_Dig_InRange
280 			ReportCount(1)
281 			Input(Var|Abs)
282 			ReportSize(16)
283 			ReportCount(1)
284 			PushPop(
285 				UsagePage_GenericDesktop
286 				Unit(cm)
287 				UnitExponent(-1)
288 				PhysicalMinimum_i16(0)
289 				PhysicalMaximum_i16(266)
290 				LogicalMinimum_i16(0)
291 				LogicalMaximum_i16(32767)
292 				Usage_GD_X
293 				Input(Var|Abs) // Bytes 2+3
294 				PhysicalMinimum_i16(0)
295 				PhysicalMaximum_i16(166)
296 				LogicalMinimum_i16(0)
297 				LogicalMaximum_i16(32767)
298 				Usage_GD_Y
299 				Input(Var|Abs) // Bytes 4+5
300 			)
301 			UsagePage_Digitizers
302 			Usage_Dig_TipPressure
303 			LogicalMinimum_i16(0)
304 			LogicalMaximum_i16(8191)
305 			Input(Var|Abs) // Byte 6+7
306 			ReportSize(8)
307 			ReportCount(2)
308 			LogicalMinimum_i8(-60)
309 			LogicalMaximum_i8(60)
310 			Usage_Dig_XTilt
311 			Usage_Dig_YTilt
312 			Input(Var|Abs) // Byte 8+9
313 		)
314 	)
315 };
316 
317 static const __u8 fixed_rdesc_vendor[] = {
318 	UsagePage_Digitizers
319 	Usage_Dig_Pen
320 	CollectionApplication(
321 		// Byte 0
322 		// We leave the pen on the vendor report ID
323 		ReportId(VENDOR_REPORT_ID)
324 		Usage_Dig_Pen
325 		CollectionPhysical(
326 			// Byte 1 are the buttons
327 			LogicalMinimum_i8(0)
328 			LogicalMaximum_i8(1)
329 			ReportSize(1)
330 			Usage_Dig_TipSwitch
331 			Usage_Dig_BarrelSwitch
332 			Usage_Dig_SecondaryBarrelSwitch
333 			ReportCount(3)
334 			Input(Var|Abs)
335 			ReportCount(4) // Padding
336 			Input(Const)
337 			Usage_Dig_InRange
338 			ReportCount(1)
339 			Input(Var|Abs)
340 			ReportSize(16)
341 			ReportCount(1)
342 			PushPop(
343 				UsagePage_GenericDesktop
344 				Unit(cm)
345 				UnitExponent(-1)
346 				// Note: reported logical range differs
347 				// from the pen report ID for x and y
348 				LogicalMinimum_i16(0)
349 				LogicalMaximum_i16(53340)
350 				PhysicalMinimum_i16(0)
351 				PhysicalMaximum_i16(266)
352 				// Bytes 2/3 in report
353 				Usage_GD_X
354 				Input(Var|Abs)
355 				LogicalMinimum_i16(0)
356 				LogicalMaximum_i16(33340)
357 				PhysicalMinimum_i16(0)
358 				PhysicalMaximum_i16(166)
359 				// Bytes 4/5 in report
360 				Usage_GD_Y
361 				Input(Var|Abs)
362 			)
363 			// Bytes 6/7 in report
364 			LogicalMinimum_i16(0)
365 			LogicalMaximum_i16(8191)
366 			Usage_Dig_TipPressure
367 			Input(Var|Abs)
368 			// Bytes 8/9 in report
369 			ReportCount(1) // Padding
370 			Input(Const)
371 			LogicalMinimum_i8(-60)
372 			LogicalMaximum_i8(60)
373 			// Byte 10 in report
374 			Usage_Dig_XTilt
375 			// Byte 11 in report
376 			Usage_Dig_YTilt
377 			ReportSize(8)
378 			ReportCount(2)
379 			Input(Var|Abs)
380 		)
381 	)
382 	UsagePage_GenericDesktop
383 	Usage_GD_Keypad
384 	CollectionApplication(
385 		// Byte 0
386 		ReportId(PAD_REPORT_ID)
387 		LogicalMinimum_i8(0)
388 		LogicalMaximum_i8(1)
389 		UsagePage_Digitizers
390 		Usage_Dig_TabletFunctionKeys
391 		CollectionPhysical(
392 			// Byte 1 are the buttons
393 			Usage_Dig_BarrelSwitch	 // BTN_STYLUS, needed so we get to be a tablet pad
394 			ReportCount(1)
395 			ReportSize(1)
396 			Input(Var|Abs)
397 			ReportCount(7) // Padding
398 			Input(Const)
399 			// Bytes 2/3 - x/y just exist so we get to be a tablet pad
400 			UsagePage_GenericDesktop
401 			Usage_GD_X
402 			Usage_GD_Y
403 			ReportCount(2)
404 			ReportSize(8)
405 			Input(Var|Abs)
406 			// Byte 4 is the button state
407 			UsagePage_Button
408 			UsageMinimum_i8(0x1)
409 			UsageMaximum_i8(0x8)
410 			LogicalMinimum_i8(0x0)
411 			LogicalMaximum_i8(0x1)
412 			ReportCount(8)
413 			ReportSize(1)
414 			Input(Var|Abs)
415 			// Byte 5 is the top dial
416 			UsagePage_GenericDesktop
417 			Usage_GD_Wheel
418 			LogicalMinimum_i8(-1)
419 			LogicalMaximum_i8(1)
420 			ReportCount(1)
421 			ReportSize(8)
422 			Input(Var|Rel)
423 			// Byte 6 is the bottom dial
424 			UsagePage_Consumer
425 			Usage_Con_ACPan
426 			Input(Var|Rel)
427 		)
428 		// Make sure we match our original report length
429 		FixedSizeVendorReport(VENDOR_REPORT_LENGTH)
430 	)
431 };
432 
433 static const __u8 disabled_rdesc_pen[] = {
434 	FixedSizeVendorReport(PEN_REPORT_LENGTH)
435 };
436 
437 static const __u8 disabled_rdesc_pad[] = {
438 	FixedSizeVendorReport(PAD_REPORT_LENGTH)
439 };
440 
441 SEC(HID_BPF_RDESC_FIXUP)
442 int BPF_PROG(dial_2_fix_rdesc, struct hid_bpf_ctx *hctx)
443 {
444 	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
445 	__s32 rdesc_size = hctx->size;
446 	__u8 have_fw_id;
447 
448 	if (!data)
449 		return 0; /* EPERM check */
450 
451 	/* If we have a firmware ID and it matches our expected prefix, we
452 	 * disable the default pad/pen nodes. They won't send events
453 	 * but cause duplicate devices.
454 	 */
455 	have_fw_id = __builtin_memcmp(UDEV_PROP_HUION_FIRMWARE_ID,
456 				      EXPECTED_FIRMWARE_ID,
457 				      sizeof(EXPECTED_FIRMWARE_ID) - 1) == 0;
458 	if (rdesc_size == PAD_REPORT_DESCRIPTOR_LENGTH) {
459 		if (have_fw_id) {
460 			__builtin_memcpy(data, disabled_rdesc_pad, sizeof(disabled_rdesc_pad));
461 			return sizeof(disabled_rdesc_pad);
462 		}
463 
464 		__builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad));
465 		return sizeof(fixed_rdesc_pad);
466 	}
467 	if (rdesc_size == PEN_REPORT_DESCRIPTOR_LENGTH) {
468 		if (have_fw_id) {
469 			__builtin_memcpy(data, disabled_rdesc_pen, sizeof(disabled_rdesc_pen));
470 			return sizeof(disabled_rdesc_pen);
471 		}
472 
473 		__builtin_memcpy(data, fixed_rdesc_pen, sizeof(fixed_rdesc_pen));
474 		return sizeof(fixed_rdesc_pen);
475 	}
476 	/* Always fix the vendor mode so the tablet will work even if nothing sets
477 	 * the udev property (e.g. huion-switcher run manually)
478 	 */
479 	if (rdesc_size == VENDOR_REPORT_DESCRIPTOR_LENGTH) {
480 		__builtin_memcpy(data, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor));
481 		return sizeof(fixed_rdesc_vendor);
482 
483 	}
484 	return 0;
485 }
486 
487 SEC(HID_BPF_DEVICE_EVENT)
488 int BPF_PROG(dial_2_fix_events, struct hid_bpf_ctx *hctx)
489 {
490 	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 16 /* size */);
491 	static __u8 button;
492 
493 	if (!data)
494 		return 0; /* EPERM check */
495 
496 	/* Only sent if tablet is in default mode */
497 	if (data[0] == PAD_REPORT_ID) {
498 		/* Nicely enough, this device only supports one button down at a time so
499 		 * the reports are easy to match. Buttons numbered from the top
500 		 *   Button released: 03 00 00 00 00 00 00 00
501 		 *   Button 1: 03 00 05 00 00 00 00 00 -> b
502 		 *   Button 2: 03 00 08 00 00 00 00 00 -> e
503 		 *   Button 3: 03 00 0c 00 00 00 00 00 -> i
504 		 *   Button 4: 03 00 e0 16 00 00 00 00 -> Ctrl S
505 		 *   Button 5: 03 00 2c 00 00 00 00 00 -> space
506 		 *   Button 6: 03 00 e0 e2 1d 00 00 00 -> Ctrl Alt Z
507 		 */
508 		button &= 0xc0;
509 
510 		switch ((data[2] << 16) | (data[3] << 8) | data[4]) {
511 		case 0x000000:
512 			break;
513 		case 0x050000:
514 			button |= BIT(0);
515 			break;
516 		case 0x080000:
517 			button |= BIT(1);
518 			break;
519 		case 0x0c0000:
520 			button |= BIT(2);
521 			break;
522 		case 0xe01600:
523 			button |= BIT(3);
524 			break;
525 		case 0x2c0000:
526 			button |= BIT(4);
527 			break;
528 		case 0xe0e21d:
529 			button |= BIT(5);
530 			break;
531 		}
532 
533 		__u8 report[8] = {PAD_REPORT_ID, 0x0, 0x0, 0x0, 0x00, button};
534 
535 		__builtin_memcpy(data, report, sizeof(report));
536 		return sizeof(report);
537 	}
538 
539 	/* Only sent if tablet is in default mode */
540 	if (data[0] == DIAL_REPORT_ID) {
541 		/*
542 		 * In default mode, both dials are merged together:
543 		 *
544 		 *   Dial down: 11 00 ff ff 00 00 00 00 00 -> Dial -1
545 		 *   Dial up:   11 00 01 00 00 00 00 00 00 -> Dial 1
546 		 */
547 		__u16 dial = data[3] << 8 | data[2];
548 
549 		button &= 0x3f;
550 		button |= !!data[1] << 6;
551 
552 		__u8 report[] = {PAD_REPORT_ID, 0x0, 0x0, 0x0, dial, button};
553 
554 		__builtin_memcpy(data, report, sizeof(report));
555 		return sizeof(report);
556 	}
557 
558 	/* Nothing to do for the PEN_REPORT_ID, it's already mapped */
559 
560 	/* Only sent if tablet is in raw mode */
561 	if (data[0] == VENDOR_REPORT_ID) {
562 		/* Pad reports */
563 		if (data[1] & 0x20) {
564 			/* See fixed_rdesc_pad */
565 			struct pad_report {
566 				__u8 report_id;
567 				__u8 btn_stylus;
568 				__u8 x;
569 				__u8 y;
570 				__u8 buttons;
571 				__u8 dial_1;
572 				__u8 dial_2;
573 			} __attribute__((packed)) *pad_report;
574 			__u8 dial_1 = 0, dial_2 = 0;
575 
576 			/* Dial report */
577 			if (data[1] == 0xf1) {
578 				__u8 d = 0;
579 
580 				if (data[5] == 2)
581 					d = 0xff;
582 				else
583 					d = data[5];
584 
585 				if (data[3] == 1)
586 					dial_1 = d;
587 				else
588 					dial_2 = d;
589 			} else {
590 				/* data[4] are the buttons, mapped correctly */
591 				last_button_state = data[4];
592 				dial_1 = 0; // dial
593 				dial_2 = 0;
594 			}
595 
596 			pad_report = (struct pad_report *)data;
597 
598 			pad_report->report_id = PAD_REPORT_ID;
599 			pad_report->btn_stylus = 0;
600 			pad_report->x = 0;
601 			pad_report->y = 0;
602 			pad_report->buttons = last_button_state;
603 			pad_report->dial_1 = dial_1;
604 			pad_report->dial_2 = dial_2;
605 
606 			return sizeof(struct pad_report);
607 		}
608 
609 		/* Pen reports need nothing done */
610 	}
611 
612 	return 0;
613 }
614 
615 HID_BPF_OPS(inspiroy_dial2) = {
616 	.hid_device_event = (void *)dial_2_fix_events,
617 	.hid_rdesc_fixup = (void *)dial_2_fix_rdesc,
618 };
619 
620 SEC("syscall")
621 int probe(struct hid_bpf_probe_args *ctx)
622 {
623 	switch (ctx->rdesc_size) {
624 	case PAD_REPORT_DESCRIPTOR_LENGTH:
625 	case PEN_REPORT_DESCRIPTOR_LENGTH:
626 	case VENDOR_REPORT_DESCRIPTOR_LENGTH:
627 		ctx->retval = 0;
628 		break;
629 	default:
630 		ctx->retval = -EINVAL;
631 	}
632 
633 	return 0;
634 }
635 
636 char _license[] SEC("license") = "GPL";
637