1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2024 Benjamin Tissoires
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_HUION 0x256C
11 #define PID_KAMVAS_PRO_19 0x006B
12 #define NAME_KAMVAS_PRO_19 "HUION Huion Tablet_GT1902"
13
14 #define TEST_PREFIX "uhid test "
15
16 HID_BPF_CONFIG(
17 HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, VID_HUION, PID_KAMVAS_PRO_19),
18 );
19
20 bool prev_was_out_of_range;
21 bool in_eraser_mode;
22
23 /*
24 * We need to amend the report descriptor for the following:
25 * - the second button is reported through Secondary Tip Switch instead of Secondary Barrel Switch
26 * - the third button is reported through Invert, and we need some room to report it.
27 *
28 */
29 static const __u8 fixed_rdesc[] = {
30 0x05, 0x0d, // Usage Page (Digitizers) 0
31 0x09, 0x02, // Usage (Pen) 2
32 0xa1, 0x01, // Collection (Application) 4
33 0x85, 0x0a, // Report ID (10) 6
34 0x09, 0x20, // Usage (Stylus) 8
35 0xa1, 0x01, // Collection (Application) 10
36 0x09, 0x42, // Usage (Tip Switch) 12
37 0x09, 0x44, // Usage (Barrel Switch) 14
38 0x09, 0x5a, // Usage (Secondary Barrel Switch) 16 /* changed from Secondary Tip Switch */
39 0x09, 0x3c, // Usage (Invert) 18
40 0x09, 0x45, // Usage (Eraser) 20
41 0x15, 0x00, // Logical Minimum (0) 22
42 0x25, 0x01, // Logical Maximum (1) 24
43 0x75, 0x01, // Report Size (1) 26
44 0x95, 0x05, // Report Count (5) 28 /* changed (was 6) */
45 0x81, 0x02, // Input (Data,Var,Abs) 30
46 0x05, 0x09, // Usage Page (Button) /* inserted */
47 0x09, 0x4a, // Usage (0x4a) /* inserted to be translated as input usage 0x149: BTN_STYLUS3 */
48 0x95, 0x01, // Report Count (1) /* inserted */
49 0x81, 0x02, // Input (Data,Var,Abs) /* inserted */
50 0x05, 0x0d, // Usage Page (Digitizers) /* inserted */
51 0x09, 0x32, // Usage (In Range) 32
52 0x75, 0x01, // Report Size (1) 34
53 0x95, 0x01, // Report Count (1) 36
54 0x81, 0x02, // Input (Data,Var,Abs) 38
55 0x81, 0x03, // Input (Cnst,Var,Abs) 40
56 0x05, 0x01, // Usage Page (Generic Desktop) 42
57 0x09, 0x30, // Usage (X) 44
58 0x09, 0x31, // Usage (Y) 46
59 0x55, 0x0d, // Unit Exponent (-3) 48
60 0x65, 0x33, // Unit (EnglishLinear: in³) 50
61 0x26, 0xff, 0x7f, // Logical Maximum (32767) 52
62 0x35, 0x00, // Physical Minimum (0) 55
63 0x46, 0x00, 0x08, // Physical Maximum (2048) 57
64 0x75, 0x10, // Report Size (16) 60
65 0x95, 0x02, // Report Count (2) 62
66 0x81, 0x02, // Input (Data,Var,Abs) 64
67 0x05, 0x0d, // Usage Page (Digitizers) 66
68 0x09, 0x30, // Usage (Tip Pressure) 68
69 0x26, 0xff, 0x3f, // Logical Maximum (16383) 70
70 0x75, 0x10, // Report Size (16) 73
71 0x95, 0x01, // Report Count (1) 75
72 0x81, 0x02, // Input (Data,Var,Abs) 77
73 0x09, 0x3d, // Usage (X Tilt) 79
74 0x09, 0x3e, // Usage (Y Tilt) 81
75 0x15, 0xa6, // Logical Minimum (-90) 83
76 0x25, 0x5a, // Logical Maximum (90) 85
77 0x75, 0x08, // Report Size (8) 87
78 0x95, 0x02, // Report Count (2) 89
79 0x81, 0x02, // Input (Data,Var,Abs) 91
80 0xc0, // End Collection 93
81 0xc0, // End Collection 94
82 0x05, 0x0d, // Usage Page (Digitizers) 95
83 0x09, 0x04, // Usage (Touch Screen) 97
84 0xa1, 0x01, // Collection (Application) 99
85 0x85, 0x04, // Report ID (4) 101
86 0x09, 0x22, // Usage (Finger) 103
87 0xa1, 0x02, // Collection (Logical) 105
88 0x05, 0x0d, // Usage Page (Digitizers) 107
89 0x95, 0x01, // Report Count (1) 109
90 0x75, 0x06, // Report Size (6) 111
91 0x09, 0x51, // Usage (Contact Id) 113
92 0x15, 0x00, // Logical Minimum (0) 115
93 0x25, 0x3f, // Logical Maximum (63) 117
94 0x81, 0x02, // Input (Data,Var,Abs) 119
95 0x09, 0x42, // Usage (Tip Switch) 121
96 0x25, 0x01, // Logical Maximum (1) 123
97 0x75, 0x01, // Report Size (1) 125
98 0x95, 0x01, // Report Count (1) 127
99 0x81, 0x02, // Input (Data,Var,Abs) 129
100 0x75, 0x01, // Report Size (1) 131
101 0x95, 0x01, // Report Count (1) 133
102 0x81, 0x03, // Input (Cnst,Var,Abs) 135
103 0x05, 0x01, // Usage Page (Generic Desktop) 137
104 0x75, 0x10, // Report Size (16) 139
105 0x55, 0x0e, // Unit Exponent (-2) 141
106 0x65, 0x11, // Unit (SILinear: cm) 143
107 0x09, 0x30, // Usage (X) 145
108 0x26, 0xff, 0x7f, // Logical Maximum (32767) 147
109 0x35, 0x00, // Physical Minimum (0) 150
110 0x46, 0x15, 0x0c, // Physical Maximum (3093) 152
111 0x81, 0x42, // Input (Data,Var,Abs,Null) 155
112 0x09, 0x31, // Usage (Y) 157
113 0x26, 0xff, 0x7f, // Logical Maximum (32767) 159
114 0x46, 0xcb, 0x06, // Physical Maximum (1739) 162
115 0x81, 0x42, // Input (Data,Var,Abs,Null) 165
116 0x05, 0x0d, // Usage Page (Digitizers) 167
117 0x09, 0x30, // Usage (Tip Pressure) 169
118 0x26, 0xff, 0x1f, // Logical Maximum (8191) 171
119 0x75, 0x10, // Report Size (16) 174
120 0x95, 0x01, // Report Count (1) 176
121 0x81, 0x02, // Input (Data,Var,Abs) 178
122 0xc0, // End Collection 180
123 0x05, 0x0d, // Usage Page (Digitizers) 181
124 0x09, 0x22, // Usage (Finger) 183
125 0xa1, 0x02, // Collection (Logical) 185
126 0x05, 0x0d, // Usage Page (Digitizers) 187
127 0x95, 0x01, // Report Count (1) 189
128 0x75, 0x06, // Report Size (6) 191
129 0x09, 0x51, // Usage (Contact Id) 193
130 0x15, 0x00, // Logical Minimum (0) 195
131 0x25, 0x3f, // Logical Maximum (63) 197
132 0x81, 0x02, // Input (Data,Var,Abs) 199
133 0x09, 0x42, // Usage (Tip Switch) 201
134 0x25, 0x01, // Logical Maximum (1) 203
135 0x75, 0x01, // Report Size (1) 205
136 0x95, 0x01, // Report Count (1) 207
137 0x81, 0x02, // Input (Data,Var,Abs) 209
138 0x75, 0x01, // Report Size (1) 211
139 0x95, 0x01, // Report Count (1) 213
140 0x81, 0x03, // Input (Cnst,Var,Abs) 215
141 0x05, 0x01, // Usage Page (Generic Desktop) 217
142 0x75, 0x10, // Report Size (16) 219
143 0x55, 0x0e, // Unit Exponent (-2) 221
144 0x65, 0x11, // Unit (SILinear: cm) 223
145 0x09, 0x30, // Usage (X) 225
146 0x26, 0xff, 0x7f, // Logical Maximum (32767) 227
147 0x35, 0x00, // Physical Minimum (0) 230
148 0x46, 0x15, 0x0c, // Physical Maximum (3093) 232
149 0x81, 0x42, // Input (Data,Var,Abs,Null) 235
150 0x09, 0x31, // Usage (Y) 237
151 0x26, 0xff, 0x7f, // Logical Maximum (32767) 239
152 0x46, 0xcb, 0x06, // Physical Maximum (1739) 242
153 0x81, 0x42, // Input (Data,Var,Abs,Null) 245
154 0x05, 0x0d, // Usage Page (Digitizers) 247
155 0x09, 0x30, // Usage (Tip Pressure) 249
156 0x26, 0xff, 0x1f, // Logical Maximum (8191) 251
157 0x75, 0x10, // Report Size (16) 254
158 0x95, 0x01, // Report Count (1) 256
159 0x81, 0x02, // Input (Data,Var,Abs) 258
160 0xc0, // End Collection 260
161 0x05, 0x0d, // Usage Page (Digitizers) 261
162 0x09, 0x56, // Usage (Scan Time) 263
163 0x55, 0x00, // Unit Exponent (0) 265
164 0x65, 0x00, // Unit (None) 267
165 0x27, 0xff, 0xff, 0xff, 0x7f, // Logical Maximum (2147483647) 269
166 0x95, 0x01, // Report Count (1) 274
167 0x75, 0x20, // Report Size (32) 276
168 0x81, 0x02, // Input (Data,Var,Abs) 278
169 0x09, 0x54, // Usage (Contact Count) 280
170 0x25, 0x7f, // Logical Maximum (127) 282
171 0x95, 0x01, // Report Count (1) 284
172 0x75, 0x08, // Report Size (8) 286
173 0x81, 0x02, // Input (Data,Var,Abs) 288
174 0x75, 0x08, // Report Size (8) 290
175 0x95, 0x08, // Report Count (8) 292
176 0x81, 0x03, // Input (Cnst,Var,Abs) 294
177 0x85, 0x05, // Report ID (5) 296
178 0x09, 0x55, // Usage (Contact Max) 298
179 0x25, 0x0a, // Logical Maximum (10) 300
180 0x75, 0x08, // Report Size (8) 302
181 0x95, 0x01, // Report Count (1) 304
182 0xb1, 0x02, // Feature (Data,Var,Abs) 306
183 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 308
184 0x09, 0xc5, // Usage (Vendor Usage 0xc5) 311
185 0x85, 0x06, // Report ID (6) 313
186 0x15, 0x00, // Logical Minimum (0) 315
187 0x26, 0xff, 0x00, // Logical Maximum (255) 317
188 0x75, 0x08, // Report Size (8) 320
189 0x96, 0x00, 0x01, // Report Count (256) 322
190 0xb1, 0x02, // Feature (Data,Var,Abs) 325
191 0xc0, // End Collection 327
192 /* New in Firmware Version: HUION_M220_240524 */
193 0x05, 0x01, // Usage Page (Generic Desktop) 328
194 0x09, 0x01, // Usage (Pointer) 330
195 0xa1, 0x01, // Collection (Application) 332
196 0x09, 0x01, // Usage (Pointer) 334
197 0xa1, 0x00, // Collection (Physical) 336
198 0x05, 0x09, // Usage Page (Button) 338
199 0x19, 0x01, // UsageMinimum (1) 340
200 0x29, 0x03, // UsageMaximum (3) 342
201 0x15, 0x00, // Logical Minimum (0) 344
202 0x25, 0x01, // Logical Maximum (1) 346
203 0x85, 0x02, // Report ID (2) 348
204 0x95, 0x03, // Report Count (3) 350
205 0x75, 0x01, // Report Size (1) 352
206 0x81, 0x02, // Input (Data,Var,Abs) 354
207 0x95, 0x01, // Report Count (1) 356
208 0x75, 0x05, // Report Size (5) 358
209 0x81, 0x01, // Input (Cnst,Arr,Abs) 360
210 0x05, 0x01, // Usage Page (Generic Desktop) 362
211 0x09, 0x30, // Usage (X) 364
212 0x09, 0x31, // Usage (Y) 366
213 0x15, 0x81, // Logical Minimum (-127) 368
214 0x25, 0x7f, // Logical Maximum (127) 370
215 0x75, 0x08, // Report Size (8) 372
216 0x95, 0x02, // Report Count (2) 374
217 0x81, 0x06, // Input (Data,Var,Rel) 376
218 0x95, 0x04, // Report Count (4) 378
219 0x75, 0x08, // Report Size (8) 380
220 0x81, 0x01, // Input (Cnst,Arr,Abs) 382
221 0xc0, // End Collection 384
222 0xc0, // End Collection 385
223 0x05, 0x0d, // Usage Page (Digitizers) 386
224 0x09, 0x05, // Usage (Touch Pad) 388
225 0xa1, 0x01, // Collection (Application) 390
226 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page FF00) 392
227 0x09, 0x0c, // Usage (Vendor Usage 0x0c) 395
228 0x15, 0x00, // Logical Minimum (0) 397
229 0x26, 0xff, 0x00, // Logical Maximum (255) 399
230 0x75, 0x08, // Report Size (8) 402
231 0x95, 0x10, // Report Count (16) 404
232 0x85, 0x3f, // Report ID (63) 406
233 0x81, 0x22, // Input (Data,Var,Abs,NoPref) 408
234 0xc0, // End Collection 410
235 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page FF00) 411
236 0x09, 0x0c, // Usage (Vendor Usage 0x0c) 414
237 0xa1, 0x01, // Collection (Application) 416
238 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page FF00) 418
239 0x09, 0x0c, // Usage (Vendor Usage 0x0c) 421
240 0x15, 0x00, // Logical Minimum (0) 423
241 0x26, 0xff, 0x00, // Logical Maximum (255) 425
242 0x85, 0x44, // Report ID (68) 428
243 0x75, 0x08, // Report Size (8) 430
244 0x96, 0x6b, 0x05, // Report Count (1387) 432
245 0x81, 0x00, // Input (Data,Arr,Abs) 435
246 0xc0, // End Collection 437
247 };
248
249 #define PRE_240524_RDESC_SIZE 328
250 #define PRE_240524_RDESC_FIXED_SIZE 338 /* The original bits of the descriptor */
251 #define FW_240524_RDESC_SIZE 438
252 #define FW_240524_RDESC_FIXED_SIZE sizeof(fixed_rdesc)
253
SEC(HID_BPF_RDESC_FIXUP)254 SEC(HID_BPF_RDESC_FIXUP)
255 int BPF_PROG(hid_fix_rdesc_huion_kamvas_pro_19, struct hid_bpf_ctx *hctx)
256 {
257 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
258
259 if (!data)
260 return 0; /* EPERM check */
261
262 if (hctx->size == FW_240524_RDESC_SIZE) {
263 __builtin_memcpy(data, fixed_rdesc, FW_240524_RDESC_FIXED_SIZE);
264 return sizeof(fixed_rdesc);
265 }
266
267 __builtin_memcpy(data, fixed_rdesc, PRE_240524_RDESC_FIXED_SIZE);
268
269 return PRE_240524_RDESC_FIXED_SIZE;
270 }
271
272 /*
273 * This tablet reports the 3rd button through invert, but this conflict
274 * with the normal eraser mode.
275 * Fortunately, before entering eraser mode, (so Invert = 1),
276 * the tablet always sends an out-of-proximity event.
277 * So we can detect that single event and:
278 * - if there was none but the invert bit was toggled: this is the
279 * third button
280 * - if there was this out-of-proximity event, we are entering
281 * eraser mode, and we will until the next out-of-proximity.
282 */
SEC(HID_BPF_DEVICE_EVENT)283 SEC(HID_BPF_DEVICE_EVENT)
284 int BPF_PROG(kamvas_pro_19_fix_3rd_button, struct hid_bpf_ctx *hctx)
285 {
286 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);
287
288 if (!data)
289 return 0; /* EPERM check */
290
291 if (data[0] != 0x0a) /* not the pen report ID */
292 return 0;
293
294 /* stylus is out of range */
295 if (!(data[1] & 0x40)) {
296 prev_was_out_of_range = true;
297 in_eraser_mode = false;
298 return 0;
299 }
300
301 /* going into eraser mode (Invert = 1) only happens after an
302 * out of range event
303 */
304 if (prev_was_out_of_range && (data[1] & 0x18))
305 in_eraser_mode = true;
306
307 /* eraser mode works fine */
308 if (in_eraser_mode)
309 return 0;
310
311 /* copy the Invert bit reported for the 3rd button in bit 7 */
312 if (data[1] & 0x08)
313 data[1] |= 0x20;
314
315 /* clear Invert bit now that it was copied */
316 data[1] &= 0xf7;
317
318 prev_was_out_of_range = false;
319
320 return 0;
321 }
322
323 HID_BPF_OPS(huion_Kamvas_pro_19) = {
324 .hid_rdesc_fixup = (void *)hid_fix_rdesc_huion_kamvas_pro_19,
325 .hid_device_event = (void *)kamvas_pro_19_fix_3rd_button,
326 };
327
328 SEC("syscall")
probe(struct hid_bpf_probe_args * ctx)329 int probe(struct hid_bpf_probe_args *ctx)
330 {
331
332 ctx->retval = !((ctx->rdesc_size == PRE_240524_RDESC_SIZE) ||
333 (ctx->rdesc_size == FW_240524_RDESC_SIZE));
334 if (ctx->retval)
335 ctx->retval = -EINVAL;
336
337 /* ensure the kernel isn't fixed already */
338 if (ctx->rdesc[17] != 0x43) /* Secondary Tip Switch */
339 ctx->retval = -EINVAL;
340
341 struct hid_bpf_ctx *hctx = hid_bpf_allocate_context(ctx->hid);
342
343 if (!hctx) {
344 return ctx->retval = -EINVAL;
345 return 0;
346 }
347
348 const char *name = hctx->hid->name;
349
350 /* strip out TEST_PREFIX */
351 if (!__builtin_memcmp(name, TEST_PREFIX, sizeof(TEST_PREFIX) - 1))
352 name += sizeof(TEST_PREFIX) - 1;
353
354 if (__builtin_memcmp(name, NAME_KAMVAS_PRO_19, sizeof(NAME_KAMVAS_PRO_19)))
355 ctx->retval = -EINVAL;
356
357 hid_bpf_release_context(hctx);
358
359 return 0;
360 }
361
362 char _license[] SEC("license") = "GPL";
363