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 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
SEC(HID_BPF_RDESC_FIXUP)92 SEC(HID_BPF_RDESC_FIXUP)
93 int 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
105 static __u8 prev_state = 0;
106
107 /*
108 * There are a few cases where the device is sending wrong event
109 * sequences, all related to the second button (the pen doesn't
110 * have an eraser switch on the tail end):
111 *
112 * whenever the second button gets pressed or released, an
113 * out-of-proximity event is generated and then the firmware
114 * compensate for the missing state (and the firmware uses
115 * eraser for that button):
116 *
117 * - if the pen is in range, an extra out-of-range is sent
118 * when the second button is pressed/released:
119 * // Pen is in range
120 * E: InRange
121 *
122 * // Second button is pressed
123 * E:
124 * E: Eraser InRange
125 *
126 * // Second button is released
127 * E:
128 * E: InRange
129 *
130 * This case is ignored by this filter, it's "valid"
131 * and userspace knows how to deal with it, there are just
132 * a few out-of-prox events generated, but the user doesn´t
133 * see them.
134 *
135 * - if the pen is in contact, 2 extra events are added when
136 * the second button is pressed/released: an out of range
137 * and an in range:
138 *
139 * // Pen is in contact
140 * E: TipSwitch InRange
141 *
142 * // Second button is pressed
143 * E: <- false release, needs to be filtered out
144 * E: Eraser InRange <- false release, needs to be filtered out
145 * E: TipSwitch Eraser InRange
146 *
147 * // Second button is released
148 * E: <- false release, needs to be filtered out
149 * E: InRange <- false release, needs to be filtered out
150 * E: TipSwitch InRange
151 *
152 */
SEC(HID_BPF_DEVICE_EVENT)153 SEC(HID_BPF_DEVICE_EVENT)
154 int 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, and
173 * it goes into out of proximity.
174 *
175 * Ideally we should hold the event, start a timer and deliver it
176 * 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, this
179 * 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 between
189 * previous and current state
190 */
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 happen
199 * in real life.
200 * This is likely to be added by the firmware to emulate the
201 * 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
209 HID_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
214 SEC("syscall")
probe(struct hid_bpf_probe_args * ctx)215 int 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
231 char _license[] SEC("license") = "GPL";
232