brintos

brintos / linux-shallow public Read only

0
0
Text · 11.8 KiB · a669525 Raw
289 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* Copyright (c) 2023 Benjamin Tissoires3 */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 0x095A12#define PID_ARTIST_PRO16_GEN2 0x095B13 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 Switch22 * - when the eraser button is pressed and the stylus is touching the tablet,23 *   the device sends Tip Switch instead of sending Eraser24 *25 * This descriptor uses physical dimensions of the 16" device.26 */27static const __u8 fixed_rdesc[] = {28	0x05, 0x0d,                    // Usage Page (Digitizers)             029	0x09, 0x02,                    // Usage (Pen)                         230	0xa1, 0x01,                    // Collection (Application)            431	0x85, 0x07,                    //  Report ID (7)                      632	0x09, 0x20,                    //  Usage (Stylus)                     833	0xa1, 0x00,                    //  Collection (Physical)              1034	0x09, 0x42,                    //   Usage (Tip Switch)                1235	0x09, 0x44,                    //   Usage (Barrel Switch)             1436	0x09, 0x5a,                    //   Usage (Secondary Barrel Switch)   16  /* changed from 0x45 (Eraser) to 0x5a (Secondary Barrel Switch) */37	0x09, 0x3c,                    //   Usage (Invert)                    1838	0x09, 0x45,                    //   Usage (Eraser)                    16  /* created over a padding bit at offset 29-33 */39	0x15, 0x00,                    //   Logical Minimum (0)               2040	0x25, 0x01,                    //   Logical Maximum (1)               2241	0x75, 0x01,                    //   Report Size (1)                   2442	0x95, 0x05,                    //   Report Count (5)                  26  /* changed from 4 to 5 */43	0x81, 0x02,                    //   Input (Data,Var,Abs)              2844	0x09, 0x32,                    //   Usage (In Range)                  3445	0x15, 0x00,                    //   Logical Minimum (0)               3646	0x25, 0x01,                    //   Logical Maximum (1)               3847	0x95, 0x01,                    //   Report Count (1)                  4048	0x81, 0x02,                    //   Input (Data,Var,Abs)              4249	0x95, 0x02,                    //   Report Count (2)                  4450	0x81, 0x03,                    //   Input (Cnst,Var,Abs)              4651	0x75, 0x10,                    //   Report Size (16)                  4852	0x95, 0x01,                    //   Report Count (1)                  5053	0x35, 0x00,                    //   Physical Minimum (0)              5254	0xa4,                          //   Push                              5455	0x05, 0x01,                    //   Usage Page (Generic Desktop)      5556	0x09, 0x30,                    //   Usage (X)                         5757	0x65, 0x13,                    //   Unit (EnglishLinear: in)          5958	0x55, 0x0d,                    //   Unit Exponent (-3)                6159	0x46, 0xff, 0x34,              //   Physical Maximum (13567)          6360	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           6661	0x81, 0x02,                    //   Input (Data,Var,Abs)              6962	0x09, 0x31,                    //   Usage (Y)                         7163	0x46, 0x20, 0x21,              //   Physical Maximum (8480)           7364	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           7665	0x81, 0x02,                    //   Input (Data,Var,Abs)              7966	0xb4,                          //   Pop                               8167	0x09, 0x30,                    //   Usage (Tip Pressure)              8268	0x45, 0x00,                    //   Physical Maximum (0)              8469	0x26, 0xff, 0x3f,              //   Logical Maximum (16383)           8670	0x81, 0x42,                    //   Input (Data,Var,Abs,Null)         8971	0x09, 0x3d,                    //   Usage (X Tilt)                    9172	0x15, 0x81,                    //   Logical Minimum (-127)            9373	0x25, 0x7f,                    //   Logical Maximum (127)             9574	0x75, 0x08,                    //   Report Size (8)                   9775	0x95, 0x01,                    //   Report Count (1)                  9976	0x81, 0x02,                    //   Input (Data,Var,Abs)              10177	0x09, 0x3e,                    //   Usage (Y Tilt)                    10378	0x15, 0x81,                    //   Logical Minimum (-127)            10579	0x25, 0x7f,                    //   Logical Maximum (127)             10780	0x81, 0x02,                    //   Input (Data,Var,Abs)              10981	0xc0,                          //  End Collection                     11182	0xc0,                          // End Collection                      11283};84 85SEC(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 device96	 * 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 108static int xppen_16_fix_eraser(struct hid_bpf_ctx *hctx)109{110	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);111 112	if (!data)113		return 0; /* EPERM check */114 115	if ((data[1] & 0x29) != 0x29) /* tip switch=1 invert=1 inrange=1 */116		return 0;117 118	/* xor bits 0,3 and 4: convert Tip Switch + Invert into Eraser only */119	data[1] ^= 0x19;120 121	return 0;122}123 124/*125 * Static coordinate offset table based on positive only angles126 * Two tables are needed, because the logical coordinates are scaled127 *128 * The table can be generated by Python like this:129 * >>> full_scale = 11.874 # the display width/height in inches130 * >>> tip_height = 0.055677699 # the center of the pen coil distance from screen in inch (empirical)131 * >>> h = tip_height * (32767 / full_scale) # height of the coil in logical coordinates132 * >>> [round(h*math.sin(math.radians(d))) for d in range(0, 128)]133 * [0, 13, 26, ....]134 */135 136/* 14" inch screen 11.874" x 7.421" */137static const __u16 angle_offsets_horizontal_14[128] = {138	0, 3, 5, 8, 11, 13, 16, 19, 21, 24, 27, 29, 32, 35, 37, 40, 42, 45, 47, 50, 53,139	55, 58, 60, 62, 65, 67, 70, 72, 74, 77, 79, 81, 84, 86, 88, 90, 92, 95, 97, 99,140	101, 103, 105, 107, 109, 111, 112, 114, 116, 118, 119, 121, 123, 124, 126, 127,141	129, 130, 132, 133, 134, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,142	147, 148, 148, 149, 150, 150, 151, 151, 152, 152, 153, 153, 153, 153, 153, 154,143	154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 151, 151, 150, 150, 149,144	148, 148, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, 134, 133,145	132, 130, 129, 127, 126, 124, 123146};147static const __u16 angle_offsets_vertical_14[128] = {148	0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 59, 64, 68, 72, 76, 80, 84,149	88, 92, 96, 100, 104, 108, 112, 115, 119, 123, 127, 130, 134, 137, 141, 145, 148,150	151, 155, 158, 161, 165, 168, 171, 174, 177, 180, 183, 186, 188, 191, 194, 196,151	199, 201, 204, 206, 208, 211, 213, 215, 217, 219, 221, 223, 225, 226, 228, 230,152	231, 232, 234, 235, 236, 237, 239, 240, 240, 241, 242, 243, 243, 244, 244, 245,153	245, 246, 246, 246, 246, 246, 246, 246, 245, 245, 244, 244, 243, 243, 242, 241,154	240, 240, 239, 237, 236, 235, 234, 232, 231, 230, 228, 226, 225, 223, 221, 219,155	217, 215, 213, 211, 208, 206, 204, 201, 199, 196156};157 158/* 16" inch screen 13.567" x 8.480" */159static const __u16 angle_offsets_horizontal_16[128] = {160	0, 2, 5, 7, 9, 12, 14, 16, 19, 21, 23, 26, 28, 30, 33, 35, 37, 39, 42, 44, 46, 48,161	50, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 86, 88, 90,162	92, 93, 95, 97, 98, 100, 101, 103, 105, 106, 107, 109, 110, 111, 113, 114, 115,163	116, 118, 119, 120, 121, 122, 123, 124, 125, 126, 126, 127, 128, 129, 129, 130,164	130, 131, 132, 132, 132, 133, 133, 133, 134, 134, 134, 134, 134, 134, 134, 134,165	134, 134, 134, 134, 134, 133, 133, 133, 132, 132, 132, 131, 130, 130, 129, 129,166	128, 127, 126, 126, 125, 124, 123, 122, 121, 120, 119, 118, 116, 115, 114, 113,167	111, 110, 109, 107168};169static const __u16 angle_offsets_vertical_16[128] = {170	0, 4, 8, 11, 15, 19, 22, 26, 30, 34, 37, 41, 45, 48, 52, 56, 59, 63, 66, 70, 74,171	77, 81, 84, 88, 91, 94, 98, 101, 104, 108, 111, 114, 117, 120, 123, 126, 129, 132,172	135, 138, 141, 144, 147, 149, 152, 155, 157, 160, 162, 165, 167, 170, 172, 174,173	176, 178, 180, 182, 184, 186, 188, 190, 192, 193, 195, 197, 198, 199, 201, 202,174	203, 205, 206, 207, 208, 209, 210, 210, 211, 212, 212, 213, 214, 214, 214, 215,175	215, 215, 215, 215, 215, 215, 215, 215, 214, 214, 214, 213, 212, 212, 211, 210,176	210, 209, 208, 207, 206, 205, 203, 202, 201, 199, 198, 197, 195, 193, 192, 190,177	188, 186, 184, 182, 180, 178, 176, 174, 172178};179 180static void compensate_coordinates_by_tilt(__u8 *data, const __u8 idx,181		const __s8 tilt, const __u16 (*compensation_table)[128])182{183	__u16 coords = data[idx+1];184 185	coords <<= 8;186	coords += data[idx];187 188	__u8 direction = tilt > 0 ? 0 : 1; /* Positive tilt means we need to subtract the compensation (vs. negative angle where we need to add) */189	__u8 angle = tilt > 0 ? tilt : -tilt;190 191	if (angle > 127)192		return;193 194	__u16 compensation = (*compensation_table)[angle];195 196	if (direction == 0) {197		coords = (coords > compensation) ? coords - compensation : 0;198	} else {199		const __u16 logical_maximum = 32767;200		__u16 max = logical_maximum - compensation;201 202		coords = (coords < max) ? coords + compensation : logical_maximum;203	}204 205	data[idx] = coords & 0xff;206	data[idx+1] = coords >> 8;207}208 209static int xppen_16_fix_angle_offset(struct hid_bpf_ctx *hctx)210{211	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);212 213	if (!data)214		return 0; /* EPERM check */215 216	/*217	 * Compensate X and Y offset caused by tilt.218	 *219	 * The magnetic center moves when the pen is tilted, because the coil220	 * is not touching the screen.221	 *222	 * a (tilt angle)223	 * |  /... h (coil distance from tip)224	 * | /225	 * |/______226	 *         |x (position offset)227	 *228	 * x = sin a * h229	 *230	 * Subtract the offset from the coordinates. Use the precomputed table!231	 *232	 * bytes 0   - report id233	 *       1   - buttons234	 *       2-3 - X coords (logical)235	 *       4-5 - Y coords236	 *       6-7 - pressure (ignore)237	 *       8   - tilt X238	 *       9   - tilt Y239	 */240 241	__s8 tilt_x = (__s8) data[8];242	__s8 tilt_y = (__s8) data[9];243 244	if (hctx->hid->product == PID_ARTIST_PRO14_GEN2) {245		compensate_coordinates_by_tilt(data, 2, tilt_x, &angle_offsets_horizontal_14);246		compensate_coordinates_by_tilt(data, 4, tilt_y, &angle_offsets_vertical_14);247	} else if (hctx->hid->product == PID_ARTIST_PRO16_GEN2) {248		compensate_coordinates_by_tilt(data, 2, tilt_x, &angle_offsets_horizontal_16);249		compensate_coordinates_by_tilt(data, 4, tilt_y, &angle_offsets_vertical_16);250	}251 252	return 0;253}254 255SEC(HID_BPF_DEVICE_EVENT)256int BPF_PROG(xppen_artist_pro_16_device_event, struct hid_bpf_ctx *hctx)257{258	int ret = xppen_16_fix_angle_offset(hctx);259 260	if (ret)261		return ret;262 263	return xppen_16_fix_eraser(hctx);264}265 266HID_BPF_OPS(xppen_artist_pro_16) = {267	.hid_rdesc_fixup = (void *)hid_fix_rdesc_xppen_artistpro16gen2,268	.hid_device_event = (void *)xppen_artist_pro_16_device_event,269};270 271SEC("syscall")272int probe(struct hid_bpf_probe_args *ctx)273{274	/*275	 * The device exports 3 interfaces.276	 */277	ctx->retval = ctx->rdesc_size != 113;278	if (ctx->retval)279		ctx->retval = -EINVAL;280 281	/* ensure the kernel isn't fixed already */282	if (ctx->rdesc[17] != 0x45) /* Eraser */283		ctx->retval = -EINVAL;284 285	return 0;286}287 288char _license[] SEC("license") = "GPL";289