Enhance eMMC provisioning scripts and documentation: add troubleshooting section for device connection issues, update flash script to prevent concurrent runs, and improve logging. Adjust deployment scripts to verify presence of boot files in mass-storage-gadget.
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
# Ensure CM4 boot-mode USB device is accessible to rpiboot (libusb).
|
||||
# Load before 90-cm4-boot-mode.rules so permissions are set before the trigger runs.
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="2b8e", MODE="0666"
|
||||
SUBSYSTEM=="usb", ATTR{idVendor}=="0a5c", ATTR{idProduct}=="2711", MODE="0666"
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# Called by udev when CM4 in boot mode is connected. Starts the provisioning script in the
|
||||
# background (rpiboot + wait for portal Backup/Deploy choice). Install to /usr/local/bin/cm4-flash-trigger.sh
|
||||
# Called by udev when CM4 in boot mode (0a5c:2711 or 2b8e) is connected.
|
||||
# Start the flash service via systemd so it runs under systemd, not udev —
|
||||
# otherwise udev kills the process when the device re-enumerates (first→second stage).
|
||||
# --no-block: return immediately so udev doesn't wait; the service runs in the background.
|
||||
|
||||
FLASH_SCRIPT="${CM4_FLASH_SCRIPT:-/opt/cm4-provisioning/flash-emmc-on-connect.sh}"
|
||||
exec systemd-run --no-block --unit=cm4-flash-once "$FLASH_SCRIPT"
|
||||
systemctl --no-block start cm4-flash.service
|
||||
|
||||
21
chromium-setup/emmc-provisioning/host/cm4-flash.service
Normal file
21
chromium-setup/emmc-provisioning/host/cm4-flash.service
Normal file
@@ -0,0 +1,21 @@
|
||||
[Unit]
|
||||
Description=CM4 eMMC provisioning (rpiboot + backup/deploy)
|
||||
# Run after udev has settled; do not block boot
|
||||
After=systemd-udevd.service
|
||||
DefaultDependencies=yes
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
# Delay so USB device is enumerated and udev permissions applied before we run
|
||||
ExecStartPre=/bin/sleep 5
|
||||
ExecStart=/opt/cm4-provisioning/flash-emmc-on-connect.sh
|
||||
# Run as root; flash script logs to /var/lib/cm4-provisioning/flash.log
|
||||
User=root
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
# Allow long run (rpiboot + device scan + wait for user choice)
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
# Only started by udev trigger, not at boot
|
||||
WantedBy=multi-user.target
|
||||
@@ -4,7 +4,11 @@
|
||||
# to choose Backup or Deploy in the portal — no auto-flash; action runs only after portal choice.
|
||||
# Run this from udev or a systemd service. Requires: usbboot (rpiboot) built, golden image for deploy.
|
||||
|
||||
set -e
|
||||
# Do NOT use set -e: rpiboot and intermediate commands may return non-zero without being fatal
|
||||
|
||||
# Redirect all output to log file (stdout + stderr) so nothing is lost to nohup buffering
|
||||
LOG_FILE="${LOG_FILE:-/var/lib/cm4-provisioning/flash.log}"
|
||||
exec >> "$LOG_FILE" 2>&1
|
||||
|
||||
# Load overrides from env file if present
|
||||
[[ -f /opt/cm4-provisioning/env ]] && source /opt/cm4-provisioning/env
|
||||
@@ -12,13 +16,14 @@ set -e
|
||||
# Configuration - adjust paths and size for your setup
|
||||
RPIBOOT_DIR="${RPIBOOT_DIR:-/opt/usbboot}"
|
||||
GOLDEN_IMAGE="${GOLDEN_IMAGE:-/var/lib/cm4-provisioning/golden.img}"
|
||||
# Expected eMMC size in bytes (reTerminal CM4 often 8GB or 16GB). Used to identify the correct block device.
|
||||
EMMC_SIZE_BYTES="${EMMC_SIZE_BYTES:-$(( 8 * 1024 * 1024 * 1024 ))}"
|
||||
# Expected eMMC size in bytes. reTerminal DM (CM4) has 32 GB eMMC (~31268536320 bytes).
|
||||
EMMC_SIZE_BYTES="${EMMC_SIZE_BYTES:-$(( 32 * 1024 * 1024 * 1024 ))}"
|
||||
LOG_TAG="cm4-flash"
|
||||
STATUS_FILE="${STATUS_FILE:-/var/lib/cm4-provisioning/status.json}"
|
||||
LOG_FILE="${LOG_FILE:-/var/lib/cm4-provisioning/flash.log}"
|
||||
LOCK_FILE="${LOCK_FILE:-/var/lib/cm4-provisioning/flash.lock}"
|
||||
|
||||
log() { echo "[$LOG_TAG] $*"; logger -t "$LOG_TAG" "$*"; echo "[$(date -Iseconds)] $*" >> "$LOG_FILE" 2>/dev/null || true; }
|
||||
log() { echo "[$(date -Iseconds)] [$LOG_TAG] $*"; logger -t "$LOG_TAG" "$*" 2>/dev/null || true; }
|
||||
|
||||
write_status() {
|
||||
local phase="$1" message="$2" progress="${3:-null}" error="${4:-}"
|
||||
@@ -33,6 +38,14 @@ write_status() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Prevent concurrent runs (udev fires multiple times during rpiboot re-enumeration)
|
||||
exec 9>"$LOCK_FILE"
|
||||
if ! flock -n 9; then
|
||||
echo "[$(date -Iseconds)] Another flash-emmc instance is already running; exiting."
|
||||
exit 0
|
||||
fi
|
||||
trap 'rm -f "$LOCK_FILE" "$CURRENT_DEVICE_FILE" "$DEVICE_SOURCE_FILE" 2>/dev/null; exec 9>&-' EXIT
|
||||
|
||||
# Optional: only run if this file exists (safety)
|
||||
ENABLE_FILE="${ENABLE_FILE:-/etc/cm4-provisioning/enabled}"
|
||||
if [[ -n "$ENABLE_FILE" && ! -f "$ENABLE_FILE" ]]; then
|
||||
@@ -72,6 +85,12 @@ if [[ -z "$RPIBOOT_GADGET" ]]; then
|
||||
write_status "error" "rpiboot gadget missing" "null" "Copy mass-storage-gadget(64) to $RPIBOOT_DIR"
|
||||
exit 1
|
||||
fi
|
||||
# rpiboot requires bootfiles.bin or one of bootcode*.bin in the gadget dir; empty dir causes "No 'bootcode' files found"
|
||||
if [[ ! -f "$RPIBOOT_GADGET/bootfiles.bin" && ! -f "$RPIBOOT_GADGET/bootcode.bin" && ! -f "$RPIBOOT_GADGET/bootcode4.bin" && ! -f "$RPIBOOT_GADGET/bootcode5.bin" ]]; then
|
||||
log "rpiboot gadget dir has no boot files: $RPIBOOT_GADGET (reinstall usbboot)"
|
||||
write_status "error" "rpiboot gadget empty" "null" "No boot files in $RPIBOOT_GADGET. Reinstall usbboot: run install-usbboot-on-host.sh on the host or build-and-deploy-usbboot-to-host.sh from your machine."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure status dir exists and start with running state
|
||||
mkdir -p "$(dirname "$STATUS_FILE")" "$BACKUPS_DIR" 2>/dev/null || true
|
||||
@@ -81,35 +100,47 @@ write_status "rpiboot" "Connecting to CM4 in boot mode…" "0"
|
||||
before_devs=$(lsblk -nd -o NAME 2>/dev/null | sort)
|
||||
|
||||
log "Starting rpiboot to expose CM4 eMMC as mass storage..."
|
||||
if ! "$RPIBOOT_BIN" -d "$RPIBOOT_GADGET"; then
|
||||
log "rpiboot failed or no device connected"
|
||||
write_status "error" "rpiboot failed" "null" "rpiboot failed or no device connected"
|
||||
# Run rpiboot with 90s timeout so we don't hang if it doesn't exit cleanly when device switches to mass storage
|
||||
rpiboot_exit=0
|
||||
timeout 90 "$RPIBOOT_BIN" -d "$RPIBOOT_GADGET" || rpiboot_exit=$?
|
||||
# timeout returns 124 if killed by timeout; 0 or other if rpiboot exited on its own
|
||||
if [[ "$rpiboot_exit" -eq 124 ]]; then
|
||||
log "rpiboot timed out after 90s (device may have switched to mass storage)"
|
||||
elif [[ "$rpiboot_exit" -ne 0 ]]; then
|
||||
log "rpiboot exited with code $rpiboot_exit"
|
||||
write_status "error" "rpiboot failed" "null" "rpiboot failed or no device connected. Check flash.log on host. Try unplug/replug USB."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# rpiboot exits when mass storage appears; give udev a moment to create /dev/sdX
|
||||
sleep 3
|
||||
echo "[$(date -Iseconds)] rpiboot finished (exit=$rpiboot_exit); starting device scan"
|
||||
log "rpiboot completed; waiting for block device..."
|
||||
write_status "rpiboot" "rpiboot done, waiting for block device…" "10"
|
||||
|
||||
# Find new block device (prefer one matching expected eMMC size)
|
||||
# rpiboot exits when device switches to mass storage; udev may need several seconds to create /dev/sdX
|
||||
# Poll for new block device for up to 30s (device switch can be slow)
|
||||
target_dev=""
|
||||
for dev in /dev/sd[a-z] /dev/sd[a-z][a-z]; do
|
||||
[[ -b "$dev" ]] || continue
|
||||
# Skip partitions
|
||||
[[ "$dev" =~ [0-9]$ ]] && continue
|
||||
size=$(blockdev --getsize64 "$dev" 2>/dev/null || true)
|
||||
if [[ -n "$size" ]]; then
|
||||
# Allow 5% tolerance on size
|
||||
if (( size >= EMMC_SIZE_BYTES * 95 / 100 && size <= EMMC_SIZE_BYTES * 105 / 100 )); then
|
||||
target_dev=$dev
|
||||
break
|
||||
for wait_sec in $(seq 2 2 10) $(seq 12 2 30); do
|
||||
sleep 2
|
||||
for dev in /dev/sd[a-z] /dev/sd[a-z][a-z]; do
|
||||
[[ -b "$dev" ]] || continue
|
||||
[[ "$dev" =~ [0-9]$ ]] && continue
|
||||
size=$(blockdev --getsize64 "$dev" 2>/dev/null || true)
|
||||
if [[ -n "$size" ]]; then
|
||||
if (( size >= EMMC_SIZE_BYTES * 95 / 100 && size <= EMMC_SIZE_BYTES * 105 / 100 )); then
|
||||
target_dev=$dev
|
||||
break 2
|
||||
fi
|
||||
if [[ -z "$target_dev" && "$before_devs" != *"${dev#/dev/}"* ]]; then
|
||||
target_dev=$dev
|
||||
fi
|
||||
fi
|
||||
# Otherwise take first new disk that appeared (fallback)
|
||||
if [[ -z "$target_dev" && "$before_devs" != *"${dev#/dev/}"* ]]; then
|
||||
target_dev=$dev
|
||||
fi
|
||||
fi
|
||||
done
|
||||
[[ -n "$target_dev" ]] && break
|
||||
log "Waiting for block device... ${wait_sec}s"
|
||||
write_status "rpiboot" "Waiting for eMMC block device… (${wait_sec}s)" "10"
|
||||
done
|
||||
|
||||
log "Device scan complete. before_devs=[$before_devs] target_dev=[$target_dev]"
|
||||
if [[ -z "$target_dev" ]]; then
|
||||
log "No suitable block device found after rpiboot (expected ~${EMMC_SIZE_BYTES} bytes)"
|
||||
write_status "error" "No eMMC device found" "null" "No suitable block device after rpiboot"
|
||||
@@ -131,7 +162,7 @@ for (( i = 0; i < WAIT_TIMEOUT; i += 2 )); do
|
||||
backup_path="$BACKUPS_DIR/$backup_name"
|
||||
write_status "backup" "Creating backup…" "null"
|
||||
log "Backing up $target_dev to $backup_path..."
|
||||
if dd if="$target_dev" of="$backup_path" bs=4M status=progress conv=fsync 2>>"$LOG_FILE"; then
|
||||
if dd if="$target_dev" of="$backup_path" bs=4M status=progress conv=fsync; then
|
||||
log "Backup complete: $backup_path"
|
||||
write_status "done" "Backup complete: $backup_name" "100"
|
||||
else
|
||||
@@ -141,12 +172,11 @@ for (( i = 0; i < WAIT_TIMEOUT; i += 2 )); do
|
||||
if [[ ! -f "$GOLDEN_IMAGE" ]]; then
|
||||
log "Golden image not found; cannot deploy."
|
||||
write_status "error" "Deploy unavailable" "null" "Golden image not found. Add golden.img to /var/lib/cm4-provisioning/ for deploy."
|
||||
rm -f "$CURRENT_DEVICE_FILE" "$DEVICE_SOURCE_FILE" 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
write_status "flashing" "Writing golden image…" "null"
|
||||
log "Flashing $GOLDEN_IMAGE to $target_dev..."
|
||||
if dd if="$GOLDEN_IMAGE" of="$target_dev" bs=4M status=progress conv=fsync 2>>"$LOG_FILE"; then
|
||||
if dd if="$GOLDEN_IMAGE" of="$target_dev" bs=4M status=progress conv=fsync; then
|
||||
log "Flash complete. Remove eMMC disable jumper and power cycle the reTerminal."
|
||||
write_status "done" "Flash complete. Remove eMMC disable jumper and power cycle the reTerminal." "100"
|
||||
else
|
||||
@@ -155,11 +185,9 @@ for (( i = 0; i < WAIT_TIMEOUT; i += 2 )); do
|
||||
else
|
||||
write_status "error" "Unknown action" "null" "action_request must be 'backup' or 'deploy'"
|
||||
fi
|
||||
rm -f "$CURRENT_DEVICE_FILE" "$DEVICE_SOURCE_FILE" 2>/dev/null || true
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
log "Timeout waiting for user choice"
|
||||
write_status "idle" "Timeout waiting for choice. Connect the device again to retry." "null"
|
||||
rm -f "$CURRENT_DEVICE_FILE" "$DEVICE_SOURCE_FILE" 2>/dev/null || true
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user