// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2024 Benjamin Tissoires
*/
#include "vmlinux.h"
#include "hid_bpf.h"
#include "hid_bpf_helpers.h"
#include <bpf/bpf_tracing.h>
#define VID_HUION 0 x256C
#define PID_KAMVAS_PRO_19 0 x006B
#define NAME_KAMVAS_PRO_19 "HUION Huion Tablet_GT1902"
#define TEST_PREFIX "uhid test "
HID_BPF_CONFIG(
HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, VID_HUION, PID_KAMVAS_PRO_19),
);
bool prev_was_out_of_range;
bool in_eraser_mode;
/*
* We need to amend the report descriptor for the following:
* - the second button is reported through Secondary Tip Switch instead of Secondary Barrel Switch
* - the third button is reported through Invert, and we need some room to report it.
*
*/
static const __u8 fixed_rdesc[] = {
0 x05, 0 x0d, // Usage Page (Digitizers) 0
0 x09, 0 x02, // Usage (Pen) 2
0 xa1, 0 x01, // Collection (Application) 4
0 x85, 0 x0a, // Report ID (10) 6
0 x09, 0 x20, // Usage (Stylus) 8
0 xa1, 0 x01, // Collection (Application) 10
0 x09, 0 x42, // Usage (Tip Switch) 12
0 x09, 0 x44, // Usage (Barrel Switch) 14
0 x09, 0 x5a, // Usage (Secondary Barrel Switch) 16 /* changed from Secondary Tip Switch */
0 x09, 0 x3c, // Usage (Invert) 18
0 x09, 0 x45, // Usage (Eraser) 20
0 x15, 0 x00, // Logical Minimum (0) 22
0 x25, 0 x01, // Logical Maximum (1) 24
0 x75, 0 x01, // Report Size (1) 26
0 x95, 0 x05, // Report Count (5) 28 /* changed (was 6) */
0 x81, 0 x02, // Input (Data,Var,Abs) 30
0 x05, 0 x09, // Usage Page (Button) /* inserted */
0 x09, 0 x4a, // Usage (0x4a) /* inserted to be translated as input usage 0x149: BTN_STYLUS3 */
0 x95, 0 x01, // Report Count (1) /* inserted */
0 x81, 0 x02, // Input (Data,Var,Abs) /* inserted */
0 x05, 0 x0d, // Usage Page (Digitizers) /* inserted */
0 x09, 0 x32, // Usage (In Range) 32
0 x75, 0 x01, // Report Size (1) 34
0 x95, 0 x01, // Report Count (1) 36
0 x81, 0 x02, // Input (Data,Var,Abs) 38
0 x81, 0 x03, // Input (Cnst,Var,Abs) 40
0 x05, 0 x01, // Usage Page (Generic Desktop) 42
0 x09, 0 x30, // Usage (X) 44
0 x09, 0 x31, // Usage (Y) 46
0 x55, 0 x0d, // Unit Exponent (-3) 48
0 x65, 0 x33, // Unit (EnglishLinear: in³) 50
0 x26, 0 xff, 0 x7f, // Logical Maximum (32767) 52
0 x35, 0 x00, // Physical Minimum (0) 55
0 x46, 0 x00, 0 x08, // Physical Maximum (2048) 57
0 x75, 0 x10, // Report Size (16) 60
0 x95, 0 x02, // Report Count (2) 62
0 x81, 0 x02, // Input (Data,Var,Abs) 64
0 x05, 0 x0d, // Usage Page (Digitizers) 66
0 x09, 0 x30, // Usage (Tip Pressure) 68
0 x26, 0 xff, 0 x3f, // Logical Maximum (16383) 70
0 x75, 0 x10, // Report Size (16) 73
0 x95, 0 x01, // Report Count (1) 75
0 x81, 0 x02, // Input (Data,Var,Abs) 77
0 x09, 0 x3d, // Usage (X Tilt) 79
0 x09, 0 x3e, // Usage (Y Tilt) 81
0 x15, 0 xa6, // Logical Minimum (-90) 83
0 x25, 0 x5a, // Logical Maximum (90) 85
0 x75, 0 x08, // Report Size (8) 87
0 x95, 0 x02, // Report Count (2) 89
0 x81, 0 x02, // Input (Data,Var,Abs) 91
0 xc0, // End Collection 93
0 xc0, // End Collection 94
0 x05, 0 x0d, // Usage Page (Digitizers) 95
0 x09, 0 x04, // Usage (Touch Screen) 97
0 xa1, 0 x01, // Collection (Application) 99
0 x85, 0 x04, // Report ID (4) 101
0 x09, 0 x22, // Usage (Finger) 103
0 xa1, 0 x02, // Collection (Logical) 105
0 x05, 0 x0d, // Usage Page (Digitizers) 107
0 x95, 0 x01, // Report Count (1) 109
0 x75, 0 x06, // Report Size (6) 111
0 x09, 0 x51, // Usage (Contact Id) 113
0 x15, 0 x00, // Logical Minimum (0) 115
0 x25, 0 x3f, // Logical Maximum (63) 117
0 x81, 0 x02, // Input (Data,Var,Abs) 119
0 x09, 0 x42, // Usage (Tip Switch) 121
0 x25, 0 x01, // Logical Maximum (1) 123
0 x75, 0 x01, // Report Size (1) 125
0 x95, 0 x01, // Report Count (1) 127
0 x81, 0 x02, // Input (Data,Var,Abs) 129
0 x75, 0 x01, // Report Size (1) 131
0 x95, 0 x01, // Report Count (1) 133
0 x81, 0 x03, // Input (Cnst,Var,Abs) 135
0 x05, 0 x01, // Usage Page (Generic Desktop) 137
0 x75, 0 x10, // Report Size (16) 139
0 x55, 0 x0e, // Unit Exponent (-2) 141
0 x65, 0 x11, // Unit (SILinear: cm) 143
0 x09, 0 x30, // Usage (X) 145
0 x26, 0 xff, 0 x7f, // Logical Maximum (32767) 147
0 x35, 0 x00, // Physical Minimum (0) 150
0 x46, 0 x15, 0 x0c, // Physical Maximum (3093) 152
0 x81, 0 x42, // Input (Data,Var,Abs,Null) 155
0 x09, 0 x31, // Usage (Y) 157
0 x26, 0 xff, 0 x7f, // Logical Maximum (32767) 159
0 x46, 0 xcb, 0 x06, // Physical Maximum (1739) 162
0 x81, 0 x42, // Input (Data,Var,Abs,Null) 165
0 x05, 0 x0d, // Usage Page (Digitizers) 167
0 x09, 0 x30, // Usage (Tip Pressure) 169
0 x26, 0 xff, 0 x1f, // Logical Maximum (8191) 171
0 x75, 0 x10, // Report Size (16) 174
0 x95, 0 x01, // Report Count (1) 176
0 x81, 0 x02, // Input (Data,Var,Abs) 178
0 xc0, // End Collection 180
0 x05, 0 x0d, // Usage Page (Digitizers) 181
0 x09, 0 x22, // Usage (Finger) 183
0 xa1, 0 x02, // Collection (Logical) 185
0 x05, 0 x0d, // Usage Page (Digitizers) 187
0 x95, 0 x01, // Report Count (1) 189
0 x75, 0 x06, // Report Size (6) 191
0 x09, 0 x51, // Usage (Contact Id) 193
0 x15, 0 x00, // Logical Minimum (0) 195
0 x25, 0 x3f, // Logical Maximum (63) 197
0 x81, 0 x02, // Input (Data,Var,Abs) 199
0 x09, 0 x42, // Usage (Tip Switch) 201
0 x25, 0 x01, // Logical Maximum (1) 203
0 x75, 0 x01, // Report Size (1) 205
0 x95, 0 x01, // Report Count (1) 207
0 x81, 0 x02, // Input (Data,Var,Abs) 209
0 x75, 0 x01, // Report Size (1) 211
0 x95, 0 x01, // Report Count (1) 213
0 x81, 0 x03, // Input (Cnst,Var,Abs) 215
0 x05, 0 x01, // Usage Page (Generic Desktop) 217
0 x75, 0 x10, // Report Size (16) 219
0 x55, 0 x0e, // Unit Exponent (-2) 221
0 x65, 0 x11, // Unit (SILinear: cm) 223
0 x09, 0 x30, // Usage (X) 225
0 x26, 0 xff, 0 x7f, // Logical Maximum (32767) 227
0 x35, 0 x00, // Physical Minimum (0) 230
0 x46, 0 x15, 0 x0c, // Physical Maximum (3093) 232
0 x81, 0 x42, // Input (Data,Var,Abs,Null) 235
0 x09, 0 x31, // Usage (Y) 237
0 x26, 0 xff, 0 x7f, // Logical Maximum (32767) 239
0 x46, 0 xcb, 0 x06, // Physical Maximum (1739) 242
0 x81, 0 x42, // Input (Data,Var,Abs,Null) 245
0 x05, 0 x0d, // Usage Page (Digitizers) 247
0 x09, 0 x30, // Usage (Tip Pressure) 249
0 x26, 0 xff, 0 x1f, // Logical Maximum (8191) 251
0 x75, 0 x10, // Report Size (16) 254
0 x95, 0 x01, // Report Count (1) 256
0 x81, 0 x02, // Input (Data,Var,Abs) 258
0 xc0, // End Collection 260
0 x05, 0 x0d, // Usage Page (Digitizers) 261
0 x09, 0 x56, // Usage (Scan Time) 263
0 x55, 0 x00, // Unit Exponent (0) 265
0 x65, 0 x00, // Unit (None) 267
0 x27, 0 xff, 0 xff, 0 xff, 0 x7f, // Logical Maximum (2147483647) 269
0 x95, 0 x01, // Report Count (1) 274
0 x75, 0 x20, // Report Size (32) 276
0 x81, 0 x02, // Input (Data,Var,Abs) 278
0 x09, 0 x54, // Usage (Contact Count) 280
0 x25, 0 x7f, // Logical Maximum (127) 282
0 x95, 0 x01, // Report Count (1) 284
0 x75, 0 x08, // Report Size (8) 286
0 x81, 0 x02, // Input (Data,Var,Abs) 288
0 x75, 0 x08, // Report Size (8) 290
0 x95, 0 x08, // Report Count (8) 292
0 x81, 0 x03, // Input (Cnst,Var,Abs) 294
0 x85, 0 x05, // Report ID (5) 296
0 x09, 0 x55, // Usage (Contact Max) 298
0 x25, 0 x0a, // Logical Maximum (10) 300
0 x75, 0 x08, // Report Size (8) 302
0 x95, 0 x01, // Report Count (1) 304
0 xb1, 0 x02, // Feature (Data,Var,Abs) 306
0 x06, 0 x00, 0 xff, // Usage Page (Vendor Defined Page 1) 308
0 x09, 0 xc5, // Usage (Vendor Usage 0xc5) 311
0 x85, 0 x06, // Report ID (6) 313
0 x15, 0 x00, // Logical Minimum (0) 315
0 x26, 0 xff, 0 x00, // Logical Maximum (255) 317
0 x75, 0 x08, // Report Size (8) 320
0 x96, 0 x00, 0 x01, // Report Count (256) 322
0 xb1, 0 x02, // Feature (Data,Var,Abs) 325
0 xc0, // End Collection 327
/* New in Firmware Version: HUION_M220_240524 */
0 x05, 0 x01, // Usage Page (Generic Desktop) 328
0 x09, 0 x01, // Usage (Pointer) 330
0 xa1, 0 x01, // Collection (Application) 332
0 x09, 0 x01, // Usage (Pointer) 334
0 xa1, 0 x00, // Collection (Physical) 336
0 x05, 0 x09, // Usage Page (Button) 338
0 x19, 0 x01, // UsageMinimum (1) 340
0 x29, 0 x03, // UsageMaximum (3) 342
0 x15, 0 x00, // Logical Minimum (0) 344
0 x25, 0 x01, // Logical Maximum (1) 346
0 x85, 0 x02, // Report ID (2) 348
0 x95, 0 x03, // Report Count (3) 350
0 x75, 0 x01, // Report Size (1) 352
0 x81, 0 x02, // Input (Data,Var,Abs) 354
0 x95, 0 x01, // Report Count (1) 356
0 x75, 0 x05, // Report Size (5) 358
0 x81, 0 x01, // Input (Cnst,Arr,Abs) 360
0 x05, 0 x01, // Usage Page (Generic Desktop) 362
0 x09, 0 x30, // Usage (X) 364
0 x09, 0 x31, // Usage (Y) 366
0 x15, 0 x81, // Logical Minimum (-127) 368
0 x25, 0 x7f, // Logical Maximum (127) 370
0 x75, 0 x08, // Report Size (8) 372
0 x95, 0 x02, // Report Count (2) 374
0 x81, 0 x06, // Input (Data,Var,Rel) 376
0 x95, 0 x04, // Report Count (4) 378
0 x75, 0 x08, // Report Size (8) 380
0 x81, 0 x01, // Input (Cnst,Arr,Abs) 382
0 xc0, // End Collection 384
0 xc0, // End Collection 385
0 x05, 0 x0d, // Usage Page (Digitizers) 386
0 x09, 0 x05, // Usage (Touch Pad) 388
0 xa1, 0 x01, // Collection (Application) 390
0 x06, 0 x00, 0 xff, // Usage Page (Vendor Defined Page FF00) 392
0 x09, 0 x0c, // Usage (Vendor Usage 0x0c) 395
0 x15, 0 x00, // Logical Minimum (0) 397
0 x26, 0 xff, 0 x00, // Logical Maximum (255) 399
0 x75, 0 x08, // Report Size (8) 402
0 x95, 0 x10, // Report Count (16) 404
0 x85, 0 x3f, // Report ID (63) 406
0 x81, 0 x22, // Input (Data,Var,Abs,NoPref) 408
0 xc0, // End Collection 410
0 x06, 0 x00, 0 xff, // Usage Page (Vendor Defined Page FF00) 411
0 x09, 0 x0c, // Usage (Vendor Usage 0x0c) 414
0 xa1, 0 x01, // Collection (Application) 416
0 x06, 0 x00, 0 xff, // Usage Page (Vendor Defined Page FF00) 418
0 x09, 0 x0c, // Usage (Vendor Usage 0x0c) 421
0 x15, 0 x00, // Logical Minimum (0) 423
0 x26, 0 xff, 0 x00, // Logical Maximum (255) 425
0 x85, 0 x44, // Report ID (68) 428
0 x75, 0 x08, // Report Size (8) 430
0 x96, 0 x6b, 0 x05, // Report Count (1387) 432
0 x81, 0 x00, // Input (Data,Arr,Abs) 435
0 xc0, // End Collection 437
};
#define PRE_240524_RDESC_SIZE 328
#define PRE_240524_RDESC_FIXED_SIZE 338 /* The original bits of the descriptor */
#define FW_240524_RDESC_SIZE 438
#define FW_240524_RDESC_FIXED_SIZE sizeof (fixed_rdesc)
SEC(HID_BPF_RDESC_FIXUP)
int BPF_PROG(hid_fix_rdesc_huion_kamvas_pro_19, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
if (!data)
return 0 ; /* EPERM check */
if (hctx->size == FW_240524_RDESC_SIZE) {
__builtin_memcpy(data, fixed_rdesc, FW_240524_RDESC_FIXED_SIZE);
return sizeof (fixed_rdesc);
}
__builtin_memcpy(data, fixed_rdesc, PRE_240524_RDESC_FIXED_SIZE);
return PRE_240524_RDESC_FIXED_SIZE;
}
/*
* This tablet reports the 3rd button through invert, but this conflict
* with the normal eraser mode.
* Fortunately, before entering eraser mode, (so Invert = 1),
* the tablet always sends an out-of-proximity event.
* So we can detect that single event and:
* - if there was none but the invert bit was toggled: this is the
* third button
* - if there was this out-of-proximity event, we are entering
* eraser mode, and we will until the next out-of-proximity.
*/
SEC(HID_BPF_DEVICE_EVENT)
int BPF_PROG(kamvas_pro_19_fix_3rd_button, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);
if (!data)
return 0 ; /* EPERM check */
if (data[0 ] != 0 x0a) /* not the pen report ID */
return 0 ;
/* stylus is out of range */
if (!(data[1 ] & 0 x40)) {
prev_was_out_of_range = true ;
in_eraser_mode = false ;
return 0 ;
}
/* going into eraser mode (Invert = 1) only happens after an
* out of range event
*/
if (prev_was_out_of_range && (data[1 ] & 0 x18))
in_eraser_mode = true ;
/* eraser mode works fine */
if (in_eraser_mode)
return 0 ;
/* copy the Invert bit reported for the 3rd button in bit 7 */
if (data[1 ] & 0 x08)
data[1 ] |= 0 x20;
/* clear Invert bit now that it was copied */
data[1 ] &= 0 xf7;
prev_was_out_of_range = false ;
return 0 ;
}
HID_BPF_OPS(huion_Kamvas_pro_19) = {
.hid_rdesc_fixup = (void *)hid_fix_rdesc_huion_kamvas_pro_19,
.hid_device_event = (void *)kamvas_pro_19_fix_3rd_button,
};
SEC("syscall" )
int probe(struct hid_bpf_probe_args *ctx)
{
ctx->retval = !((ctx->rdesc_size == PRE_240524_RDESC_SIZE) ||
(ctx->rdesc_size == FW_240524_RDESC_SIZE));
if (ctx->retval)
ctx->retval = -EINVAL;
/* ensure the kernel isn't fixed already */
if (ctx->rdesc[17 ] != 0 x43) /* Secondary Tip Switch */
ctx->retval = -EINVAL;
struct hid_bpf_ctx *hctx = hid_bpf_allocate_context(ctx->hid);
if (!hctx) {
return ctx->retval = -EINVAL;
return 0 ;
}
const char *name = hctx->hid->name;
/* strip out TEST_PREFIX */
if (!__builtin_memcmp(name, TEST_PREFIX, sizeof (TEST_PREFIX) - 1 ))
name += sizeof (TEST_PREFIX) - 1 ;
if (__builtin_memcmp(name, NAME_KAMVAS_PRO_19, sizeof (NAME_KAMVAS_PRO_19)))
ctx->retval = -EINVAL;
hid_bpf_release_context(hctx);
return 0 ;
}
char _license[] SEC("license" ) = "GPL" ;
Messung V0.5 in Prozent C=94 H=91 G=92
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-06-08)
¤
*© Formatika GbR, Deutschland