linux/drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c
Benjamin Tissoires 531a1cc667 HID: bpf: Added updated Kamvas Pro 19 descriptor
This adds an updated HID descriptor for the Huion Kamvas Pro 19 that is
present on newer firmware revisions, while also trying to keep
compat with the older versions.

Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests/164
Signed-off-by: Aki Van Ness <aki@lethalbit.net>
Acked-by: Jiri Kosina <jkosina@suse.com>
Link: https://patch.msgid.link/20250207-bpf-import-2025-02-07-v1-3-6048fdd5a206@kernel.org
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
2025-02-24 09:25:34 +01:00

362 lines
20 KiB
C

// 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 0x256C
#define PID_KAMVAS_PRO_19 0x006B
#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[] = {
0x05, 0x0d, // Usage Page (Digitizers) 0
0x09, 0x02, // Usage (Pen) 2
0xa1, 0x01, // Collection (Application) 4
0x85, 0x0a, // Report ID (10) 6
0x09, 0x20, // Usage (Stylus) 8
0xa1, 0x01, // Collection (Application) 10
0x09, 0x42, // Usage (Tip Switch) 12
0x09, 0x44, // Usage (Barrel Switch) 14
0x09, 0x5a, // Usage (Secondary Barrel Switch) 16 /* changed from Secondary Tip Switch */
0x09, 0x3c, // Usage (Invert) 18
0x09, 0x45, // Usage (Eraser) 20
0x15, 0x00, // Logical Minimum (0) 22
0x25, 0x01, // Logical Maximum (1) 24
0x75, 0x01, // Report Size (1) 26
0x95, 0x05, // Report Count (5) 28 /* changed (was 6) */
0x81, 0x02, // Input (Data,Var,Abs) 30
0x05, 0x09, // Usage Page (Button) /* inserted */
0x09, 0x4a, // Usage (0x4a) /* inserted to be translated as input usage 0x149: BTN_STYLUS3 */
0x95, 0x01, // Report Count (1) /* inserted */
0x81, 0x02, // Input (Data,Var,Abs) /* inserted */
0x05, 0x0d, // Usage Page (Digitizers) /* inserted */
0x09, 0x32, // Usage (In Range) 32
0x75, 0x01, // Report Size (1) 34
0x95, 0x01, // Report Count (1) 36
0x81, 0x02, // Input (Data,Var,Abs) 38
0x81, 0x03, // Input (Cnst,Var,Abs) 40
0x05, 0x01, // Usage Page (Generic Desktop) 42
0x09, 0x30, // Usage (X) 44
0x09, 0x31, // Usage (Y) 46
0x55, 0x0d, // Unit Exponent (-3) 48
0x65, 0x33, // Unit (EnglishLinear: in³) 50
0x26, 0xff, 0x7f, // Logical Maximum (32767) 52
0x35, 0x00, // Physical Minimum (0) 55
0x46, 0x00, 0x08, // Physical Maximum (2048) 57
0x75, 0x10, // Report Size (16) 60
0x95, 0x02, // Report Count (2) 62
0x81, 0x02, // Input (Data,Var,Abs) 64
0x05, 0x0d, // Usage Page (Digitizers) 66
0x09, 0x30, // Usage (Tip Pressure) 68
0x26, 0xff, 0x3f, // Logical Maximum (16383) 70
0x75, 0x10, // Report Size (16) 73
0x95, 0x01, // Report Count (1) 75
0x81, 0x02, // Input (Data,Var,Abs) 77
0x09, 0x3d, // Usage (X Tilt) 79
0x09, 0x3e, // Usage (Y Tilt) 81
0x15, 0xa6, // Logical Minimum (-90) 83
0x25, 0x5a, // Logical Maximum (90) 85
0x75, 0x08, // Report Size (8) 87
0x95, 0x02, // Report Count (2) 89
0x81, 0x02, // Input (Data,Var,Abs) 91
0xc0, // End Collection 93
0xc0, // End Collection 94
0x05, 0x0d, // Usage Page (Digitizers) 95
0x09, 0x04, // Usage (Touch Screen) 97
0xa1, 0x01, // Collection (Application) 99
0x85, 0x04, // Report ID (4) 101
0x09, 0x22, // Usage (Finger) 103
0xa1, 0x02, // Collection (Logical) 105
0x05, 0x0d, // Usage Page (Digitizers) 107
0x95, 0x01, // Report Count (1) 109
0x75, 0x06, // Report Size (6) 111
0x09, 0x51, // Usage (Contact Id) 113
0x15, 0x00, // Logical Minimum (0) 115
0x25, 0x3f, // Logical Maximum (63) 117
0x81, 0x02, // Input (Data,Var,Abs) 119
0x09, 0x42, // Usage (Tip Switch) 121
0x25, 0x01, // Logical Maximum (1) 123
0x75, 0x01, // Report Size (1) 125
0x95, 0x01, // Report Count (1) 127
0x81, 0x02, // Input (Data,Var,Abs) 129
0x75, 0x01, // Report Size (1) 131
0x95, 0x01, // Report Count (1) 133
0x81, 0x03, // Input (Cnst,Var,Abs) 135
0x05, 0x01, // Usage Page (Generic Desktop) 137
0x75, 0x10, // Report Size (16) 139
0x55, 0x0e, // Unit Exponent (-2) 141
0x65, 0x11, // Unit (SILinear: cm) 143
0x09, 0x30, // Usage (X) 145
0x26, 0xff, 0x7f, // Logical Maximum (32767) 147
0x35, 0x00, // Physical Minimum (0) 150
0x46, 0x15, 0x0c, // Physical Maximum (3093) 152
0x81, 0x42, // Input (Data,Var,Abs,Null) 155
0x09, 0x31, // Usage (Y) 157
0x26, 0xff, 0x7f, // Logical Maximum (32767) 159
0x46, 0xcb, 0x06, // Physical Maximum (1739) 162
0x81, 0x42, // Input (Data,Var,Abs,Null) 165
0x05, 0x0d, // Usage Page (Digitizers) 167
0x09, 0x30, // Usage (Tip Pressure) 169
0x26, 0xff, 0x1f, // Logical Maximum (8191) 171
0x75, 0x10, // Report Size (16) 174
0x95, 0x01, // Report Count (1) 176
0x81, 0x02, // Input (Data,Var,Abs) 178
0xc0, // End Collection 180
0x05, 0x0d, // Usage Page (Digitizers) 181
0x09, 0x22, // Usage (Finger) 183
0xa1, 0x02, // Collection (Logical) 185
0x05, 0x0d, // Usage Page (Digitizers) 187
0x95, 0x01, // Report Count (1) 189
0x75, 0x06, // Report Size (6) 191
0x09, 0x51, // Usage (Contact Id) 193
0x15, 0x00, // Logical Minimum (0) 195
0x25, 0x3f, // Logical Maximum (63) 197
0x81, 0x02, // Input (Data,Var,Abs) 199
0x09, 0x42, // Usage (Tip Switch) 201
0x25, 0x01, // Logical Maximum (1) 203
0x75, 0x01, // Report Size (1) 205
0x95, 0x01, // Report Count (1) 207
0x81, 0x02, // Input (Data,Var,Abs) 209
0x75, 0x01, // Report Size (1) 211
0x95, 0x01, // Report Count (1) 213
0x81, 0x03, // Input (Cnst,Var,Abs) 215
0x05, 0x01, // Usage Page (Generic Desktop) 217
0x75, 0x10, // Report Size (16) 219
0x55, 0x0e, // Unit Exponent (-2) 221
0x65, 0x11, // Unit (SILinear: cm) 223
0x09, 0x30, // Usage (X) 225
0x26, 0xff, 0x7f, // Logical Maximum (32767) 227
0x35, 0x00, // Physical Minimum (0) 230
0x46, 0x15, 0x0c, // Physical Maximum (3093) 232
0x81, 0x42, // Input (Data,Var,Abs,Null) 235
0x09, 0x31, // Usage (Y) 237
0x26, 0xff, 0x7f, // Logical Maximum (32767) 239
0x46, 0xcb, 0x06, // Physical Maximum (1739) 242
0x81, 0x42, // Input (Data,Var,Abs,Null) 245
0x05, 0x0d, // Usage Page (Digitizers) 247
0x09, 0x30, // Usage (Tip Pressure) 249
0x26, 0xff, 0x1f, // Logical Maximum (8191) 251
0x75, 0x10, // Report Size (16) 254
0x95, 0x01, // Report Count (1) 256
0x81, 0x02, // Input (Data,Var,Abs) 258
0xc0, // End Collection 260
0x05, 0x0d, // Usage Page (Digitizers) 261
0x09, 0x56, // Usage (Scan Time) 263
0x55, 0x00, // Unit Exponent (0) 265
0x65, 0x00, // Unit (None) 267
0x27, 0xff, 0xff, 0xff, 0x7f, // Logical Maximum (2147483647) 269
0x95, 0x01, // Report Count (1) 274
0x75, 0x20, // Report Size (32) 276
0x81, 0x02, // Input (Data,Var,Abs) 278
0x09, 0x54, // Usage (Contact Count) 280
0x25, 0x7f, // Logical Maximum (127) 282
0x95, 0x01, // Report Count (1) 284
0x75, 0x08, // Report Size (8) 286
0x81, 0x02, // Input (Data,Var,Abs) 288
0x75, 0x08, // Report Size (8) 290
0x95, 0x08, // Report Count (8) 292
0x81, 0x03, // Input (Cnst,Var,Abs) 294
0x85, 0x05, // Report ID (5) 296
0x09, 0x55, // Usage (Contact Max) 298
0x25, 0x0a, // Logical Maximum (10) 300
0x75, 0x08, // Report Size (8) 302
0x95, 0x01, // Report Count (1) 304
0xb1, 0x02, // Feature (Data,Var,Abs) 306
0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 308
0x09, 0xc5, // Usage (Vendor Usage 0xc5) 311
0x85, 0x06, // Report ID (6) 313
0x15, 0x00, // Logical Minimum (0) 315
0x26, 0xff, 0x00, // Logical Maximum (255) 317
0x75, 0x08, // Report Size (8) 320
0x96, 0x00, 0x01, // Report Count (256) 322
0xb1, 0x02, // Feature (Data,Var,Abs) 325
0xc0, // End Collection 327
/* New in Firmware Version: HUION_M220_240524 */
0x05, 0x01, // Usage Page (Generic Desktop) 328
0x09, 0x01, // Usage (Pointer) 330
0xa1, 0x01, // Collection (Application) 332
0x09, 0x01, // Usage (Pointer) 334
0xa1, 0x00, // Collection (Physical) 336
0x05, 0x09, // Usage Page (Button) 338
0x19, 0x01, // UsageMinimum (1) 340
0x29, 0x03, // UsageMaximum (3) 342
0x15, 0x00, // Logical Minimum (0) 344
0x25, 0x01, // Logical Maximum (1) 346
0x85, 0x02, // Report ID (2) 348
0x95, 0x03, // Report Count (3) 350
0x75, 0x01, // Report Size (1) 352
0x81, 0x02, // Input (Data,Var,Abs) 354
0x95, 0x01, // Report Count (1) 356
0x75, 0x05, // Report Size (5) 358
0x81, 0x01, // Input (Cnst,Arr,Abs) 360
0x05, 0x01, // Usage Page (Generic Desktop) 362
0x09, 0x30, // Usage (X) 364
0x09, 0x31, // Usage (Y) 366
0x15, 0x81, // Logical Minimum (-127) 368
0x25, 0x7f, // Logical Maximum (127) 370
0x75, 0x08, // Report Size (8) 372
0x95, 0x02, // Report Count (2) 374
0x81, 0x06, // Input (Data,Var,Rel) 376
0x95, 0x04, // Report Count (4) 378
0x75, 0x08, // Report Size (8) 380
0x81, 0x01, // Input (Cnst,Arr,Abs) 382
0xc0, // End Collection 384
0xc0, // End Collection 385
0x05, 0x0d, // Usage Page (Digitizers) 386
0x09, 0x05, // Usage (Touch Pad) 388
0xa1, 0x01, // Collection (Application) 390
0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page FF00) 392
0x09, 0x0c, // Usage (Vendor Usage 0x0c) 395
0x15, 0x00, // Logical Minimum (0) 397
0x26, 0xff, 0x00, // Logical Maximum (255) 399
0x75, 0x08, // Report Size (8) 402
0x95, 0x10, // Report Count (16) 404
0x85, 0x3f, // Report ID (63) 406
0x81, 0x22, // Input (Data,Var,Abs,NoPref) 408
0xc0, // End Collection 410
0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page FF00) 411
0x09, 0x0c, // Usage (Vendor Usage 0x0c) 414
0xa1, 0x01, // Collection (Application) 416
0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page FF00) 418
0x09, 0x0c, // Usage (Vendor Usage 0x0c) 421
0x15, 0x00, // Logical Minimum (0) 423
0x26, 0xff, 0x00, // Logical Maximum (255) 425
0x85, 0x44, // Report ID (68) 428
0x75, 0x08, // Report Size (8) 430
0x96, 0x6b, 0x05, // Report Count (1387) 432
0x81, 0x00, // Input (Data,Arr,Abs) 435
0xc0, // 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] != 0x0a) /* not the pen report ID */
return 0;
/* stylus is out of range */
if (!(data[1] & 0x40)) {
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] & 0x18))
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] & 0x08)
data[1] |= 0x20;
/* clear Invert bit now that it was copied */
data[1] &= 0xf7;
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] != 0x43) /* 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";