File Index
Files and directories
Sign in
FILE

XPPen__ArtistPro16Gen2.bpf.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hid/bpf/progs/XPPen__ArtistPro16Gen2.bpf.c C
  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
 15HID_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 */
 29static 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
 87SEC(HID_BPF_RDESC_FIXUP)
 88int 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
116static 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" */
145static 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};
155static 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" */
167static 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};
177static 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" */
189static 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};
198static 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
209static 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
238static 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
291SEC(HID_BPF_DEVICE_EVENT)
292int 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
302HID_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
307SEC("syscall")
308int 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
324char _license[] SEC("license") = "GPL";
~/ Read-only file preview.