mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-11 13:27:47 +01:00
On the 2026-09-15 and 2026-09-17 boots the wheel enumerated as 046D:C260 (PS3/PS4 selector in PS4 position, bound by hid-generic). The BPF udev rule, the ensure unit and its hotplug rule all keyed on C24F, so the unit failed once with "no G29 found" and nothing was said; 1st/3rd stayed on the firmware's bad decode until the selector was flipped a day later. - fixes/g29_wheel_mode.sh (new, sourced): classify the HID bus as native/ps4/compat/absent; root-safe desktop notification via /run/user/*/bus + sudo -u. - --ensure: absent -> exit 0; ps4/compat -> exit 1 + critical notification; native -> attach. --status prints the mode first. - g29-shifter-bpf.service: Restart=on-failure, RestartSec=5min so a notification lost before the session bus exists at boot is re-sent. - 99-g29-shifter-ensure.rules: also fires on C260. - tests/test_fix_g29_shifter.bats: 13 tests against a fake sysfs tree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NVfC2T6DyvCFqse9A99zef
80 lines
3.0 KiB
Bash
Executable File
80 lines
3.0 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# ============================================================================
|
|
# Logitech G29: which mode is the wheel in, and how to tell the desktop.
|
|
#
|
|
# Sourced by fix_g29_shifter.sh and tests/test_fix_g29_shifter.bats.
|
|
#
|
|
# The G29 has a physical PS3/PS4 selector. Only the PS3 position ends up as
|
|
# the native 046D:C24F device that hid-logitech (new-lg4ff) binds and the
|
|
# HID-BPF shifter fix targets. The PS4 position enumerates as 046D:C260:
|
|
# hid-generic, a different report layout, no kernel driver, no shifter fix.
|
|
# Nothing used to notice -- on the 2026-09-15 and 2026-09-17 boots the wheel
|
|
# came up in PS4 mode and 1st/3rd were dead all day while every layer of the
|
|
# safety net looked for C24F only. Details: ../docs/DOCS-g29-shifter.md.
|
|
# ============================================================================
|
|
|
|
[[ -n "${_G29_WHEEL_MODE_LOADED:-}" ]] && return 0
|
|
_G29_WHEEL_MODE_LOADED=1
|
|
|
|
# Overridable so tests can point at a fake sysfs tree / run dir.
|
|
G29_HID_SYSFS="${G29_HID_SYSFS:-/sys/bus/hid/devices}"
|
|
G29_RUN_USER_DIR="${G29_RUN_USER_DIR:-/run/user}"
|
|
readonly G29_PID_NATIVE="0003:046D:C24F"
|
|
readonly G29_PID_PS4="0003:046D:C260"
|
|
readonly G29_PID_COMPAT="0003:046D:C294"
|
|
readonly G29_NOTIFY_ID=29029 # fixed notify-send id: repeats replace, never stack
|
|
|
|
# Print the sysfs path of the first $1 device that has a report descriptor
|
|
# (the wheel exposes two HID interfaces; only the joystick one has one).
|
|
g29_sysfs_device() {
|
|
local dev
|
|
for dev in "$G29_HID_SYSFS/$1".*; do
|
|
if [[ -s "$dev/report_descriptor" ]]; then
|
|
echo "$dev"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# Print one of: native | ps4 | compat | absent
|
|
g29_wheel_mode() {
|
|
if g29_sysfs_device "$G29_PID_NATIVE" >/dev/null; then
|
|
echo native
|
|
elif g29_sysfs_device "$G29_PID_PS4" >/dev/null; then
|
|
echo ps4
|
|
elif g29_sysfs_device "$G29_PID_COMPAT" >/dev/null; then
|
|
echo compat
|
|
else
|
|
echo absent
|
|
fi
|
|
}
|
|
|
|
# One line a human can act on, for any non-native mode.
|
|
g29_mode_explanation() {
|
|
case "$1" in
|
|
ps4) echo "G29 is in PS4 mode (046D:C260): flip the PS3/PS4 selector on the wheel to PS3. The shifter fix, hid-logitech and FFB only work in PS3 mode." ;;
|
|
compat) echo "G29 is stuck in compatibility mode (046D:C294): hid-logitech did not switch it to native. Check 'modinfo hid_logitech', then replug." ;;
|
|
absent) echo "no G29 on the HID bus" ;;
|
|
native) echo "G29 in native mode (046D:C24F)" ;;
|
|
*) echo "unknown G29 mode '$1'" ;;
|
|
esac
|
|
}
|
|
|
|
# Critical desktop notification to every user with a session bus. This runs
|
|
# from a root oneshot at boot/hotplug, so $DISPLAY / $DBUS_SESSION_BUS_ADDRESS
|
|
# are not set -- it walks /run/user/*/bus instead. Never fails the caller.
|
|
g29_notify_desktop() {
|
|
local title="$1" body="$2" bus uid user
|
|
for bus in "$G29_RUN_USER_DIR"/*/bus; do
|
|
[[ -e "$bus" ]] || continue
|
|
uid="${bus#"$G29_RUN_USER_DIR"/}"
|
|
uid="${uid%/bus}"
|
|
user="$(id -nu "$uid" 2>/dev/null)" || continue
|
|
sudo -u "$user" DBUS_SESSION_BUS_ADDRESS="unix:path=$bus" \
|
|
notify-send -u critical -r "$G29_NOTIFY_ID" -a g29-shifter \
|
|
"$title" "$body" 2>/dev/null || true
|
|
done
|
|
}
|