2025-06-01 21:28:09 -05:00
|
|
|
|
#!/usr/bin/env bash
|
2025-06-29 23:24:20 -05:00
|
|
|
|
# lesavka‑core.sh – one‑shot USB‑gadget bring‑up (Pi‑5 / Arch‑ARM)
|
|
|
|
|
|
# Presents: • Boot‑protocol keyboard (hidg0)
|
|
|
|
|
|
# • Boot‑protocol mouse (hidg1)
|
|
|
|
|
|
# • Stereo UAC2 speaker + microphone
|
2025-06-01 21:28:09 -05:00
|
|
|
|
set -euo pipefail
|
2025-06-06 20:25:26 -05:00
|
|
|
|
|
2025-06-29 23:24:20 -05:00
|
|
|
|
#──────────────────────────────────────────────────
|
|
|
|
|
|
# 1. Ensure overlay + kernel modules
|
|
|
|
|
|
#──────────────────────────────────────────────────
|
2025-06-06 20:45:09 -05:00
|
|
|
|
CFG=/boot/config.txt
|
|
|
|
|
|
grep -q 'dtoverlay=dwc2,dr_mode=peripheral' "$CFG" || echo 'dtoverlay=dwc2,dr_mode=peripheral' >> "$CFG"
|
2025-06-06 20:25:26 -05:00
|
|
|
|
|
2025-06-06 20:55:01 -05:00
|
|
|
|
modprobe dwc2 || { echo "dwc2 not in kernel; abort" >&2; exit 1; }
|
2025-06-06 00:41:32 -05:00
|
|
|
|
modprobe libcomposite || { echo "libcomposite not in kernel; abort" >&2; exit 1; }
|
2025-06-25 22:48:45 -05:00
|
|
|
|
|
|
|
|
|
|
modprobe -r uvcvideo 2>/dev/null || true
|
2025-06-23 19:44:03 -05:00
|
|
|
|
modprobe uvcvideo || { echo "uvcvideo not in kernel; abort" >&2; exit 1; }
|
2025-06-25 22:48:45 -05:00
|
|
|
|
|
2025-06-23 19:44:03 -05:00
|
|
|
|
udevadm control --reload
|
|
|
|
|
|
udevadm trigger --subsystem-match=video4linux
|
|
|
|
|
|
udevadm settle
|
2025-06-06 20:45:09 -05:00
|
|
|
|
|
2025-06-29 23:24:20 -05:00
|
|
|
|
#──────────────────────────────────────────────────
|
|
|
|
|
|
# 2. Wait for UDC device to appear (max 10 s)
|
|
|
|
|
|
#──────────────────────────────────────────────────
|
2025-06-25 22:48:45 -05:00
|
|
|
|
echo "[lesavka-core] ⏳ waiting for UDC to register ..."
|
|
|
|
|
|
UDC=""
|
2025-06-25 23:06:16 -05:00
|
|
|
|
for _ in {1..100}; do # 100 × 100ms = 10s
|
|
|
|
|
|
UDC=$(ls /sys/class/udc 2>/dev/null | head -n1) && [[ -n $UDC ]] && break
|
2025-06-25 22:48:45 -05:00
|
|
|
|
sleep 0.1
|
|
|
|
|
|
done
|
2025-06-25 23:06:16 -05:00
|
|
|
|
|
|
|
|
|
|
if [[ -z $UDC ]]; then
|
2025-06-28 15:45:35 -05:00
|
|
|
|
echo "[lesavka-core] ⚠️ UDC still absent - trying manual bind"
|
2025-06-25 23:06:16 -05:00
|
|
|
|
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 → “1000480000.usb”
|
|
|
|
|
|
echo "$node" >"$drv_root/bind" 2>/dev/null || continue
|
|
|
|
|
|
done
|
|
|
|
|
|
done
|
2025-06-28 15:45:35 -05:00
|
|
|
|
# re-check for another 5 s
|
2025-06-25 23:06:16 -05:00
|
|
|
|
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 ]] || { echo "❌ UDC not present after manual bind"; exit 1; }
|
2025-06-25 22:48:45 -05:00
|
|
|
|
echo "[lesavka-core] ✅ UDC detected: $UDC"
|
|
|
|
|
|
|
2025-06-29 23:24:20 -05:00
|
|
|
|
#──────────────────────────────────────────────────
|
|
|
|
|
|
# 3. (Re‑)create gadget
|
|
|
|
|
|
#──────────────────────────────────────────────────
|
2025-06-06 20:25:26 -05:00
|
|
|
|
mountpoint -q /sys/kernel/config || mount -t configfs none /sys/kernel/config
|
2025-06-23 00:26:02 -05:00
|
|
|
|
G=/sys/kernel/config/usb_gadget/lesavka
|
2025-06-01 21:28:09 -05:00
|
|
|
|
|
2025-06-02 20:18:19 -05:00
|
|
|
|
if [[ -d $G ]]; then
|
2025-06-06 22:10:59 -05:00
|
|
|
|
echo '' >"$G/UDC" 2>/dev/null || true
|
|
|
|
|
|
sleep 0.2
|
2025-06-06 20:55:01 -05:00
|
|
|
|
find "$G/configs" -type l -delete 2>/dev/null || true
|
2025-06-15 17:39:39 -05:00
|
|
|
|
rm -rf "$G" 2>/dev/null || true
|
2025-06-02 20:18:19 -05:00
|
|
|
|
fi
|
2025-06-01 21:28:09 -05:00
|
|
|
|
|
|
|
|
|
|
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"
|
2025-06-06 20:25:26 -05:00
|
|
|
|
echo "$(cat /proc/sys/kernel/random/uuid)" >"$G/strings/0x409/serialnumber"
|
2025-06-23 07:18:26 -05:00
|
|
|
|
echo "Lesavka" >"$G/strings/0x409/manufacturer"
|
|
|
|
|
|
echo "Lesavka Composite" >"$G/strings/0x409/product"
|
2025-06-01 21:28:09 -05:00
|
|
|
|
|
2025-06-15 20:19:27 -05:00
|
|
|
|
# ----------------------- HID keyboard (usb0) -----------------------
|
2025-06-01 21:28:09 -05:00
|
|
|
|
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"
|
2025-06-15 20:19:27 -05:00
|
|
|
|
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"
|
2025-06-01 21:28:09 -05:00
|
|
|
|
|
2025-06-15 20:19:27 -05:00
|
|
|
|
# ----------------------- HID mouse (usb1) --------------------------
|
2025-06-11 22:01:16 -05:00
|
|
|
|
mkdir -p "$G/functions/hid.usb1"
|
2025-06-19 01:02:10 -05:00
|
|
|
|
echo 2 > "$G/functions/hid.usb1/protocol" # Boot mouse
|
|
|
|
|
|
echo 1 > "$G/functions/hid.usb1/subclass"
|
2025-06-11 22:01:16 -05:00
|
|
|
|
echo 4 > "$G/functions/hid.usb1/report_length"
|
2025-06-18 23:46:32 -05:00
|
|
|
|
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"
|
2025-06-11 22:01:16 -05:00
|
|
|
|
|
2025-06-29 22:57:54 -05:00
|
|
|
|
# ---------- UAC2 function – speaker + mic, 2×48 kHz stereo ---------
|
|
|
|
|
|
mkdir -p "$G/functions/uac2.usb0"
|
2025-06-29 23:24:20 -05:00
|
|
|
|
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
|
2025-06-06 20:25:26 -05:00
|
|
|
|
|
2025-06-15 20:19:27 -05:00
|
|
|
|
# ----------------------- configuration -----------------------------
|
2025-06-06 23:03:17 -05:00
|
|
|
|
mkdir -p "$G/configs/c.1/strings/0x409"
|
|
|
|
|
|
echo 500 > "$G/configs/c.1/MaxPower"
|
2025-06-29 23:24:20 -05:00
|
|
|
|
# echo "Config 1" > "$G/configs/c.1/strings/0x409/configuration"
|
|
|
|
|
|
echo "Config 1: HID + UAC2" >"$G/configs/c.1/strings/0x409/configuration"
|
2025-06-06 20:55:01 -05:00
|
|
|
|
|
2025-06-29 23:29:00 -05:00
|
|
|
|
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/"
|
2025-06-01 21:28:09 -05:00
|
|
|
|
|
2025-06-29 23:24:20 -05:00
|
|
|
|
#──────────────────────────────────────────────────
|
|
|
|
|
|
# 4. Bind gadget
|
|
|
|
|
|
#──────────────────────────────────────────────────
|
|
|
|
|
|
echo "$UDC" >"$G/UDC"
|
|
|
|
|
|
log "🎉 gadget bound on $UDC (hidg0, hidg1, UAC2 L+R)"
|
|
|
|
|
|
|
|
|
|
|
|
exit 0
|