mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-11-01 02:48:18 +00:00
In case of MCU with multiple firmware slots support, change of active slot requires reset. This obviously results in MCU entering the serial recovery mode in bootloader, with 5 sec timeout, which in case of UART based MCUs isn't automatically detected and handled in the same way as USB based devices (hotplug). Starting host side support script when the MCU is waiting for MCUmgr commands during recovery is wrong. This fixes the problem by requesting UART based MCU to boot the firmware after active slot change followed by reset. While at it, change also how single slot type MCUs are handled during upgrade (always request reset after the upgrade). Signed-off-by: Piotr Dymacz <pepe2k@gmail.com>
502 lines
10 KiB
Bash
502 lines
10 KiB
Bash
#!/bin/sh
|
|
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
# Product name and VID:PID used by OpenWrt/OpenWiFi MCUboot fork
|
|
MCUBOOT_USB_PRODUCT="MCUboot serial recovery"
|
|
MCUBOOT_USB_VID_PID="16c005e1"
|
|
|
|
# Host support and firmware directories
|
|
MCU_HS_DIR="/etc/mcu.d"
|
|
MCU_FW_DIR="/lib/firmware/mcu"
|
|
|
|
MCU_FLOCK_FILE="/tmp/lock/mcu"
|
|
|
|
MCU_SYSINFO_OUTPUT=
|
|
MCU_IMGLIST_OUTPUT=
|
|
|
|
MCU_SCRIPT_NAME=""
|
|
|
|
# logger helpers
|
|
mcu_log() {
|
|
if [ -n "$MCU_SCRIPT_NAME" ]; then
|
|
logger -p "$1" -t "${MCU_SCRIPT_NAME}[$$]" "$2"
|
|
else
|
|
logger -p "$1" "$2"
|
|
fi
|
|
}
|
|
|
|
mcu_loge() {
|
|
mcu_log "err" "$1"
|
|
}
|
|
|
|
mcu_logd() {
|
|
mcu_log "debug" "$1"
|
|
}
|
|
|
|
mcu_logi() {
|
|
mcu_log "info" "$1"
|
|
}
|
|
|
|
mcu_logn() {
|
|
mcu_log "notice" "$1"
|
|
}
|
|
|
|
mcu_logw() {
|
|
mcu_log "warn" "$1"
|
|
}
|
|
|
|
mcu_fetch_fwlist() {
|
|
local uart="$1"
|
|
local baud="$2"
|
|
local flow="$3"
|
|
|
|
if [ "$flow" = "1" ]; then
|
|
flow=" -f"
|
|
else
|
|
flow=""
|
|
fi
|
|
|
|
[ -n "$baud" ] || baud="115200"
|
|
|
|
[ -n "$MCU_IMGLIST_OUTPUT" ] && return 0
|
|
|
|
MCU_IMGLIST_OUTPUT="$(umcumgr -s -d "$uart" -b "$baud$flow" list)"
|
|
[ $? -eq 0 ] || {
|
|
mcu_loge "request 'list' failed (uart='$uart', baud='$baud', flow='$flow')"
|
|
return 1
|
|
}
|
|
}
|
|
|
|
mcu_fetch_sysinfo() {
|
|
local uart="$1"
|
|
local baud="$2"
|
|
local flow="$3"
|
|
|
|
if [ "$flow" = "1" ]; then
|
|
flow=" -f"
|
|
else
|
|
flow=""
|
|
fi
|
|
|
|
[ -n "$baud" ] || baud="115200"
|
|
|
|
[ -n "$MCU_SYSINFO_OUTPUT" ] && return 0
|
|
|
|
MCU_SYSINFO_OUTPUT="$(umcumgr -s -d "$uart" -b "$baud$flow" sysinfo)"
|
|
[ $? -eq 0 ] || {
|
|
mcu_loge "request 'sysinfo' failed (uart='$uart', baud='$baud', flow='$flow')"
|
|
return 1
|
|
}
|
|
}
|
|
|
|
mcu_get() {
|
|
local param="$1"
|
|
local slot="$2"
|
|
|
|
local value
|
|
local metadata
|
|
local sysinfo_field
|
|
|
|
case "$param" in
|
|
"board"|\
|
|
"soft_ver"|\
|
|
"serial_num"|\
|
|
"active_slot")
|
|
sysinfo_field="$param"
|
|
;;
|
|
"slots_num")
|
|
sysinfo_field="single_slot"
|
|
;;
|
|
"fwname")
|
|
[ -n "$slot" ] || return 1
|
|
param="image list: slot${slot} fw_name"
|
|
|
|
metadata="$(echo "$MCU_IMGLIST_OUTPUT" | grep "slot${slot}_metadata=" | cut -d '=' -f 2)"
|
|
[ -n "$metadata" ] && {
|
|
json_load "$metadata"
|
|
json_get_var value fw_name
|
|
}
|
|
;;
|
|
"fwsha")
|
|
[ -n "$slot" ] || return 1
|
|
param="image list: slot${slot}_hash"
|
|
|
|
value="$(echo "$MCU_IMGLIST_OUTPUT" | grep "slot${slot}_hash=" | cut -d '=' -f 2)"
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
[ -n "$sysinfo_field" ] && {
|
|
value="$(echo "$MCU_SYSINFO_OUTPUT" | grep "${sysinfo_field}=" | cut -d '=' -f 2)"
|
|
|
|
[ "$sysinfo_field" = "single_slot" ] && {
|
|
[ -n "$value" ] || value="1"
|
|
[ "$value" != "1" ] && value="2"
|
|
}
|
|
|
|
param="sysinfo: $param"
|
|
}
|
|
|
|
[ -n "$value" ] && mcu_logd "$param: '$value'"
|
|
echo "$value"
|
|
}
|
|
|
|
mcu_req() {
|
|
local cmd="$1"
|
|
local uart="$2"
|
|
local baud="$3"
|
|
local flow="$4"
|
|
|
|
case "$cmd" in
|
|
"boot"|\
|
|
"reset")
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
if [ "$flow" = "1" ]; then
|
|
flow=" -f"
|
|
else
|
|
flow=""
|
|
fi
|
|
|
|
[ -n "$baud" ] || baud="115200"
|
|
|
|
umcumgr -s -d "$uart" -b "$baud$flow" "$cmd" || {
|
|
mcu_loge "request '$cmd' failed (uart='$uart', baud='$baud', flow='$flow')"
|
|
return 1
|
|
}
|
|
|
|
mcu_logi "MCU requested '$cmd'"
|
|
}
|
|
|
|
mcu_sel_slot() {
|
|
local slot="$1"
|
|
local uart="$2"
|
|
local baud="$3"
|
|
local flow="$4"
|
|
|
|
if [ "$flow" = "1" ]; then
|
|
flow=" -f"
|
|
else
|
|
flow=""
|
|
fi
|
|
|
|
[ -n "$baud" ] || baud="115200"
|
|
|
|
# Request firmware active slot change
|
|
umcumgr -s -d "$uart" -b "$baud$flow" select "$slot" || {
|
|
mcu_loge "request 'select slot' failed (uart='$uart', baud='$baud', flow='$flow')"
|
|
return 1
|
|
}
|
|
|
|
mcu_logi "active firmware slot changed to: '$slot'"
|
|
}
|
|
|
|
mcu_fwfile_sha() {
|
|
local path="$1"
|
|
|
|
local value
|
|
|
|
[ -f "$path" ] || return 1
|
|
|
|
value="$(umcumgr -s hash "$path" | grep "hash=" | cut -d '=' -f 2)"
|
|
[ -n "$value" ] || return 1
|
|
|
|
echo "$value"
|
|
}
|
|
|
|
mcu_fw_upload() {
|
|
local board="$1"
|
|
local slot="$2"
|
|
local fw_name="$3"
|
|
local uart="$4"
|
|
local baud="$5"
|
|
local flow="$6"
|
|
|
|
local fw_path
|
|
|
|
if [ "$flow" = "1" ]; then
|
|
flow=" -f"
|
|
else
|
|
flow=""
|
|
fi
|
|
|
|
[ -n "$baud" ] || baud="115200"
|
|
|
|
fw_path="${MCU_FW_DIR}/${board}/${fw_name}/slot${slot}.bin"
|
|
umcumgr -q info "$fw_path" > /dev/null 2>&1 || {
|
|
mcu_loge "invalid or missing firmware file: '$fw_path'"
|
|
return 1
|
|
}
|
|
|
|
mcu_logi "uploading '$fw_name' to slot: '$slot'..."
|
|
|
|
# Upload fw to selected slot (TODO: slots numbering Zephyr vs. MCUboot)
|
|
[ "$slot" = "1" ] && slot="2"
|
|
umcumgr -q -n "$slot" -d "$uart" -b "$baud$flow" upload "$fw_path" || {
|
|
mcu_loge "request 'upload' failed (uart='$uart', baud='$baud', flow='$flow')"
|
|
return 1
|
|
}
|
|
|
|
mcu_logi "firmware uploaded!"
|
|
}
|
|
|
|
mcu_enable_pin_set() {
|
|
local gpio="$1"
|
|
local gpio_value="$2"
|
|
|
|
mcu_logd "setting MCU enable_pin '$(basename "$gpio")' to '$gpio_value'"
|
|
echo "$gpio_value" > "${gpio}/value" 2>/dev/null
|
|
}
|
|
|
|
mcu_sn_check_and_update() {
|
|
local sn="$1"
|
|
local uart="$2"
|
|
local baud="$3"
|
|
local flow="$4"
|
|
|
|
local sn_dev
|
|
|
|
# Fetch sysinfo
|
|
mcu_fetch_sysinfo "$uart" "$baud" "$flow" || return 1
|
|
|
|
sn_dev="$(mcu_get "serial_num")"
|
|
[ -n "$sn_dev" ] || return 1
|
|
|
|
[ -n "$sn_dev" ] && {
|
|
if [ -z "$sn" ]; then
|
|
[ -d /etc/config-shadow ] && {
|
|
uci -c /etc/config-shadow -q set mcu.${SECT}.sn="$sn_dev"
|
|
uci -c /etc/config-shadow -q commit mcu
|
|
}
|
|
|
|
uci -q set mcu.${SECT}.sn="$sn_dev"
|
|
uci -q commit mcu
|
|
else
|
|
[ "$sn" != "$sn_dev" ] && {
|
|
mcu_loge "MCU S/N mismatch ('$sn_dev' != '$sn')!"
|
|
return 1
|
|
}
|
|
fi
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
# Returns:
|
|
# 0 if MCU was requested to boot firmware
|
|
# 1 on error
|
|
# 2 if MCU was requested to reset
|
|
mcu_fw_check_and_update() {
|
|
local uart="$1"
|
|
local baud="$2"
|
|
local flow="$3"
|
|
|
|
local active_slot
|
|
local fw_slots
|
|
local slot0_fw
|
|
local slot0_sha
|
|
local slot1_fw
|
|
local slot1_sha
|
|
local firmware
|
|
local fw0_sha
|
|
local fw1_sha
|
|
local board
|
|
local soft_ver
|
|
|
|
config_get firmware "$SECT" firmware
|
|
[ -n "$firmware" ] || mcu_logw "option 'firmware' unset"
|
|
|
|
# Fetch sysinfo and firmware images list
|
|
mcu_fetch_sysinfo "$uart" "$baud" "$flow" || return 1
|
|
mcu_fetch_fwlist "$uart" "$baud" "$flow" || return 1
|
|
|
|
# MCU board name and software version
|
|
board="$(mcu_get "board")"
|
|
[ $? -eq 0 ] || return 1
|
|
|
|
soft_ver="$(mcu_get "soft_ver")"
|
|
[ $? -eq 0 ] || return 1
|
|
|
|
# Number of firmware slots and active slot
|
|
fw_slots="$(mcu_get "slots_num")"
|
|
[ $? -eq 0 ] || return 1
|
|
[ -n "$fw_slots" ] || fw_slots="1"
|
|
|
|
[ "$fw_slots" = "2" ] && {
|
|
active_slot="$(mcu_get "active_slot")"
|
|
[ $? -eq 0 ] || return 1
|
|
}
|
|
[ -n "$active_slot" ] || active_slot="0"
|
|
|
|
# Firmware available?
|
|
[ -n "$firmware" ] && {
|
|
if [ "$fw_slots" = "2" ]; then
|
|
[ -f "${MCU_FW_DIR}/${board}/${firmware}/slot0.bin" -a \
|
|
-f "${MCU_FW_DIR}/${board}/${firmware}/slot1.bin" ] || {
|
|
mcu_loge "firmware '$firmware' doesn't exist"
|
|
return 1
|
|
}
|
|
|
|
fw1_sha="$(mcu_fwfile_sha "${MCU_FW_DIR}/${board}/${firmware}/slot1.bin")"
|
|
else
|
|
[ -f "${MCU_FW_DIR}/${board}/${firmware}/slot0.bin" ] || {
|
|
mcu_loge "firmware '$firmware' doesn't exist"
|
|
return 1
|
|
}
|
|
fi
|
|
|
|
fw0_sha="$(mcu_fwfile_sha "${MCU_FW_DIR}/${board}/${firmware}/slot0.bin")"
|
|
}
|
|
|
|
slot0_fw="$(mcu_get "fwname" "0")"
|
|
[ $? -eq 0 ] || return 1
|
|
|
|
[ -n "$slot0_fw" ] && {
|
|
slot0_sha="$(mcu_get "fwsha" "0")"
|
|
[ $? -eq 0 ] || return 1
|
|
}
|
|
|
|
[ "$fw_slots" = "2" ] && {
|
|
slot1_fw="$(mcu_get "fwname" "1")"
|
|
[ $? -eq 0 ] || return 1
|
|
|
|
[ -n "$slot1_fw" ] && {
|
|
slot1_sha="$(mcu_get "fwsha" "1")"
|
|
[ $? -eq 0 ] || return 1
|
|
}
|
|
}
|
|
|
|
# No target firmware provided, check what's on device and update config
|
|
[ -n "$firmware" ] || {
|
|
firmware="$slot0_fw"
|
|
[ "$active_slot" = "1" ] && firmware="$slot1_fw"
|
|
|
|
[ -n "$firmware" ] && {
|
|
[ -d /etc/config-shadow ] && {
|
|
uci -c /etc/config-shadow -q set mcu.${SECT}.firmware="$firmware"
|
|
uci -c /etc/config-shadow -q commit mcu
|
|
}
|
|
|
|
uci -q set mcu.${SECT}.firmware="$firmware"
|
|
uci -q commit mcu
|
|
|
|
mcu_req "boot" "$uart" "$baud" "$flow"
|
|
[ $? -ne 0 ] && return 1
|
|
|
|
return 0
|
|
}
|
|
|
|
return 1
|
|
}
|
|
|
|
# Do we have target firmware installed in the first slot?
|
|
[ "$firmware" = "$slot0_fw" -a "$slot0_sha" = "$fw0_sha" ] && {
|
|
mcu_logi "found matching firmware installed in slot '0'"
|
|
|
|
if [ "$fw_slots" = "2" -a "$active_slot" != "0" ]; then
|
|
mcu_sel_slot "0" "$uart" "$baud" "$flow"
|
|
[ $? -ne 0 ] && return 1
|
|
|
|
# Changing active slots requires MCU reset at the moment
|
|
mcu_req "reset" "$uart" "$baud" "$flow"
|
|
[ $? -ne 0 ] && return 1
|
|
|
|
return 2
|
|
else
|
|
mcu_req "boot" "$uart" "$baud" "$flow"
|
|
[ $? -ne 0 ] && return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
mcu_logi "no matching firmware found in slot '0'"
|
|
|
|
# Upload firmware and reset on single-slot device
|
|
[ "$fw_slots" = "1" ] && {
|
|
mcu_fw_upload "$board" "0" "$firmware" "$uart" "$baud" "$flow"
|
|
[ $? -ne 0 ] && return 1
|
|
|
|
mcu_req "reset" "$uart" "$baud" "$flow"
|
|
[ $? -ne 0 ] && return 1
|
|
|
|
return 2
|
|
}
|
|
|
|
# Do we have target firmware installed in the second slot?
|
|
[ "$firmware" = "$slot1_fw" -a "$slot1_sha" = "$fw1_sha" ] && {
|
|
mcu_logi "found matching firmware installed in slot '1'"
|
|
|
|
if [ "$active_slot" != "1" ]; then
|
|
mcu_sel_slot "1" "$uart" "$baud" "$flow"
|
|
[ $? -ne 0 ] && return 1
|
|
|
|
# Changing active slots requires MCU reset at the moment
|
|
mcu_req "reset" "$uart" "$baud" "$flow"
|
|
[ $? -ne 0 ] && return 1
|
|
|
|
return 2
|
|
else
|
|
mcu_req "boot" "$uart" "$baud" "$flow"
|
|
[ $? -ne 0 ] && return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
mcu_logi "no matching firmware found in slot '1'"
|
|
|
|
# Upload and boot firmware on multi-slot device
|
|
# Always use inactive slot
|
|
if [ "$active_slot" = "0" ]; then
|
|
active_slot="1"
|
|
else
|
|
active_slot="0"
|
|
fi
|
|
|
|
mcu_fw_upload "$board" "$active_slot" "$firmware" "$uart" "$baud" "$flow"
|
|
[ $? -ne 0 ] && return 1
|
|
|
|
mcu_sel_slot "$active_slot" "$uart" "$baud" "$flow"
|
|
[ $? -ne 0 ] && return 1
|
|
|
|
# Changing active slots requires MCU reset at the moment
|
|
mcu_req "reset" "$uart" "$baud" "$flow"
|
|
[ $? -ne 0 ] && return 1
|
|
|
|
return 2
|
|
}
|
|
|
|
mcu_add_uci_config() {
|
|
local name="$1"
|
|
local interface="$2"
|
|
local bootloader="$3"
|
|
local firmware="$4"
|
|
local enable_pin="$5"
|
|
local uart_path="$6"
|
|
local uart_baud="$7"
|
|
local uart_flow="$8"
|
|
|
|
uci -q set mcu.${name}="mcu"
|
|
uci -q set mcu.${name}.interface="$interface"
|
|
uci -q set mcu.${name}.bootloader="$bootloader"
|
|
uci -q set mcu.${name}.firmware="$firmware"
|
|
|
|
[ -n "$enable_pin" ] && uci -q set mcu.${name}.enable_pin="$enable_pin"
|
|
[ -n "$uart_path" ] && uci -q set mcu.${name}.uart_path="$uart_path"
|
|
[ -n "$uart_baud" ] && uci -q set mcu.${name}.uart_baud="$uart_baud"
|
|
|
|
[ "$uart_flow" = "1" ] && uci -q set mcu.${name}.uart_flow="1"
|
|
|
|
uci -q set mcu.${name}.disabled="0"
|
|
|
|
uci -q commit mcu
|
|
}
|