Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/hid/bpf/progs/Huion__KeydialK20.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_KEYDIAL_K20 0x0069
13
14
HID_BPF_CONFIG(
15
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HUION, PID_KEYDIAL_K20),
16
);
17
18
/* Filled in by udev-hid-bpf */
19
char UDEV_PROP_HUION_FIRMWARE_ID[64];
20
21
/* The prefix of the firmware ID we expect for this device. The full firmware
22
* string has a date suffix, e.g. HUION_T21h_230511
23
*/
24
char EXPECTED_FIRMWARE_ID[] = "HUION_T21h_";
25
26
/* How this BPF program works: the tablet has two modes, firmware mode and
27
* tablet mode. In firmware mode (out of the box) the tablet sends button events
28
* as keyboard shortcuts and the dial as wheel but it's not forwarded by the kernel.
29
* In tablet mode it uses a vendor specific hid report to report everything instead.
30
* Depending on the mode some hid reports are never sent and the corresponding
31
* devices are mute.
32
*
33
* To switch the tablet use e.g. https://github.com/whot/huion-switcher
34
* or one of the tools from the digimend project
35
*
36
* This BPF currently works for both modes only. The huion-switcher tool sets the
37
* HUION_FIRMWARE_ID udev property - if that is set then we disable the firmware
38
* pad and pen reports (by making them vendor collections that are ignored).
39
* If that property is not set we fix all hidraw nodes so the tablet works in
40
* either mode though the drawback is that the device will show up twice if
41
* you bind it to all event nodes
42
*
43
* Default report descriptor for the first exposed hidraw node:
44
*
45
* # HUION Huion Keydial_K20
46
* # Report descriptor length: 18 bytes
47
* # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 0xFF00) 0
48
* # 0x09, 0x01, // Usage (Vendor Usage 0x01) 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 0x01) 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 report descriptor appears to be identical for all Huion devices.
59
*
60
* Second hidraw node is the Pad. This one sends the button events until the tablet is
61
* switched to raw mode, then it's mute.
62
*
63
* # HUION Huion Keydial_K20
64
* # Report descriptor length: 135 bytes
65
* # 0x05, 0x01, // Usage Page (Generic Desktop) 0
66
* # 0x09, 0x06, // Usage (Keyboard) 2
67
* # 0xa1, 0x01, // Collection (Application) 4
68
* # 0x85, 0x03, // Report ID (3) 6
69
* # 0x05, 0x07, // Usage Page (Keyboard/Keypad) 8
70
* # 0x19, 0xe0, // UsageMinimum (224) 10
71
* # 0x29, 0xe7, // UsageMaximum (231) 12
72
* # 0x15, 0x00, // Logical Minimum (0) 14
73
* # 0x25, 0x01, // Logical Maximum (1) 16
74
* # 0x75, 0x01, // Report Size (1) 18
75
* # 0x95, 0x08, // Report Count (8) 20
76
* # 0x81, 0x02, // Input (Data,Var,Abs) 22
77
* # 0x05, 0x07, // Usage Page (Keyboard/Keypad) 24
78
* # 0x19, 0x00, // UsageMinimum (0) 26
79
* # 0x29, 0xff, // UsageMaximum (255) 28
80
* # 0x26, 0xff, 0x00, // Logical Maximum (255) 30
81
* # 0x75, 0x08, // Report Size (8) 33
82
* # 0x95, 0x06, // Report Count (6) 35
83
* # 0x81, 0x00, // Input (Data,Arr,Abs) 37
84
* # 0xc0, // End Collection 39
85
* # 0x05, 0x0c, // Usage Page (Consumer) 40
86
* # 0x09, 0x01, // Usage (Consumer Control) 42
87
* # 0xa1, 0x01, // Collection (Application) 44
88
* # 0x85, 0x04, // Report ID (4) 46
89
* # 0x05, 0x0c, // Usage Page (Consumer) 48
90
* # 0x19, 0x00, // UsageMinimum (0) 50
91
* # 0x2a, 0x80, 0x03, // UsageMaximum (896) 52
92
* # 0x15, 0x00, // Logical Minimum (0) 55
93
* # 0x26, 0x80, 0x03, // Logical Maximum (896) 57
94
* # 0x75, 0x10, // Report Size (16) 60
95
* # 0x95, 0x01, // Report Count (1) 62
96
* # 0x81, 0x00, // Input (Data,Arr,Abs) 64
97
* # 0xc0, // End Collection 66
98
* # 0x05, 0x01, // Usage Page (Generic Desktop) 67
99
* # 0x09, 0x02, // Usage (Mouse) 69
100
* # 0xa1, 0x01, // Collection (Application) 71
101
* # 0x09, 0x01, // Usage (Pointer) 73
102
* # 0x85, 0x05, // Report ID (5) 75
103
* # 0xa1, 0x00, // Collection (Physical) 77
104
* # 0x05, 0x09, // Usage Page (Button) 79
105
* # 0x19, 0x01, // UsageMinimum (1) 81
106
* # 0x29, 0x05, // UsageMaximum (5) 83
107
* # 0x15, 0x00, // Logical Minimum (0) 85
108
* # 0x25, 0x01, // Logical Maximum (1) 87
109
* # 0x95, 0x05, // Report Count (5) 89
110
* # 0x75, 0x01, // Report Size (1) 91
111
* # 0x81, 0x02, // Input (Data,Var,Abs) 93
112
* # 0x95, 0x01, // Report Count (1) 95
113
* # 0x75, 0x03, // Report Size (3) 97
114
* # 0x81, 0x01, // Input (Cnst,Arr,Abs) 99
115
* # 0x05, 0x01, // Usage Page (Generic Desktop) 101
116
* # 0x09, 0x30, // Usage (X) 103
117
* # 0x09, 0x31, // Usage (Y) 105
118
* # 0x16, 0x00, 0x80, // Logical Minimum (-32768) 107
119
* # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 110
120
* # 0x75, 0x10, // Report Size (16) 113
121
* # 0x95, 0x02, // Report Count (2) 115
122
* # 0x81, 0x06, // Input (Data,Var,Rel) 117
123
* # 0x95, 0x01, // Report Count (1) 119
124
* # 0x75, 0x08, // Report Size (8) 121
125
* # 0x05, 0x01, // Usage Page (Generic Desktop) 123
126
* # 0x09, 0x38, // Usage (Wheel) 125
127
* # 0x15, 0x81, // Logical Minimum (-127) 127
128
* # 0x25, 0x7f, // Logical Maximum (127) 129
129
* # 0x81, 0x06, // Input (Data,Var,Rel) 131
130
* # 0xc0, // End Collection 133
131
* # 0xc0, // End Collection 134
132
* R: 135 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 05 0c 09 01 a1 01 85 04 05 0c 19 00 2a 80 03 15 00 26 80 03 75 10 95 01 81 00 c0 05 01 09 02 a1 01 09 01 85 05 a1 00 05 09 19 01 29 05 15 00 25 01 95 05 75 01 81 02 95 01 75 03 81 01 05 01 09 30 09 31 16 00 80 26 ff 7f 7510 95 02 81 06 95 01 75 08 05 01 09 38 15 81 25 7f 81 06 c0 c0
133
*
134
* Third hidraw node is a multi-axis controller which sends the dial events
135
* and the button inside the dial. If the tablet is switched to raw mode it is mute.
136
*
137
* # HUION Huion Keydial_K20
138
* # Report descriptor length: 108 bytes
139
* # 0x05, 0x01, // Usage Page (Generic Desktop) 0
140
* # 0x09, 0x0e, // Usage (System Multi-Axis Controller) 2
141
* # 0xa1, 0x01, // Collection (Application) 4
142
* # 0x85, 0x11, // Report ID (17) 6
143
* # 0x05, 0x0d, // Usage Page (Digitizers) 8
144
* # 0x09, 0x21, // Usage (Puck) 10
145
* # 0xa1, 0x02, // Collection (Logical) 12
146
* # 0x15, 0x00, // Logical Minimum (0) 14
147
* # 0x25, 0x01, // Logical Maximum (1) 16
148
* # 0x75, 0x01, // Report Size (1) 18
149
* # 0x95, 0x01, // Report Count (1) 20
150
* # 0xa1, 0x00, // Collection (Physical) 22
151
* # 0x05, 0x09, // Usage Page (Button) 24
152
* # 0x09, 0x01, // Usage (Button 1) 26
153
* # 0x81, 0x02, // Input (Data,Var,Abs) 28
154
* # 0x05, 0x0d, // Usage Page (Digitizers) 30
155
* # 0x09, 0x33, // Usage (Touch) 32
156
* # 0x81, 0x02, // Input (Data,Var,Abs) 34
157
* # 0x95, 0x06, // Report Count (6) 36
158
* # 0x81, 0x03, // Input (Cnst,Var,Abs) 38
159
* # 0xa1, 0x02, // Collection (Logical) 40
160
* # 0x05, 0x01, // Usage Page (Generic Desktop) 42
161
* # 0x09, 0x37, // Usage (Dial) 44
162
* # 0x16, 0x00, 0x80, // Logical Minimum (-32768) 46
163
* # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 49
164
* # 0x75, 0x10, // Report Size (16) 52
165
* # 0x95, 0x01, // Report Count (1) 54
166
* # 0x81, 0x06, // Input (Data,Var,Rel) 56
167
* # 0x35, 0x00, // Physical Minimum (0) 58
168
* # 0x46, 0x10, 0x0e, // Physical Maximum (3600) 60
169
* # 0x15, 0x00, // Logical Minimum (0) 63
170
* # 0x26, 0x10, 0x0e, // Logical Maximum (3600) 65
171
* # 0x09, 0x48, // Usage (Resolution Multiplier) 68
172
* # 0xb1, 0x02, // Feature (Data,Var,Abs) 70
173
* # 0x45, 0x00, // Physical Maximum (0) 72
174
* # 0xc0, // End Collection 74
175
* # 0x75, 0x08, // Report Size (8) 75
176
* # 0x95, 0x01, // Report Count (1) 77
177
* # 0x81, 0x01, // Input (Cnst,Arr,Abs) 79
178
* # 0x75, 0x08, // Report Size (8) 81
179
* # 0x95, 0x01, // Report Count (1) 83
180
* # 0x81, 0x01, // Input (Cnst,Arr,Abs) 85
181
* # 0x75, 0x08, // Report Size (8) 87
182
* # 0x95, 0x01, // Report Count (1) 89
183
* # 0x81, 0x01, // Input (Cnst,Arr,Abs) 91
184
* # 0x75, 0x08, // Report Size (8) 93
185
* # 0x95, 0x01, // Report Count (1) 95
186
* # 0x81, 0x01, // Input (Cnst,Arr,Abs) 97
187
* # 0x75, 0x08, // Report Size (8) 99
188
* # 0x95, 0x01, // Report Count (1) 101
189
* # 0x81, 0x01, // Input (Cnst,Arr,Abs) 103
190
* # 0xc0, // End Collection 105
191
* # 0xc0, // End Collection 106
192
* # 0xc0, // End Collection 107
193
* R: 108 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
194
*
195
*/
196
197
#define PAD_REPORT_DESCRIPTOR_LENGTH 135
198
#define PUCK_REPORT_DESCRIPTOR_LENGTH 108
199
#define VENDOR_REPORT_DESCRIPTOR_LENGTH 18
200
#define PAD_KBD_REPORT_ID 3
201
#define PAD_CC_REPORT_ID 3 // never sends events
202
#define PAD_MOUSE_REPORT_ID 4 // never sends events
203
#define PUCK_REPORT_ID 17
204
#define VENDOR_REPORT_ID 8
205
#define PAD_KBD_REPORT_LENGTH 8
206
#define PAD_CC_REPORT_LENGTH 3
207
#define PAD_MOUSE_REPORT_LENGTH 7
208
#define PUCK_REPORT_LENGTH 9
209
#define VENDOR_REPORT_LENGTH 12
210
211
__u32 last_button_state;
212
213
static const __u8 disabled_rdesc_puck[] = {
214
FixedSizeVendorReport(PUCK_REPORT_LENGTH)
215
};
216
217
static const __u8 disabled_rdesc_pad[] = {
218
FixedSizeVendorReport(PAD_KBD_REPORT_LENGTH)
219
FixedSizeVendorReport(PAD_CC_REPORT_LENGTH)
220
FixedSizeVendorReport(PAD_MOUSE_REPORT_LENGTH)
221
};
222
223
static const __u8 fixed_rdesc_vendor[] = {
224
UsagePage_GenericDesktop
225
Usage_GD_Keypad
226
CollectionApplication(
227
// Byte 0
228
// We send our pad events on the vendor report id because why not
229
ReportId(VENDOR_REPORT_ID)
230
UsagePage_Digitizers
231
Usage_Dig_TabletFunctionKeys
232
CollectionPhysical(
233
// Byte 1 is a button so we look like a tablet
234
Usage_Dig_BarrelSwitch // BTN_STYLUS, needed so we get to be a tablet pad
235
ReportCount(1)
236
ReportSize(1)
237
Input(Var|Abs)
238
ReportCount(7) // Padding
239
Input(Const)
240
// Bytes 2/3 - x/y just exist so we get to be a tablet pad
241
UsagePage_GenericDesktop
242
Usage_GD_X
243
Usage_GD_Y
244
LogicalMinimum_i8(0x0)
245
LogicalMaximum_i8(0x1)
246
ReportCount(2)
247
ReportSize(8)
248
Input(Var|Abs)
249
// Bytes 4-7 are the button state for 19 buttons + pad out to u32
250
// We send the first 10 buttons as buttons 1-10 which is BTN_0 -> BTN_9
251
UsagePage_Button
252
UsageMinimum_i8(1)
253
UsageMaximum_i8(10)
254
LogicalMinimum_i8(0x0)
255
LogicalMaximum_i8(0x1)
256
ReportCount(10)
257
ReportSize(1)
258
Input(Var|Abs)
259
// We send the other 9 buttons as buttons 0x31 and above -> BTN_A - BTN_TL2
260
UsageMinimum_i8(0x31)
261
UsageMaximum_i8(0x3a)
262
ReportCount(9)
263
ReportSize(1)
264
Input(Var|Abs)
265
ReportCount(13)
266
ReportSize(1)
267
Input(Const) // padding
268
// Byte 6 is the wheel
269
UsagePage_GenericDesktop
270
Usage_GD_Wheel
271
LogicalMinimum_i8(-1)
272
LogicalMaximum_i8(1)
273
ReportCount(1)
274
ReportSize(8)
275
Input(Var|Rel)
276
)
277
// Make sure we match our original report length
278
FixedSizeVendorReport(VENDOR_REPORT_LENGTH)
279
)
280
};
281
282
/* Identical to fixed_rdesc_pad but with different FixedSizeVendorReport */
283
static const __u8 fixed_rdesc_pad[] = {
284
UsagePage_GenericDesktop
285
Usage_GD_Keypad
286
CollectionApplication(
287
// Byte 0
288
// We send our pad events on the vendor report id because why not
289
ReportId(VENDOR_REPORT_ID)
290
UsagePage_Digitizers
291
Usage_Dig_TabletFunctionKeys
292
CollectionPhysical(
293
// Byte 1 is a button so we look like a tablet
294
Usage_Dig_BarrelSwitch // BTN_STYLUS, needed so we get to be a tablet pad
295
ReportCount(1)
296
ReportSize(1)
297
Input(Var|Abs)
298
ReportCount(7) // Padding
299
Input(Const)
300
// Bytes 2/3 - x/y just exist so we get to be a tablet pad
301
UsagePage_GenericDesktop
302
Usage_GD_X
303
Usage_GD_Y
304
LogicalMinimum_i8(0x0)
305
LogicalMaximum_i8(0x1)
306
ReportCount(2)
307
ReportSize(8)
308
Input(Var|Abs)
309
// Bytes 4-7 are the button state for 19 buttons + pad out to u32
310
// We send the first 10 buttons as buttons 1-10 which is BTN_0 -> BTN_9
311
UsagePage_Button
312
UsageMinimum_i8(1)
313
UsageMaximum_i8(10)
314
LogicalMinimum_i8(0x0)
315
LogicalMaximum_i8(0x1)
316
ReportCount(10)
317
ReportSize(1)
318
Input(Var|Abs)
319
// We send the other 9 buttons as buttons 0x31 and above -> BTN_A - BTN_TL2
320
UsageMinimum_i8(0x31)
321
UsageMaximum_i8(0x3a)
322
ReportCount(9)
323
ReportSize(1)
324
Input(Var|Abs)
325
ReportCount(13)
326
ReportSize(1)
327
Input(Const) // padding
328
// Byte 6 is the wheel
329
UsagePage_GenericDesktop
330
Usage_GD_Wheel
331
LogicalMinimum_i8(-1)
332
LogicalMaximum_i8(1)
333
ReportCount(1)
334
ReportSize(8)
335
Input(Var|Rel)
336
)
337
// Make sure we match our original report lengths
338
FixedSizeVendorReport(PAD_KBD_REPORT_LENGTH)
339
FixedSizeVendorReport(PAD_CC_REPORT_LENGTH)
340
FixedSizeVendorReport(PAD_MOUSE_REPORT_LENGTH)
341
)
342
};
343
344
SEC(HID_BPF_RDESC_FIXUP)
345
int BPF_PROG(k20_fix_rdesc, struct hid_bpf_ctx *hctx)
346
{
347
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
348
__s32 rdesc_size = hctx->size;
349
__u8 have_fw_id;
350
351
if (!data)
352
return 0; /* EPERM check */
353
354
/* If we have a firmware ID and it matches our expected prefix, we
355
* disable the default pad/puck nodes. They won't send events
356
* but cause duplicate devices.
357
*/
358
have_fw_id = __builtin_memcmp(UDEV_PROP_HUION_FIRMWARE_ID,
359
EXPECTED_FIRMWARE_ID,
360
sizeof(EXPECTED_FIRMWARE_ID) - 1) == 0;
361
if (rdesc_size == PAD_REPORT_DESCRIPTOR_LENGTH) {
362
if (have_fw_id) {
363
__builtin_memcpy(data, disabled_rdesc_pad, sizeof(disabled_rdesc_pad));
364
return sizeof(disabled_rdesc_pad);
365
} else {
366
__builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad));
367
return sizeof(fixed_rdesc_pad);
368
369
}
370
}
371
if (rdesc_size == PUCK_REPORT_DESCRIPTOR_LENGTH) {
372
if (have_fw_id) {
373
__builtin_memcpy(data, disabled_rdesc_puck, sizeof(disabled_rdesc_puck));
374
return sizeof(disabled_rdesc_puck);
375
}
376
}
377
/* Always fix the vendor mode so the tablet will work even if nothing sets
378
* the udev property (e.g. huion-switcher run manually)
379
*/
380
if (rdesc_size == VENDOR_REPORT_DESCRIPTOR_LENGTH) {
381
__builtin_memcpy(data, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor));
382
return sizeof(fixed_rdesc_vendor);
383
384
}
385
return 0;
386
}
387
388
SEC(HID_BPF_DEVICE_EVENT)
389
int BPF_PROG(k20_fix_events, struct hid_bpf_ctx *hctx)
390
{
391
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);
392
393
if (!data)
394
return 0; /* EPERM check */
395
396
/* Only sent if tablet is in raw mode */
397
if (data[0] == VENDOR_REPORT_ID) {
398
/* See fixed_rdesc_pad */
399
struct pad_report {
400
__u8 report_id;
401
__u8 btn_stylus:1;
402
__u8 pad:7;
403
__u8 x;
404
__u8 y;
405
__u32 buttons;
406
__u8 wheel;
407
} __attribute__((packed)) *pad_report;
408
409
__u8 wheel = 0;
410
411
/* Wheel report */
412
if (data[1] == 0xf1) {
413
if (data[5] == 2)
414
wheel = 0xff;
415
else
416
wheel = data[5];
417
} else {
418
/* data[4..6] are the buttons, mapped correctly */
419
last_button_state = data[4] | (data[5] << 8) | (data[6] << 16);
420
wheel = 0; // wheel
421
}
422
423
pad_report = (struct pad_report *)data;
424
pad_report->report_id = VENDOR_REPORT_ID;
425
pad_report->btn_stylus = 0;
426
pad_report->x = 0;
427
pad_report->y = 0;
428
pad_report->buttons = last_button_state;
429
pad_report->wheel = wheel;
430
431
return sizeof(struct pad_report);
432
}
433
434
if (data[0] == PAD_KBD_REPORT_ID) {
435
const __u8 button_mapping[] = {
436
0x0e, /* Button 1: K */
437
0x0a, /* Button 2: G */
438
0x0f, /* Button 3: L */
439
0x4c, /* Button 4: Delete */
440
0x0c, /* Button 5: I */
441
0x07, /* Button 6: D */
442
0x05, /* Button 7: B */
443
0x08, /* Button 8: E */
444
0x16, /* Button 9: S */
445
0x1d, /* Button 10: Z */
446
0x06, /* Button 11: C */
447
0x19, /* Button 12: V */
448
0xff, /* Button 13: LeftControl */
449
0xff, /* Button 14: LeftAlt */
450
0xff, /* Button 15: LeftShift */
451
0x28, /* Button 16: Return Enter */
452
0x2c, /* Button 17: Spacebar */
453
0x11, /* Button 18: N */
454
};
455
/* See fixed_rdesc_pad */
456
struct pad_report {
457
__u8 report_id;
458
__u8 btn_stylus:1;
459
__u8 pad:7;
460
__u8 x;
461
__u8 y;
462
__u32 buttons;
463
__u8 wheel;
464
} __attribute__((packed)) *pad_report;
465
int i, b;
466
__u8 modifiers = data[1];
467
__u32 buttons = 0;
468
469
if (modifiers & 0x01) { /* Control */
470
buttons |= BIT(12);
471
}
472
if (modifiers & 0x02) { /* Shift */
473
buttons |= BIT(14);
474
}
475
if (modifiers & 0x04) { /* Alt */
476
buttons |= BIT(13);
477
}
478
479
for (i = 2; i < PAD_KBD_REPORT_LENGTH; i++) {
480
if (!data[i])
481
break;
482
483
for (b = 0; b < ARRAY_SIZE(button_mapping); b++) {
484
if (data[i] == button_mapping[b]) {
485
buttons |= BIT(b);
486
break;
487
}
488
}
489
data[i] = 0;
490
}
491
492
pad_report = (struct pad_report *)data;
493
pad_report->report_id = VENDOR_REPORT_ID;
494
pad_report->btn_stylus = 0;
495
pad_report->x = 0;
496
pad_report->y = 0;
497
pad_report->buttons = buttons;
498
// The wheel happens on a different hidraw node but its
499
// values are unreliable (as is the button inside the wheel).
500
// So the wheel is simply always zero, if you want the wheel
501
// to work reliably, use the tablet mode.
502
pad_report->wheel = 0;
503
504
return sizeof(struct pad_report);
505
}
506
507
return 0;
508
}
509
510
HID_BPF_OPS(keydial_k20) = {
511
.hid_device_event = (void *)k20_fix_events,
512
.hid_rdesc_fixup = (void *)k20_fix_rdesc,
513
};
514
515
SEC("syscall")
516
int probe(struct hid_bpf_probe_args *ctx)
517
{
518
switch (ctx->rdesc_size) {
519
case PAD_REPORT_DESCRIPTOR_LENGTH:
520
case PUCK_REPORT_DESCRIPTOR_LENGTH:
521
case VENDOR_REPORT_DESCRIPTOR_LENGTH:
522
ctx->retval = 0;
523
break;
524
default:
525
ctx->retval = -EINVAL;
526
}
527
528
return 0;
529
}
530
531
char _license[] SEC("license") = "GPL";
532
533