232 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* Copyright (c) 2023 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_UGEE 0x28BD /* VID is shared with SinoWealth and Glorious and prob others */11#define PID_ARTIST_24 0x093A12#define PID_ARTIST_24_PRO 0x092D13 14HID_BPF_CONFIG(15 HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_24),16 HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_24_PRO)17);18 19/*20 * We need to amend the report descriptor for the following:21 * - the device reports Eraser instead of using Secondary Barrel Switch22 * - the pen doesn't have a rubber tail, so basically we are removing any23 * eraser/invert bits24 */25static const __u8 fixed_rdesc[] = {26 0x05, 0x0d, // Usage Page (Digitizers) 027 0x09, 0x02, // Usage (Pen) 228 0xa1, 0x01, // Collection (Application) 429 0x85, 0x07, // Report ID (7) 630 0x09, 0x20, // Usage (Stylus) 831 0xa1, 0x00, // Collection (Physical) 1032 0x09, 0x42, // Usage (Tip Switch) 1233 0x09, 0x44, // Usage (Barrel Switch) 1434 0x09, 0x5a, // Usage (Secondary Barrel Switch) 16 /* changed from 0x45 (Eraser) to 0x5a (Secondary Barrel Switch) */35 0x15, 0x00, // Logical Minimum (0) 1836 0x25, 0x01, // Logical Maximum (1) 2037 0x75, 0x01, // Report Size (1) 2238 0x95, 0x03, // Report Count (3) 2439 0x81, 0x02, // Input (Data,Var,Abs) 2640 0x95, 0x02, // Report Count (2) 2841 0x81, 0x03, // Input (Cnst,Var,Abs) 3042 0x09, 0x32, // Usage (In Range) 3243 0x95, 0x01, // Report Count (1) 3444 0x81, 0x02, // Input (Data,Var,Abs) 3645 0x95, 0x02, // Report Count (2) 3846 0x81, 0x03, // Input (Cnst,Var,Abs) 4047 0x75, 0x10, // Report Size (16) 4248 0x95, 0x01, // Report Count (1) 4449 0x35, 0x00, // Physical Minimum (0) 4650 0xa4, // Push 4851 0x05, 0x01, // Usage Page (Generic Desktop) 4952 0x09, 0x30, // Usage (X) 5153 0x65, 0x13, // Unit (EnglishLinear: in) 5354 0x55, 0x0d, // Unit Exponent (-3) 5555 0x46, 0xf0, 0x50, // Physical Maximum (20720) 5756 0x26, 0xff, 0x7f, // Logical Maximum (32767) 6057 0x81, 0x02, // Input (Data,Var,Abs) 6358 0x09, 0x31, // Usage (Y) 6559 0x46, 0x91, 0x2d, // Physical Maximum (11665) 6760 0x26, 0xff, 0x7f, // Logical Maximum (32767) 7061 0x81, 0x02, // Input (Data,Var,Abs) 7362 0xb4, // Pop 7563 0x09, 0x30, // Usage (Tip Pressure) 7664 0x45, 0x00, // Physical Maximum (0) 7865 0x26, 0xff, 0x1f, // Logical Maximum (8191) 8066 0x81, 0x42, // Input (Data,Var,Abs,Null) 8367 0x09, 0x3d, // Usage (X Tilt) 8568 0x15, 0x81, // Logical Minimum (-127) 8769 0x25, 0x7f, // Logical Maximum (127) 8970 0x75, 0x08, // Report Size (8) 9171 0x95, 0x01, // Report Count (1) 9372 0x81, 0x02, // Input (Data,Var,Abs) 9573 0x09, 0x3e, // Usage (Y Tilt) 9774 0x15, 0x81, // Logical Minimum (-127) 9975 0x25, 0x7f, // Logical Maximum (127) 10176 0x81, 0x02, // Input (Data,Var,Abs) 10377 0xc0, // End Collection 10578 0xc0, // End Collection 10679};80 81#define TIP_SWITCH BIT(0)82#define BARREL_SWITCH BIT(1)83#define ERASER BIT(2)84/* padding BIT(3) */85/* padding BIT(4) */86#define IN_RANGE BIT(5)87/* padding BIT(6) */88/* padding BIT(7) */89 90#define U16(index) (data[index] | (data[index + 1] << 8))91 92SEC(HID_BPF_RDESC_FIXUP)93int BPF_PROG(hid_fix_rdesc_xppen_artist24, struct hid_bpf_ctx *hctx)94{95 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);96 97 if (!data)98 return 0; /* EPERM check */99 100 __builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc));101 102 return sizeof(fixed_rdesc);103}104 105static __u8 prev_state = 0;106 107/*108 * There are a few cases where the device is sending wrong event109 * sequences, all related to the second button (the pen doesn't110 * have an eraser switch on the tail end):111 *112 * whenever the second button gets pressed or released, an113 * out-of-proximity event is generated and then the firmware114 * compensate for the missing state (and the firmware uses115 * eraser for that button):116 *117 * - if the pen is in range, an extra out-of-range is sent118 * when the second button is pressed/released:119 * // Pen is in range120 * E: InRange121 *122 * // Second button is pressed123 * E:124 * E: Eraser InRange125 *126 * // Second button is released127 * E:128 * E: InRange129 *130 * This case is ignored by this filter, it's "valid"131 * and userspace knows how to deal with it, there are just132 * a few out-of-prox events generated, but the user doesn´t133 * see them.134 *135 * - if the pen is in contact, 2 extra events are added when136 * the second button is pressed/released: an out of range137 * and an in range:138 *139 * // Pen is in contact140 * E: TipSwitch InRange141 *142 * // Second button is pressed143 * E: <- false release, needs to be filtered out144 * E: Eraser InRange <- false release, needs to be filtered out145 * E: TipSwitch Eraser InRange146 *147 * // Second button is released148 * E: <- false release, needs to be filtered out149 * E: InRange <- false release, needs to be filtered out150 * E: TipSwitch InRange151 *152 */153SEC(HID_BPF_DEVICE_EVENT)154int BPF_PROG(xppen_24_fix_eraser, struct hid_bpf_ctx *hctx)155{156 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);157 __u8 current_state, changed_state;158 bool prev_tip;159 160 if (!data)161 return 0; /* EPERM check */162 163 current_state = data[1];164 165 /* if the state is identical to previously, early return */166 if (current_state == prev_state)167 return 0;168 169 prev_tip = !!(prev_state & TIP_SWITCH);170 171 /*172 * Illegal transition: pen is in range with the tip pressed, and173 * it goes into out of proximity.174 *175 * Ideally we should hold the event, start a timer and deliver it176 * only if the timer ends, but we are not capable of that now.177 *178 * And it doesn't matter because when we are in such cases, this179 * means we are detecting a false release.180 */181 if ((current_state & IN_RANGE) == 0) {182 if (prev_tip)183 return HID_IGNORE_EVENT;184 return 0;185 }186 187 /*188 * XOR to only set the bits that have changed between189 * previous and current state190 */191 changed_state = prev_state ^ current_state;192 193 /* Store the new state for future processing */194 prev_state = current_state;195 196 /*197 * We get both a tipswitch and eraser change in the same HID report:198 * this is not an authorized transition and is unlikely to happen199 * in real life.200 * This is likely to be added by the firmware to emulate the201 * eraser mode so we can skip the event.202 */203 if ((changed_state & (TIP_SWITCH | ERASER)) == (TIP_SWITCH | ERASER)) /* we get both a tipswitch and eraser change at the same time */204 return HID_IGNORE_EVENT;205 206 return 0;207}208 209HID_BPF_OPS(xppen_artist_24) = {210 .hid_rdesc_fixup = (void *)hid_fix_rdesc_xppen_artist24,211 .hid_device_event = (void *)xppen_24_fix_eraser,212};213 214SEC("syscall")215int probe(struct hid_bpf_probe_args *ctx)216{217 /*218 * The device exports 3 interfaces.219 */220 ctx->retval = ctx->rdesc_size != 107;221 if (ctx->retval)222 ctx->retval = -EINVAL;223 224 /* ensure the kernel isn't fixed already */225 if (ctx->rdesc[17] != 0x45) /* Eraser */226 ctx->retval = -EINVAL;227 228 return 0;229}230 231char _license[] SEC("license") = "GPL";232