mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 20:27:02 +01:00
Live on this host since 2026-09-11 but never committed. The wheel firmware decodes the shifter's hall sensor itself and this unit's top row rests at Y 180-193, below the ~195 engage threshold, so 1st/3rd often did not register. g29_shifter.bpf.c re-decodes those two gates from the raw X/Y bytes with hysteresis (engage >= 160, release <= 140) and ORs the bit in before hid-logitech parses the report; it never clears a bit, so the firmware's working gates cannot regress. fix_g29_shifter.sh builds, attaches and installs it persistently (udev-hid-bpf rule + a systemd safety-net unit pulled in on every re-enumeration); g29_shifter_capture.py records and grades raw captures per gate. Two files were over the 250-line cap: the test file is split at the record/CLI boundary with the report builders moved into a conftest fixture (the naming hook allows only test_*.py in tests/), and the grader's docstrings are tightened. No behaviour change to the installed fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SS8M5wk88nJTXxDWz61KKk
195 lines
5.6 KiB
Bash
Executable File
195 lines
5.6 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# ============================================================================
|
|
# Logitech G29 Driving Force Shifter: make 1st/3rd engage reliably (HID-BPF)
|
|
#
|
|
# Builds g29_shifter.bpf.c (same dir) against the udev-hid-bpf headers and
|
|
# attaches it to the wheel. Why and what the numbers mean: ../docs/DOCS-g29-shifter.md.
|
|
#
|
|
# fix_g29_shifter.sh build + persistent install (udev rule)
|
|
# fix_g29_shifter.sh --test build + attach until unplug/--remove only
|
|
# fix_g29_shifter.sh --remove detach from the running device
|
|
# fix_g29_shifter.sh --ensure attach only if not attached (used by systemd)
|
|
# fix_g29_shifter.sh --status show whether a program is attached
|
|
# ============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_NAME="$(basename "$0")"
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
readonly SCRIPT_NAME SCRIPT_DIR
|
|
readonly SRC="$SCRIPT_DIR/g29_shifter.bpf.c"
|
|
readonly HEADERS_REPO="https://gitlab.freedesktop.org/libevdev/udev-hid-bpf.git"
|
|
readonly HEADERS_TAG="2.3.0-20260703" # must match the installed udev-hid-bpf
|
|
# HOME is unset under systemd, and `set -u` would abort before --ensure runs.
|
|
readonly CACHE_DIR="${XDG_CACHE_HOME:-${HOME:-/var/cache}/.cache}/g29_shifter"
|
|
readonly HEADERS_DIR="$CACHE_DIR/udev-hid-bpf"
|
|
readonly BUILD_DIR="$CACHE_DIR/build"
|
|
readonly OBJ="$BUILD_DIR/g29_shifter.bpf.o"
|
|
readonly VID_PID="0003:046D:C24F"
|
|
readonly INSTALLED_OBJ="/etc/udev-hid-bpf/g29_shifter.bpf.o"
|
|
readonly INSTALLED_RULE="/etc/udev/rules.d/99-hid-bpf-g29_shifter.rules"
|
|
readonly UNIT_NAME="g29-shifter-bpf.service"
|
|
readonly UNIT_SRC_DIR="$SCRIPT_DIR/systemd"
|
|
readonly ENSURE_RULE="/etc/udev/rules.d/99-g29-shifter-ensure.rules"
|
|
readonly PACKAGES=(udev-hid-bpf bpf clang libbpf git)
|
|
|
|
MODE="install"
|
|
|
|
usage() {
|
|
sed -n '4,13p' "$0" | sed 's/^# \{0,3\}//'
|
|
exit 0
|
|
}
|
|
|
|
log() { echo "[$SCRIPT_NAME] $*"; }
|
|
|
|
install_packages() {
|
|
local -a missing=()
|
|
local pkg
|
|
for pkg in "${PACKAGES[@]}"; do
|
|
pacman -Q "$pkg" &>/dev/null || missing+=("$pkg")
|
|
done
|
|
if ((${#missing[@]})); then
|
|
log "installing: ${missing[*]}"
|
|
sudo pacman -S --needed --noconfirm "${missing[@]}"
|
|
fi
|
|
}
|
|
|
|
# The Arch package ships no headers; fetch the matching upstream tag once.
|
|
fetch_headers() {
|
|
if [[ -f "$HEADERS_DIR/src/bpf/hid_bpf_helpers.h" ]]; then
|
|
return
|
|
fi
|
|
log "fetching udev-hid-bpf headers ($HEADERS_TAG)"
|
|
rm -rf "$HEADERS_DIR"
|
|
git clone -q --depth 1 --branch "$HEADERS_TAG" "$HEADERS_REPO" "$HEADERS_DIR"
|
|
}
|
|
|
|
# Mirrors upstream src/bpf/meson.build: clang -target bpf, then a bpftool strip.
|
|
build() {
|
|
mkdir -p "$BUILD_DIR"
|
|
log "compiling $(basename "$SRC")"
|
|
clang -std=gnu11 -fno-stack-protector -O2 -target bpf -g -c \
|
|
-fms-extensions -Wno-microsoft-anon-tag "-D__$(uname -m)__" \
|
|
-I"$HEADERS_DIR/src/bpf" -idirafter /usr/include \
|
|
"$SRC" -o "$OBJ.unstripped"
|
|
bpftool gen object "$OBJ" "$OBJ.unstripped"
|
|
log "built $OBJ"
|
|
}
|
|
|
|
# The wheel exposes two HID interfaces; only the joystick one has a descriptor.
|
|
find_device() {
|
|
local dev
|
|
for dev in /sys/bus/hid/devices/"$VID_PID".*; do
|
|
if [[ -s "$dev/report_descriptor" ]]; then
|
|
echo "$dev"
|
|
return
|
|
fi
|
|
done
|
|
echo "Error: no G29 ($VID_PID) with a report descriptor found in sysfs" >&2
|
|
exit 1
|
|
}
|
|
|
|
is_attached() {
|
|
sudo udev-hid-bpf list-loaded 2>/dev/null | grep -q g29_shifter
|
|
}
|
|
|
|
# The udev rule is the primary attach path; this unit exists because a missed
|
|
# attach is silent — the wheel still works, just with the old bad decode.
|
|
install_safety_net() {
|
|
local unit="/etc/systemd/system/$UNIT_NAME"
|
|
sed "s#__SCRIPT__#$SCRIPT_DIR/$SCRIPT_NAME#" "$UNIT_SRC_DIR/$UNIT_NAME" |
|
|
sudo install -m 644 /dev/stdin "$unit"
|
|
sudo install -m 644 "$UNIT_SRC_DIR/$(basename "$ENSURE_RULE")" "$ENSURE_RULE"
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable "$UNIT_NAME"
|
|
log "safety net installed: $unit + $(basename "$ENSURE_RULE")"
|
|
}
|
|
|
|
show_status() {
|
|
local dev
|
|
dev="$(find_device)"
|
|
log "device: $dev"
|
|
if [[ -f "$INSTALLED_OBJ" && -f "$INSTALLED_RULE" ]]; then
|
|
log "persistent: $INSTALLED_OBJ + $(basename "$INSTALLED_RULE")"
|
|
else
|
|
log "persistent: not installed (run without --test)"
|
|
fi
|
|
if is_attached; then
|
|
log "attached now: yes"
|
|
else
|
|
log "attached now: NO"
|
|
fi
|
|
if systemctl is-enabled --quiet "$UNIT_NAME" 2>/dev/null; then
|
|
log "safety net: enabled"
|
|
else
|
|
log "safety net: NOT enabled"
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
local dev
|
|
case "$MODE" in
|
|
status)
|
|
show_status
|
|
;;
|
|
ensure)
|
|
if is_attached; then
|
|
log "already attached; nothing to do"
|
|
exit 0
|
|
fi
|
|
dev="$(find_device)"
|
|
if [[ -f "$INSTALLED_OBJ" ]]; then
|
|
sudo udev-hid-bpf add "$dev" "$INSTALLED_OBJ"
|
|
log "re-attached $INSTALLED_OBJ to $dev (udev rule had missed it)"
|
|
else
|
|
log "not installed; run $SCRIPT_NAME without --test first" >&2
|
|
exit 1
|
|
fi
|
|
;;
|
|
remove)
|
|
dev="$(find_device)"
|
|
sudo udev-hid-bpf --verbose remove "$dev"
|
|
log "detached from $dev"
|
|
;;
|
|
test)
|
|
install_packages
|
|
fetch_headers
|
|
build
|
|
dev="$(find_device)"
|
|
sudo udev-hid-bpf --verbose add "$dev" "$OBJ"
|
|
log "attached to $dev (non-persistent; --remove or re-plug undoes it)"
|
|
;;
|
|
install)
|
|
install_packages
|
|
fetch_headers
|
|
build
|
|
sudo udev-hid-bpf install --force "$OBJ"
|
|
sudo udevadm control --reload
|
|
dev="$(find_device)"
|
|
# Re-trigger so the new rule attaches without a re-plug.
|
|
sudo udevadm trigger --action=add "$dev"
|
|
install_safety_net
|
|
log "installed to /etc/udev-hid-bpf/ and attached to $dev"
|
|
show_status
|
|
;;
|
|
esac
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--test) MODE="test" ;;
|
|
--ensure) MODE="ensure" ;;
|
|
--remove) MODE="remove" ;;
|
|
--status) MODE="status" ;;
|
|
-h | --help) usage ;;
|
|
*)
|
|
echo "Unknown option: $1" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
main
|