Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/hid/bpf/progs/XPPen__ArtistPro16Gen2.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_PRO14_GEN2 0x095A
12
#define PID_ARTIST_PRO16_GEN2 0x095B
13
#define PID_ARTIST_PRO19_GEN2 0x096A
14
15
HID_BPF_CONFIG(
16
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_PRO14_GEN2),
17
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_PRO16_GEN2),
18
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_PRO19_GEN2)
19
);
20
21
/*
22
* We need to amend the report descriptor for the following:
23
* - the device reports Eraser instead of using Secondary Barrel Switch
24
* - when the eraser button is pressed and the stylus is touching the tablet,
25
* the device sends Tip Switch instead of sending Eraser
26
*
27
* This descriptor uses the physical dimensions of the 16" device.
28
*/
29
static const __u8 fixed_rdesc[] = {
30
0x05, 0x0d, // Usage Page (Digitizers) 0
31
0x09, 0x02, // Usage (Pen) 2
32
0xa1, 0x01, // Collection (Application) 4
33
0x85, 0x07, // Report ID (7) 6
34
0x09, 0x20, // Usage (Stylus) 8
35
0xa1, 0x00, // Collection (Physical) 10
36
0x09, 0x42, // Usage (Tip Switch) 12
37
0x09, 0x44, // Usage (Barrel Switch) 14
38
0x09, 0x5a, // Usage (Secondary Barrel Switch) 16 /* changed from 0x45 (Eraser) to 0x5a (Secondary Barrel Switch) */
39
0x09, 0x3c, // Usage (Invert) 18
40
0x09, 0x45, // Usage (Eraser) 16 /* created over a padding bit at offset 29-33 */
41
0x15, 0x00, // Logical Minimum (0) 20
42
0x25, 0x01, // Logical Maximum (1) 22
43
0x75, 0x01, // Report Size (1) 24
44
0x95, 0x05, // Report Count (5) 26 /* changed from 4 to 5 */
45
0x81, 0x02, // Input (Data,Var,Abs) 28
46
0x09, 0x32, // Usage (In Range) 34
47
0x15, 0x00, // Logical Minimum (0) 36
48
0x25, 0x01, // Logical Maximum (1) 38
49
0x95, 0x01, // Report Count (1) 40
50
0x81, 0x02, // Input (Data,Var,Abs) 42
51
0x95, 0x02, // Report Count (2) 44
52
0x81, 0x03, // Input (Cnst,Var,Abs) 46
53
0x75, 0x10, // Report Size (16) 48
54
0x95, 0x01, // Report Count (1) 50
55
0x35, 0x00, // Physical Minimum (0) 52
56
0xa4, // Push 54
57
0x05, 0x01, // Usage Page (Generic Desktop) 55
58
0x09, 0x30, // Usage (X) 57
59
0x65, 0x13, // Unit (EnglishLinear: in) 59
60
0x55, 0x0d, // Unit Exponent (-3) 61
61
0x46, 0xff, 0x34, // Physical Maximum (13567) 63
62
0x26, 0xff, 0x7f, // Logical Maximum (32767) 66
63
0x81, 0x02, // Input (Data,Var,Abs) 69
64
0x09, 0x31, // Usage (Y) 71
65
0x46, 0x20, 0x21, // Physical Maximum (8480) 73
66
0x26, 0xff, 0x7f, // Logical Maximum (32767) 76
67
0x81, 0x02, // Input (Data,Var,Abs) 79
68
0xb4, // Pop 81
69
0x09, 0x30, // Usage (Tip Pressure) 82
70
0x45, 0x00, // Physical Maximum (0) 84
71
0x26, 0xff, 0x3f, // Logical Maximum (16383) 86
72
0x81, 0x42, // Input (Data,Var,Abs,Null) 89
73
0x09, 0x3d, // Usage (X Tilt) 91
74
0x15, 0x81, // Logical Minimum (-127) 93
75
0x25, 0x7f, // Logical Maximum (127) 95
76
0x75, 0x08, // Report Size (8) 97
77
0x95, 0x01, // Report Count (1) 99
78
0x81, 0x02, // Input (Data,Var,Abs) 101
79
0x09, 0x3e, // Usage (Y Tilt) 103
80
0x15, 0x81, // Logical Minimum (-127) 105
81
0x25, 0x7f, // Logical Maximum (127) 107
82
0x81, 0x02, // Input (Data,Var,Abs) 109
83
0xc0, // End Collection 111
84
0xc0, // End Collection 112
85
};
86
87
SEC(HID_BPF_RDESC_FIXUP)
88
int BPF_PROG(hid_fix_rdesc_xppen_artistpro16gen2, struct hid_bpf_ctx *hctx)
89
{
90
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
91
92
if (!data)
93
return 0; /* EPERM check */
94
95
__builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc));
96
97
/* Fix the Physical maximum values for different sizes of the device
98
* The 14" screen device descriptor size is 11.874" x 7.421"
99
*/
100
if (hctx->hid->product == PID_ARTIST_PRO14_GEN2) {
101
data[63] = 0x2e;
102
data[62] = 0x62;
103
data[73] = 0x1c;
104
data[72] = 0xfd;
105
} else if (hctx->hid->product == PID_ARTIST_PRO19_GEN2) {
106
/* 19" screen reports 16.101" x 9.057" */
107
data[63] = 0x3e;
108
data[62] = 0xe5;
109
data[73] = 0x23;
110
data[72] = 0x61;
111
}
112
113
return sizeof(fixed_rdesc);
114
}
115
116
static int xppen_16_fix_eraser(struct hid_bpf_ctx *hctx)
117
{
118
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);
119
120
if (!data)
121
return 0; /* EPERM check */
122
123
if ((data[1] & 0x29) != 0x29) /* tip switch=1 invert=1 inrange=1 */
124
return 0;
125
126
/* xor bits 0,3 and 4: convert Tip Switch + Invert into Eraser only */
127
data[1] ^= 0x19;
128
129
return 0;
130
}
131
132
/*
133
* Static coordinate offset table based on positive only angles
134
* Two tables are needed, because the logical coordinates are scaled
135
*
136
* The table can be generated by Python like this:
137
* >>> full_scale = 11.874 # the display width/height in inches
138
* >>> tip_height = 0.055677699 # the center of the pen coil distance from screen in inch (empirical)
139
* >>> h = tip_height * (32767 / full_scale) # height of the coil in logical coordinates
140
* >>> [round(h*math.sin(math.radians(d))) for d in range(0, 128)]
141
* [0, 13, 26, ....]
142
*/
143
144
/* 14" inch screen 11.874" x 7.421" */
145
static const __u16 angle_offsets_horizontal_14[128] = {
146
0, 3, 5, 8, 11, 13, 16, 19, 21, 24, 27, 29, 32, 35, 37, 40, 42, 45, 47, 50, 53,
147
55, 58, 60, 62, 65, 67, 70, 72, 74, 77, 79, 81, 84, 86, 88, 90, 92, 95, 97, 99,
148
101, 103, 105, 107, 109, 111, 112, 114, 116, 118, 119, 121, 123, 124, 126, 127,
149
129, 130, 132, 133, 134, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,
150
147, 148, 148, 149, 150, 150, 151, 151, 152, 152, 153, 153, 153, 153, 153, 154,
151
154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 151, 151, 150, 150, 149,
152
148, 148, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, 134, 133,
153
132, 130, 129, 127, 126, 124, 123
154
};
155
static const __u16 angle_offsets_vertical_14[128] = {
156
0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 59, 64, 68, 72, 76, 80, 84,
157
88, 92, 96, 100, 104, 108, 112, 115, 119, 123, 127, 130, 134, 137, 141, 145, 148,
158
151, 155, 158, 161, 165, 168, 171, 174, 177, 180, 183, 186, 188, 191, 194, 196,
159
199, 201, 204, 206, 208, 211, 213, 215, 217, 219, 221, 223, 225, 226, 228, 230,
160
231, 232, 234, 235, 236, 237, 239, 240, 240, 241, 242, 243, 243, 244, 244, 245,
161
245, 246, 246, 246, 246, 246, 246, 246, 245, 245, 244, 244, 243, 243, 242, 241,
162
240, 240, 239, 237, 236, 235, 234, 232, 231, 230, 228, 226, 225, 223, 221, 219,
163
217, 215, 213, 211, 208, 206, 204, 201, 199, 196
164
};
165
166
/* 16" inch screen 13.567" x 8.480" */
167
static const __u16 angle_offsets_horizontal_16[128] = {
168
0, 2, 5, 7, 9, 12, 14, 16, 19, 21, 23, 26, 28, 30, 33, 35, 37, 39, 42, 44, 46, 48,
169
50, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 86, 88, 90,
170
92, 93, 95, 97, 98, 100, 101, 103, 105, 106, 107, 109, 110, 111, 113, 114, 115,
171
116, 118, 119, 120, 121, 122, 123, 124, 125, 126, 126, 127, 128, 129, 129, 130,
172
130, 131, 132, 132, 132, 133, 133, 133, 134, 134, 134, 134, 134, 134, 134, 134,
173
134, 134, 134, 134, 134, 133, 133, 133, 132, 132, 132, 131, 130, 130, 129, 129,
174
128, 127, 126, 126, 125, 124, 123, 122, 121, 120, 119, 118, 116, 115, 114, 113,
175
111, 110, 109, 107
176
};
177
static const __u16 angle_offsets_vertical_16[128] = {
178
0, 4, 8, 11, 15, 19, 22, 26, 30, 34, 37, 41, 45, 48, 52, 56, 59, 63, 66, 70, 74,
179
77, 81, 84, 88, 91, 94, 98, 101, 104, 108, 111, 114, 117, 120, 123, 126, 129, 132,
180
135, 138, 141, 144, 147, 149, 152, 155, 157, 160, 162, 165, 167, 170, 172, 174,
181
176, 178, 180, 182, 184, 186, 188, 190, 192, 193, 195, 197, 198, 199, 201, 202,
182
203, 205, 206, 207, 208, 209, 210, 210, 211, 212, 212, 213, 214, 214, 214, 215,
183
215, 215, 215, 215, 215, 215, 215, 215, 214, 214, 214, 213, 212, 212, 211, 210,
184
210, 209, 208, 207, 206, 205, 203, 202, 201, 199, 198, 197, 195, 193, 192, 190,
185
188, 186, 184, 182, 180, 178, 176, 174, 172
186
};
187
188
/* 19" inch screen 16.101" x 9.057" */
189
static const __u16 angle_offsets_horizontal_19[128] = {
190
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 25, 27, 29, 31, 33, 35, 37, 39, 41,
191
42, 44, 46, 48, 50, 51, 53, 55, 57, 58, 60, 62, 63, 65, 67, 68, 70, 71, 73, 74, 76,
192
77, 79, 80, 82, 83, 84, 86, 87, 88, 89, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100,
193
101, 102, 103, 104, 104, 105, 106, 106, 107, 108, 108, 109, 109, 110, 110, 111,
194
111, 112, 112, 112, 112, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
195
113, 113, 112, 112, 112, 112, 111, 111, 110, 110, 109, 109, 108, 108, 107, 106,
196
106, 105, 104, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 90
197
};
198
static const __u16 angle_offsets_vertical_19[128] = {
199
0, 4, 7, 11, 14, 18, 21, 25, 28, 32, 35, 38, 42, 45, 49, 52, 56, 59, 62, 66, 69, 72,
200
75, 79, 82, 85, 88, 91, 95, 98, 101, 104, 107, 110, 113, 116, 118, 121, 124, 127,
201
129, 132, 135, 137, 140, 142, 145, 147, 150, 152, 154, 157, 159, 161, 163, 165, 167,
202
169, 171, 173, 174, 176, 178, 179, 181, 183, 184, 185, 187, 188, 189, 190, 192, 193,
203
194, 195, 195, 196, 197, 198, 198, 199, 199, 200, 200, 201, 201, 201, 201, 201, 201,
204
201, 201, 201, 201, 201, 200, 200, 199, 199, 198, 198, 197, 196, 195, 195, 194, 193,
205
192, 190, 189, 188, 187, 185, 184, 183, 181, 179, 178, 176, 174, 173, 171, 169, 167,
206
165, 163, 161
207
};
208
209
static void compensate_coordinates_by_tilt(__u8 *data, const __u8 idx,
210
const __s8 tilt, const __u16 (*compensation_table)[128])
211
{
212
__u16 coords = data[idx+1];
213
214
coords <<= 8;
215
coords += data[idx];
216
217
__u8 direction = tilt > 0 ? 0 : 1; /* Positive tilt means we need to subtract the compensation (vs. negative angle where we need to add) */
218
__u8 angle = tilt > 0 ? tilt : -tilt;
219
220
if (angle > 127)
221
return;
222
223
__u16 compensation = (*compensation_table)[angle];
224
225
if (direction == 0) {
226
coords = (coords > compensation) ? coords - compensation : 0;
227
} else {
228
const __u16 logical_maximum = 32767;
229
__u16 max = logical_maximum - compensation;
230
231
coords = (coords < max) ? coords + compensation : logical_maximum;
232
}
233
234
data[idx] = coords & 0xff;
235
data[idx+1] = coords >> 8;
236
}
237
238
static int xppen_16_fix_angle_offset(struct hid_bpf_ctx *hctx)
239
{
240
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);
241
242
if (!data)
243
return 0; /* EPERM check */
244
245
/*
246
* Compensate X and Y offset caused by tilt.
247
*
248
* The magnetic center moves when the pen is tilted, because the coil
249
* is not touching the screen.
250
*
251
* a (tilt angle)
252
* | /... h (coil distance from tip)
253
* | /
254
* |/______
255
* |x (position offset)
256
*
257
* x = sin a * h
258
*
259
* Subtract the offset from the coordinates. Use the precomputed table!
260
*
261
* bytes 0 - report id
262
* 1 - buttons
263
* 2-3 - X coords (logical)
264
* 4-5 - Y coords
265
* 6-7 - pressure (ignore)
266
* 8 - tilt X
267
* 9 - tilt Y
268
*/
269
270
__s8 tilt_x = (__s8) data[8];
271
__s8 tilt_y = (__s8) data[9];
272
273
switch (hctx->hid->product) {
274
case PID_ARTIST_PRO14_GEN2:
275
compensate_coordinates_by_tilt(data, 2, tilt_x, &angle_offsets_horizontal_14);
276
compensate_coordinates_by_tilt(data, 4, tilt_y, &angle_offsets_vertical_14);
277
break;
278
case PID_ARTIST_PRO16_GEN2:
279
compensate_coordinates_by_tilt(data, 2, tilt_x, &angle_offsets_horizontal_16);
280
compensate_coordinates_by_tilt(data, 4, tilt_y, &angle_offsets_vertical_16);
281
break;
282
case PID_ARTIST_PRO19_GEN2:
283
compensate_coordinates_by_tilt(data, 2, tilt_x, &angle_offsets_horizontal_19);
284
compensate_coordinates_by_tilt(data, 4, tilt_y, &angle_offsets_vertical_19);
285
break;
286
}
287
288
return 0;
289
}
290
291
SEC(HID_BPF_DEVICE_EVENT)
292
int BPF_PROG(xppen_artist_pro_16_device_event, struct hid_bpf_ctx *hctx)
293
{
294
int ret = xppen_16_fix_angle_offset(hctx);
295
296
if (ret)
297
return ret;
298
299
return xppen_16_fix_eraser(hctx);
300
}
301
302
HID_BPF_OPS(xppen_artist_pro_16) = {
303
.hid_rdesc_fixup = (void *)hid_fix_rdesc_xppen_artistpro16gen2,
304
.hid_device_event = (void *)xppen_artist_pro_16_device_event,
305
};
306
307
SEC("syscall")
308
int probe(struct hid_bpf_probe_args *ctx)
309
{
310
/*
311
* The device exports 3 interfaces.
312
*/
313
ctx->retval = ctx->rdesc_size != 113;
314
if (ctx->retval)
315
ctx->retval = -EINVAL;
316
317
/* ensure the kernel isn't fixed already */
318
if (ctx->rdesc[17] != 0x45) /* Eraser */
319
ctx->retval = -EINVAL;
320
321
return 0;
322
}
323
324
char _license[] SEC("license") = "GPL";
325
326