Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/hid/bpf/progs/WALTOP__Batteryless-Tablet.bpf.c
38241 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/* Copyright (c) 2025 Red Hat
3
*/
4
5
#include "vmlinux.h"
6
#include "hid_bpf.h"
7
#include "hid_bpf_helpers.h"
8
#include <bpf/bpf_tracing.h>
9
10
#define VID_WALTOP 0x172F
11
#define PID_BATTERYLESS_TABLET 0x0505
12
13
HID_BPF_CONFIG(
14
HID_DEVICE(BUS_USB, HID_GROUP_ANY, VID_WALTOP, PID_BATTERYLESS_TABLET)
15
);
16
17
#define EXPECTED_RDESC_SIZE 335
18
#define PEN_REPORT_ID 16
19
20
#define TIP_SWITCH BIT(0)
21
#define BARREL_SWITCH BIT(1)
22
#define SECONDARY_BARREL_SWITCH BIT(5)
23
24
static __u8 last_button_state;
25
26
static const __u8 fixed_rdesc[] = {
27
0x05, 0x01, // Usage Page (Generic Desktop)
28
0x09, 0x02, // Usage (Mouse)
29
0xa1, 0x01, // Collection (Application)
30
0x85, 0x01, // Report ID (1)
31
0x09, 0x01, // Usage (Pointer)
32
0xa1, 0x00, // Collection (Physical)
33
0x05, 0x09, // Usage Page (Button)
34
0x19, 0x01, // Usage Minimum (1)
35
0x29, 0x05, // Usage Maximum (5)
36
0x15, 0x00, // Logical Minimum (0)
37
0x25, 0x01, // Logical Maximum (1)
38
0x75, 0x01, // Report Size (1)
39
0x95, 0x05, // Report Count (5)
40
0x81, 0x02, // Input (Data,Var,Abs)
41
0x75, 0x03, // Report Size (3)
42
0x95, 0x01, // Report Count (1)
43
0x81, 0x03, // Input (Cnst,Var,Abs)
44
0x05, 0x01, // Usage Page (Generic Desktop)
45
0x09, 0x30, // Usage (X)
46
0x09, 0x31, // Usage (Y)
47
0x09, 0x38, // Usage (Wheel)
48
0x15, 0x81, // Logical Minimum (-127)
49
0x25, 0x7f, // Logical Maximum (127)
50
0x75, 0x08, // Report Size (8)
51
0x95, 0x03, // Report Count (3)
52
0x81, 0x06, // Input (Data,Var,Rel)
53
0x05, 0x0c, // Usage Page (Consumer)
54
0x15, 0x81, // Logical Minimum (-127)
55
0x25, 0x7f, // Logical Maximum (127)
56
0x75, 0x08, // Report Size (8)
57
0x95, 0x01, // Report Count (1)
58
0x0a, 0x38, 0x02, // Usage (AC Pan)
59
0x81, 0x06, // Input (Data,Var,Rel)
60
0xc0, // End Collection
61
0xc0, // End Collection
62
0x05, 0x0d, // Usage Page (Digitizers)
63
0x09, 0x02, // Usage (Pen)
64
0xa1, 0x01, // Collection (Application)
65
0x85, 0x02, // Report ID (2)
66
0x09, 0x20, // Usage (Stylus)
67
0xa1, 0x00, // Collection (Physical)
68
0x09, 0x00, // Usage (0x0000)
69
0x15, 0x00, // Logical Minimum (0)
70
0x26, 0xff, 0x00, // Logical Maximum (255)
71
0x75, 0x08, // Report Size (8)
72
0x95, 0x09, // Report Count (9)
73
0x81, 0x02, // Input (Data,Var,Abs)
74
0x09, 0x3f, // Usage (Azimuth)
75
0x09, 0x40, // Usage (Altitude)
76
0x15, 0x00, // Logical Minimum (0)
77
0x26, 0xff, 0x00, // Logical Maximum (255)
78
0x75, 0x08, // Report Size (8)
79
0x95, 0x02, // Report Count (2)
80
0xb1, 0x02, // Feature (Data,Var,Abs)
81
0xc0, // End Collection
82
0x85, 0x05, // Report ID (5)
83
0x05, 0x0d, // Usage Page (Digitizers)
84
0x09, 0x20, // Usage (Stylus)
85
0xa1, 0x00, // Collection (Physical)
86
0x09, 0x00, // Usage (0x0000)
87
0x15, 0x00, // Logical Minimum (0)
88
0x26, 0xff, 0x00, // Logical Maximum (255)
89
0x75, 0x08, // Report Size (8)
90
0x95, 0x07, // Report Count (7)
91
0x81, 0x02, // Input (Data,Var,Abs)
92
0xc0, // End Collection
93
0x85, 0x0a, // Report ID (10)
94
0x05, 0x0d, // Usage Page (Digitizers)
95
0x09, 0x20, // Usage (Stylus)
96
0xa1, 0x00, // Collection (Physical)
97
0x09, 0x00, // Usage (0x0000)
98
0x15, 0x00, // Logical Minimum (0)
99
0x26, 0xff, 0x00, // Logical Maximum (255)
100
0x75, 0x08, // Report Size (8)
101
0x95, 0x07, // Report Count (7)
102
0x81, 0x02, // Input (Data,Var,Abs)
103
0xc0, // End Collection
104
0x85, 0x10, // Report ID (16)
105
0x09, 0x20, // Usage (Stylus)
106
0xa1, 0x00, // Collection (Physical)
107
0x09, 0x42, // Usage (Tip Switch)
108
0x09, 0x44, // Usage (Barrel Switch)
109
0x09, 0x3c, // Usage (Invert)
110
0x09, 0x45, // Usage (Eraser)
111
0x09, 0x32, // Usage (In Range)
112
0x09, 0x5a, // Usage (Secondary Barrel Switch) <-- added
113
0x15, 0x00, // Logical Minimum (0)
114
0x25, 0x01, // Logical Maximum (1)
115
0x75, 0x01, // Report Size (1)
116
0x95, 0x06, // Report Count (6) <--- changed from 5
117
0x81, 0x02, // Input (Data,Var,Abs)
118
0x95, 0x02, // Report Count (2) <--- changed from 3
119
0x81, 0x03, // Input (Cnst,Var,Abs)
120
0x05, 0x01, // Usage Page (Generic Desktop)
121
0x09, 0x30, // Usage (X)
122
0x75, 0x10, // Report Size (16)
123
0x95, 0x01, // Report Count (1)
124
0xa4, // Push
125
0x55, 0x0d, // Unit Exponent (-3)
126
0x65, 0x33, // Unit (EnglishLinear: in³)
127
0x15, 0x00, // Logical Minimum (0)
128
0x26, 0x00, 0x7d, // Logical Maximum (32000)
129
0x35, 0x00, // Physical Minimum (0)
130
0x46, 0x00, 0x7d, // Physical Maximum (32000)
131
0x81, 0x02, // Input (Data,Var,Abs)
132
0x09, 0x31, // Usage (Y)
133
0x15, 0x00, // Logical Minimum (0)
134
0x26, 0x20, 0x4e, // Logical Maximum (20000)
135
0x35, 0x00, // Physical Minimum (0)
136
0x46, 0x20, 0x4e, // Physical Maximum (20000)
137
0x81, 0x02, // Input (Data,Var,Abs)
138
0x05, 0x0d, // Usage Page (Digitizers)
139
0x09, 0x30, // Usage (Tip Pressure)
140
0x15, 0x00, // Logical Minimum (0)
141
0x26, 0xff, 0x07, // Logical Maximum (2047)
142
0x35, 0x00, // Physical Minimum (0)
143
0x46, 0xff, 0x07, // Physical Maximum (2047)
144
0x81, 0x02, // Input (Data,Var,Abs)
145
0x05, 0x0d, // Usage Page (Digitizers)
146
0x09, 0x3d, // Usage (X Tilt)
147
0x09, 0x3e, // Usage (Y Tilt)
148
0x15, 0xc4, // Logical Minimum (-60) <- changed from -127
149
0x25, 0x3c, // Logical Maximum (60) <- changed from 127
150
0x75, 0x08, // Report Size (8)
151
0x95, 0x02, // Report Count (2)
152
0x81, 0x02, // Input (Data,Var,Abs)
153
0xc0, // End Collection
154
0xc0, // End Collection
155
0x05, 0x01, // Usage Page (Generic Desktop)
156
0x09, 0x06, // Usage (Keyboard)
157
0xa1, 0x01, // Collection (Application)
158
0x85, 0x0d, // Report ID (13)
159
0x05, 0x07, // Usage Page (Keyboard/Keypad)
160
0x19, 0xe0, // Usage Minimum (224)
161
0x29, 0xe7, // Usage Maximum (231)
162
0x15, 0x00, // Logical Minimum (0)
163
0x25, 0x01, // Logical Maximum (1)
164
0x75, 0x01, // Report Size (1)
165
0x95, 0x08, // Report Count (8)
166
0x81, 0x02, // Input (Data,Var,Abs)
167
0x75, 0x08, // Report Size (8)
168
0x95, 0x01, // Report Count (1)
169
0x81, 0x01, // Input (Cnst,Arr,Abs)
170
0x05, 0x07, // Usage Page (Keyboard/Keypad)
171
0x19, 0x00, // Usage Minimum (0)
172
0x29, 0x65, // Usage Maximum (101)
173
0x15, 0x00, // Logical Minimum (0)
174
0x25, 0x65, // Logical Maximum (101)
175
0x75, 0x08, // Report Size (8)
176
0x95, 0x05, // Report Count (5)
177
0x81, 0x00, // Input (Data,Arr,Abs)
178
0xc0, // End Collection
179
0x05, 0x0c, // Usage Page (Consumer)
180
0x09, 0x01, // Usage (Consumer Control)
181
0xa1, 0x01, // Collection (Application)
182
0x85, 0x0c, // Report ID (12)
183
0x09, 0xe9, // Usage (Volume Increment)
184
0x09, 0xea, // Usage (Volume Decrement)
185
0x09, 0xe2, // Usage (Mute)
186
0x15, 0x00, // Logical Minimum (0)
187
0x25, 0x01, // Logical Maximum (1)
188
0x75, 0x01, // Report Size (1)
189
0x95, 0x03, // Report Count (3)
190
0x81, 0x06, // Input (Data,Var,Rel)
191
0x75, 0x05, // Report Size (5)
192
0x95, 0x01, // Report Count (1)
193
0x81, 0x07, // Input (Cnst,Var,Rel)
194
0xc0, // End Collection
195
};
196
197
static inline unsigned int bitwidth32(__u32 x)
198
{
199
return 32 - __builtin_clzg(x, 32);
200
}
201
202
static inline unsigned int floor_log2_32(__u32 x)
203
{
204
return bitwidth32(x) - 1;
205
}
206
207
/* Maps the interval [0, 2047] to itself using a scaled
208
* approximation of the function log2(x+1).
209
*/
210
static unsigned int scaled_log2(__u16 v)
211
{
212
const unsigned int XMAX = 2047;
213
const unsigned int YMAX = 11; /* log2(2048) = 11 */
214
215
unsigned int x = v + 1;
216
unsigned int n = floor_log2_32(x);
217
unsigned int b = 1 << n;
218
219
/* Fixed-point fraction in [0, 1), linearly
220
* interpolated using delta-y = 1 and
221
* delta-x = (2b - b) = b.
222
*/
223
unsigned int frac = (x - b) << YMAX;
224
unsigned int lerp = frac / b;
225
unsigned int log2 = (n << YMAX) + lerp;
226
227
return ((log2 * XMAX) / YMAX) >> YMAX;
228
}
229
230
SEC(HID_BPF_RDESC_FIXUP)
231
int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
232
{
233
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
234
235
if (!data)
236
return 0; /* EPERM check */
237
238
__builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc));
239
240
return sizeof(fixed_rdesc);
241
}
242
243
SEC(HID_BPF_DEVICE_EVENT)
244
int BPF_PROG(waltop_fix_events, struct hid_bpf_ctx *hctx)
245
{
246
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);
247
248
if (!data)
249
return 0; /* EPERM check */
250
251
__u8 report_id = data[0];
252
253
if (report_id != PEN_REPORT_ID)
254
return 0;
255
256
/* On this tablet if the secondary barrel switch is pressed,
257
* the tablet sends tip down and barrel down. Change this to
258
* just secondary barrel down when there is no ambiguity.
259
*
260
* It's possible that there is a bug in the firmware and the
261
* device intends to set invert + eraser instead (i.e. the
262
* pysical button is an eraser button) but since
263
* the pressure is always zero, said eraser button
264
* would be useless anyway.
265
*
266
* So let's just change the button to secondary barrel down.
267
*/
268
269
__u8 tip_switch = data[1] & TIP_SWITCH;
270
__u8 barrel_switch = data[1] & BARREL_SWITCH;
271
272
__u8 tip_held = last_button_state & TIP_SWITCH;
273
__u8 barrel_held = last_button_state & BARREL_SWITCH;
274
275
if (tip_switch && barrel_switch && !tip_held && !barrel_held) {
276
data[1] &= ~(TIP_SWITCH | BARREL_SWITCH); /* release tip and barrel */
277
data[1] |= SECONDARY_BARREL_SWITCH; /* set secondary barrel switch */
278
}
279
280
last_button_state = data[1];
281
282
/* The pressure sensor on this tablet maps around half of the
283
* logical pressure range into the interval [0-100]. Further
284
* pressure causes the sensor value to increase exponentially
285
* up to a maximum value of 2047.
286
*
287
* The values 12 and 102 were chosen to have an integer slope
288
* with smooth transition between the two curves around the
289
* value 100.
290
*/
291
292
__u16 pressure = (((__u16)data[6]) << 0) | (((__u16)data[7]) << 8);
293
294
if (pressure <= 102)
295
pressure *= 12;
296
else
297
pressure = scaled_log2(pressure);
298
299
data[6] = pressure >> 0;
300
data[7] = pressure >> 8;
301
302
return 0;
303
}
304
305
HID_BPF_OPS(waltop_batteryless) = {
306
.hid_device_event = (void *)waltop_fix_events,
307
.hid_rdesc_fixup = (void *)hid_fix_rdesc,
308
};
309
310
SEC("syscall")
311
int probe(struct hid_bpf_probe_args *ctx)
312
{
313
if (ctx->rdesc_size == EXPECTED_RDESC_SIZE)
314
ctx->retval = 0;
315
else
316
ctx->retval = -EINVAL;
317
318
return 0;
319
}
320
321
char _license[] SEC("license") = "GPL";
322
323