Path: blob/master/drivers/hid/bpf/progs/Microsoft__Xbox-Elite-2.bpf.c
26285 views
// SPDX-License-Identifier: GPL-2.0-only1/* Copyright (c) 2024 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_MICROSOFT 0x045e10#define PID_XBOX_ELITE_2 0x0b221112HID_BPF_CONFIG(13HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_GENERIC, VID_MICROSOFT, PID_XBOX_ELITE_2)14);1516/*17* When using the Xbox Wireless Controller Elite 2 over Bluetooth,18* the device exports the paddles on the back of the device as a single19* bitfield value of usage "Assign Selection".20*21* The kernel doesn't process the paddles usage properly and reports KEY_UNKNOWN.22*23* SDL doesn't know how to interpret KEY_UNKNOWN and thus ignores the paddles.24*25* Given that over USB the kernel uses BTN_TRIGGER_HAPPY[5-8], we26* can tweak the report descriptor to make the kernel interpret it properly:27* - We need an application collection of gamepad (so we have to close the current28* Consumer Control one)29* - We need to change the usage to be buttons from 0x15 to 0x1830*/3132#define OFFSET_ASSIGN_SELECTION 21133#define ORIGINAL_RDESC_SIZE 4643435const __u8 rdesc_assign_selection[] = {360x0a, 0x99, 0x00, // Usage (Media Select Security) 211370x15, 0x00, // Logical Minimum (0) 214380x26, 0xff, 0x00, // Logical Maximum (255) 216390x95, 0x01, // Report Count (1) 219400x75, 0x04, // Report Size (4) 221410x81, 0x02, // Input (Data,Var,Abs) 223420x15, 0x00, // Logical Minimum (0) 225430x25, 0x00, // Logical Maximum (0) 227440x95, 0x01, // Report Count (1) 229450x75, 0x04, // Report Size (4) 231460x81, 0x03, // Input (Cnst,Var,Abs) 233470x0a, 0x81, 0x00, // Usage (Assign Selection) 235480x15, 0x00, // Logical Minimum (0) 238490x26, 0xff, 0x00, // Logical Maximum (255) 240500x95, 0x01, // Report Count (1) 243510x75, 0x04, // Report Size (4) 245520x81, 0x02, // Input (Data,Var,Abs) 24753};5455/*56* we replace the above report descriptor extract57* with the one below.58* To make things equal in size, we take out a larger59* portion than just the "Assign Selection" range, because60* we need to insert a new application collection to force61* the kernel to use BTN_TRIGGER_HAPPY[4-7].62*/63const __u8 fixed_rdesc_assign_selection[] = {640x0a, 0x99, 0x00, // Usage (Media Select Security) 211650x15, 0x00, // Logical Minimum (0) 214660x26, 0xff, 0x00, // Logical Maximum (255) 216670x95, 0x01, // Report Count (1) 219680x75, 0x04, // Report Size (4) 221690x81, 0x02, // Input (Data,Var,Abs) 22370/* 0x15, 0x00, */ // Logical Minimum (0) ignored710x25, 0x01, // Logical Maximum (1) 225720x95, 0x04, // Report Count (4) 227730x75, 0x01, // Report Size (1) 229740x81, 0x03, // Input (Cnst,Var,Abs) 231750xc0, // End Collection 233760x05, 0x01, // Usage Page (Generic Desktop) 234770x0a, 0x05, 0x00, // Usage (Game Pad) 236780xa1, 0x01, // Collection (Application) 239790x05, 0x09, // Usage Page (Button) 241800x19, 0x15, // Usage Minimum (21) 243810x29, 0x18, // Usage Maximum (24) 24582/* 0x15, 0x00, */ // Logical Minimum (0) ignored83/* 0x25, 0x01, */ // Logical Maximum (1) ignored84/* 0x95, 0x01, */ // Report Size (1) ignored85/* 0x75, 0x04, */ // Report Count (4) ignored860x81, 0x02, // Input (Data,Var,Abs) 24787};8889_Static_assert(sizeof(rdesc_assign_selection) == sizeof(fixed_rdesc_assign_selection),90"Rdesc and fixed rdesc of different size");91_Static_assert(sizeof(rdesc_assign_selection) + OFFSET_ASSIGN_SELECTION < ORIGINAL_RDESC_SIZE,92"Rdesc at given offset is too big");9394SEC(HID_BPF_RDESC_FIXUP)95int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)96{97__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);9899if (!data)100return 0; /* EPERM check */101102/* Check that the device is compatible */103if (__builtin_memcmp(data + OFFSET_ASSIGN_SELECTION,104rdesc_assign_selection,105sizeof(rdesc_assign_selection)))106return 0;107108__builtin_memcpy(data + OFFSET_ASSIGN_SELECTION,109fixed_rdesc_assign_selection,110sizeof(fixed_rdesc_assign_selection));111112return 0;113}114115HID_BPF_OPS(xbox_elite_2) = {116.hid_rdesc_fixup = (void *)hid_fix_rdesc,117};118119SEC("syscall")120int probe(struct hid_bpf_probe_args *ctx)121{122/* only bind to the keyboard interface */123ctx->retval = ctx->rdesc_size != ORIGINAL_RDESC_SIZE;124if (ctx->retval)125ctx->retval = -EINVAL;126127if (__builtin_memcmp(ctx->rdesc + OFFSET_ASSIGN_SELECTION,128rdesc_assign_selection,129sizeof(rdesc_assign_selection)))130ctx->retval = -EINVAL;131132return 0;133}134135char _license[] SEC("license") = "GPL";136137138