From cf89b15573007d17917d1278dac4242d60a4dcef Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 12 May 2026 14:31:23 -0300 Subject: [PATCH] audio: improve tethys uac recovery hints --- .../run_tethys_downstream_audio_sanity.sh | 25 +++++++++++++++---- ...tethys_downstream_audio_manual_contract.rs | 6 ++++- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/scripts/manual/run_tethys_downstream_audio_sanity.sh b/scripts/manual/run_tethys_downstream_audio_sanity.sh index f41419d..8eca0d6 100755 --- a/scripts/manual/run_tethys_downstream_audio_sanity.sh +++ b/scripts/manual/run_tethys_downstream_audio_sanity.sh @@ -25,14 +25,28 @@ run_pactl() { timeout "${PULSE_TIMEOUT}s" pactl "$@"; } run_wpctl() { timeout "${PULSE_TIMEOUT}s" wpctl "$@"; } print_rebind_hint() { - local card iface driver - card=$(aplay -l 2>/dev/null | awk '/Lesavka|Composite|UAC2|USB Audio/ {if (match($0, /card [0-9]+/)) {print substr($0, RSTART + 5, RLENGTH - 5); exit}}' || true) + local card iface driver device_root audio_ifaces + card=$({ + aplay -l 2>/dev/null | awk '/Lesavka|Composite|UAC2|USB Audio/ {if (match($0, /card [0-9]+/)) {print substr($0, RSTART + 5, RLENGTH - 5); exit}}' + awk '/Lesavka|Composite|UAC2|USB Device 0x1d6b:0x104|1d6b:0x104/ {print $1; exit}' /proc/asound/cards 2>/dev/null + } | awk 'NF {print; exit}' || true) if [[ -n ${card:-} && -e /sys/class/sound/card${card}/device ]]; then iface=$(basename "$(readlink -f "/sys/class/sound/card${card}/device")") driver=$(basename "$(readlink -f "/sys/class/sound/card${card}/device/driver" 2>/dev/null || true)") - if [[ -n $iface && -n $driver ]]; then - echo "Tethys root-level soft rebind, if the user audio graph is wedged:" >&2 - echo " sudo sh -c 'echo -n \"$iface\" > /sys/bus/usb/drivers/$driver/unbind; sleep 2; echo -n \"$iface\" > /sys/bus/usb/drivers/$driver/bind'" >&2 + device_root=${iface%:*} + if [[ -n $iface && -n $driver && -n $device_root ]]; then + audio_ifaces=$(for candidate in "/sys/bus/usb/devices/${device_root}":*; do + [[ -e $candidate/bInterfaceClass && -e $candidate/bInterfaceSubClass ]] || continue + [[ $(cat "$candidate/bInterfaceClass" 2>/dev/null) == "01" ]] || continue + case "$(cat "$candidate/bInterfaceSubClass" 2>/dev/null)" in + 01|02) basename "$candidate" ;; + esac + done | sort -V | xargs echo) + [[ -n $audio_ifaces ]] || audio_ifaces=$iface + echo "Tethys root-level audio-interface rebind, if the UAC graph is wedged:" >&2 + echo " sudo sh -c 'for i in $audio_ifaces; do echo -n \"\$i\" > /sys/bus/usb/drivers/$driver/unbind 2>/dev/null || true; done; sleep 2; for i in $audio_ifaces; do echo -n \"\$i\" > /sys/bus/usb/drivers/$driver/bind 2>/dev/null || true; done'" >&2 + echo "Tethys full composite re-enumeration, if audio interfaces bind but ALSA creates no PCM:" >&2 + echo " sudo sh -c 'echo 0 > /sys/bus/usb/devices/$device_root/authorized; sleep 3; echo 1 > /sys/bus/usb/devices/$device_root/authorized'" >&2 fi fi } @@ -52,6 +66,7 @@ sink_line=$(run_pactl list short sinks | awk '/alsa_output\.usb-Lesavka|Lesavka| if [[ -z ${sink_line:-} ]]; then fail "no Lesavka USB speaker sink is visible to PipeWire/Pulse" run_pactl list short sinks >&2 || true + print_rebind_hint exit 1 fi sink_name=$(awk '{print $2}' <<<"$sink_line") diff --git a/tests/manual/audio/tethys_downstream_audio_manual_contract.rs b/tests/manual/audio/tethys_downstream_audio_manual_contract.rs index ddb8f20..09a9bb1 100644 --- a/tests/manual/audio/tethys_downstream_audio_manual_contract.rs +++ b/tests/manual/audio/tethys_downstream_audio_manual_contract.rs @@ -36,7 +36,11 @@ fn tethys_audio_probe_keeps_repair_explicit_and_non_sudo_by_default() { "LESAVKA_TETHYS_AUDIO_REPAIR:-0", "LESAVKA_TETHYS_AUDIO_RESTART:-0", "restart Tethys user audio or replug/rebind", - "Tethys root-level soft rebind, if the user audio graph is wedged", + "Tethys root-level audio-interface rebind, if the UAC graph is wedged", + "bInterfaceSubClass", + "/proc/asound/cards", + "print_rebind_hint", + "Tethys full composite re-enumeration, if audio interfaces bind but ALSA creates no PCM", ] { assert!( SCRIPT.contains(expected),