xref: /linux/drivers/hid/bpf/progs/Thrustmaster__TCA-Yoke-Boeing.bpf.c (revision d53b8e36925256097a08d7cb749198d85cbf9b2b)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2024 Kumar Swarnam Iyer (kumar.s.iyer65@gmail.com)
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_THRUSTMASTER 0x044F
11 #define PID_TCA_YOKE_BOEING 0x0409
12 
13 HID_BPF_CONFIG(
14 	HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_THRUSTMASTER, PID_TCA_YOKE_BOEING)
15 );
16 
17 /*  The original HID descriptor of the Thrustmaster TCA Yoke Boeing joystick contains
18  *  an Input field that shows up as an axis, ABS_MISC in Linux. But it is not possible
19  *  to assign an actual physical control to this axis as they're all taken up. There
20  *  are 2 vendor-defined inputs where the Input type appears to be defined wrongly.
21  *  This bpf attempts to fix this by changing the Inputs so that it doesn't show up in
22  *  Linux at all.
23  *  This version is the short version fix that only changes 2 fields in the descriptor
24  *  instead of the whole report descriptor.
25  *  For reference, this is the original report descriptor:
26  *
27  *  0x05, 0x01,                    // Usage Page (Generic Desktop)        0
28  *  0x09, 0x04,                    // Usage (Joystick)                    2
29  *  0xa1, 0x01,                    // Collection (Application)            4
30  *  0x85, 0x01,                    //  Report ID (1)                      6
31  *  0x09, 0x39,                    //  Usage (Hat switch)                 8
32  *  0x15, 0x00,                    //  Logical Minimum (0)                10
33  *  0x25, 0x07,                    //  Logical Maximum (7)                12
34  *  0x35, 0x00,                    //  Physical Minimum (0)               14
35  *  0x46, 0x3b, 0x01,              //  Physical Maximum (315)             16
36  *  0x65, 0x14,                    //  Unit (EnglishRotation: deg)        19
37  *  0x75, 0x04,                    //  Report Size (4)                    21
38  *  0x95, 0x01,                    //  Report Count (1)                   23
39  *  0x81, 0x42,                    //  Input (Data,Var,Abs,Null)          25
40  *  0x65, 0x00,                    //  Unit (None)                        27
41  *  0x05, 0x09,                    //  Usage Page (Button)                29
42  *  0x19, 0x01,                    //  Usage Minimum (1)                  31
43  *  0x29, 0x12,                    //  Usage Maximum (18)                 33
44  *  0x15, 0x00,                    //  Logical Minimum (0)                35
45  *  0x25, 0x01,                    //  Logical Maximum (1)                37
46  *  0x75, 0x01,                    //  Report Size (1)                    39
47  *  0x95, 0x12,                    //  Report Count (18)                  41
48  *  0x81, 0x02,                    //  Input (Data,Var,Abs)               43
49  *  0x95, 0x02,                    //  Report Count (2)                   45
50  *  0x81, 0x03,                    //  Input (Cnst,Var,Abs)               47
51  *  0x05, 0x01,                    //  Usage Page (Generic Desktop)       49
52  *  0x09, 0x31,                    //  Usage (Y)                          51
53  *  0x09, 0x30,                    //  Usage (X)                          53
54  *  0x09, 0x32,                    //  Usage (Z)                          55
55  *  0x09, 0x34,                    //  Usage (Ry)                         57
56  *  0x09, 0x33,                    //  Usage (Rx)                         59
57  *  0x09, 0x35,                    //  Usage (Rz)                         61
58  *  0x15, 0x00,                    //  Logical Minimum (0)                63
59  *  0x27, 0xff, 0xff, 0x00, 0x00,  //  Logical Maximum (65535)            65
60  *  0x75, 0x10,                    //  Report Size (16)                   70
61  *  0x95, 0x06,                    //  Report Count (6)                   72
62  *  0x81, 0x02,                    //  Input (Data,Var,Abs)               74
63  *  0x06, 0xf0, 0xff,              //  Usage Page (Vendor Usage Page 0xfff0) 76
64  *  0x09, 0x59,                    //  Usage (Vendor Usage 0x59)          79
65  *  0x15, 0x00,                    //  Logical Minimum (0)                81
66  *  0x26, 0xff, 0x00,              //  Logical Maximum (255)              83
67  *  0x75, 0x08,                    //  Report Size (8)                    86
68  *  0x95, 0x01,                    //  Report Count (1)                   88
69  *  0x81, 0x02,                    //  Input (Data,Var,Abs)               90 --> Needs to be changed
70  *  0x09, 0x51,                    //  Usage (Vendor Usage 0x51)          92
71  *  0x15, 0x00,                    //  Logical Minimum (0)                94
72  *  0x26, 0xff, 0x00,              //  Logical Maximum (255)              96
73  *  0x75, 0x08,                    //  Report Size (8)                    99
74  *  0x95, 0x20,                    //  Report Count (32)                  101 --> Needs to be changed
75  *  0x81, 0x02,                    //  Input (Data,Var,Abs)               103
76  *  0x09, 0x50,                    //  Usage (Vendor Usage 0x50)          105
77  *  0x15, 0x00,                    //  Logical Minimum (0)                107
78  *  0x26, 0xff, 0x00,              //  Logical Maximum (255)              109
79  *  0x75, 0x08,                    //  Report Size (8)                    112
80  *  0x95, 0x0f,                    //  Report Count (15)                  114
81  *  0x81, 0x03,                    //  Input (Cnst,Var,Abs)               116
82  *  0x09, 0x47,                    //  Usage (Vendor Usage 0x47)          118
83  *  0x85, 0xf2,                    //  Report ID (242)                    120
84  *  0x15, 0x00,                    //  Logical Minimum (0)                122
85  *  0x26, 0xff, 0x00,              //  Logical Maximum (255)              124
86  *  0x75, 0x08,                    //  Report Size (8)                    127
87  *  0x95, 0x3f,                    //  Report Count (63)                  129
88  *  0xb1, 0x02,                    //  Feature (Data,Var,Abs)             131
89  *  0x09, 0x48,                    //  Usage (Vendor Usage 0x48)          133
90  *  0x85, 0xf3,                    //  Report ID (243)                    135
91  *  0x15, 0x00,                    //  Logical Minimum (0)                137
92  *  0x26, 0xff, 0x00,              //  Logical Maximum (255)              139
93  *  0x75, 0x08,                    //  Report Size (8)                    142
94  *  0x95, 0x3f,                    //  Report Count (63)                  144
95  *  0xb1, 0x02,                    //  Feature (Data,Var,Abs)             146
96  *  0xc0,                          // End Collection                      148
97  */
98 
99 SEC(HID_BPF_RDESC_FIXUP)
100 int BPF_PROG(hid_fix_rdesc_tca_yoke, struct hid_bpf_ctx *hctx)
101 {
102 	const int expected_length = 148;
103 
104 	if (hctx->size != expected_length)
105 		return 0;
106 
107 	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
108 
109 	if (!data)
110 		return 0; /* EPERM */
111 
112 	/* Safety check, our probe() should take care of this though */
113 	if (data[1] != 0x01 /* Generic Desktop */ || data[3] != 0x04 /* Joystick */)
114 		return 0;
115 
116 	/* The report descriptor sets incorrect Input items in 2 places, resulting in a
117 	 * non-existing axis showing up.
118 	 * This change sets the correct Input which prevents the axis from showing up in Linux.
119 	 */
120 
121 	if (data[90] == 0x81 && /* Input */
122 	    data[103] == 0x81) { /* Input */
123 		data[91] = 0x03; /* Input set to 0x03 Constant, Variable Absolute */
124 		data[104] = 0x03; /* Input set to 0X03 Constant, Variable Absolute */
125 	}
126 
127 	return 0;
128 }
129 
130 HID_BPF_OPS(tca_yoke) = {
131 	.hid_rdesc_fixup = (void *)hid_fix_rdesc_tca_yoke,
132 };
133 
134 SEC("syscall")
135 int probe(struct hid_bpf_probe_args *ctx)
136 {
137 	/* ensure the kernel isn't fixed already */
138 	if (ctx->rdesc[91] != 0x02) /* Input for 0x59 Usage type has changed */
139 		ctx->retval = -EINVAL;
140 
141 	return 0;
142 }
143 
144 char _license[] SEC("license") = "GPL";
145