Path: blob/master/drivers/hid/bpf/progs/XPPen__ArtistPro16Gen2.bpf.c
26285 views
// SPDX-License-Identifier: GPL-2.0-only1/* Copyright (c) 2023 Benjamin Tissoires2*/34#include "vmlinux.h"5#include "hid_bpf.h"6#include "hid_bpf_helpers.h"7#include <bpf/bpf_tracing.h>89#define VID_UGEE 0x28BD /* VID is shared with SinoWealth and Glorious and prob others */10#define PID_ARTIST_PRO14_GEN2 0x095A11#define PID_ARTIST_PRO16_GEN2 0x095B12#define PID_ARTIST_PRO19_GEN2 0x096A1314HID_BPF_CONFIG(15HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_PRO14_GEN2),16HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_PRO16_GEN2),17HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_PRO19_GEN2)18);1920/*21* We need to amend the report descriptor for the following:22* - the device reports Eraser instead of using Secondary Barrel Switch23* - when the eraser button is pressed and the stylus is touching the tablet,24* the device sends Tip Switch instead of sending Eraser25*26* This descriptor uses the physical dimensions of the 16" device.27*/28static const __u8 fixed_rdesc[] = {290x05, 0x0d, // Usage Page (Digitizers) 0300x09, 0x02, // Usage (Pen) 2310xa1, 0x01, // Collection (Application) 4320x85, 0x07, // Report ID (7) 6330x09, 0x20, // Usage (Stylus) 8340xa1, 0x00, // Collection (Physical) 10350x09, 0x42, // Usage (Tip Switch) 12360x09, 0x44, // Usage (Barrel Switch) 14370x09, 0x5a, // Usage (Secondary Barrel Switch) 16 /* changed from 0x45 (Eraser) to 0x5a (Secondary Barrel Switch) */380x09, 0x3c, // Usage (Invert) 18390x09, 0x45, // Usage (Eraser) 16 /* created over a padding bit at offset 29-33 */400x15, 0x00, // Logical Minimum (0) 20410x25, 0x01, // Logical Maximum (1) 22420x75, 0x01, // Report Size (1) 24430x95, 0x05, // Report Count (5) 26 /* changed from 4 to 5 */440x81, 0x02, // Input (Data,Var,Abs) 28450x09, 0x32, // Usage (In Range) 34460x15, 0x00, // Logical Minimum (0) 36470x25, 0x01, // Logical Maximum (1) 38480x95, 0x01, // Report Count (1) 40490x81, 0x02, // Input (Data,Var,Abs) 42500x95, 0x02, // Report Count (2) 44510x81, 0x03, // Input (Cnst,Var,Abs) 46520x75, 0x10, // Report Size (16) 48530x95, 0x01, // Report Count (1) 50540x35, 0x00, // Physical Minimum (0) 52550xa4, // Push 54560x05, 0x01, // Usage Page (Generic Desktop) 55570x09, 0x30, // Usage (X) 57580x65, 0x13, // Unit (EnglishLinear: in) 59590x55, 0x0d, // Unit Exponent (-3) 61600x46, 0xff, 0x34, // Physical Maximum (13567) 63610x26, 0xff, 0x7f, // Logical Maximum (32767) 66620x81, 0x02, // Input (Data,Var,Abs) 69630x09, 0x31, // Usage (Y) 71640x46, 0x20, 0x21, // Physical Maximum (8480) 73650x26, 0xff, 0x7f, // Logical Maximum (32767) 76660x81, 0x02, // Input (Data,Var,Abs) 79670xb4, // Pop 81680x09, 0x30, // Usage (Tip Pressure) 82690x45, 0x00, // Physical Maximum (0) 84700x26, 0xff, 0x3f, // Logical Maximum (16383) 86710x81, 0x42, // Input (Data,Var,Abs,Null) 89720x09, 0x3d, // Usage (X Tilt) 91730x15, 0x81, // Logical Minimum (-127) 93740x25, 0x7f, // Logical Maximum (127) 95750x75, 0x08, // Report Size (8) 97760x95, 0x01, // Report Count (1) 99770x81, 0x02, // Input (Data,Var,Abs) 101780x09, 0x3e, // Usage (Y Tilt) 103790x15, 0x81, // Logical Minimum (-127) 105800x25, 0x7f, // Logical Maximum (127) 107810x81, 0x02, // Input (Data,Var,Abs) 109820xc0, // End Collection 111830xc0, // End Collection 11284};8586SEC(HID_BPF_RDESC_FIXUP)87int BPF_PROG(hid_fix_rdesc_xppen_artistpro16gen2, struct hid_bpf_ctx *hctx)88{89__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);9091if (!data)92return 0; /* EPERM check */9394__builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc));9596/* Fix the Physical maximum values for different sizes of the device97* The 14" screen device descriptor size is 11.874" x 7.421"98*/99if (hctx->hid->product == PID_ARTIST_PRO14_GEN2) {100data[63] = 0x2e;101data[62] = 0x62;102data[73] = 0x1c;103data[72] = 0xfd;104} else if (hctx->hid->product == PID_ARTIST_PRO19_GEN2) {105/* 19" screen reports 16.101" x 9.057" */106data[63] = 0x3e;107data[62] = 0xe5;108data[73] = 0x23;109data[72] = 0x61;110}111112return sizeof(fixed_rdesc);113}114115static int xppen_16_fix_eraser(struct hid_bpf_ctx *hctx)116{117__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);118119if (!data)120return 0; /* EPERM check */121122if ((data[1] & 0x29) != 0x29) /* tip switch=1 invert=1 inrange=1 */123return 0;124125/* xor bits 0,3 and 4: convert Tip Switch + Invert into Eraser only */126data[1] ^= 0x19;127128return 0;129}130131/*132* Static coordinate offset table based on positive only angles133* Two tables are needed, because the logical coordinates are scaled134*135* The table can be generated by Python like this:136* >>> full_scale = 11.874 # the display width/height in inches137* >>> tip_height = 0.055677699 # the center of the pen coil distance from screen in inch (empirical)138* >>> h = tip_height * (32767 / full_scale) # height of the coil in logical coordinates139* >>> [round(h*math.sin(math.radians(d))) for d in range(0, 128)]140* [0, 13, 26, ....]141*/142143/* 14" inch screen 11.874" x 7.421" */144static const __u16 angle_offsets_horizontal_14[128] = {1450, 3, 5, 8, 11, 13, 16, 19, 21, 24, 27, 29, 32, 35, 37, 40, 42, 45, 47, 50, 53,14655, 58, 60, 62, 65, 67, 70, 72, 74, 77, 79, 81, 84, 86, 88, 90, 92, 95, 97, 99,147101, 103, 105, 107, 109, 111, 112, 114, 116, 118, 119, 121, 123, 124, 126, 127,148129, 130, 132, 133, 134, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,149147, 148, 148, 149, 150, 150, 151, 151, 152, 152, 153, 153, 153, 153, 153, 154,150154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 151, 151, 150, 150, 149,151148, 148, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, 134, 133,152132, 130, 129, 127, 126, 124, 123153};154static const __u16 angle_offsets_vertical_14[128] = {1550, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 59, 64, 68, 72, 76, 80, 84,15688, 92, 96, 100, 104, 108, 112, 115, 119, 123, 127, 130, 134, 137, 141, 145, 148,157151, 155, 158, 161, 165, 168, 171, 174, 177, 180, 183, 186, 188, 191, 194, 196,158199, 201, 204, 206, 208, 211, 213, 215, 217, 219, 221, 223, 225, 226, 228, 230,159231, 232, 234, 235, 236, 237, 239, 240, 240, 241, 242, 243, 243, 244, 244, 245,160245, 246, 246, 246, 246, 246, 246, 246, 245, 245, 244, 244, 243, 243, 242, 241,161240, 240, 239, 237, 236, 235, 234, 232, 231, 230, 228, 226, 225, 223, 221, 219,162217, 215, 213, 211, 208, 206, 204, 201, 199, 196163};164165/* 16" inch screen 13.567" x 8.480" */166static const __u16 angle_offsets_horizontal_16[128] = {1670, 2, 5, 7, 9, 12, 14, 16, 19, 21, 23, 26, 28, 30, 33, 35, 37, 39, 42, 44, 46, 48,16850, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 86, 88, 90,16992, 93, 95, 97, 98, 100, 101, 103, 105, 106, 107, 109, 110, 111, 113, 114, 115,170116, 118, 119, 120, 121, 122, 123, 124, 125, 126, 126, 127, 128, 129, 129, 130,171130, 131, 132, 132, 132, 133, 133, 133, 134, 134, 134, 134, 134, 134, 134, 134,172134, 134, 134, 134, 134, 133, 133, 133, 132, 132, 132, 131, 130, 130, 129, 129,173128, 127, 126, 126, 125, 124, 123, 122, 121, 120, 119, 118, 116, 115, 114, 113,174111, 110, 109, 107175};176static const __u16 angle_offsets_vertical_16[128] = {1770, 4, 8, 11, 15, 19, 22, 26, 30, 34, 37, 41, 45, 48, 52, 56, 59, 63, 66, 70, 74,17877, 81, 84, 88, 91, 94, 98, 101, 104, 108, 111, 114, 117, 120, 123, 126, 129, 132,179135, 138, 141, 144, 147, 149, 152, 155, 157, 160, 162, 165, 167, 170, 172, 174,180176, 178, 180, 182, 184, 186, 188, 190, 192, 193, 195, 197, 198, 199, 201, 202,181203, 205, 206, 207, 208, 209, 210, 210, 211, 212, 212, 213, 214, 214, 214, 215,182215, 215, 215, 215, 215, 215, 215, 215, 214, 214, 214, 213, 212, 212, 211, 210,183210, 209, 208, 207, 206, 205, 203, 202, 201, 199, 198, 197, 195, 193, 192, 190,184188, 186, 184, 182, 180, 178, 176, 174, 172185};186187/* 19" inch screen 16.101" x 9.057" */188static const __u16 angle_offsets_horizontal_19[128] = {1890, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 25, 27, 29, 31, 33, 35, 37, 39, 41,19042, 44, 46, 48, 50, 51, 53, 55, 57, 58, 60, 62, 63, 65, 67, 68, 70, 71, 73, 74, 76,19177, 79, 80, 82, 83, 84, 86, 87, 88, 89, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100,192101, 102, 103, 104, 104, 105, 106, 106, 107, 108, 108, 109, 109, 110, 110, 111,193111, 112, 112, 112, 112, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,194113, 113, 112, 112, 112, 112, 111, 111, 110, 110, 109, 109, 108, 108, 107, 106,195106, 105, 104, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 90196};197static const __u16 angle_offsets_vertical_19[128] = {1980, 4, 7, 11, 14, 18, 21, 25, 28, 32, 35, 38, 42, 45, 49, 52, 56, 59, 62, 66, 69, 72,19975, 79, 82, 85, 88, 91, 95, 98, 101, 104, 107, 110, 113, 116, 118, 121, 124, 127,200129, 132, 135, 137, 140, 142, 145, 147, 150, 152, 154, 157, 159, 161, 163, 165, 167,201169, 171, 173, 174, 176, 178, 179, 181, 183, 184, 185, 187, 188, 189, 190, 192, 193,202194, 195, 195, 196, 197, 198, 198, 199, 199, 200, 200, 201, 201, 201, 201, 201, 201,203201, 201, 201, 201, 201, 200, 200, 199, 199, 198, 198, 197, 196, 195, 195, 194, 193,204192, 190, 189, 188, 187, 185, 184, 183, 181, 179, 178, 176, 174, 173, 171, 169, 167,205165, 163, 161206};207208static void compensate_coordinates_by_tilt(__u8 *data, const __u8 idx,209const __s8 tilt, const __u16 (*compensation_table)[128])210{211__u16 coords = data[idx+1];212213coords <<= 8;214coords += data[idx];215216__u8 direction = tilt > 0 ? 0 : 1; /* Positive tilt means we need to subtract the compensation (vs. negative angle where we need to add) */217__u8 angle = tilt > 0 ? tilt : -tilt;218219if (angle > 127)220return;221222__u16 compensation = (*compensation_table)[angle];223224if (direction == 0) {225coords = (coords > compensation) ? coords - compensation : 0;226} else {227const __u16 logical_maximum = 32767;228__u16 max = logical_maximum - compensation;229230coords = (coords < max) ? coords + compensation : logical_maximum;231}232233data[idx] = coords & 0xff;234data[idx+1] = coords >> 8;235}236237static int xppen_16_fix_angle_offset(struct hid_bpf_ctx *hctx)238{239__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);240241if (!data)242return 0; /* EPERM check */243244/*245* Compensate X and Y offset caused by tilt.246*247* The magnetic center moves when the pen is tilted, because the coil248* is not touching the screen.249*250* a (tilt angle)251* | /... h (coil distance from tip)252* | /253* |/______254* |x (position offset)255*256* x = sin a * h257*258* Subtract the offset from the coordinates. Use the precomputed table!259*260* bytes 0 - report id261* 1 - buttons262* 2-3 - X coords (logical)263* 4-5 - Y coords264* 6-7 - pressure (ignore)265* 8 - tilt X266* 9 - tilt Y267*/268269__s8 tilt_x = (__s8) data[8];270__s8 tilt_y = (__s8) data[9];271272switch (hctx->hid->product) {273case PID_ARTIST_PRO14_GEN2:274compensate_coordinates_by_tilt(data, 2, tilt_x, &angle_offsets_horizontal_14);275compensate_coordinates_by_tilt(data, 4, tilt_y, &angle_offsets_vertical_14);276break;277case PID_ARTIST_PRO16_GEN2:278compensate_coordinates_by_tilt(data, 2, tilt_x, &angle_offsets_horizontal_16);279compensate_coordinates_by_tilt(data, 4, tilt_y, &angle_offsets_vertical_16);280break;281case PID_ARTIST_PRO19_GEN2:282compensate_coordinates_by_tilt(data, 2, tilt_x, &angle_offsets_horizontal_19);283compensate_coordinates_by_tilt(data, 4, tilt_y, &angle_offsets_vertical_19);284break;285}286287return 0;288}289290SEC(HID_BPF_DEVICE_EVENT)291int BPF_PROG(xppen_artist_pro_16_device_event, struct hid_bpf_ctx *hctx)292{293int ret = xppen_16_fix_angle_offset(hctx);294295if (ret)296return ret;297298return xppen_16_fix_eraser(hctx);299}300301HID_BPF_OPS(xppen_artist_pro_16) = {302.hid_rdesc_fixup = (void *)hid_fix_rdesc_xppen_artistpro16gen2,303.hid_device_event = (void *)xppen_artist_pro_16_device_event,304};305306SEC("syscall")307int probe(struct hid_bpf_probe_args *ctx)308{309/*310* The device exports 3 interfaces.311*/312ctx->retval = ctx->rdesc_size != 113;313if (ctx->retval)314ctx->retval = -EINVAL;315316/* ensure the kernel isn't fixed already */317if (ctx->rdesc[17] != 0x45) /* Eraser */318ctx->retval = -EINVAL;319320return 0;321}322323char _license[] SEC("license") = "GPL";324325326