Files
b0hemia e0bc4785e9 diskOS installer: initial public beta
Flashes the diskOS custom UI onto the FiiO Snowsky Disc over Ingenic mask-ROM USB,
building the image from your own stock firmware. Runs from source via install.sh.
2026-08-26 15:26:14 +10:00

89 lines
3.0 KiB
Bash
Executable File

#!/bin/sh
# diskOS dev/recovery: USB CDC-ACM serial console on the device USB-C port.
# Host enumerates 0525:a4a7 -> /dev/ttyACM0 -> root shell (raw, no login).
#
# Robust against the failure that killed the previous attempt: the stock
# serial_config.sh binds the UDC exactly once and never retries, so a UDC that
# registers (or a host that plugs in) a moment later was missed. This runs a
# persistent supervisor: (re)bind whenever a UDC is present, keep a shell alive
# on ttyGS0. All steps best-effort + logged to a persistent (rw) partition.
LOG=/usr/data/fiio/usbserial.log
G=/sys/kernel/config/usb_gadget/serial_demo
log() { echo "$(date 2>/dev/null) $*" >> "$LOG" 2>/dev/null; }
build_gadget() {
[ -d /sys/kernel/config/usb_gadget ] || mount -t configfs none /sys/kernel/config 2>/dev/null
[ -d "$G" ] && return 0
mkdir -p "$G" || return 1
echo 0x0525 > "$G/idVendor"
echo 0xa4a7 > "$G/idProduct"
echo 0x0200 > "$G/bcdUSB"
echo 0x2400 > "$G/bcdDevice"
echo 0x02 > "$G/bDeviceClass"
echo 0x00 > "$G/bDeviceSubClass"
echo 0x00 > "$G/bDeviceProtocol"
mkdir -p "$G/strings/0x409"
echo "INGENIC" > "$G/strings/0x409/manufacturer"
echo "diskOS Serial" > "$G/strings/0x409/product"
echo "diskos-serial" > "$G/strings/0x409/serialnumber"
mkdir -p "$G/configs/c.1/strings/0x409"
echo 120 > "$G/configs/c.1/MaxPower"
echo 0x80 > "$G/configs/c.1/bmAttributes"
echo "serial" > "$G/configs/c.1/strings/0x409/configuration"
mkdir -p "$G/functions/acm.0"
ln -sf "$G/functions/acm.0" "$G/configs/c.1/" 2>/dev/null
log "gadget built"
}
# Best-effort nudge into peripheral mode. dr_mode=otg already auto-switches to
# device when plugged into a host (CC/pin), so this is only a safety net. Only
# the unambiguous "device" token is written -- never anything that could select
# host mode and break enumeration.
force_device_mode() {
for f in /sys/devices/platform/*otg*/dwc2_mode \
/sys/devices/platform/*otg*/dwc2_force_dr_mode \
/sys/devices/platform/*/13500000.otg/dwc2_mode; do
[ -e "$f" ] || continue
echo device > "$f" 2>/dev/null && log "wrote device-mode to $f"
done
}
bound() { [ -n "$(cat "$G/UDC" 2>/dev/null)" ]; }
bind_when_ready() {
udc=$(ls /sys/class/udc/ 2>/dev/null | head -n1)
[ -n "$udc" ] || return 1
echo "$udc" > "$G/UDC" 2>/dev/null && { log "bound UDC=$udc"; return 0; }
return 1
}
# Delegate the ttyGS0 shell to diskos-debug (the SINGLE owner - fuser-guarded + self-respawning).
# Two independent shell respawners on one tty is the classic "answers once then goes silent" wedge,
# so this script no longer binds its own shell; it only builds the gadget and asks diskos-debug to
# attach the one shell. Idempotent: serial-on is a no-op if a shell already owns ttyGS0.
DEBUG=/usr/project/diskos-debug.sh
ensure_shell() {
[ -c /dev/ttyGS0 ] || return 0
[ -x "$DEBUG" ] && "$DEBUG" serial-on >/dev/null 2>&1
}
main() {
: > "$LOG" 2>/dev/null
log "S99usbserial start"
build_gadget
force_device_mode
while true; do
bound || bind_when_ready
ensure_shell
sleep 2
done
}
case "$1" in
start) main & ;;
*) exit 1 ;;
esac
exit 0