Add one-shot recovery script for post-intervention testing

Waits for bare FX2 or SkyWalker-1 enumeration, loads firmware,
then runs a full diagnostic: I2C gateway, BOOT_8PSK, signal
monitor, and config status readback.

Usage: bash tools/recover.sh
This commit is contained in:
Ryan Malloy 2026-02-20 12:05:28 -07:00
parent 33b6aa92db
commit 2b87228e29

152
tools/recover.sh Executable file
View File

@ -0,0 +1,152 @@
#!/bin/bash
# Quick recovery script — run after physical intervention (SDA pull-up / SOIC clip)
# Waits for bare FX2 or SkyWalker enumeration, loads firmware, runs full test
set -euo pipefail
TOOLS="$(cd "$(dirname "$0")" && pwd)"
FW="$TOOLS/../firmware/build/skywalker1.ihx"
if [ ! -f "$FW" ]; then
echo "ERROR: Firmware not found at $FW"
echo "Run: cd firmware && make"
exit 1
fi
echo "=== SkyWalker-1 Recovery ==="
echo "Waiting for device to appear... (plug in now, Ctrl+C to stop)"
echo ""
# Poll for up to 60 seconds
for i in $(seq 1 120); do
DEV=$(lsusb -d 09c0:0203 2>/dev/null || true)
BARE=$(lsusb -d 04b4:8613 2>/dev/null || true)
if [ -n "$DEV" ]; then
echo "FOUND SkyWalker-1 (09c0:0203): $DEV"
echo "Device already has firmware — skipping load"
break
elif [ -n "$BARE" ]; then
echo "FOUND bare FX2 (04b4:8613): $BARE"
sleep 0.5
echo "Loading firmware..."
python3 "$TOOLS/fw_load.py" load "$FW"
echo "Waiting 3s for re-enumeration..."
sleep 3
# Verify it came back
DEV=$(lsusb -d 09c0:0203 2>/dev/null || true)
if [ -z "$DEV" ]; then
echo "WARNING: Device did not re-enumerate as SkyWalker-1"
echo "Check dmesg for errors"
exit 1
fi
echo "Re-enumerated: $DEV"
break
fi
[ $((i % 20)) -eq 0 ] && printf "."
sleep 0.5
done
# If we got here without finding anything
DEV=$(lsusb -d 09c0:0203 2>/dev/null || true)
if [ -z "$DEV" ]; then
echo ""
echo "Timeout — no device detected in 60s"
echo "Check physical connections and try again"
exit 1
fi
echo ""
echo "--- Running post-recovery test ---"
python3 -c "
import usb.core, usb.util, time, sys
dev = usb.core.find(idVendor=0x09C0, idProduct=0x0203)
if not dev:
print('Device not found'); sys.exit(1)
print(f'Product: {dev.product}')
# Detach kernel drivers
for c in dev:
for i in c:
if dev.is_kernel_driver_active(i.bInterfaceNumber):
dev.detach_kernel_driver(i.bInterfaceNumber)
try: dev.set_configuration()
except: pass
A0 = 0xA0
VIN = usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_IN
# Read FX2 registers
def read_reg(addr, n=1):
return bytes(dev.ctrl_transfer(VIN, A0, addr, 0, n, 2000))
cpucs = read_reg(0xE600)[0]
i2cs = read_reg(0xE678)[0]
print(f'CPUCS=0x{cpucs:02X} I2CS=0x{i2cs:02X}')
# Test BCM3440 I2C gateway
print()
print('--- I2C Gateway Test ---')
try:
d = dev.ctrl_transfer(VIN, 0x84, 0x10, 0xA7, 1, 3000)
print(f'BCM3440 gateway (A7): 0x{d[0]:02X} -- OK')
except Exception as e:
print(f'BCM3440 gateway: FAIL ({e})')
# Boot BCM4500 (wValue=1 = full boot, wIndex=0 = normal)
print()
print('--- BOOT_8PSK (wValue=1) ---')
try:
d = dev.ctrl_transfer(VIN, 0x89, 1, 0, 3, 15000)
config = d[0]
stage = d[1]
print(f'config_status=0x{config:02X} boot_stage=0x{stage:02X}')
if config & 0x03 == 0x03:
print('Boot: SUCCESS (started + fw_loaded)')
elif stage == 0xFF:
print('Boot: SUCCESS (stage=0xFF = complete)')
else:
print(f'Boot: INCOMPLETE (check stage 0x{stage:02X})')
except Exception as e:
print(f'Boot: FAIL ({e})')
# Wait for demod to settle
time.sleep(3)
# Signal check
print()
print('--- Signal Monitor ---')
try:
d = dev.ctrl_transfer(VIN, 0x90, 0, 0, 8, 3000)
snr = (d[0] << 8) | d[1]
agc1 = (d[2] << 8) | d[3]
agc2 = (d[4] << 8) | d[5]
lock = d[6]
status = d[7]
print(f'SNR={snr} AGC1={agc1} AGC2={agc2} Lock=0x{lock:02X} Status=0x{status:02X}')
except Exception as e:
print(f'Signal: FAIL ({e})')
# GET_8PSK_CONFIG
print()
print('--- Config Status ---')
try:
d = dev.ctrl_transfer(VIN, 0x80, 0, 0, 1, 2000)
print(f'config_status: 0x{d[0]:02X}')
flags = []
if d[0] & 0x01: flags.append('STARTED')
if d[0] & 0x02: flags.append('FW_LOADED')
if d[0] & 0x04: flags.append('INTERSIL')
if d[0] & 0x08: flags.append('DVB_MODE')
if d[0] & 0x80: flags.append('ARMED')
print(f'Flags: {\" | \".join(flags) if flags else \"(none)\"}')
except Exception as e:
print(f'Config: FAIL ({e})')
print()
print('=== Recovery test complete ===')
" 2>&1
echo ""
echo "=== Done ==="