Compare commits

...

2 Commits

Author SHA1 Message Date
b62adc5421 fix(round): re-disable agent + storage-gadget enables wiped by force-push
PR #175 commented out four ln -sf lines (two for secubox-eye-gadget.service
storage-only mode, two for secubox-eye-agent.service Pydantic-v2/SIGILL
crash loop). A force-push for the dwc2 fix earlier today inadvertently
overwrote that branch HEAD, dropping the four edits. Re-applying them
identically so the branch's intent — only secubox-otg-gadget.service
active at boot — is restored.

Combined with the dwc2 modprobe fix already on the branch, the rpiz now
boots into:
  - secubox-otg-gadget.service modprobes dwc2, libcomposite, usb_f_*
  - waits for /sys/class/udc to populate (poll, 5s)
  - creates composite ECM+ACM gadget on the UDC
  - MOCHAbin enumerates "Linux Foundation Multifunction Composite Gadget"
2026-05-17 11:24:31 +02:00
8e2262f502 fix(round): modprobe dwc2 in secubox-otg-gadget chain (rpiz UDC was implicit on eye-gadget)
The previous build had two gadget services enabled at boot:
  - secubox-eye-gadget.service: ExecStartPre=modprobe dwc2 + libcomposite
  - secubox-otg-gadget.service: ExecStartPre=modprobe libcomposite + usb_f_*
                                (NO dwc2 — relied on eye-gadget loading it first)

When PR #175 disabled secubox-eye-gadget at boot (to stop the UDC conflict),
nobody loaded dwc2 anymore. systemd-modules-load reads /etc/modules but the
order of dwc2 load vs the otg-gadget service start window was tight — and
on the rpiz the journal showed no successful gadget enumeration after the
PR #175 image deployed.

Defensive fix in two places:

1. secubox-otg-gadget.service: prepend ExecStartPre=/sbin/modprobe dwc2.
   Drop the ConditionPathIsDirectory=/sys/class/udc gate so the service
   actually runs and can load dwc2 if it isn't loaded yet (the path is a
   sysfs dir that exists when its parent /sys/class is mounted — it was
   harmless, but removing it makes the chain self-bootstrapping).

2. secubox-otg-gadget.sh check_prerequisites(): also modprobe dwc2 (in
   case the service is invoked manually by an operator), then poll
   /sys/class/udc for up to 5s waiting for the BCM USB controller to
   bind asynchronously. Diagnostic message updated to point at the two
   places to check (dtoverlay + /etc/modules) if no UDC ever shows up.
2026-05-17 11:22:55 +02:00
3 changed files with 43 additions and 12 deletions

View File

@ -178,16 +178,30 @@ check_prerequisites() {
fi fi
fi fi
# Charger les modules nécessaires # Charger les modules nécessaires. dwc2 must be loaded FIRST — it creates
# the UDC node that the gadget functions bind to. Historically dwc2 was
# loaded implicitly by secubox-eye-gadget.service's ExecStartPre; with
# that service disabled at boot (storage-only mode is opt-in), the
# gadget chain now owns its own dwc2 modprobe explicitly.
modprobe dwc2 2>/dev/null || true
modprobe libcomposite 2>/dev/null || true modprobe libcomposite 2>/dev/null || true
modprobe usb_f_ecm 2>/dev/null || true modprobe usb_f_ecm 2>/dev/null || true
modprobe usb_f_rndis 2>/dev/null || true modprobe usb_f_rndis 2>/dev/null || true
modprobe usb_f_acm 2>/dev/null || true modprobe usb_f_acm 2>/dev/null || true
modprobe usb_f_mass_storage 2>/dev/null || true modprobe usb_f_mass_storage 2>/dev/null || true
# dwc2 binds asynchronously to the BCM USB controller — wait up to 5s
# for the UDC node to appear (typically <500ms on a Pi Zero W).
for _ in 1 2 3 4 5 6 7 8 9 10; do
if [[ -d /sys/class/udc ]] && [[ -n "$(ls /sys/class/udc 2>/dev/null)" ]]; then
break
fi
sleep 0.5
done
# Vérifier la présence d'un UDC (USB Device Controller) # Vérifier la présence d'un UDC (USB Device Controller)
if [[ ! -d /sys/class/udc ]] || [[ -z "$(ls /sys/class/udc 2>/dev/null)" ]]; then if [[ ! -d /sys/class/udc ]] || [[ -z "$(ls /sys/class/udc 2>/dev/null)" ]]; then
err "Aucun UDC trouvé — ce script doit être exécuté sur un RPi Zero W" err "Aucun UDC trouvé — vérifier dtoverlay=dwc2 dans /boot/config.txt et module dwc2 dans /etc/modules"
return 1 return 1
fi fi

View File

@ -650,9 +650,14 @@ mkdir -p "$ROOT_MNT/etc/systemd/system/dnsmasq.service.d"
cp "$SCRIPT_DIR/files/etc/systemd/system/dnsmasq.service.d/secubox-eye.conf" \ cp "$SCRIPT_DIR/files/etc/systemd/system/dnsmasq.service.d/secubox-eye.conf" \
"$ROOT_MNT/etc/systemd/system/dnsmasq.service.d/" "$ROOT_MNT/etc/systemd/system/dnsmasq.service.d/"
# Enable new gadget service # secubox-eye-gadget is STORAGE-only mode for U-Boot rescue. Enabling it
ln -sf /etc/systemd/system/secubox-eye-gadget.service \ # at boot alongside secubox-otg-gadget.service (composite ECM+ACM) caused
"$ROOT_MNT/etc/systemd/system/multi-user.target.wants/" # UDC contention. Install the unit but DO NOT enable at boot.
# Manual U-Boot rescue mode:
# systemctl disable secubox-otg-gadget.service
# systemctl enable --now secubox-eye-gadget.service
# ln -sf /etc/systemd/system/secubox-eye-gadget.service \
# "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/"
# Copy framebuffer dashboard (Pi Zero W has no NEON, can't run Chromium) # Copy framebuffer dashboard (Pi Zero W has no NEON, can't run Chromium)
log "Installing framebuffer dashboard..." log "Installing framebuffer dashboard..."
@ -684,10 +689,15 @@ if [[ -f "$SCRIPT_DIR/secubox-eye-agent.service" && -f "$SCRIPT_DIR/config.toml.
# Install agent service # Install agent service
cp "$SCRIPT_DIR/secubox-eye-agent.service" "$ROOT_MNT/etc/systemd/system/" cp "$SCRIPT_DIR/secubox-eye-agent.service" "$ROOT_MNT/etc/systemd/system/"
# Enable agent service via symlink (atomic, no chroot needed) # The agent depends on Pydantic v2 (pydantic_core, Rust) which has no
# ARMv6 wheel — pip ships an ARMv7 wheel that crashes with SIGILL on
# the Pi Zero W BCM2835 (status=4/ILL). v2.2.1 design moved metrics
# rendering to secubox-fallback-display.service (pure-Python Pillow),
# so we install the unit but DO NOT enable at boot. ARMv7+ boards:
# systemctl enable --now secubox-eye-agent.service
mkdir -p "$ROOT_MNT/etc/systemd/system/multi-user.target.wants" mkdir -p "$ROOT_MNT/etc/systemd/system/multi-user.target.wants"
ln -sf /etc/systemd/system/secubox-eye-agent.service \ # ln -sf /etc/systemd/system/secubox-eye-agent.service \
"$ROOT_MNT/etc/systemd/system/multi-user.target.wants/" # "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/"
# v2.2.0: Install menu system icons for radial menu # v2.2.0: Install menu system icons for radial menu
if [[ -d "$SCRIPT_DIR/assets/icons" ]]; then if [[ -d "$SCRIPT_DIR/assets/icons" ]]; then
@ -766,7 +776,10 @@ ln -sf /etc/systemd/system/eye-firstboot-hostname.service "$ROOT_MNT/etc/systemd
ln -sf /etc/systemd/system/hyperpixel2r-init.service "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/" 2>/dev/null || true ln -sf /etc/systemd/system/hyperpixel2r-init.service "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/" 2>/dev/null || true
# Eye Remote services # Eye Remote services
ln -sf /etc/systemd/system/secubox-eye-gadget.service "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/" 2>/dev/null || true # secubox-eye-gadget (storage-only, U-Boot rescue) is NOT enabled at boot
# to avoid UDC contention with secubox-otg-gadget.service. See comment at
# line 653 for the manual rescue-mode recipe.
# ln -sf /etc/systemd/system/secubox-eye-gadget.service "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/" 2>/dev/null || true
# v2.2.1: Use fallback-display instead of eye-agent (3D cube + rainbow rings, stable) # v2.2.1: Use fallback-display instead of eye-agent (3D cube + rainbow rings, stable)
ln -sf /etc/systemd/system/secubox-fallback-display.service "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/" 2>/dev/null || true ln -sf /etc/systemd/system/secubox-fallback-display.service "$ROOT_MNT/etc/systemd/system/multi-user.target.wants/" 2>/dev/null || true
# NOTE: secubox-eye-agent is broken (import errors) - disabled pending fix # NOTE: secubox-eye-agent is broken (import errors) - disabled pending fix

View File

@ -17,15 +17,19 @@ After=systemd-modules-load.service
Before=network-pre.target Before=network-pre.target
Wants=network-pre.target Wants=network-pre.target
# Conditions : seulement sur un périphérique avec UDC (RPi Zero W) # Conditions : configfs disponible (UDC est crééable dynamiquement via dwc2,
ConditionPathIsDirectory=/sys/class/udc # voir ExecStartPre — pas de pré-condition stricte sur /sys/class/udc).
ConditionPathExists=/sys/kernel/config ConditionPathExists=/sys/kernel/config
[Service] [Service]
Type=oneshot Type=oneshot
RemainAfterExit=yes RemainAfterExit=yes
# Chargement des modules nécessaires # Chargement des modules nécessaires. dwc2 est chargé en premier pour créer
# le UDC (sinon le gadget ne peut s'attacher). secubox-eye-gadget.service
# le chargeait historiquement avant; depuis qu'il est désactivé au boot
# (storage-only mode opt-in), on doit le charger ici.
ExecStartPre=/sbin/modprobe dwc2
ExecStartPre=/sbin/modprobe libcomposite ExecStartPre=/sbin/modprobe libcomposite
ExecStartPre=/sbin/modprobe usb_f_ecm ExecStartPre=/sbin/modprobe usb_f_ecm
ExecStartPre=/sbin/modprobe usb_f_acm ExecStartPre=/sbin/modprobe usb_f_acm