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