Skip to content

Commit 029dff1

Browse files
Benjamin TissoiresJiri Kosina
authored andcommitted
HID: bpf: Add fixup for Logitech SpaceNavigator variants
The 3Dconnexion SpaceNavigator HID report descriptor declares its axis data to be "relative" when it is actually "absolute". This quirk was addressed in the kernel in 2.6.33, but some SpaceNavigator variants have a slightly different report descriptor whose axis input items are at different offsets than those assumed by the kernel fixup. Add a BPF fixup to handle both sets of offsets for known SpaceNavigator variants if the descriptor has not already been fixed by the kernel. Signed-off-by: Curran Muhlberger <curran@cs.cornell.edu> Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests/181 Signed-off-by: Benjamin Tissoires <bentiss@kernel.org> Signed-off-by: Jiri Kosina <jkosina@suse.com>
1 parent 0412be1 commit 029dff1

1 file changed

Lines changed: 86 additions & 0 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/* Copyright (c) 2025 Curran Muhlberger
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_LOGITECH 0x046D
11+
#define PID_SPACENAVIGATOR 0xC626
12+
13+
HID_BPF_CONFIG(
14+
HID_DEVICE(BUS_USB, HID_GROUP_ANY, VID_LOGITECH, PID_SPACENAVIGATOR)
15+
);
16+
17+
/*
18+
* The 3Dconnexion SpaceNavigator 3D Mouse is a multi-axis controller with 6
19+
* axes (grouped as X,Y,Z and Rx,Ry,Rz). Axis data is absolute, but the report
20+
* descriptor erroneously declares it to be relative. We fix the report
21+
* descriptor to mark both axis collections as absolute.
22+
*
23+
* The kernel attempted to fix this in commit 24985cf68612 (HID: support
24+
* Logitech/3DConnexion SpaceTraveler and SpaceNavigator), but the descriptor
25+
* data offsets are incorrect for at least some SpaceNavigator units.
26+
*/
27+
28+
SEC(HID_BPF_RDESC_FIXUP)
29+
int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
30+
{
31+
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
32+
33+
if (!data)
34+
return 0; /* EPERM check */
35+
36+
/* Offset of Input item in X,Y,Z and Rx,Ry,Rz collections for all known
37+
* firmware variants.
38+
* - 2009 model: X,Y,Z @ 32-33, Rx,Ry,Rz @ 49-50 (fixup originally
39+
* applied in kernel)
40+
* - 2016 model (size==228): X,Y,Z @ 36-37, Rx,Ry,Rz @ 53-54
41+
*
42+
* The descriptor size of the 2009 model is not known, and there is evidence
43+
* for at least two other variants (with sizes 202 & 217) besides the 2016
44+
* model, so we try all known offsets regardless of descriptor size.
45+
*/
46+
const u8 offsets[] = {32, 36, 49, 53};
47+
48+
for (size_t idx = 0; idx < ARRAY_SIZE(offsets); idx++) {
49+
u8 offset = offsets[idx];
50+
51+
/* if Input (Data,Var,Rel) , make it Input (Data,Var,Abs) */
52+
if (data[offset] == 0x81 && data[offset + 1] == 0x06)
53+
data[offset + 1] = 0x02;
54+
}
55+
56+
return 0;
57+
}
58+
59+
HID_BPF_OPS(logitech_spacenavigator) = {
60+
.hid_rdesc_fixup = (void *)hid_fix_rdesc,
61+
};
62+
63+
SEC("syscall")
64+
int probe(struct hid_bpf_probe_args *ctx)
65+
{
66+
/* Ensure report descriptor size matches one of the known variants. */
67+
if (ctx->rdesc_size != 202 &&
68+
ctx->rdesc_size != 217 &&
69+
ctx->rdesc_size != 228) {
70+
ctx->retval = -EINVAL;
71+
return 0;
72+
}
73+
74+
/* Check whether the kernel has already applied the fix. */
75+
if ((ctx->rdesc[32] == 0x81 && ctx->rdesc[33] == 0x02 &&
76+
ctx->rdesc[49] == 0x81 && ctx->rdesc[50] == 0x02) ||
77+
(ctx->rdesc[36] == 0x81 && ctx->rdesc[37] == 0x02 &&
78+
ctx->rdesc[53] == 0x81 && ctx->rdesc[54] == 0x02))
79+
ctx->retval = -EINVAL;
80+
else
81+
ctx->retval = 0;
82+
83+
return 0;
84+
}
85+
86+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)