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