Path: blob/master/drivers/hid/bpf/progs/XPPen__ACK05.bpf.c
26285 views
// SPDX-License-Identifier: GPL-2.0-only1/* Copyright (c) 2024 Red Hat, Inc2*/34#include "vmlinux.h"5#include "hid_bpf.h"6#include "hid_bpf_helpers.h"7#include "hid_report_helpers.h"8#include <bpf/bpf_tracing.h>910#define HID_BPF_ASYNC_MAX_CTX 111#include "hid_bpf_async.h"1213#define VID_UGEE 0x28BD14/* same PID whether connected directly or through the provided dongle: */15#define PID_ACK05_REMOTE 0x0202161718HID_BPF_CONFIG(19HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ACK05_REMOTE),20);2122/*23* By default, the pad reports the buttons through a set of key sequences.24*25* The pad reports a classic keyboard report descriptor:26* # HANVON UGEE Shortcut Remote27* Report descriptor length: 102 bytes28* 0x05, 0x01, // Usage Page (Generic Desktop) 029* 0x09, 0x02, // Usage (Mouse) 230* 0xa1, 0x01, // Collection (Application) 431* 0x85, 0x09, // Report ID (9) 632* 0x09, 0x01, // Usage (Pointer) 833* 0xa1, 0x00, // Collection (Physical) 1034* 0x05, 0x09, // Usage Page (Button) 1235* 0x19, 0x01, // UsageMinimum (1) 1436* 0x29, 0x03, // UsageMaximum (3) 1637* 0x15, 0x00, // Logical Minimum (0) 1838* 0x25, 0x01, // Logical Maximum (1) 2039* 0x95, 0x03, // Report Count (3) 2240* 0x75, 0x01, // Report Size (1) 2441* 0x81, 0x02, // Input (Data,Var,Abs) 2642* 0x95, 0x05, // Report Count (5) 2843* 0x81, 0x01, // Input (Cnst,Arr,Abs) 3044* 0x05, 0x01, // Usage Page (Generic Desktop) 3245* 0x09, 0x30, // Usage (X) 3446* 0x09, 0x31, // Usage (Y) 3647* 0x26, 0xff, 0x7f, // Logical Maximum (32767) 3848* 0x95, 0x02, // Report Count (2) 4149* 0x75, 0x10, // Report Size (16) 4350* 0x81, 0x02, // Input (Data,Var,Abs) 4551* 0x05, 0x0d, // Usage Page (Digitizers) 4752* 0x09, 0x30, // Usage (Tip Pressure) 4953* 0x26, 0xff, 0x07, // Logical Maximum (2047) 5154* 0x95, 0x01, // Report Count (1) 5455* 0x75, 0x10, // Report Size (16) 5656* 0x81, 0x02, // Input (Data,Var,Abs) 5857* 0xc0, // End Collection 6058* 0xc0, // End Collection 6159* 0x05, 0x01, // Usage Page (Generic Desktop) 6260* 0x09, 0x06, // Usage (Keyboard) 6461* 0xa1, 0x01, // Collection (Application) 6662* 0x85, 0x06, // Report ID (6) 6863* 0x05, 0x07, // Usage Page (Keyboard/Keypad) 7064* 0x19, 0xe0, // UsageMinimum (224) 7265* 0x29, 0xe7, // UsageMaximum (231) 7466* 0x15, 0x00, // Logical Minimum (0) 7667* 0x25, 0x01, // Logical Maximum (1) 7868* 0x75, 0x01, // Report Size (1) 8069* 0x95, 0x08, // Report Count (8) 8270* 0x81, 0x02, // Input (Data,Var,Abs) 8471* 0x05, 0x07, // Usage Page (Keyboard/Keypad) 8672* 0x19, 0x00, // UsageMinimum (0) 8873* 0x29, 0xff, // UsageMaximum (255) 9074* 0x26, 0xff, 0x00, // Logical Maximum (255) 9275* 0x75, 0x08, // Report Size (8) 9576* 0x95, 0x06, // Report Count (6) 9777* 0x81, 0x00, // Input (Data,Arr,Abs) 9978* 0xc0, // End Collection 10179*80* Each button gets assigned the following events:81*82* Buttons released: 06 00 00 00 00 00 00 0083* Button 1: 06 01 12 00 00 00 00 00 -> LControl + o84* Button 2: 06 01 11 00 00 00 00 00 -> LControl + n85* Button 3: 06 00 3e 00 00 00 00 00 -> F586* Button 4: 06 02 00 00 00 00 00 00 -> LShift87* Button 5: 06 01 00 00 00 00 00 00 -> LControl88* Button 6: 06 04 00 00 00 00 00 00 -> LAlt89* Button 7: 06 01 16 00 00 00 00 00 -> LControl + s90* Button 8: 06 01 1d 00 00 00 00 00 -> LControl + z91* Button 9: 06 00 2c 00 00 00 00 00 -> Space92* Button 10: 06 03 1d 00 00 00 00 00 -> LControl + LShift + z93* Wheel: 06 01 57 00 00 00 00 00 -> clockwise rotation (LControl + Keypad Plus)94* Wheel: 06 01 56 00 00 00 00 00 -> counter-clockwise rotation95* (LControl + Keypad Minus)96*97* However, multiple buttons can be pressed at the same time, and when this happens,98* each button gets assigned a new slot in the Input (Data,Arr,Abs):99*100* Button 1 + 3: 06 01 12 3e 00 00 00 00 -> LControl + o + F5101*102* When a modifier is pressed (Button 4, 5, or 6), the assigned key is set to 00:103*104* Button 5 + 7: 06 01 00 16 00 00 00 00 -> LControl + s105*106* This is mostly fine, but with Button 8 and Button 10 sharing the same107* key value ("z"), there are cases where we can not know which is which.108*109*/110111#define PAD_WIRED_DESCRIPTOR_LENGTH 102112#define PAD_DONGLE_DESCRIPTOR_LENGTH 177113#define STYLUS_DESCRIPTOR_LENGTH 109114#define VENDOR_DESCRIPTOR_LENGTH 36115#define PAD_REPORT_ID 6116#define RAW_PAD_REPORT_ID 0xf0117#define RAW_BATTERY_REPORT_ID 0xf2118#define VENDOR_REPORT_ID 2119#define PAD_REPORT_LENGTH 8120#define VENDOR_REPORT_LENGTH 12121122__u16 last_button_state;123124static const __u8 disabled_rdesc[] = {125// Make sure we match our original report length126FixedSizeVendorReport(VENDOR_REPORT_LENGTH)127};128129static const __u8 fixed_rdesc_vendor[] = {130UsagePage_GenericDesktop131Usage_GD_Keypad132CollectionApplication(133// -- Byte 0 in report134ReportId(RAW_PAD_REPORT_ID)135// Byte 1 in report - same than report ID136ReportCount(1)137ReportSize(8)138Input(Const) // padding (internal report ID)139LogicalMaximum_i8(0)140LogicalMaximum_i8(1)141UsagePage_Digitizers142Usage_Dig_TabletFunctionKeys143CollectionPhysical(144// Byte 2-3 is the button state145UsagePage_Button146UsageMinimum_i8(0x01)147UsageMaximum_i8(0x0a)148LogicalMinimum_i8(0x0)149LogicalMaximum_i8(0x1)150ReportCount(10)151ReportSize(1)152Input(Var|Abs)153Usage_i8(0x31) // will be mapped as BTN_A / BTN_SOUTH154ReportCount(1)155Input(Var|Abs)156ReportCount(5) // padding157Input(Const)158// Byte 4 in report - just exists so we get to be a tablet pad159UsagePage_Digitizers160Usage_Dig_BarrelSwitch // BTN_STYLUS161ReportCount(1)162ReportSize(1)163Input(Var|Abs)164ReportCount(7) // padding165Input(Const)166// Bytes 5/6 in report - just exists so we get to be a tablet pad167UsagePage_GenericDesktop168Usage_GD_X169Usage_GD_Y170ReportCount(2)171ReportSize(8)172Input(Var|Abs)173// Byte 7 in report is the dial174Usage_GD_Wheel175LogicalMinimum_i8(-1)176LogicalMaximum_i8(1)177ReportCount(1)178ReportSize(8)179Input(Var|Rel)180)181// -- Byte 0 in report182ReportId(RAW_BATTERY_REPORT_ID)183// Byte 1 in report - same than report ID184ReportCount(1)185ReportSize(8)186Input(Const) // padding (internal report ID)187// Byte 2 in report - always 0x01188Input(Const) // padding (internal report ID)189UsagePage_Digitizers190/*191* We represent the device as a stylus to force the kernel to not192* directly query its battery state. Instead the kernel will rely193* only on the provided events.194*/195Usage_Dig_Stylus196CollectionPhysical(197// Byte 3 in report - battery value198UsagePage_BatterySystem199Usage_BS_AbsoluteStateOfCharge200LogicalMinimum_i8(0)201LogicalMaximum_i8(100)202ReportCount(1)203ReportSize(8)204Input(Var|Abs)205// Byte 4 in report - charging state206Usage_BS_Charging207LogicalMinimum_i8(0)208LogicalMaximum_i8(1)209ReportCount(1)210ReportSize(8)211Input(Var|Abs)212)213)214};215216SEC(HID_BPF_RDESC_FIXUP)217int BPF_PROG(ack05_fix_rdesc, struct hid_bpf_ctx *hctx)218{219__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);220__s32 rdesc_size = hctx->size;221222if (!data)223return 0; /* EPERM check */224225if (rdesc_size == VENDOR_DESCRIPTOR_LENGTH) {226/*227* The vendor fixed rdesc is appended after the current one,228* to keep the output reports working.229*/230__builtin_memcpy(data + rdesc_size, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor));231return sizeof(fixed_rdesc_vendor) + rdesc_size;232}233234hid_set_name(hctx->hid, "Disabled by HID-BPF Hanvon Ugee Shortcut Remote");235236__builtin_memcpy(data, disabled_rdesc, sizeof(disabled_rdesc));237return sizeof(disabled_rdesc);238}239240static int HID_BPF_ASYNC_FUN(switch_to_raw_mode)(struct hid_bpf_ctx *hid)241{242static __u8 magic_0[32] = {0x02, 0xb0, 0x04, 0x00, 0x00};243int err;244245/*246* The proprietary driver sends the 3 following packets after the247* above one.248* These don't seem to have any effect, so we don't send them to save249* some processing time.250*251* static __u8 magic_1[32] = {0x02, 0xb4, 0x01, 0x00, 0x01};252* static __u8 magic_2[32] = {0x02, 0xb4, 0x01, 0x00, 0xff};253* static __u8 magic_3[32] = {0x02, 0xb8, 0x04, 0x00, 0x00};254*/255256err = hid_bpf_hw_output_report(hid, magic_0, sizeof(magic_0));257if (err < 0)258return err;259260return 0;261}262263SEC(HID_BPF_DEVICE_EVENT)264int BPF_PROG(ack05_fix_events, struct hid_bpf_ctx *hctx)265{266__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, PAD_REPORT_LENGTH);267int ret = 0;268269if (!data)270return 0; /* EPERM check */271272if (data[0] != VENDOR_REPORT_ID)273return 0;274275/* reconnect event */276if (data[1] == 0xf8 && data[2] == 02 && data[3] == 0x01)277HID_BPF_ASYNC_DELAYED_CALL(switch_to_raw_mode, hctx, 10);278279/* button event */280if (data[1] == RAW_PAD_REPORT_ID) {281data[0] = data[1];282if (data[7] == 0x02)283data[7] = 0xff;284ret = 8;285} else if (data[1] == RAW_BATTERY_REPORT_ID) {286data[0] = data[1];287ret = 5;288}289290return ret;291}292293HID_BPF_OPS(xppen_ack05_remote) = {294.hid_device_event = (void *)ack05_fix_events,295.hid_rdesc_fixup = (void *)ack05_fix_rdesc,296};297298SEC("syscall")299int probe(struct hid_bpf_probe_args *ctx)300{301switch (ctx->rdesc_size) {302case PAD_WIRED_DESCRIPTOR_LENGTH:303case PAD_DONGLE_DESCRIPTOR_LENGTH:304case STYLUS_DESCRIPTOR_LENGTH:305case VENDOR_DESCRIPTOR_LENGTH:306ctx->retval = 0;307break;308default:309ctx->retval = -EINVAL;310break;311}312313if (ctx->rdesc_size == VENDOR_DESCRIPTOR_LENGTH) {314struct hid_bpf_ctx *hctx = hid_bpf_allocate_context(ctx->hid);315316if (!hctx) {317ctx->retval = -EINVAL;318return 0;319}320321ctx->retval = HID_BPF_ASYNC_INIT(switch_to_raw_mode) ||322switch_to_raw_mode(hctx);323324hid_bpf_release_context(hctx);325}326327return 0;328}329330char _license[] SEC("license") = "GPL";331332333