brintos

brintos / linux-shallow public Read only

0
0
Text · 9.0 KiB · c24fe90 Raw
232 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_24 0x093A12#define PID_ARTIST_24_PRO 0x092D13 14HID_BPF_CONFIG(15	HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_24),16	HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_24_PRO)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 * - the pen doesn't have a rubber tail, so basically we are removing any23 *   eraser/invert bits24 */25static const __u8 fixed_rdesc[] = {26	0x05, 0x0d,                    // Usage Page (Digitizers)             027	0x09, 0x02,                    // Usage (Pen)                         228	0xa1, 0x01,                    // Collection (Application)            429	0x85, 0x07,                    //  Report ID (7)                      630	0x09, 0x20,                    //  Usage (Stylus)                     831	0xa1, 0x00,                    //  Collection (Physical)              1032	0x09, 0x42,                    //   Usage (Tip Switch)                1233	0x09, 0x44,                    //   Usage (Barrel Switch)             1434	0x09, 0x5a,                    //   Usage (Secondary Barrel Switch)   16  /* changed from 0x45 (Eraser) to 0x5a (Secondary Barrel Switch) */35	0x15, 0x00,                    //   Logical Minimum (0)               1836	0x25, 0x01,                    //   Logical Maximum (1)               2037	0x75, 0x01,                    //   Report Size (1)                   2238	0x95, 0x03,                    //   Report Count (3)                  2439	0x81, 0x02,                    //   Input (Data,Var,Abs)              2640	0x95, 0x02,                    //   Report Count (2)                  2841	0x81, 0x03,                    //   Input (Cnst,Var,Abs)              3042	0x09, 0x32,                    //   Usage (In Range)                  3243	0x95, 0x01,                    //   Report Count (1)                  3444	0x81, 0x02,                    //   Input (Data,Var,Abs)              3645	0x95, 0x02,                    //   Report Count (2)                  3846	0x81, 0x03,                    //   Input (Cnst,Var,Abs)              4047	0x75, 0x10,                    //   Report Size (16)                  4248	0x95, 0x01,                    //   Report Count (1)                  4449	0x35, 0x00,                    //   Physical Minimum (0)              4650	0xa4,                          //   Push                              4851	0x05, 0x01,                    //   Usage Page (Generic Desktop)      4952	0x09, 0x30,                    //   Usage (X)                         5153	0x65, 0x13,                    //   Unit (EnglishLinear: in)          5354	0x55, 0x0d,                    //   Unit Exponent (-3)                5555	0x46, 0xf0, 0x50,              //   Physical Maximum (20720)          5756	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           6057	0x81, 0x02,                    //   Input (Data,Var,Abs)              6358	0x09, 0x31,                    //   Usage (Y)                         6559	0x46, 0x91, 0x2d,              //   Physical Maximum (11665)          6760	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           7061	0x81, 0x02,                    //   Input (Data,Var,Abs)              7362	0xb4,                          //   Pop                               7563	0x09, 0x30,                    //   Usage (Tip Pressure)              7664	0x45, 0x00,                    //   Physical Maximum (0)              7865	0x26, 0xff, 0x1f,              //   Logical Maximum (8191)            8066	0x81, 0x42,                    //   Input (Data,Var,Abs,Null)         8367	0x09, 0x3d,                    //   Usage (X Tilt)                    8568	0x15, 0x81,                    //   Logical Minimum (-127)            8769	0x25, 0x7f,                    //   Logical Maximum (127)             8970	0x75, 0x08,                    //   Report Size (8)                   9171	0x95, 0x01,                    //   Report Count (1)                  9372	0x81, 0x02,                    //   Input (Data,Var,Abs)              9573	0x09, 0x3e,                    //   Usage (Y Tilt)                    9774	0x15, 0x81,                    //   Logical Minimum (-127)            9975	0x25, 0x7f,                    //   Logical Maximum (127)             10176	0x81, 0x02,                    //   Input (Data,Var,Abs)              10377	0xc0,                          //  End Collection                     10578	0xc0,                          // End Collection                      10679};80 81#define TIP_SWITCH		BIT(0)82#define BARREL_SWITCH		BIT(1)83#define ERASER			BIT(2)84/* padding			BIT(3) */85/* padding			BIT(4) */86#define IN_RANGE		BIT(5)87/* padding			BIT(6) */88/* padding			BIT(7) */89 90#define U16(index) (data[index] | (data[index + 1] << 8))91 92SEC(HID_BPF_RDESC_FIXUP)93int BPF_PROG(hid_fix_rdesc_xppen_artist24, struct hid_bpf_ctx *hctx)94{95	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);96 97	if (!data)98		return 0; /* EPERM check */99 100	__builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc));101 102	return sizeof(fixed_rdesc);103}104 105static __u8 prev_state = 0;106 107/*108 * There are a few cases where the device is sending wrong event109 * sequences, all related to the second button (the pen doesn't110 * have an eraser switch on the tail end):111 *112 *   whenever the second button gets pressed or released, an113 *   out-of-proximity event is generated and then the firmware114 *   compensate for the missing state (and the firmware uses115 *   eraser for that button):116 *117 *   - if the pen is in range, an extra out-of-range is sent118 *     when the second button is pressed/released:119 *     // Pen is in range120 *     E:                               InRange121 *122 *     // Second button is pressed123 *     E:124 *     E:                        Eraser InRange125 *126 *     // Second button is released127 *     E:128 *     E:                               InRange129 *130 *     This case is ignored by this filter, it's "valid"131 *     and userspace knows how to deal with it, there are just132 *     a few out-of-prox events generated, but the user doesn´t133 *     see them.134 *135 *   - if the pen is in contact, 2 extra events are added when136 *     the second button is pressed/released: an out of range137 *     and an in range:138 *139 *     // Pen is in contact140 *     E: TipSwitch                     InRange141 *142 *     // Second button is pressed143 *     E:                                         <- false release, needs to be filtered out144 *     E:                        Eraser InRange   <- false release, needs to be filtered out145 *     E: TipSwitch              Eraser InRange146 *147 *     // Second button is released148 *     E:                                         <- false release, needs to be filtered out149 *     E:                               InRange   <- false release, needs to be filtered out150 *     E: TipSwitch                     InRange151 *152 */153SEC(HID_BPF_DEVICE_EVENT)154int BPF_PROG(xppen_24_fix_eraser, struct hid_bpf_ctx *hctx)155{156	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);157	__u8 current_state, changed_state;158	bool prev_tip;159 160	if (!data)161		return 0; /* EPERM check */162 163	current_state = data[1];164 165	/* if the state is identical to previously, early return */166	if (current_state == prev_state)167		return 0;168 169	prev_tip = !!(prev_state & TIP_SWITCH);170 171	/*172	 * Illegal transition: pen is in range with the tip pressed, and173	 * it goes into out of proximity.174	 *175	 * Ideally we should hold the event, start a timer and deliver it176	 * only if the timer ends, but we are not capable of that now.177	 *178	 * And it doesn't matter because when we are in such cases, this179	 * means we are detecting a false release.180	 */181	if ((current_state & IN_RANGE) == 0) {182		if (prev_tip)183			return HID_IGNORE_EVENT;184		return 0;185	}186 187	/*188	 * XOR to only set the bits that have changed between189	 * previous and current state190	 */191	changed_state = prev_state ^ current_state;192 193	/* Store the new state for future processing */194	prev_state = current_state;195 196	/*197	 * We get both a tipswitch and eraser change in the same HID report:198	 * this is not an authorized transition and is unlikely to happen199	 * in real life.200	 * This is likely to be added by the firmware to emulate the201	 * eraser mode so we can skip the event.202	 */203	if ((changed_state & (TIP_SWITCH | ERASER)) == (TIP_SWITCH | ERASER)) /* we get both a tipswitch and eraser change at the same time */204		return HID_IGNORE_EVENT;205 206	return 0;207}208 209HID_BPF_OPS(xppen_artist_24) = {210	.hid_rdesc_fixup = (void *)hid_fix_rdesc_xppen_artist24,211	.hid_device_event = (void *)xppen_24_fix_eraser,212};213 214SEC("syscall")215int probe(struct hid_bpf_probe_args *ctx)216{217	/*218	 * The device exports 3 interfaces.219	 */220	ctx->retval = ctx->rdesc_size != 107;221	if (ctx->retval)222		ctx->retval = -EINVAL;223 224	/* ensure the kernel isn't fixed already */225	if (ctx->rdesc[17] != 0x45) /* Eraser */226		ctx->retval = -EINVAL;227 228	return 0;229}230 231char _license[] SEC("license") = "GPL";232