296 lines
15 KiB
C
296 lines
15 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 5) */
|
||
|
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
|
||
|
};
|
||
|
|
||
|
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 */
|
||
|
|
||
|
__builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc));
|
||
|
|
||
|
return sizeof(fixed_rdesc);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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 != 328;
|
||
|
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";
|