File Index
Files and directories
Sign in
FILE

Huion__Dial-2.bpf.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hid/bpf/progs/Huion__Dial-2.bpf.c 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
 15HID_BPF_CONFIG(
 16	HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HUION, PID_DIAL_2),
 17);
 18
 19/* Filled in by udev-hid-bpf */
 20char 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 */
 25char 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
211static 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
260static 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
317static 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
433static const __u8 disabled_rdesc_pen[] = {
434	FixedSizeVendorReport(PEN_REPORT_LENGTH)
435};
436
437static const __u8 disabled_rdesc_pad[] = {
438	FixedSizeVendorReport(PAD_REPORT_LENGTH)
439};
440
441SEC(HID_BPF_RDESC_FIXUP)
442int 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
487SEC(HID_BPF_DEVICE_EVENT)
488int 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
615HID_BPF_OPS(inspiroy_dial2) = {
616	.hid_device_event = (void *)dial_2_fix_events,
617	.hid_rdesc_fixup = (void *)dial_2_fix_rdesc,
618};
619
620SEC("syscall")
621int 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
636char _license[] SEC("license") = "GPL";
~/ Read-only file preview.