Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/hid/bpf/progs/XPPen__ACK05.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 HID_BPF_ASYNC_MAX_CTX 1
12
#include "hid_bpf_async.h"
13
14
#define VID_UGEE 0x28BD
15
/* same PID whether connected directly or through the provided dongle: */
16
#define PID_ACK05_REMOTE 0x0202
17
18
19
HID_BPF_CONFIG(
20
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ACK05_REMOTE),
21
);
22
23
/*
24
* By default, the pad reports the buttons through a set of key sequences.
25
*
26
* The pad reports a classic keyboard report descriptor:
27
* # HANVON UGEE Shortcut Remote
28
* Report descriptor length: 102 bytes
29
* 0x05, 0x01, // Usage Page (Generic Desktop) 0
30
* 0x09, 0x02, // Usage (Mouse) 2
31
* 0xa1, 0x01, // Collection (Application) 4
32
* 0x85, 0x09, // Report ID (9) 6
33
* 0x09, 0x01, // Usage (Pointer) 8
34
* 0xa1, 0x00, // Collection (Physical) 10
35
* 0x05, 0x09, // Usage Page (Button) 12
36
* 0x19, 0x01, // UsageMinimum (1) 14
37
* 0x29, 0x03, // UsageMaximum (3) 16
38
* 0x15, 0x00, // Logical Minimum (0) 18
39
* 0x25, 0x01, // Logical Maximum (1) 20
40
* 0x95, 0x03, // Report Count (3) 22
41
* 0x75, 0x01, // Report Size (1) 24
42
* 0x81, 0x02, // Input (Data,Var,Abs) 26
43
* 0x95, 0x05, // Report Count (5) 28
44
* 0x81, 0x01, // Input (Cnst,Arr,Abs) 30
45
* 0x05, 0x01, // Usage Page (Generic Desktop) 32
46
* 0x09, 0x30, // Usage (X) 34
47
* 0x09, 0x31, // Usage (Y) 36
48
* 0x26, 0xff, 0x7f, // Logical Maximum (32767) 38
49
* 0x95, 0x02, // Report Count (2) 41
50
* 0x75, 0x10, // Report Size (16) 43
51
* 0x81, 0x02, // Input (Data,Var,Abs) 45
52
* 0x05, 0x0d, // Usage Page (Digitizers) 47
53
* 0x09, 0x30, // Usage (Tip Pressure) 49
54
* 0x26, 0xff, 0x07, // Logical Maximum (2047) 51
55
* 0x95, 0x01, // Report Count (1) 54
56
* 0x75, 0x10, // Report Size (16) 56
57
* 0x81, 0x02, // Input (Data,Var,Abs) 58
58
* 0xc0, // End Collection 60
59
* 0xc0, // End Collection 61
60
* 0x05, 0x01, // Usage Page (Generic Desktop) 62
61
* 0x09, 0x06, // Usage (Keyboard) 64
62
* 0xa1, 0x01, // Collection (Application) 66
63
* 0x85, 0x06, // Report ID (6) 68
64
* 0x05, 0x07, // Usage Page (Keyboard/Keypad) 70
65
* 0x19, 0xe0, // UsageMinimum (224) 72
66
* 0x29, 0xe7, // UsageMaximum (231) 74
67
* 0x15, 0x00, // Logical Minimum (0) 76
68
* 0x25, 0x01, // Logical Maximum (1) 78
69
* 0x75, 0x01, // Report Size (1) 80
70
* 0x95, 0x08, // Report Count (8) 82
71
* 0x81, 0x02, // Input (Data,Var,Abs) 84
72
* 0x05, 0x07, // Usage Page (Keyboard/Keypad) 86
73
* 0x19, 0x00, // UsageMinimum (0) 88
74
* 0x29, 0xff, // UsageMaximum (255) 90
75
* 0x26, 0xff, 0x00, // Logical Maximum (255) 92
76
* 0x75, 0x08, // Report Size (8) 95
77
* 0x95, 0x06, // Report Count (6) 97
78
* 0x81, 0x00, // Input (Data,Arr,Abs) 99
79
* 0xc0, // End Collection 101
80
*
81
* Each button gets assigned the following events:
82
*
83
* Buttons released: 06 00 00 00 00 00 00 00
84
* Button 1: 06 01 12 00 00 00 00 00 -> LControl + o
85
* Button 2: 06 01 11 00 00 00 00 00 -> LControl + n
86
* Button 3: 06 00 3e 00 00 00 00 00 -> F5
87
* Button 4: 06 02 00 00 00 00 00 00 -> LShift
88
* Button 5: 06 01 00 00 00 00 00 00 -> LControl
89
* Button 6: 06 04 00 00 00 00 00 00 -> LAlt
90
* Button 7: 06 01 16 00 00 00 00 00 -> LControl + s
91
* Button 8: 06 01 1d 00 00 00 00 00 -> LControl + z
92
* Button 9: 06 00 2c 00 00 00 00 00 -> Space
93
* Button 10: 06 03 1d 00 00 00 00 00 -> LControl + LShift + z
94
* Wheel: 06 01 57 00 00 00 00 00 -> clockwise rotation (LControl + Keypad Plus)
95
* Wheel: 06 01 56 00 00 00 00 00 -> counter-clockwise rotation
96
* (LControl + Keypad Minus)
97
*
98
* However, multiple buttons can be pressed at the same time, and when this happens,
99
* each button gets assigned a new slot in the Input (Data,Arr,Abs):
100
*
101
* Button 1 + 3: 06 01 12 3e 00 00 00 00 -> LControl + o + F5
102
*
103
* When a modifier is pressed (Button 4, 5, or 6), the assigned key is set to 00:
104
*
105
* Button 5 + 7: 06 01 00 16 00 00 00 00 -> LControl + s
106
*
107
* This is mostly fine, but with Button 8 and Button 10 sharing the same
108
* key value ("z"), there are cases where we can not know which is which.
109
*
110
*/
111
112
#define PAD_WIRED_DESCRIPTOR_LENGTH 102
113
#define PAD_DONGLE_DESCRIPTOR_LENGTH 177
114
#define STYLUS_DESCRIPTOR_LENGTH 109
115
#define VENDOR_DESCRIPTOR_LENGTH 36
116
#define PAD_REPORT_ID 6
117
#define RAW_PAD_REPORT_ID 0xf0
118
#define RAW_BATTERY_REPORT_ID 0xf2
119
#define VENDOR_REPORT_ID 2
120
#define PAD_REPORT_LENGTH 8
121
#define VENDOR_REPORT_LENGTH 12
122
123
__u16 last_button_state;
124
125
static const __u8 disabled_rdesc[] = {
126
// Make sure we match our original report length
127
FixedSizeVendorReport(VENDOR_REPORT_LENGTH)
128
};
129
130
static const __u8 fixed_rdesc_vendor[] = {
131
UsagePage_GenericDesktop
132
Usage_GD_Keypad
133
CollectionApplication(
134
// -- Byte 0 in report
135
ReportId(RAW_PAD_REPORT_ID)
136
// Byte 1 in report - same than report ID
137
ReportCount(1)
138
ReportSize(8)
139
Input(Const) // padding (internal report ID)
140
LogicalMaximum_i8(0)
141
LogicalMaximum_i8(1)
142
UsagePage_Digitizers
143
Usage_Dig_TabletFunctionKeys
144
CollectionPhysical(
145
// Byte 2-3 is the button state
146
UsagePage_Button
147
UsageMinimum_i8(0x01)
148
UsageMaximum_i8(0x0a)
149
LogicalMinimum_i8(0x0)
150
LogicalMaximum_i8(0x1)
151
ReportCount(10)
152
ReportSize(1)
153
Input(Var|Abs)
154
Usage_i8(0x31) // will be mapped as BTN_A / BTN_SOUTH
155
ReportCount(1)
156
Input(Var|Abs)
157
ReportCount(5) // padding
158
Input(Const)
159
// Byte 4 in report - just exists so we get to be a tablet pad
160
UsagePage_Digitizers
161
Usage_Dig_BarrelSwitch // BTN_STYLUS
162
ReportCount(1)
163
ReportSize(1)
164
Input(Var|Abs)
165
ReportCount(7) // padding
166
Input(Const)
167
// Bytes 5/6 in report - just exists so we get to be a tablet pad
168
UsagePage_GenericDesktop
169
Usage_GD_X
170
Usage_GD_Y
171
ReportCount(2)
172
ReportSize(8)
173
Input(Var|Abs)
174
// Byte 7 in report is the dial
175
Usage_GD_Wheel
176
LogicalMinimum_i8(-1)
177
LogicalMaximum_i8(1)
178
ReportCount(1)
179
ReportSize(8)
180
Input(Var|Rel)
181
)
182
// -- Byte 0 in report
183
ReportId(RAW_BATTERY_REPORT_ID)
184
// Byte 1 in report - same than report ID
185
ReportCount(1)
186
ReportSize(8)
187
Input(Const) // padding (internal report ID)
188
// Byte 2 in report - always 0x01
189
Input(Const) // padding (internal report ID)
190
UsagePage_Digitizers
191
/*
192
* We represent the device as a stylus to force the kernel to not
193
* directly query its battery state. Instead the kernel will rely
194
* only on the provided events.
195
*/
196
Usage_Dig_Stylus
197
CollectionPhysical(
198
// Byte 3 in report - battery value
199
UsagePage_BatterySystem
200
Usage_BS_AbsoluteStateOfCharge
201
LogicalMinimum_i8(0)
202
LogicalMaximum_i8(100)
203
ReportCount(1)
204
ReportSize(8)
205
Input(Var|Abs)
206
// Byte 4 in report - charging state
207
Usage_BS_Charging
208
LogicalMinimum_i8(0)
209
LogicalMaximum_i8(1)
210
ReportCount(1)
211
ReportSize(8)
212
Input(Var|Abs)
213
)
214
)
215
};
216
217
SEC(HID_BPF_RDESC_FIXUP)
218
int BPF_PROG(ack05_fix_rdesc, struct hid_bpf_ctx *hctx)
219
{
220
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
221
__s32 rdesc_size = hctx->size;
222
223
if (!data)
224
return 0; /* EPERM check */
225
226
if (rdesc_size == VENDOR_DESCRIPTOR_LENGTH) {
227
/*
228
* The vendor fixed rdesc is appended after the current one,
229
* to keep the output reports working.
230
*/
231
__builtin_memcpy(data + rdesc_size, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor));
232
return sizeof(fixed_rdesc_vendor) + rdesc_size;
233
}
234
235
hid_set_name(hctx->hid, "Disabled by HID-BPF Hanvon Ugee Shortcut Remote");
236
237
__builtin_memcpy(data, disabled_rdesc, sizeof(disabled_rdesc));
238
return sizeof(disabled_rdesc);
239
}
240
241
static int HID_BPF_ASYNC_FUN(switch_to_raw_mode)(struct hid_bpf_ctx *hid)
242
{
243
static __u8 magic_0[32] = {0x02, 0xb0, 0x04, 0x00, 0x00};
244
int err;
245
246
/*
247
* The proprietary driver sends the 3 following packets after the
248
* above one.
249
* These don't seem to have any effect, so we don't send them to save
250
* some processing time.
251
*
252
* static __u8 magic_1[32] = {0x02, 0xb4, 0x01, 0x00, 0x01};
253
* static __u8 magic_2[32] = {0x02, 0xb4, 0x01, 0x00, 0xff};
254
* static __u8 magic_3[32] = {0x02, 0xb8, 0x04, 0x00, 0x00};
255
*/
256
257
err = hid_bpf_hw_output_report(hid, magic_0, sizeof(magic_0));
258
if (err < 0)
259
return err;
260
261
return 0;
262
}
263
264
SEC(HID_BPF_DEVICE_EVENT)
265
int BPF_PROG(ack05_fix_events, struct hid_bpf_ctx *hctx)
266
{
267
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, PAD_REPORT_LENGTH);
268
int ret = 0;
269
270
if (!data)
271
return 0; /* EPERM check */
272
273
if (data[0] != VENDOR_REPORT_ID)
274
return 0;
275
276
/* reconnect event */
277
if (data[1] == 0xf8 && data[2] == 02 && data[3] == 0x01)
278
HID_BPF_ASYNC_DELAYED_CALL(switch_to_raw_mode, hctx, 10);
279
280
/* button event */
281
if (data[1] == RAW_PAD_REPORT_ID) {
282
data[0] = data[1];
283
if (data[7] == 0x02)
284
data[7] = 0xff;
285
ret = 8;
286
} else if (data[1] == RAW_BATTERY_REPORT_ID) {
287
data[0] = data[1];
288
ret = 5;
289
}
290
291
return ret;
292
}
293
294
HID_BPF_OPS(xppen_ack05_remote) = {
295
.hid_device_event = (void *)ack05_fix_events,
296
.hid_rdesc_fixup = (void *)ack05_fix_rdesc,
297
};
298
299
SEC("syscall")
300
int probe(struct hid_bpf_probe_args *ctx)
301
{
302
switch (ctx->rdesc_size) {
303
case PAD_WIRED_DESCRIPTOR_LENGTH:
304
case PAD_DONGLE_DESCRIPTOR_LENGTH:
305
case STYLUS_DESCRIPTOR_LENGTH:
306
case VENDOR_DESCRIPTOR_LENGTH:
307
ctx->retval = 0;
308
break;
309
default:
310
ctx->retval = -EINVAL;
311
break;
312
}
313
314
if (ctx->rdesc_size == VENDOR_DESCRIPTOR_LENGTH) {
315
struct hid_bpf_ctx *hctx = hid_bpf_allocate_context(ctx->hid);
316
317
if (!hctx) {
318
ctx->retval = -EINVAL;
319
return 0;
320
}
321
322
ctx->retval = HID_BPF_ASYNC_INIT(switch_to_raw_mode) ||
323
switch_to_raw_mode(hctx);
324
325
hid_bpf_release_context(hctx);
326
}
327
328
return 0;
329
}
330
331
char _license[] SEC("license") = "GPL";
332
333