Bring up the USB gadget link (package 0b, Pi side and talos side)

The Pi presents NCM Ethernet and ACM serial from a configfs gadget, started by
robot-usb-gadget.service before networking. usb0 gets 10.55.0.2/30 with no
gateway through netplan, matched by interface name: a MAC match turns into
PermanentMACAddress= in networkd, which a gadget interface does not have, so it
stayed unmanaged. talos reaches the Pi at 10.55.0.2 (ping ~2.4 ms, SSH works)
through a NetworkManager profile, host/omarchy/robot-usb-link.sh, matched by the
gadget's fixed host MAC.

Also clamp the face's tunable parameters (focus sets a blur radius, persistence
divides) and fps, after a review flagged unvalidated node parameters.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AFN8nRRCiDiMN7ChBpHJhs
This commit is contained in:
2026-10-04 20:53:09 -07:00
co-authored by Claude Sonnet 5.5
parent 55d33ab490
commit 69a93eeb2f
9 changed files with 181 additions and 11 deletions
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
# Pi side of the USB link (HANDOFF package 0b). Run as root from this directory. Re-runnable.
# Needs a reboot afterwards for the dwc2 mode change to take effect.
set -euo pipefail
cd "$(dirname "$0")"
install -m 755 usb-gadget.sh /usr/local/sbin/robot-usb-gadget
install -m 644 robot-usb-gadget.service /etc/systemd/system/robot-usb-gadget.service
install -m 600 netplan-usb0.yaml /etc/netplan/60-usb0.yaml
echo libcomposite > /etc/modules-load.d/robot-usb-gadget.conf
cfg=/boot/firmware/config.txt
[ -e "$cfg.bak-usbgadget" ] || cp "$cfg" "$cfg.bak-usbgadget"
if grep -qx 'dtoverlay=dwc2' "$cfg"; then
sed -i 's/^dtoverlay=dwc2$/dtoverlay=dwc2,dr_mode=peripheral/' "$cfg"
elif ! grep -qx 'dtoverlay=dwc2,dr_mode=peripheral' "$cfg"; then
printf '\n[all]\ndtoverlay=dwc2,dr_mode=peripheral\n' >> "$cfg"
fi
netplan generate
systemctl daemon-reload
systemctl enable robot-usb-gadget.service
systemctl enable serial-getty@ttyGS0.service
echo "installed; reboot to apply"
+14
View File
@@ -0,0 +1,14 @@
# The robot's end of the USB link: /etc/netplan/60-usb0.yaml (mode 600).
# Matched by name: the kernel names the gadget's NCM interface usb0. (A MAC match does not work:
# netplan matches the *permanent* MAC, which a gadget interface does not have.) No gateway, so the
# robot never routes its internet traffic through the host; Wi-Fi stays the way out.
network:
version: 2
ethernets:
usb0:
match:
name: usb0
addresses: [10.55.0.2/30]
dhcp4: false
dhcp6: false
optional: true
+14
View File
@@ -0,0 +1,14 @@
[Unit]
Description=Robot USB gadget (NCM Ethernet + ACM serial)
After=sys-kernel-config.mount systemd-modules-load.service
Before=network-pre.target
Wants=network-pre.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/robot-usb-gadget start
ExecStop=/usr/local/sbin/robot-usb-gadget stop
[Install]
WantedBy=multi-user.target
+65
View File
@@ -0,0 +1,65 @@
#!/bin/bash
# Composite USB gadget for the robot, via configfs: NCM Ethernet (usb0) + ACM serial (ttyGS0).
# Installed as /usr/local/sbin/robot-usb-gadget and run by robot-usb-gadget.service.
# robot-usb-gadget start | stop
set -euo pipefail
G=/sys/kernel/config/usb_gadget/hermes
HOST_MAC=02:68:65:72:6d:01 # the host's end of the link (locally administered, spells "herm")
DEV_MAC=02:68:65:72:6d:02 # the robot's end (usb0)
stop() {
[ -d "$G" ] || return 0
cd "$G"
echo "" > UDC 2>/dev/null || true
rm -f configs/c.1/ncm.usb0 configs/c.1/acm.usb0
rmdir configs/c.1/strings/0x409 configs/c.1 functions/ncm.usb0 functions/acm.usb0 strings/0x409 2>/dev/null || true
cd /
rmdir "$G" 2>/dev/null || true
}
start() {
modprobe libcomposite
mountpoint -q /sys/kernel/config || mount -t configfs none /sys/kernel/config
stop
udc=
for _ in $(seq 40); do # dwc2 can take a moment to register after boot
udc=$(ls /sys/class/udc 2>/dev/null | head -n1 || true)
[ -n "$udc" ] && break
sleep 0.5
done
[ -n "$udc" ] || { echo "robot-usb-gadget: no UDC (is dtoverlay=dwc2,dr_mode=peripheral set?)" >&2; exit 1; }
mkdir -p "$G"
cd "$G"
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # multifunction composite gadget
echo 0x0100 > bcdDevice
echo 0x0200 > bcdUSB
echo 0xEF > bDeviceClass # composite with interface association descriptors
echo 0x02 > bDeviceSubClass
echo 0x01 > bDeviceProtocol
mkdir -p strings/0x409
echo "$(awk '/^Serial/ {print $3}' /proc/cpuinfo)" > strings/0x409/serialnumber
echo "Hermes" > strings/0x409/manufacturer
echo "Hermes robot" > strings/0x409/product
mkdir -p configs/c.1/strings/0x409
echo "NCM + ACM" > configs/c.1/strings/0x409/configuration
echo 250 > configs/c.1/MaxPower
mkdir -p functions/ncm.usb0
echo "$HOST_MAC" > functions/ncm.usb0/host_addr
echo "$DEV_MAC" > functions/ncm.usb0/dev_addr
mkdir -p functions/acm.usb0
ln -s functions/ncm.usb0 configs/c.1/
ln -s functions/acm.usb0 configs/c.1/
echo "$udc" > UDC
}
case "${1:-}" in
start) start ;;
stop) stop ;;
*) echo "usage: $0 start|stop" >&2; exit 2 ;;
esac