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
SEC(HID_BPF_RDESC_FIXUP)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
SEC(HID_BPF_DEVICE_EVENT)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")
probe(struct hid_bpf_probe_args * ctx)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