lesavka/scripts/daemon/lesavka-core.sh
2025-07-04 11:19:35 -05:00

179 lines
7.7 KiB
Bash
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# lesavkacore.sh - oneshot USBgadget bringup (Pi5 / ArchARM)
# Presents: • Bootprotocol keyboard (hidg0)
# • Bootprotocol mouse (hidg1)
# • Stereo UAC2 speaker + microphone
set -euo pipefail
log() { printf '[lesavka-core] %s\n' "$*"; }
#──────────────────────────────────────────────────
# 1. Ensure overlay + kernel modules
#──────────────────────────────────────────────────
CFG=/boot/config.txt
grep -q 'dtoverlay=dwc2,dr_mode=peripheral' "$CFG" || echo 'dtoverlay=dwc2,dr_mode=peripheral' >> "$CFG"
modprobe dwc2 || { echo "dwc2 not in kernel; abort" >&2; exit 1; }
modprobe libcomposite || { echo "libcomposite not in kernel; abort" >&2; exit 1; }
modprobe -r uvcvideo 2>/dev/null || true
modprobe uvcvideo || { echo "uvcvideo not in kernel; abort" >&2; exit 1; }
udevadm control --reload
udevadm trigger --subsystem-match=video4linux
udevadm settle
#──────────────────────────────────────────────────
# 2. Wait for UDC device to appear (max 10s)
#──────────────────────────────────────────────────
log "⏳ waiting for UDC to register ..."
UDC=""
for _ in {1..100}; do # 100 × 100ms = 10s
UDC=$(ls /sys/class/udc 2>/dev/null | head -n1) && [[ -n $UDC ]] && break
sleep 0.1
done
if [[ -z $UDC ]]; then
log "⚠️ UDC still absent - trying manual bind"
for drv in dwc2 dwc3; do
drv_root="/sys/bus/platform/drivers/$drv"
[[ -d $drv_root ]] || continue
for node in /sys/bus/platform/devices/*usb*; do
node=${node##*/} # strip path
echo "$node" >"$drv_root/bind" 2>/dev/null || continue
done
done
# re-check for another 5s
for i in {1..50}; do
UDC=$(ls /sys/class/udc 2>/dev/null | head -n1) && [[ -n $UDC ]] && break
sleep 0.1
done
fi
[[ -n $UDC ]] || { log "❌ UDC not present after manual bind"; exit 1; }
log "✅ UDC detected: $UDC"
#──────────────────────────────────────────────────
# 3. (Re)create gadget
#──────────────────────────────────────────────────
mountpoint -q /sys/kernel/config || mount -t configfs none /sys/kernel/config
G=/sys/kernel/config/usb_gadget/lesavka
if [[ -d $G ]]; then
echo '' >"$G/UDC" 2>/dev/null || true
sleep 0.2
find "$G/configs" -type l -delete 2>/dev/null || true
rm -rf "$G" 2>/dev/null || true
fi
mkdir -p "$G"
echo 0x1d6b >"$G/idVendor" # Linux Foundation
echo 0x0104 >"$G/idProduct" # Multifunction Composite Gadget
echo 0x0200 >"$G/bcdUSB"
mkdir -p "$G/strings/0x409"
echo "$(cat /proc/sys/kernel/random/uuid)" >"$G/strings/0x409/serialnumber"
echo "Lesavka" >"$G/strings/0x409/manufacturer"
echo "Lesavka Composite" >"$G/strings/0x409/product"
# ----------------------- HID keyboard (usb0) -----------------------
mkdir -p "$G/functions/hid.usb0"
echo 1 >"$G/functions/hid.usb0/protocol"
echo 1 >"$G/functions/hid.usb0/subclass"
echo 8 >"$G/functions/hid.usb0/report_length"
printf '\x05\x01\x09\x06\xa1\x01\x05\x07\x19\xe0\x29\xe7\x15\x00\x25\x01'\
'\x75\x01\x95\x08\x81\x02\x95\x01\x75\x08\x81\x01\x95\x05\x75\x01\x05'\
'\x08\x19\x01\x29\x05\x91\x02\x95\x01\x75\x03\x91\x01\x95\x06\x75\x08'\
'\x15\x00\x25\x65\x05\x07\x19\x00\x29\x65\x81\x00\xc0' \
>"$G/functions/hid.usb0/report_desc"
# ----------------------- HID mouse (usb1) --------------------------
mkdir -p "$G/functions/hid.usb1"
echo 2 > "$G/functions/hid.usb1/protocol" # Boot mouse
echo 1 > "$G/functions/hid.usb1/subclass"
echo 4 > "$G/functions/hid.usb1/report_length"
printf '\x05\x01\x09\x02\xa1\x01\x09\x01\xa1\x00'\
'\x05\x09\x19\x01\x29\x03\x15\x00\x25\x01\x95\x03\x75\x01\x81\x02'\
'\x95\x01\x75\x05\x81\x03'\
'\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×48kHz 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" # 16bit
# 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
# ----------------------- UVC function (usbvideo) ------------------
mkdir -p "$G/functions/uvc.usb0"
F="$G/functions/uvc.usb0"
# ── 1. FORMAT DESCRIPTOR ──────────────────────────────────────────
# Uncompressed YUY2, 16bpp
mkdir -p "$F/streaming/uncompressed/u"
echo YUY2 >"$F/streaming/uncompressed/u/guidFormat"
echo 16 >"$F/streaming/uncompressed/u/bBitsPerPixel"
# ── 2. FRAME DESCRIPTOR (index1) ─────────────────────────────────
mkdir -p "$F/streaming/uncompressed/u/f1"
echo 1280 >"$F/streaming/uncompressed/u/f1/wWidth"
echo 720 >"$F/streaming/uncompressed/u/f1/wHeight"
echo 2764800 >"$F/streaming/uncompressed/u/f1/dwMaxVideoFrameBufferSize"
echo 333333 >"$F/streaming/uncompressed/u/f1/dwDefaultFrameInterval" # 30fps
echo 333333 >"$F/streaming/uncompressed/u/f1/dwFrameInterval"
# ── 3. REQUIRED HEADER LINKS ──────────────────────────────────────
mkdir -p "$F/streaming/header"
ln -sf ../../uncompressed/u "$F/streaming/header/h"
mkdir -p "$F/control/header"
ln -sf ../../streaming/header/h "$F/control/header/h"
# Friendly string
mkdir -p "$F/control/header/strings/0x409"
echo "Lesavka UVC" >"$F/control/header/strings/0x409/label"
# ----------------------- 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"
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/
# mkdir -p $G/functions/hid.usb0/os_desc
# mkdir -p $G/functions/hid.usb1/os_desc
# mkdir -p $U/os_desc
# ---------- optional Microsoft OS descriptors ----------------------
# if [ -e "$G/os_desc/use" ]; then
# echo 1 >"$G/os_desc/use"
# echo 0xcd >"$G/os_desc/b_vendor_code"
# echo "MSFT100" >"$G/os_desc/qw_sign"
# ln -s "$G/configs/c.1" "$G/os_desc" # creates $G/os_desc/conf
# echo "Lesavka Keyboard" >"$G/functions/hid.usb0/os_desc/interface"
# echo "Lesavka Mouse" >"$G/functions/hid.usb1/os_desc/interface"
# echo "Lesavka Mic+Spkr" >"$U/os_desc/interface"
# fi
#──────────────────────────────────────────────────
# 4. Bind gadget
#──────────────────────────────────────────────────
echo "$UDC" >"$G/UDC"
log "🎉 gadget bound on $UDC (hidg0, hidg1, UAC2 L+R, UVC)"
exit 0