Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/hid/bpf/progs/Microsoft__Xbox-Elite-2.bpf.c
26285 views
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
13
HID_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
36
const __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
*/
64
const __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
95
SEC(HID_BPF_RDESC_FIXUP)
96
int 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
116
HID_BPF_OPS(xbox_elite_2) = {
117
.hid_rdesc_fixup = (void *)hid_fix_rdesc,
118
};
119
120
SEC("syscall")
121
int 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
136
char _license[] SEC("license") = "GPL";
137
138