diff --git a/scripts/daemon/lesavka-core.sh b/scripts/daemon/lesavka-core.sh index 5a1ad6e..7870d36 100644 --- a/scripts/daemon/lesavka-core.sh +++ b/scripts/daemon/lesavka-core.sh @@ -8,6 +8,25 @@ set -euo pipefail log() { printf '[lesavka-core] %s\n' "$*"; } cleanup() { echo "" >"$G/UDC" 2>/dev/null || true; } +DISABLE_UAC=${LESAVKA_DISABLE_UAC:-} +DISABLE_UVC=${LESAVKA_DISABLE_UVC:-} +UVC_FALLBACK=${LESAVKA_UVC_FALLBACK:-1} + +wait_for_enum() { + local tries=${1:-50} # 50 x 100ms = 5s + UDC_STATE="unknown" + UDC_SPEED="unknown" + for ((i=0; i/dev/null || echo "unknown") + UDC_SPEED=$(cat "/sys/class/udc/$UDC/current_speed" 2>/dev/null || echo "unknown") + if [[ "$UDC_STATE" != "not attached" && "$UDC_STATE" != "unknown" ]]; then + return 0 + fi + sleep 0.1 + done + return 1 +} + exec 2> >(tee -a /tmp/lesavka-core.debug.$(date +%s).log) set -x echo "[lesavka-core] running: $0 (sha1sum=$(sha1sum "$0" | cut -d' ' -f1))" @@ -103,114 +122,102 @@ printf '\x05\x01\x09\x02\xa1\x01\x09\x01\xa1\x00'\ '\x05\x01\x09\x30\x09\x31\x09\x38\x15\x81\x25\x7f\x75\x08\x95\x03\x81\x06'\ '\xc0\xc0' >"$G/functions/hid.usb1/report_desc" -# ---------- UAC2 function - speaker + mic, 2×48 kHz stereo --------- -mkdir -p "$G/functions/uac2.usb0" -U="$G/functions/uac2.usb0" -# Playback (speaker) -echo 0x3 >"$U/p_chmask" # L+R -echo 48000 >"$U/p_srate" -echo 2 >"$U/p_ssize" # 16 bit -# Capture (microphone) -echo 0x3 >"$U/c_chmask" -echo 48000 >"$U/c_srate" -echo 2 >"$U/c_ssize" -# Optional: allocate a few extra request buffers -echo 32 >"$U/req_number" 2>/dev/null || true +if [[ -z $DISABLE_UAC ]]; then + # ---------- UAC2 function - speaker + mic, 2×48 kHz stereo --------- + mkdir -p "$G/functions/uac2.usb0" + U="$G/functions/uac2.usb0" + # Playback (speaker) + echo 0x3 >"$U/p_chmask" # L+R + echo 48000 >"$U/p_srate" + echo 2 >"$U/p_ssize" # 16 bit + # Capture (microphone) + echo 0x3 >"$U/c_chmask" + echo 48000 >"$U/c_srate" + echo 2 >"$U/c_ssize" + # Optional: allocate a few extra request buffers + echo 32 >"$U/req_number" 2>/dev/null || true +else + log "🔇 UAC2 disabled (LESAVKA_DISABLE_UAC set)" +fi -# ----------------------- UVC function (usb‑video) ------------------ -mkdir -p "$G/functions/uvc.usb0" -F="$G/functions/uvc.usb0" +if [[ -z $DISABLE_UVC ]]; then + # ----------------------- UVC function (usb‑video) ------------------ + mkdir -p "$G/functions/uvc.usb0" + F="$G/functions/uvc.usb0" -# ── 1. FORMAT DESCRIPTOR (uncompressed YUY2, 16 bpp) ────────────── -mkdir -p "$F/streaming/uncompressed/u" -# GUID = {59555932-0000-0010-8000-00aa00389b71} (“YUY2”) little‑endian -printf '\x59\x55\x59\x32\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71' \ - >"$F/streaming/uncompressed/u/guidFormat" -echo 16 >"$F/streaming/uncompressed/u/bBitsPerPixel" +# ── 1. FORMAT DESCRIPTOR (uncompressed YUY2, 16 bpp) ────────────── + mkdir -p "$F/streaming/uncompressed/yuyv" +# GUID = {59555932-0000-0010-8000-00aa00389b71} (“YUY2”) little-endian + printf '\x59\x55\x59\x32\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71' \ + >"$F/streaming/uncompressed/yuyv/guidFormat" + echo 16 >"$F/streaming/uncompressed/yuyv/bBitsPerPixel" -# ── 2. FRAME DESCRIPTOR (index 1 @ 30 fps) ──────────────────────── -mkdir -p "$F/streaming/uncompressed/u/f1" -echo 1280 >"$F/streaming/uncompressed/u/f1/wWidth" -echo 720 >"$F/streaming/uncompressed/u/f1/wHeight" -echo 1843200 >"$F/streaming/uncompressed/u/f1/dwMaxVideoFrameBufferSize" -echo 333333 >"$F/streaming/uncompressed/u/f1/dwDefaultFrameInterval" # 30 fps -echo 333333 >"$F/streaming/uncompressed/u/f1/dwFrameInterval" +# ── 2. FRAME DESCRIPTOR (720p @ 30 fps) ─────────────────────────── + mkdir -p "$F/streaming/uncompressed/yuyv/720p" + echo 1280 >"$F/streaming/uncompressed/yuyv/720p/wWidth" + echo 720 >"$F/streaming/uncompressed/yuyv/720p/wHeight" + echo 1843200 >"$F/streaming/uncompressed/yuyv/720p/dwMaxVideoFrameBufferSize" + echo 333333 >"$F/streaming/uncompressed/yuyv/720p/dwDefaultFrameInterval" + cat <<'EOF' >"$F/streaming/uncompressed/yuyv/720p/dwFrameInterval" +333333 +EOF -# ── 3. REQUIRED HEADER LINKS (absolute‑paths, no “1”) ────────────── -header_h="$F/streaming/header/h" # convenience variables -fmt_dir="$F/streaming/uncompressed/u" +# ── 3. REQUIRED HEADER LINKS (per UVC gadget docs) ──────────────── + mkdir -p "$F/streaming/header/h" + pushd "$F/streaming/header/h" >/dev/null + ln -s ../../uncompressed/yuyv yuyv + popd >/dev/null -mkdir -p "$header_h" - -# wait until the kernel has rebuilt the directory and added its attribute -# files (bmInfo is always created by the driver) -for _ in {1..50}; do - [ -e "$header_h/bmInfo" ] && break - sleep 0.010 # max 0.5 s total -done - -# ABSOLUTE symlink → no relative elements, name is “fmt” (not “1”) -ln -sf "$fmt_dir" "$header_h/fmt" -# echo 1 >"$header_h/bNumFormats" -# echo 0 >"$header_h/bmInfo" - -# per‑speed class directories (absolute links) -for s in fs hs ss; do - mkdir -p "$F/streaming/class/$s" - ln -sf "$header_h" "$F/streaming/class/$s/h" -done + for s in fs hs ss; do + mkdir -p "$F/streaming/class/$s" + pushd "$F/streaming/class/$s" >/dev/null + ln -s ../../header/h h + popd >/dev/null + done # ── 4. Video‑Control interface ───────────────────────────────────── -set +e # relax errors for configfs quirks -mkdir -p "$F/control/header/h" # real dir – mandatory -mkdir -p "$F/control/class" # parent once -mkdir -p "$F/control/class/fs" "$F/control/class/hs" "$F/control/class/ss" 2>/dev/null || true - -echo "[lesavka-core] ★ directory tree just before links:" -tree -L 3 "$F/control" | sed 's/^/[lesavka-core] /' - -for s in fs hs ss; do - # best-effort: some UDCs reject certain speeds; skip on failure - if mkdir -p "$F/control/class/$s" 2>/dev/null; then - ln -snf "$F/control/header/h" "$F/control/class/$s/h" 2>/dev/null || \ - log "⚠️ control/class/$s/h link missing (continuing)" - else - log "⚠️ skipping control/class/$s (mkdir failed)" - fi -done - -for s in fs hs ss; do - [ -L "$F/control/class/$s/h" ] || log "⚠️ $s/h link missing (continuing)" -done - -echo "[lesavka-core] ★ directory tree just before bind:" -tree -L 3 "$F/control" | sed 's/^/[lesavka-core] /' - -for s in fs hs ss; do - [ -L "$F/control/class/$s" ] || log "⚠️ $s link missing (continuing)" -done -set -e # back to strict mode + mkdir -p "$F/control/header/h" + set +e + for s in fs hs ss; do + mkdir -p "$F/control/class/$s" 2>/dev/null || continue + pushd "$F/control/class/$s" >/dev/null + ln -s ../../header/h h 2>/dev/null || true + popd >/dev/null + done + set -e # optional: hide unsupported controls -echo 0 >"$F/control/terminal/camera/default/bmControls" 2>/dev/null || true -echo 0 >"$F/control/processing/default/bmControls" 2>/dev/null || true + echo 0 >"$F/control/terminal/camera/default/bmControls" 2>/dev/null || true + echo 0 >"$F/control/processing/default/bmControls" 2>/dev/null || true # friendly label -set +e -mkdir -p "$F/control/header/strings/0x409" 2>/dev/null || log "⚠️ skipping control/header strings (mkdir failed)" -echo "Lesavka UVC" >"$F/control/header/strings/0x409/label" 2>/dev/null || log "⚠️ unable to set UVC label (continuing)" -set -e + mkdir -p "$F/control/header/h/strings/0x409" 2>/dev/null || true + echo "Lesavka UVC" >"$F/control/header/h/strings/0x409/label" 2>/dev/null || true +else + log "📷 UVC disabled (LESAVKA_DISABLE_UVC set)" +fi # ----------------------- configuration ----------------------------- mkdir -p "$G/configs/c.1/strings/0x409" echo 500 > "$G/configs/c.1/MaxPower" # echo "Config 1" > "$G/configs/c.1/strings/0x409/configuration" -echo "Config 1: HID + UAC2" >"$G/configs/c.1/strings/0x409/configuration" +config_label="Config 1: HID" +if [[ -z $DISABLE_UAC ]]; then + config_label+=" + UAC2" +fi +if [[ -z $DISABLE_UVC ]]; then + config_label+=" + UVC" +fi +echo "$config_label" >"$G/configs/c.1/strings/0x409/configuration" ln -s $G/functions/hid.usb0 $G/configs/c.1/ ln -s $G/functions/hid.usb1 $G/configs/c.1/ -ln -s $U $G/configs/c.1/ -ln -s $G/functions/uvc.usb0 $G/configs/c.1/ +if [[ -z $DISABLE_UAC ]]; then + ln -s $U $G/configs/c.1/ +fi +if [[ -z $DISABLE_UVC ]]; then + ln -s $G/functions/uvc.usb0 $G/configs/c.1/ +fi # mkdir -p $G/functions/hid.usb0/os_desc # mkdir -p $G/functions/hid.usb1/os_desc @@ -233,6 +240,19 @@ ln -s $G/functions/uvc.usb0 $G/configs/c.1/ # 4. Bind gadget #────────────────────────────────────────────────── echo "$UDC" >"$G/UDC" -log "🎉 gadget bound on $UDC (hidg0, hidg1, UAC2 L+R, UVC)" +parts="hidg0,hidg1" +[[ -z $DISABLE_UAC ]] && parts+=",UAC2" +[[ -z $DISABLE_UVC ]] && parts+=",UVC" +log "🎉 gadget bound on $UDC ($parts)" + +if wait_for_enum 50; then + log "✅ UDC state is '$UDC_STATE' (speed=$UDC_SPEED)" +else + log "⚠️ UDC state is '$UDC_STATE' (speed=$UDC_SPEED). Host not enumerated." + if [[ -z $DISABLE_UVC && "$UVC_FALLBACK" != "0" ]]; then + log "♻️ retrying without UVC (LESAVKA_UVC_FALLBACK=0 to disable)" + exec env LESAVKA_DISABLE_UVC=1 LESAVKA_UVC_FALLBACK=0 "$0" + fi +fi exit 0