Path: blob/master/drivers/hid/bpf/progs/XPPen__Artist24.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_24 0x093A11#define PID_ARTIST_24_PRO 0x092D1213HID_BPF_CONFIG(14HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_24),15HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_24_PRO)16);1718/*19* We need to amend the report descriptor for the following:20* - the device reports Eraser instead of using Secondary Barrel Switch21* - the pen doesn't have a rubber tail, so basically we are removing any22* eraser/invert bits23*/24static const __u8 fixed_rdesc[] = {250x05, 0x0d, // Usage Page (Digitizers) 0260x09, 0x02, // Usage (Pen) 2270xa1, 0x01, // Collection (Application) 4280x85, 0x07, // Report ID (7) 6290x09, 0x20, // Usage (Stylus) 8300xa1, 0x00, // Collection (Physical) 10310x09, 0x42, // Usage (Tip Switch) 12320x09, 0x44, // Usage (Barrel Switch) 14330x09, 0x5a, // Usage (Secondary Barrel Switch) 16 /* changed from 0x45 (Eraser) to 0x5a (Secondary Barrel Switch) */340x15, 0x00, // Logical Minimum (0) 18350x25, 0x01, // Logical Maximum (1) 20360x75, 0x01, // Report Size (1) 22370x95, 0x03, // Report Count (3) 24380x81, 0x02, // Input (Data,Var,Abs) 26390x95, 0x02, // Report Count (2) 28400x81, 0x03, // Input (Cnst,Var,Abs) 30410x09, 0x32, // Usage (In Range) 32420x95, 0x01, // Report Count (1) 34430x81, 0x02, // Input (Data,Var,Abs) 36440x95, 0x02, // Report Count (2) 38450x81, 0x03, // Input (Cnst,Var,Abs) 40460x75, 0x10, // Report Size (16) 42470x95, 0x01, // Report Count (1) 44480x35, 0x00, // Physical Minimum (0) 46490xa4, // Push 48500x05, 0x01, // Usage Page (Generic Desktop) 49510x09, 0x30, // Usage (X) 51520x65, 0x13, // Unit (EnglishLinear: in) 53530x55, 0x0d, // Unit Exponent (-3) 55540x46, 0xf0, 0x50, // Physical Maximum (20720) 57550x26, 0xff, 0x7f, // Logical Maximum (32767) 60560x81, 0x02, // Input (Data,Var,Abs) 63570x09, 0x31, // Usage (Y) 65580x46, 0x91, 0x2d, // Physical Maximum (11665) 67590x26, 0xff, 0x7f, // Logical Maximum (32767) 70600x81, 0x02, // Input (Data,Var,Abs) 73610xb4, // Pop 75620x09, 0x30, // Usage (Tip Pressure) 76630x45, 0x00, // Physical Maximum (0) 78640x26, 0xff, 0x1f, // Logical Maximum (8191) 80650x81, 0x42, // Input (Data,Var,Abs,Null) 83660x09, 0x3d, // Usage (X Tilt) 85670x15, 0x81, // Logical Minimum (-127) 87680x25, 0x7f, // Logical Maximum (127) 89690x75, 0x08, // Report Size (8) 91700x95, 0x01, // Report Count (1) 93710x81, 0x02, // Input (Data,Var,Abs) 95720x09, 0x3e, // Usage (Y Tilt) 97730x15, 0x81, // Logical Minimum (-127) 99740x25, 0x7f, // Logical Maximum (127) 101750x81, 0x02, // Input (Data,Var,Abs) 103760xc0, // End Collection 105770xc0, // End Collection 10678};7980#define TIP_SWITCH BIT(0)81#define BARREL_SWITCH BIT(1)82#define ERASER BIT(2)83/* padding BIT(3) */84/* padding BIT(4) */85#define IN_RANGE BIT(5)86/* padding BIT(6) */87/* padding BIT(7) */8889#define U16(index) (data[index] | (data[index + 1] << 8))9091SEC(HID_BPF_RDESC_FIXUP)92int BPF_PROG(hid_fix_rdesc_xppen_artist24, struct hid_bpf_ctx *hctx)93{94__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);9596if (!data)97return 0; /* EPERM check */9899__builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc));100101return sizeof(fixed_rdesc);102}103104static __u8 prev_state = 0;105106/*107* There are a few cases where the device is sending wrong event108* sequences, all related to the second button (the pen doesn't109* have an eraser switch on the tail end):110*111* whenever the second button gets pressed or released, an112* out-of-proximity event is generated and then the firmware113* compensate for the missing state (and the firmware uses114* eraser for that button):115*116* - if the pen is in range, an extra out-of-range is sent117* when the second button is pressed/released:118* // Pen is in range119* E: InRange120*121* // Second button is pressed122* E:123* E: Eraser InRange124*125* // Second button is released126* E:127* E: InRange128*129* This case is ignored by this filter, it's "valid"130* and userspace knows how to deal with it, there are just131* a few out-of-prox events generated, but the user doesn´t132* see them.133*134* - if the pen is in contact, 2 extra events are added when135* the second button is pressed/released: an out of range136* and an in range:137*138* // Pen is in contact139* E: TipSwitch InRange140*141* // Second button is pressed142* E: <- false release, needs to be filtered out143* E: Eraser InRange <- false release, needs to be filtered out144* E: TipSwitch Eraser InRange145*146* // Second button is released147* E: <- false release, needs to be filtered out148* E: InRange <- false release, needs to be filtered out149* E: TipSwitch InRange150*151*/152SEC(HID_BPF_DEVICE_EVENT)153int BPF_PROG(xppen_24_fix_eraser, struct hid_bpf_ctx *hctx)154{155__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);156__u8 current_state, changed_state;157bool prev_tip;158159if (!data)160return 0; /* EPERM check */161162current_state = data[1];163164/* if the state is identical to previously, early return */165if (current_state == prev_state)166return 0;167168prev_tip = !!(prev_state & TIP_SWITCH);169170/*171* Illegal transition: pen is in range with the tip pressed, and172* it goes into out of proximity.173*174* Ideally we should hold the event, start a timer and deliver it175* only if the timer ends, but we are not capable of that now.176*177* And it doesn't matter because when we are in such cases, this178* means we are detecting a false release.179*/180if ((current_state & IN_RANGE) == 0) {181if (prev_tip)182return HID_IGNORE_EVENT;183return 0;184}185186/*187* XOR to only set the bits that have changed between188* previous and current state189*/190changed_state = prev_state ^ current_state;191192/* Store the new state for future processing */193prev_state = current_state;194195/*196* We get both a tipswitch and eraser change in the same HID report:197* this is not an authorized transition and is unlikely to happen198* in real life.199* This is likely to be added by the firmware to emulate the200* eraser mode so we can skip the event.201*/202if ((changed_state & (TIP_SWITCH | ERASER)) == (TIP_SWITCH | ERASER)) /* we get both a tipswitch and eraser change at the same time */203return HID_IGNORE_EVENT;204205return 0;206}207208HID_BPF_OPS(xppen_artist_24) = {209.hid_rdesc_fixup = (void *)hid_fix_rdesc_xppen_artist24,210.hid_device_event = (void *)xppen_24_fix_eraser,211};212213SEC("syscall")214int probe(struct hid_bpf_probe_args *ctx)215{216/*217* The device exports 3 interfaces.218*/219ctx->retval = ctx->rdesc_size != 107;220if (ctx->retval)221ctx->retval = -EINVAL;222223/* ensure the kernel isn't fixed already */224if (ctx->rdesc[17] != 0x45) /* Eraser */225ctx->retval = -EINVAL;226227return 0;228}229230char _license[] SEC("license") = "GPL";231232233