File Index
Files and directories
Sign in
FILE

WALTOP__Batteryless-Tablet.bpf.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hid/bpf/progs/WALTOP__Batteryless-Tablet.bpf.c C
  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
 13HID_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
 24static __u8 last_button_state;
 25
 26static 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
197static inline unsigned int bitwidth32(__u32 x)
198{
199	return 32 - __builtin_clzg(x, 32);
200}
201
202static 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 */
210static 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
230SEC(HID_BPF_RDESC_FIXUP)
231int 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
243SEC(HID_BPF_DEVICE_EVENT)
244int 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
305HID_BPF_OPS(waltop_batteryless) = {
306	.hid_device_event = (void *)waltop_fix_events,
307	.hid_rdesc_fixup = (void *)hid_fix_rdesc,
308};
309
310SEC("syscall")
311int 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
321char _license[] SEC("license") = "GPL";
~/ Read-only file preview.