296 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* Copyright (c) 2024 Benjamin Tissoires3 */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 0x256C11#define PID_KAMVAS_PRO_19 0x006B12#define NAME_KAMVAS_PRO_19 "HUION Huion Tablet_GT1902"13 14#define TEST_PREFIX "uhid test "15 16HID_BPF_CONFIG(17 HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, VID_HUION, PID_KAMVAS_PRO_19),18);19 20bool prev_was_out_of_range;21bool 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 Switch26 * - the third button is reported through Invert, and we need some room to report it.27 *28 */29static const __u8 fixed_rdesc[] = {30 0x05, 0x0d, // Usage Page (Digitizers) 031 0x09, 0x02, // Usage (Pen) 232 0xa1, 0x01, // Collection (Application) 433 0x85, 0x0a, // Report ID (10) 634 0x09, 0x20, // Usage (Stylus) 835 0xa1, 0x01, // Collection (Application) 1036 0x09, 0x42, // Usage (Tip Switch) 1237 0x09, 0x44, // Usage (Barrel Switch) 1438 0x09, 0x5a, // Usage (Secondary Barrel Switch) 16 /* changed from Secondary Tip Switch */39 0x09, 0x3c, // Usage (Invert) 1840 0x09, 0x45, // Usage (Eraser) 2041 0x15, 0x00, // Logical Minimum (0) 2242 0x25, 0x01, // Logical Maximum (1) 2443 0x75, 0x01, // Report Size (1) 2644 0x95, 0x05, // Report Count (5) 28 /* changed (was 5) */45 0x81, 0x02, // Input (Data,Var,Abs) 3046 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) 3252 0x75, 0x01, // Report Size (1) 3453 0x95, 0x01, // Report Count (1) 3654 0x81, 0x02, // Input (Data,Var,Abs) 3855 0x81, 0x03, // Input (Cnst,Var,Abs) 4056 0x05, 0x01, // Usage Page (Generic Desktop) 4257 0x09, 0x30, // Usage (X) 4458 0x09, 0x31, // Usage (Y) 4659 0x55, 0x0d, // Unit Exponent (-3) 4860 0x65, 0x33, // Unit (EnglishLinear: in³) 5061 0x26, 0xff, 0x7f, // Logical Maximum (32767) 5262 0x35, 0x00, // Physical Minimum (0) 5563 0x46, 0x00, 0x08, // Physical Maximum (2048) 5764 0x75, 0x10, // Report Size (16) 6065 0x95, 0x02, // Report Count (2) 6266 0x81, 0x02, // Input (Data,Var,Abs) 6467 0x05, 0x0d, // Usage Page (Digitizers) 6668 0x09, 0x30, // Usage (Tip Pressure) 6869 0x26, 0xff, 0x3f, // Logical Maximum (16383) 7070 0x75, 0x10, // Report Size (16) 7371 0x95, 0x01, // Report Count (1) 7572 0x81, 0x02, // Input (Data,Var,Abs) 7773 0x09, 0x3d, // Usage (X Tilt) 7974 0x09, 0x3e, // Usage (Y Tilt) 8175 0x15, 0xa6, // Logical Minimum (-90) 8376 0x25, 0x5a, // Logical Maximum (90) 8577 0x75, 0x08, // Report Size (8) 8778 0x95, 0x02, // Report Count (2) 8979 0x81, 0x02, // Input (Data,Var,Abs) 9180 0xc0, // End Collection 9381 0xc0, // End Collection 9482 0x05, 0x0d, // Usage Page (Digitizers) 9583 0x09, 0x04, // Usage (Touch Screen) 9784 0xa1, 0x01, // Collection (Application) 9985 0x85, 0x04, // Report ID (4) 10186 0x09, 0x22, // Usage (Finger) 10387 0xa1, 0x02, // Collection (Logical) 10588 0x05, 0x0d, // Usage Page (Digitizers) 10789 0x95, 0x01, // Report Count (1) 10990 0x75, 0x06, // Report Size (6) 11191 0x09, 0x51, // Usage (Contact Id) 11392 0x15, 0x00, // Logical Minimum (0) 11593 0x25, 0x3f, // Logical Maximum (63) 11794 0x81, 0x02, // Input (Data,Var,Abs) 11995 0x09, 0x42, // Usage (Tip Switch) 12196 0x25, 0x01, // Logical Maximum (1) 12397 0x75, 0x01, // Report Size (1) 12598 0x95, 0x01, // Report Count (1) 12799 0x81, 0x02, // Input (Data,Var,Abs) 129100 0x75, 0x01, // Report Size (1) 131101 0x95, 0x01, // Report Count (1) 133102 0x81, 0x03, // Input (Cnst,Var,Abs) 135103 0x05, 0x01, // Usage Page (Generic Desktop) 137104 0x75, 0x10, // Report Size (16) 139105 0x55, 0x0e, // Unit Exponent (-2) 141106 0x65, 0x11, // Unit (SILinear: cm) 143107 0x09, 0x30, // Usage (X) 145108 0x26, 0xff, 0x7f, // Logical Maximum (32767) 147109 0x35, 0x00, // Physical Minimum (0) 150110 0x46, 0x15, 0x0c, // Physical Maximum (3093) 152111 0x81, 0x42, // Input (Data,Var,Abs,Null) 155112 0x09, 0x31, // Usage (Y) 157113 0x26, 0xff, 0x7f, // Logical Maximum (32767) 159114 0x46, 0xcb, 0x06, // Physical Maximum (1739) 162115 0x81, 0x42, // Input (Data,Var,Abs,Null) 165116 0x05, 0x0d, // Usage Page (Digitizers) 167117 0x09, 0x30, // Usage (Tip Pressure) 169118 0x26, 0xff, 0x1f, // Logical Maximum (8191) 171119 0x75, 0x10, // Report Size (16) 174120 0x95, 0x01, // Report Count (1) 176121 0x81, 0x02, // Input (Data,Var,Abs) 178122 0xc0, // End Collection 180123 0x05, 0x0d, // Usage Page (Digitizers) 181124 0x09, 0x22, // Usage (Finger) 183125 0xa1, 0x02, // Collection (Logical) 185126 0x05, 0x0d, // Usage Page (Digitizers) 187127 0x95, 0x01, // Report Count (1) 189128 0x75, 0x06, // Report Size (6) 191129 0x09, 0x51, // Usage (Contact Id) 193130 0x15, 0x00, // Logical Minimum (0) 195131 0x25, 0x3f, // Logical Maximum (63) 197132 0x81, 0x02, // Input (Data,Var,Abs) 199133 0x09, 0x42, // Usage (Tip Switch) 201134 0x25, 0x01, // Logical Maximum (1) 203135 0x75, 0x01, // Report Size (1) 205136 0x95, 0x01, // Report Count (1) 207137 0x81, 0x02, // Input (Data,Var,Abs) 209138 0x75, 0x01, // Report Size (1) 211139 0x95, 0x01, // Report Count (1) 213140 0x81, 0x03, // Input (Cnst,Var,Abs) 215141 0x05, 0x01, // Usage Page (Generic Desktop) 217142 0x75, 0x10, // Report Size (16) 219143 0x55, 0x0e, // Unit Exponent (-2) 221144 0x65, 0x11, // Unit (SILinear: cm) 223145 0x09, 0x30, // Usage (X) 225146 0x26, 0xff, 0x7f, // Logical Maximum (32767) 227147 0x35, 0x00, // Physical Minimum (0) 230148 0x46, 0x15, 0x0c, // Physical Maximum (3093) 232149 0x81, 0x42, // Input (Data,Var,Abs,Null) 235150 0x09, 0x31, // Usage (Y) 237151 0x26, 0xff, 0x7f, // Logical Maximum (32767) 239152 0x46, 0xcb, 0x06, // Physical Maximum (1739) 242153 0x81, 0x42, // Input (Data,Var,Abs,Null) 245154 0x05, 0x0d, // Usage Page (Digitizers) 247155 0x09, 0x30, // Usage (Tip Pressure) 249156 0x26, 0xff, 0x1f, // Logical Maximum (8191) 251157 0x75, 0x10, // Report Size (16) 254158 0x95, 0x01, // Report Count (1) 256159 0x81, 0x02, // Input (Data,Var,Abs) 258160 0xc0, // End Collection 260161 0x05, 0x0d, // Usage Page (Digitizers) 261162 0x09, 0x56, // Usage (Scan Time) 263163 0x55, 0x00, // Unit Exponent (0) 265164 0x65, 0x00, // Unit (None) 267165 0x27, 0xff, 0xff, 0xff, 0x7f, // Logical Maximum (2147483647) 269166 0x95, 0x01, // Report Count (1) 274167 0x75, 0x20, // Report Size (32) 276168 0x81, 0x02, // Input (Data,Var,Abs) 278169 0x09, 0x54, // Usage (Contact Count) 280170 0x25, 0x7f, // Logical Maximum (127) 282171 0x95, 0x01, // Report Count (1) 284172 0x75, 0x08, // Report Size (8) 286173 0x81, 0x02, // Input (Data,Var,Abs) 288174 0x75, 0x08, // Report Size (8) 290175 0x95, 0x08, // Report Count (8) 292176 0x81, 0x03, // Input (Cnst,Var,Abs) 294177 0x85, 0x05, // Report ID (5) 296178 0x09, 0x55, // Usage (Contact Max) 298179 0x25, 0x0a, // Logical Maximum (10) 300180 0x75, 0x08, // Report Size (8) 302181 0x95, 0x01, // Report Count (1) 304182 0xb1, 0x02, // Feature (Data,Var,Abs) 306183 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 308184 0x09, 0xc5, // Usage (Vendor Usage 0xc5) 311185 0x85, 0x06, // Report ID (6) 313186 0x15, 0x00, // Logical Minimum (0) 315187 0x26, 0xff, 0x00, // Logical Maximum (255) 317188 0x75, 0x08, // Report Size (8) 320189 0x96, 0x00, 0x01, // Report Count (256) 322190 0xb1, 0x02, // Feature (Data,Var,Abs) 325191 0xc0, // End Collection 327192};193 194SEC(HID_BPF_RDESC_FIXUP)195int BPF_PROG(hid_fix_rdesc_huion_kamvas_pro_19, struct hid_bpf_ctx *hctx)196{197 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);198 199 if (!data)200 return 0; /* EPERM check */201 202 __builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc));203 204 return sizeof(fixed_rdesc);205}206 207/*208 * This tablet reports the 3rd button through invert, but this conflict209 * with the normal eraser mode.210 * Fortunately, before entering eraser mode, (so Invert = 1),211 * the tablet always sends an out-of-proximity event.212 * So we can detect that single event and:213 * - if there was none but the invert bit was toggled: this is the214 * third button215 * - if there was this out-of-proximity event, we are entering216 * eraser mode, and we will until the next out-of-proximity.217 */218SEC(HID_BPF_DEVICE_EVENT)219int BPF_PROG(kamvas_pro_19_fix_3rd_button, struct hid_bpf_ctx *hctx)220{221 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);222 223 if (!data)224 return 0; /* EPERM check */225 226 if (data[0] != 0x0a) /* not the pen report ID */227 return 0;228 229 /* stylus is out of range */230 if (!(data[1] & 0x40)) {231 prev_was_out_of_range = true;232 in_eraser_mode = false;233 return 0;234 }235 236 /* going into eraser mode (Invert = 1) only happens after an237 * out of range event238 */239 if (prev_was_out_of_range && (data[1] & 0x18))240 in_eraser_mode = true;241 242 /* eraser mode works fine */243 if (in_eraser_mode)244 return 0;245 246 /* copy the Invert bit reported for the 3rd button in bit 7 */247 if (data[1] & 0x08)248 data[1] |= 0x20;249 250 /* clear Invert bit now that it was copied */251 data[1] &= 0xf7;252 253 prev_was_out_of_range = false;254 255 return 0;256}257 258HID_BPF_OPS(huion_Kamvas_pro_19) = {259 .hid_rdesc_fixup = (void *)hid_fix_rdesc_huion_kamvas_pro_19,260 .hid_device_event = (void *)kamvas_pro_19_fix_3rd_button,261};262 263SEC("syscall")264int probe(struct hid_bpf_probe_args *ctx)265{266 ctx->retval = ctx->rdesc_size != 328;267 if (ctx->retval)268 ctx->retval = -EINVAL;269 270 /* ensure the kernel isn't fixed already */271 if (ctx->rdesc[17] != 0x43) /* Secondary Tip Switch */272 ctx->retval = -EINVAL;273 274 struct hid_bpf_ctx *hctx = hid_bpf_allocate_context(ctx->hid);275 276 if (!hctx) {277 return ctx->retval = -EINVAL;278 return 0;279 }280 281 const char *name = hctx->hid->name;282 283 /* strip out TEST_PREFIX */284 if (!__builtin_memcmp(name, TEST_PREFIX, sizeof(TEST_PREFIX) - 1))285 name += sizeof(TEST_PREFIX) - 1;286 287 if (__builtin_memcmp(name, NAME_KAMVAS_PRO_19, sizeof(NAME_KAMVAS_PRO_19)))288 ctx->retval = -EINVAL;289 290 hid_bpf_release_context(hctx);291 292 return 0;293}294 295char _license[] SEC("license") = "GPL";296