Path: blob/master/drivers/hid/bpf/progs/WALTOP__Batteryless-Tablet.bpf.c
38241 views
// SPDX-License-Identifier: GPL-2.0-only1/* Copyright (c) 2025 Red Hat2*/34#include "vmlinux.h"5#include "hid_bpf.h"6#include "hid_bpf_helpers.h"7#include <bpf/bpf_tracing.h>89#define VID_WALTOP 0x172F10#define PID_BATTERYLESS_TABLET 0x05051112HID_BPF_CONFIG(13HID_DEVICE(BUS_USB, HID_GROUP_ANY, VID_WALTOP, PID_BATTERYLESS_TABLET)14);1516#define EXPECTED_RDESC_SIZE 33517#define PEN_REPORT_ID 161819#define TIP_SWITCH BIT(0)20#define BARREL_SWITCH BIT(1)21#define SECONDARY_BARREL_SWITCH BIT(5)2223static __u8 last_button_state;2425static const __u8 fixed_rdesc[] = {260x05, 0x01, // Usage Page (Generic Desktop)270x09, 0x02, // Usage (Mouse)280xa1, 0x01, // Collection (Application)290x85, 0x01, // Report ID (1)300x09, 0x01, // Usage (Pointer)310xa1, 0x00, // Collection (Physical)320x05, 0x09, // Usage Page (Button)330x19, 0x01, // Usage Minimum (1)340x29, 0x05, // Usage Maximum (5)350x15, 0x00, // Logical Minimum (0)360x25, 0x01, // Logical Maximum (1)370x75, 0x01, // Report Size (1)380x95, 0x05, // Report Count (5)390x81, 0x02, // Input (Data,Var,Abs)400x75, 0x03, // Report Size (3)410x95, 0x01, // Report Count (1)420x81, 0x03, // Input (Cnst,Var,Abs)430x05, 0x01, // Usage Page (Generic Desktop)440x09, 0x30, // Usage (X)450x09, 0x31, // Usage (Y)460x09, 0x38, // Usage (Wheel)470x15, 0x81, // Logical Minimum (-127)480x25, 0x7f, // Logical Maximum (127)490x75, 0x08, // Report Size (8)500x95, 0x03, // Report Count (3)510x81, 0x06, // Input (Data,Var,Rel)520x05, 0x0c, // Usage Page (Consumer)530x15, 0x81, // Logical Minimum (-127)540x25, 0x7f, // Logical Maximum (127)550x75, 0x08, // Report Size (8)560x95, 0x01, // Report Count (1)570x0a, 0x38, 0x02, // Usage (AC Pan)580x81, 0x06, // Input (Data,Var,Rel)590xc0, // End Collection600xc0, // End Collection610x05, 0x0d, // Usage Page (Digitizers)620x09, 0x02, // Usage (Pen)630xa1, 0x01, // Collection (Application)640x85, 0x02, // Report ID (2)650x09, 0x20, // Usage (Stylus)660xa1, 0x00, // Collection (Physical)670x09, 0x00, // Usage (0x0000)680x15, 0x00, // Logical Minimum (0)690x26, 0xff, 0x00, // Logical Maximum (255)700x75, 0x08, // Report Size (8)710x95, 0x09, // Report Count (9)720x81, 0x02, // Input (Data,Var,Abs)730x09, 0x3f, // Usage (Azimuth)740x09, 0x40, // Usage (Altitude)750x15, 0x00, // Logical Minimum (0)760x26, 0xff, 0x00, // Logical Maximum (255)770x75, 0x08, // Report Size (8)780x95, 0x02, // Report Count (2)790xb1, 0x02, // Feature (Data,Var,Abs)800xc0, // End Collection810x85, 0x05, // Report ID (5)820x05, 0x0d, // Usage Page (Digitizers)830x09, 0x20, // Usage (Stylus)840xa1, 0x00, // Collection (Physical)850x09, 0x00, // Usage (0x0000)860x15, 0x00, // Logical Minimum (0)870x26, 0xff, 0x00, // Logical Maximum (255)880x75, 0x08, // Report Size (8)890x95, 0x07, // Report Count (7)900x81, 0x02, // Input (Data,Var,Abs)910xc0, // End Collection920x85, 0x0a, // Report ID (10)930x05, 0x0d, // Usage Page (Digitizers)940x09, 0x20, // Usage (Stylus)950xa1, 0x00, // Collection (Physical)960x09, 0x00, // Usage (0x0000)970x15, 0x00, // Logical Minimum (0)980x26, 0xff, 0x00, // Logical Maximum (255)990x75, 0x08, // Report Size (8)1000x95, 0x07, // Report Count (7)1010x81, 0x02, // Input (Data,Var,Abs)1020xc0, // End Collection1030x85, 0x10, // Report ID (16)1040x09, 0x20, // Usage (Stylus)1050xa1, 0x00, // Collection (Physical)1060x09, 0x42, // Usage (Tip Switch)1070x09, 0x44, // Usage (Barrel Switch)1080x09, 0x3c, // Usage (Invert)1090x09, 0x45, // Usage (Eraser)1100x09, 0x32, // Usage (In Range)1110x09, 0x5a, // Usage (Secondary Barrel Switch) <-- added1120x15, 0x00, // Logical Minimum (0)1130x25, 0x01, // Logical Maximum (1)1140x75, 0x01, // Report Size (1)1150x95, 0x06, // Report Count (6) <--- changed from 51160x81, 0x02, // Input (Data,Var,Abs)1170x95, 0x02, // Report Count (2) <--- changed from 31180x81, 0x03, // Input (Cnst,Var,Abs)1190x05, 0x01, // Usage Page (Generic Desktop)1200x09, 0x30, // Usage (X)1210x75, 0x10, // Report Size (16)1220x95, 0x01, // Report Count (1)1230xa4, // Push1240x55, 0x0d, // Unit Exponent (-3)1250x65, 0x33, // Unit (EnglishLinear: in³)1260x15, 0x00, // Logical Minimum (0)1270x26, 0x00, 0x7d, // Logical Maximum (32000)1280x35, 0x00, // Physical Minimum (0)1290x46, 0x00, 0x7d, // Physical Maximum (32000)1300x81, 0x02, // Input (Data,Var,Abs)1310x09, 0x31, // Usage (Y)1320x15, 0x00, // Logical Minimum (0)1330x26, 0x20, 0x4e, // Logical Maximum (20000)1340x35, 0x00, // Physical Minimum (0)1350x46, 0x20, 0x4e, // Physical Maximum (20000)1360x81, 0x02, // Input (Data,Var,Abs)1370x05, 0x0d, // Usage Page (Digitizers)1380x09, 0x30, // Usage (Tip Pressure)1390x15, 0x00, // Logical Minimum (0)1400x26, 0xff, 0x07, // Logical Maximum (2047)1410x35, 0x00, // Physical Minimum (0)1420x46, 0xff, 0x07, // Physical Maximum (2047)1430x81, 0x02, // Input (Data,Var,Abs)1440x05, 0x0d, // Usage Page (Digitizers)1450x09, 0x3d, // Usage (X Tilt)1460x09, 0x3e, // Usage (Y Tilt)1470x15, 0xc4, // Logical Minimum (-60) <- changed from -1271480x25, 0x3c, // Logical Maximum (60) <- changed from 1271490x75, 0x08, // Report Size (8)1500x95, 0x02, // Report Count (2)1510x81, 0x02, // Input (Data,Var,Abs)1520xc0, // End Collection1530xc0, // End Collection1540x05, 0x01, // Usage Page (Generic Desktop)1550x09, 0x06, // Usage (Keyboard)1560xa1, 0x01, // Collection (Application)1570x85, 0x0d, // Report ID (13)1580x05, 0x07, // Usage Page (Keyboard/Keypad)1590x19, 0xe0, // Usage Minimum (224)1600x29, 0xe7, // Usage Maximum (231)1610x15, 0x00, // Logical Minimum (0)1620x25, 0x01, // Logical Maximum (1)1630x75, 0x01, // Report Size (1)1640x95, 0x08, // Report Count (8)1650x81, 0x02, // Input (Data,Var,Abs)1660x75, 0x08, // Report Size (8)1670x95, 0x01, // Report Count (1)1680x81, 0x01, // Input (Cnst,Arr,Abs)1690x05, 0x07, // Usage Page (Keyboard/Keypad)1700x19, 0x00, // Usage Minimum (0)1710x29, 0x65, // Usage Maximum (101)1720x15, 0x00, // Logical Minimum (0)1730x25, 0x65, // Logical Maximum (101)1740x75, 0x08, // Report Size (8)1750x95, 0x05, // Report Count (5)1760x81, 0x00, // Input (Data,Arr,Abs)1770xc0, // End Collection1780x05, 0x0c, // Usage Page (Consumer)1790x09, 0x01, // Usage (Consumer Control)1800xa1, 0x01, // Collection (Application)1810x85, 0x0c, // Report ID (12)1820x09, 0xe9, // Usage (Volume Increment)1830x09, 0xea, // Usage (Volume Decrement)1840x09, 0xe2, // Usage (Mute)1850x15, 0x00, // Logical Minimum (0)1860x25, 0x01, // Logical Maximum (1)1870x75, 0x01, // Report Size (1)1880x95, 0x03, // Report Count (3)1890x81, 0x06, // Input (Data,Var,Rel)1900x75, 0x05, // Report Size (5)1910x95, 0x01, // Report Count (1)1920x81, 0x07, // Input (Cnst,Var,Rel)1930xc0, // End Collection194};195196static inline unsigned int bitwidth32(__u32 x)197{198return 32 - __builtin_clzg(x, 32);199}200201static inline unsigned int floor_log2_32(__u32 x)202{203return bitwidth32(x) - 1;204}205206/* Maps the interval [0, 2047] to itself using a scaled207* approximation of the function log2(x+1).208*/209static unsigned int scaled_log2(__u16 v)210{211const unsigned int XMAX = 2047;212const unsigned int YMAX = 11; /* log2(2048) = 11 */213214unsigned int x = v + 1;215unsigned int n = floor_log2_32(x);216unsigned int b = 1 << n;217218/* Fixed-point fraction in [0, 1), linearly219* interpolated using delta-y = 1 and220* delta-x = (2b - b) = b.221*/222unsigned int frac = (x - b) << YMAX;223unsigned int lerp = frac / b;224unsigned int log2 = (n << YMAX) + lerp;225226return ((log2 * XMAX) / YMAX) >> YMAX;227}228229SEC(HID_BPF_RDESC_FIXUP)230int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)231{232__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);233234if (!data)235return 0; /* EPERM check */236237__builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc));238239return sizeof(fixed_rdesc);240}241242SEC(HID_BPF_DEVICE_EVENT)243int BPF_PROG(waltop_fix_events, struct hid_bpf_ctx *hctx)244{245__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);246247if (!data)248return 0; /* EPERM check */249250__u8 report_id = data[0];251252if (report_id != PEN_REPORT_ID)253return 0;254255/* On this tablet if the secondary barrel switch is pressed,256* the tablet sends tip down and barrel down. Change this to257* just secondary barrel down when there is no ambiguity.258*259* It's possible that there is a bug in the firmware and the260* device intends to set invert + eraser instead (i.e. the261* pysical button is an eraser button) but since262* the pressure is always zero, said eraser button263* would be useless anyway.264*265* So let's just change the button to secondary barrel down.266*/267268__u8 tip_switch = data[1] & TIP_SWITCH;269__u8 barrel_switch = data[1] & BARREL_SWITCH;270271__u8 tip_held = last_button_state & TIP_SWITCH;272__u8 barrel_held = last_button_state & BARREL_SWITCH;273274if (tip_switch && barrel_switch && !tip_held && !barrel_held) {275data[1] &= ~(TIP_SWITCH | BARREL_SWITCH); /* release tip and barrel */276data[1] |= SECONDARY_BARREL_SWITCH; /* set secondary barrel switch */277}278279last_button_state = data[1];280281/* The pressure sensor on this tablet maps around half of the282* logical pressure range into the interval [0-100]. Further283* pressure causes the sensor value to increase exponentially284* up to a maximum value of 2047.285*286* The values 12 and 102 were chosen to have an integer slope287* with smooth transition between the two curves around the288* value 100.289*/290291__u16 pressure = (((__u16)data[6]) << 0) | (((__u16)data[7]) << 8);292293if (pressure <= 102)294pressure *= 12;295else296pressure = scaled_log2(pressure);297298data[6] = pressure >> 0;299data[7] = pressure >> 8;300301return 0;302}303304HID_BPF_OPS(waltop_batteryless) = {305.hid_device_event = (void *)waltop_fix_events,306.hid_rdesc_fixup = (void *)hid_fix_rdesc,307};308309SEC("syscall")310int probe(struct hid_bpf_probe_args *ctx)311{312if (ctx->rdesc_size == EXPECTED_RDESC_SIZE)313ctx->retval = 0;314else315ctx->retval = -EINVAL;316317return 0;318}319320char _license[] SEC("license") = "GPL";321322323