]> Git Repo - linux.git/blob - drivers/hid/bpf/progs/Huion__Dial-2.bpf.c
Linux 6.14-rc3
[linux.git] / drivers / hid / bpf / progs / Huion__Dial-2.bpf.c
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";
This page took 0.067567 seconds and 4 git commands to generate.