Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Benjamin Tissoires and committed by
Jiri Kosina
029dff1c 0412be1b

+86
+86
drivers/hid/bpf/progs/Logitech__SpaceNavigator.bpf.c
··· 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";