1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright (c) 2023 Benjamin Tissoires 3 */ 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 0x093A 12 #define PID_ARTIST_24_PRO 0x092D 13 14 HID_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 Switch 22 * - the pen doesn't have a rubber tail, so basically we are removing any 23 * eraser/invert bits 24 */ 25 static const __u8 fixed_rdesc[] = { 26 0x05, 0x0d, // Usage Page (Digitizers) 0 27 0x09, 0x02, // Usage (Pen) 2 28 0xa1, 0x01, // Collection (Application) 4 29 0x85, 0x07, // Report ID (7) 6 30 0x09, 0x20, // Usage (Stylus) 8 31 0xa1, 0x00, // Collection (Physical) 10 32 0x09, 0x42, // Usage (Tip Switch) 12 33 0x09, 0x44, // Usage (Barrel Switch) 14 34 0x09, 0x5a, // Usage (Secondary Barrel Switch) 16 /* changed from 0x45 (Eraser) to 0x5a (Secondary Barrel Switch) */ 35 0x15, 0x00, // Logical Minimum (0) 18 36 0x25, 0x01, // Logical Maximum (1) 20 37 0x75, 0x01, // Report Size (1) 22 38 0x95, 0x03, // Report Count (3) 24 39 0x81, 0x02, // Input (Data,Var,Abs) 26 40 0x95, 0x02, // Report Count (2) 28 41 0x81, 0x03, // Input (Cnst,Var,Abs) 30 42 0x09, 0x32, // Usage (In Range) 32 43 0x95, 0x01, // Report Count (1) 34 44 0x81, 0x02, // Input (Data,Var,Abs) 36 45 0x95, 0x02, // Report Count (2) 38 46 0x81, 0x03, // Input (Cnst,Var,Abs) 40 47 0x75, 0x10, // Report Size (16) 42 48 0x95, 0x01, // Report Count (1) 44 49 0x35, 0x00, // Physical Minimum (0) 46 50 0xa4, // Push 48 51 0x05, 0x01, // Usage Page (Generic Desktop) 49 52 0x09, 0x30, // Usage (X) 51 53 0x65, 0x13, // Unit (EnglishLinear: in) 53 54 0x55, 0x0d, // Unit Exponent (-3) 55 55 0x46, 0xf0, 0x50, // Physical Maximum (20720) 57 56 0x26, 0xff, 0x7f, // Logical Maximum (32767) 60 57 0x81, 0x02, // Input (Data,Var,Abs) 63 58 0x09, 0x31, // Usage (Y) 65 59 0x46, 0x91, 0x2d, // Physical Maximum (11665) 67 60 0x26, 0xff, 0x7f, // Logical Maximum (32767) 70 61 0x81, 0x02, // Input (Data,Var,Abs) 73 62 0xb4, // Pop 75 63 0x09, 0x30, // Usage (Tip Pressure) 76 64 0x45, 0x00, // Physical Maximum (0) 78 65 0x26, 0xff, 0x1f, // Logical Maximum (8191) 80 66 0x81, 0x42, // Input (Data,Var,Abs,Null) 83 67 0x09, 0x3d, // Usage (X Tilt) 85 68 0x15, 0x81, // Logical Minimum (-127) 87 69 0x25, 0x7f, // Logical Maximum (127) 89 70 0x75, 0x08, // Report Size (8) 91 71 0x95, 0x01, // Report Count (1) 93 72 0x81, 0x02, // Input (Data,Var,Abs) 95 73 0x09, 0x3e, // Usage (Y Tilt) 97 74 0x15, 0x81, // Logical Minimum (-127) 99 75 0x25, 0x7f, // Logical Maximum (127) 101 76 0x81, 0x02, // Input (Data,Var,Abs) 103 77 0xc0, // End Collection 105 78 0xc0, // End Collection 106 79 }; 80 81 #define BIT(n) (1UL << n) 82 83 #define TIP_SWITCH BIT(0) 84 #define BARREL_SWITCH BIT(1) 85 #define ERASER BIT(2) 86 /* padding BIT(3) */ 87 /* padding BIT(4) */ 88 #define IN_RANGE BIT(5) 89 /* padding BIT(6) */ 90 /* padding BIT(7) */ 91 92 #define U16(index) (data[index] | (data[index + 1] << 8)) 93 94 SEC("fmod_ret/hid_bpf_rdesc_fixup") 95 int BPF_PROG(hid_fix_rdesc_xppen_artist24, struct hid_bpf_ctx *hctx) 96 { 97 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */); 98 99 if (!data) 100 return 0; /* EPERM check */ 101 102 __builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc)); 103 104 return sizeof(fixed_rdesc); 105 } 106 107 static __u8 prev_state = 0; 108 109 /* 110 * There are a few cases where the device is sending wrong event 111 * sequences, all related to the second button (the pen doesn't 112 * have an eraser switch on the tail end): 113 * 114 * whenever the second button gets pressed or released, an 115 * out-of-proximity event is generated and then the firmware 116 * compensate for the missing state (and the firmware uses 117 * eraser for that button): 118 * 119 * - if the pen is in range, an extra out-of-range is sent 120 * when the second button is pressed/released: 121 * // Pen is in range 122 * E: InRange 123 * 124 * // Second button is pressed 125 * E: 126 * E: Eraser InRange 127 * 128 * // Second button is released 129 * E: 130 * E: InRange 131 * 132 * This case is ignored by this filter, it's "valid" 133 * and userspace knows how to deal with it, there are just 134 * a few out-of-prox events generated, but the user doesn´t 135 * see them. 136 * 137 * - if the pen is in contact, 2 extra events are added when 138 * the second button is pressed/released: an out of range 139 * and an in range: 140 * 141 * // Pen is in contact 142 * E: TipSwitch InRange 143 * 144 * // Second button is pressed 145 * E: <- false release, needs to be filtered out 146 * E: Eraser InRange <- false release, needs to be filtered out 147 * E: TipSwitch Eraser InRange 148 * 149 * // Second button is released 150 * E: <- false release, needs to be filtered out 151 * E: InRange <- false release, needs to be filtered out 152 * E: TipSwitch InRange 153 * 154 */ 155 SEC("fmod_ret/hid_bpf_device_event") 156 int BPF_PROG(xppen_24_fix_eraser, struct hid_bpf_ctx *hctx) 157 { 158 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */); 159 __u8 current_state, changed_state; 160 bool prev_tip; 161 __u16 tilt; 162 163 if (!data) 164 return 0; /* EPERM check */ 165 166 current_state = data[1]; 167 168 /* if the state is identical to previously, early return */ 169 if (current_state == prev_state) 170 return 0; 171 172 prev_tip = !!(prev_state & TIP_SWITCH); 173 174 /* 175 * Illegal transition: pen is in range with the tip pressed, and 176 * it goes into out of proximity. 177 * 178 * Ideally we should hold the event, start a timer and deliver it 179 * only if the timer ends, but we are not capable of that now. 180 * 181 * And it doesn't matter because when we are in such cases, this 182 * means we are detecting a false release. 183 */ 184 if ((current_state & IN_RANGE) == 0) { 185 if (prev_tip) 186 return HID_IGNORE_EVENT; 187 return 0; 188 } 189 190 /* 191 * XOR to only set the bits that have changed between 192 * previous and current state 193 */ 194 changed_state = prev_state ^ current_state; 195 196 /* Store the new state for future processing */ 197 prev_state = current_state; 198 199 /* 200 * We get both a tipswitch and eraser change in the same HID report: 201 * this is not an authorized transition and is unlikely to happen 202 * in real life. 203 * This is likely to be added by the firmware to emulate the 204 * eraser mode so we can skip the event. 205 */ 206 if ((changed_state & (TIP_SWITCH | ERASER)) == (TIP_SWITCH | ERASER)) /* we get both a tipswitch and eraser change at the same time */ 207 return HID_IGNORE_EVENT; 208 209 return 0; 210 } 211 212 SEC("syscall") 213 int probe(struct hid_bpf_probe_args *ctx) 214 { 215 /* 216 * The device exports 3 interfaces. 217 */ 218 ctx->retval = ctx->rdesc_size != 107; 219 if (ctx->retval) 220 ctx->retval = -EINVAL; 221 222 /* ensure the kernel isn't fixed already */ 223 if (ctx->rdesc[17] != 0x45) /* Eraser */ 224 ctx->retval = -EINVAL; 225 226 return 0; 227 } 228 229 char _license[] SEC("license") = "GPL"; 230