Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/hid/bpf/progs/Huion__Dial-2.bpf.c
26285 views
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
638