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 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 */ 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") 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