Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/hid/bpf/progs/XPPen__Artist24.bpf.c
26285 views
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
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
*/
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")
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
233