FILE
Microsoft__Xbox-Elite-2.bpf.c
File preview with syntax highlighting.
linux-6.19.14/drivers/hid/bpf/progs/Microsoft__Xbox-Elite-2.bpf.c
1// SPDX-License-Identifier: GPL-2.0-only 2/* Copyright (c) 2024 Benjamin Tissoires 3 */ 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_MICROSOFT 0x045e 11#define PID_XBOX_ELITE_2 0x0b22 12 13HID_BPF_CONFIG( 14 HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_GENERIC, VID_MICROSOFT, PID_XBOX_ELITE_2) 15); 16 17/* 18 * When using the Xbox Wireless Controller Elite 2 over Bluetooth, 19 * the device exports the paddles on the back of the device as a single 20 * bitfield value of usage "Assign Selection". 21 * 22 * The kernel doesn't process the paddles usage properly and reports KEY_UNKNOWN. 23 * 24 * SDL doesn't know how to interpret KEY_UNKNOWN and thus ignores the paddles. 25 * 26 * Given that over USB the kernel uses BTN_TRIGGER_HAPPY[5-8], we 27 * can tweak the report descriptor to make the kernel interpret it properly: 28 * - We need an application collection of gamepad (so we have to close the current 29 * Consumer Control one) 30 * - We need to change the usage to be buttons from 0x15 to 0x18 31 */ 32 33#define OFFSET_ASSIGN_SELECTION 211 34#define ORIGINAL_RDESC_SIZE 464 35 36const __u8 rdesc_assign_selection[] = { 37 0x0a, 0x99, 0x00, // Usage (Media Select Security) 211 38 0x15, 0x00, // Logical Minimum (0) 214 39 0x26, 0xff, 0x00, // Logical Maximum (255) 216 40 0x95, 0x01, // Report Count (1) 219 41 0x75, 0x04, // Report Size (4) 221 42 0x81, 0x02, // Input (Data,Var,Abs) 223 43 0x15, 0x00, // Logical Minimum (0) 225 44 0x25, 0x00, // Logical Maximum (0) 227 45 0x95, 0x01, // Report Count (1) 229 46 0x75, 0x04, // Report Size (4) 231 47 0x81, 0x03, // Input (Cnst,Var,Abs) 233 48 0x0a, 0x81, 0x00, // Usage (Assign Selection) 235 49 0x15, 0x00, // Logical Minimum (0) 238 50 0x26, 0xff, 0x00, // Logical Maximum (255) 240 51 0x95, 0x01, // Report Count (1) 243 52 0x75, 0x04, // Report Size (4) 245 53 0x81, 0x02, // Input (Data,Var,Abs) 247 54}; 55 56/* 57 * we replace the above report descriptor extract 58 * with the one below. 59 * To make things equal in size, we take out a larger 60 * portion than just the "Assign Selection" range, because 61 * we need to insert a new application collection to force 62 * the kernel to use BTN_TRIGGER_HAPPY[4-7]. 63 */ 64const __u8 fixed_rdesc_assign_selection[] = { 65 0x0a, 0x99, 0x00, // Usage (Media Select Security) 211 66 0x15, 0x00, // Logical Minimum (0) 214 67 0x26, 0xff, 0x00, // Logical Maximum (255) 216 68 0x95, 0x01, // Report Count (1) 219 69 0x75, 0x04, // Report Size (4) 221 70 0x81, 0x02, // Input (Data,Var,Abs) 223 71 /* 0x15, 0x00, */ // Logical Minimum (0) ignored 72 0x25, 0x01, // Logical Maximum (1) 225 73 0x95, 0x04, // Report Count (4) 227 74 0x75, 0x01, // Report Size (1) 229 75 0x81, 0x03, // Input (Cnst,Var,Abs) 231 76 0xc0, // End Collection 233 77 0x05, 0x01, // Usage Page (Generic Desktop) 234 78 0x0a, 0x05, 0x00, // Usage (Game Pad) 236 79 0xa1, 0x01, // Collection (Application) 239 80 0x05, 0x09, // Usage Page (Button) 241 81 0x19, 0x15, // Usage Minimum (21) 243 82 0x29, 0x18, // Usage Maximum (24) 245 83 /* 0x15, 0x00, */ // Logical Minimum (0) ignored 84 /* 0x25, 0x01, */ // Logical Maximum (1) ignored 85 /* 0x95, 0x01, */ // Report Size (1) ignored 86 /* 0x75, 0x04, */ // Report Count (4) ignored 87 0x81, 0x02, // Input (Data,Var,Abs) 247 88}; 89 90_Static_assert(sizeof(rdesc_assign_selection) == sizeof(fixed_rdesc_assign_selection), 91 "Rdesc and fixed rdesc of different size"); 92_Static_assert(sizeof(rdesc_assign_selection) + OFFSET_ASSIGN_SELECTION < ORIGINAL_RDESC_SIZE, 93 "Rdesc at given offset is too big"); 94 95SEC(HID_BPF_RDESC_FIXUP) 96int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx) 97{ 98 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */); 99 100 if (!data) 101 return 0; /* EPERM check */ 102 103 /* Check that the device is compatible */ 104 if (__builtin_memcmp(data + OFFSET_ASSIGN_SELECTION, 105 rdesc_assign_selection, 106 sizeof(rdesc_assign_selection))) 107 return 0; 108 109 __builtin_memcpy(data + OFFSET_ASSIGN_SELECTION, 110 fixed_rdesc_assign_selection, 111 sizeof(fixed_rdesc_assign_selection)); 112 113 return 0; 114} 115 116HID_BPF_OPS(xbox_elite_2) = { 117 .hid_rdesc_fixup = (void *)hid_fix_rdesc, 118}; 119 120SEC("syscall") 121int probe(struct hid_bpf_probe_args *ctx) 122{ 123 /* only bind to the keyboard interface */ 124 ctx->retval = ctx->rdesc_size != ORIGINAL_RDESC_SIZE; 125 if (ctx->retval) 126 ctx->retval = -EINVAL; 127 128 if (__builtin_memcmp(ctx->rdesc + OFFSET_ASSIGN_SELECTION, 129 rdesc_assign_selection, 130 sizeof(rdesc_assign_selection))) 131 ctx->retval = -EINVAL; 132 133 return 0; 134} 135 136char _license[] SEC("license") = "GPL";
~/
Read-only file preview.