// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2024 Benjamin Tissoires
*/
#include "vmlinux.h"
#include "hid_bpf.h"
#include "hid_bpf_helpers.h"
#include <bpf/bpf_tracing.h>
#define VID_MICROSOFT 0 x045e
#define PID_XBOX_ELITE_2 0 x0b22
HID_BPF_CONFIG(
HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_GENERIC, VID_MICROSOFT, PID_XBOX_ELITE_2)
);
/*
* When using the Xbox Wireless Controller Elite 2 over Bluetooth ,
* the device exports the paddles on the back of the device as a single
* bitfield value of usage " Assign Selection " .
*
* The kernel doesn ' t process the paddles usage properly and reports KEY_UNKNOWN .
*
* SDL doesn ' t know how to interpret KEY_UNKNOWN and thus ignores the paddles .
*
* Given that over USB the kernel uses BTN_TRIGGER_HAPPY [ 5 - 8 ] , we
* can tweak the report descriptor to make the kernel interpret it properly :
* - We need an application collection of gamepad ( so we have to close the current
* Consumer Control one )
* - We need to change the usage to be buttons from 0 x15 to 0 x18
*/
#define OFFSET_ASSIGN_SELECTION 211
#define ORIGINAL_RDESC_SIZE 464
const __u8 rdesc_assign_selection[] = {
0 x0a, 0 x99, 0 x00, // Usage (Media Select Security) 211
0 x15, 0 x00, // Logical Minimum (0) 214
0 x26, 0 xff, 0 x00, // Logical Maximum (255) 216
0 x95, 0 x01, // Report Count (1) 219
0 x75, 0 x04, // Report Size (4) 221
0 x81, 0 x02, // Input (Data,Var,Abs) 223
0 x15, 0 x00, // Logical Minimum (0) 225
0 x25, 0 x00, // Logical Maximum (0) 227
0 x95, 0 x01, // Report Count (1) 229
0 x75, 0 x04, // Report Size (4) 231
0 x81, 0 x03, // Input (Cnst,Var,Abs) 233
0 x0a, 0 x81, 0 x00, // Usage (Assign Selection) 235
0 x15, 0 x00, // Logical Minimum (0) 238
0 x26, 0 xff, 0 x00, // Logical Maximum (255) 240
0 x95, 0 x01, // Report Count (1) 243
0 x75, 0 x04, // Report Size (4) 245
0 x81, 0 x02, // Input (Data,Var,Abs) 247
};
/*
* we replace the above report descriptor extract
* with the one below .
* To make things equal in size , we take out a larger
* portion than just the " Assign Selection " range , because
* we need to insert a new application collection to force
* the kernel to use BTN_TRIGGER_HAPPY [ 4 - 7 ] .
*/
const __u8 fixed_rdesc_assign_selection[] = {
0 x0a, 0 x99, 0 x00, // Usage (Media Select Security) 211
0 x15, 0 x00, // Logical Minimum (0) 214
0 x26, 0 xff, 0 x00, // Logical Maximum (255) 216
0 x95, 0 x01, // Report Count (1) 219
0 x75, 0 x04, // Report Size (4) 221
0 x81, 0 x02, // Input (Data,Var,Abs) 223
/* 0x15, 0x00, */ // Logical Minimum (0) ignored
0 x25, 0 x01, // Logical Maximum (1) 225
0 x95, 0 x04, // Report Count (4) 227
0 x75, 0 x01, // Report Size (1) 229
0 x81, 0 x03, // Input (Cnst,Var,Abs) 231
0 xc0, // End Collection 233
0 x05, 0 x01, // Usage Page (Generic Desktop) 234
0 x0a, 0 x05, 0 x00, // Usage (Game Pad) 236
0 xa1, 0 x01, // Collection (Application) 239
0 x05, 0 x09, // Usage Page (Button) 241
0 x19, 0 x15, // Usage Minimum (21) 243
0 x29, 0 x18, // Usage Maximum (24) 245
/* 0x15, 0x00, */ // Logical Minimum (0) ignored
/* 0x25, 0x01, */ // Logical Maximum (1) ignored
/* 0x95, 0x01, */ // Report Size (1) ignored
/* 0x75, 0x04, */ // Report Count (4) ignored
0 x81, 0 x02, // Input (Data,Var,Abs) 247
};
_Static_assert(sizeof (rdesc_assign_selection) == sizeof (fixed_rdesc_assign_selection),
"Rdesc and fixed rdesc of different size" );
_Static_assert(sizeof (rdesc_assign_selection) + OFFSET_ASSIGN_SELECTION < ORIGINAL_RDESC_SIZE,
"Rdesc at given offset is too big" );
SEC(HID_BPF_RDESC_FIXUP)
int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
if (!data)
return 0 ; /* EPERM check */
/* Check that the device is compatible */
if (__builtin_memcmp(data + OFFSET_ASSIGN_SELECTION,
rdesc_assign_selection,
sizeof (rdesc_assign_selection)))
return 0 ;
__builtin_memcpy(data + OFFSET_ASSIGN_SELECTION,
fixed_rdesc_assign_selection,
sizeof (fixed_rdesc_assign_selection));
return 0 ;
}
HID_BPF_OPS(xbox_elite_2) = {
.hid_rdesc_fixup = (void *)hid_fix_rdesc,
};
SEC("syscall" )
int probe(struct hid_bpf_probe_args *ctx)
{
/* only bind to the keyboard interface */
ctx->retval = ctx->rdesc_size != ORIGINAL_RDESC_SIZE;
if (ctx->retval)
ctx->retval = -EINVAL;
if (__builtin_memcmp(ctx->rdesc + OFFSET_ASSIGN_SELECTION,
rdesc_assign_selection,
sizeof (rdesc_assign_selection)))
ctx->retval = -EINVAL;
return 0 ;
}
char _license[] SEC("license" ) = "GPL" ;
Messung V0.5 in Prozent C=66 H=100 G=84
¤ Dauer der Verarbeitung: 0.4 Sekunden
¤
*© Formatika GbR, Deutschland