Files
wlan-ap/feeds/mcu/mcu/files/mcu.init
Piotr Dymacz 560e9f326d mcu: fix host support for multi-slot UART based MCUs
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>
2023-03-23 08:06:42 +01:00

310 lines
6.2 KiB
Bash

#!/bin/sh /etc/rc.common
START=80
. /lib/functions/mcu.sh
SECT=
mcu_setup_uart() {
local uart="$1"
local baud="$2"
local flow="$3"
local gpio_path="$4"
local gpio_on="$5"
local sn="$6"
local fw_type
# Take out MCU out of reset and read basic info
mcu_enable_pin_set "$gpio_path" "$gpio_on"
sleep 1
mcu_sn_check_and_update "$sn" "$uart" "$baud" "$flow"
[ $? -ne 0 ] && return 1
mcu_fw_check_and_update "$uart" "$baud" "$flow"
rc="$?"
[ "$rc" = "1" ] && return 1
[ "$rc" = "2" ] && {
sleep 1
mcu_req "boot" "$uart" "$baud" "$flow"
[ $? -ne 0 ] && return 1
}
fw_type="$(uci -q get "mcu.${SECT}.firmware" | awk -F '__' '{print $2}')"
[ -n "$fw_type" ] || return 0
[ -x "${MCU_HS_DIR}/${fw_type}.sh" ] && \
"${MCU_HS_DIR}/${fw_type}.sh" "$SECT"
}
mcu_setup_usb() {
local gpio_path="$1"
local gpio_on="$2"
local sn="$3"
local gpio_off="0"
local uart
local fw_type
[ "$gpio_on" = "0" ] && gpio_off="1"
# If we have S/N in config, only take out the MCU from reset
[ -n "$sn" ] && {
mcu_logi "MCU S/N already set, hotplug will perform config"
mcu_enable_pin_set "$gpio_path" "$gpio_on"
return 0
}
# If S/N is missing, we need to take out MCU from reset, find out
# its S/N and save it for later
exec 9>"$MCU_FLOCK_FILE" || {
mcu_loge "failed to obtain lock (exec fail)!"
return 1
}
flock -n 9 || {
mcu_loge "failed to obtain lock (flock fail)!"
return 1
}
usb_path="/sys/bus/usb/devices/*"
devs_old=""
for dev_path in $usb_path; do
dev="$(basename "$dev_path")"
[[ $dev == *":"* ]] && continue
p="$(cat "${dev_path}/product" 2>/dev/null)"
id="$(cat "${dev_path}/idVendor" 2>/dev/null)"
id="${id}$(cat "${dev_path}/idProduct" 2>/dev/null)"
[ "$p" = "$MCUBOOT_USB_PRODUCT" -a \
"$id" = "$MCUBOOT_USB_VID_PID" ] && \
devs_old="$devs_old $dev"
done
mcu_enable_pin_set "$gpio_path" "$gpio_on"
sleep 2
dev_found=""
for dev_path in $usb_path; do
dev="$(basename "$dev_path")"
[[ $dev == *":"* ]] && continue
p="$(cat "${dev_path}/product" 2>/dev/null)"
id="$(cat "${dev_path}/idVendor" 2>/dev/null)"
id="${id}$(cat "${dev_path}/idProduct" 2>/dev/null)"
[ "$p" = "$MCUBOOT_USB_PRODUCT" -a \
"$id" = "$MCUBOOT_USB_VID_PID" ] && {
[ -n "$devs_old" ] && {
if echo "$devs_old" | grep -q "$dev"; then
continue
fi
}
dev_found="$dev"
break
}
done
[ -n "$dev_found" ] || {
mcu_loge "failed to find MCU on USB bus"
mcu_enable_pin_set "$gpio_path" "$gpio_off"
flock -u 9
return 1
}
mcu_logd "MCU found on USB bus: '$dev_found'"
# We expect just a single ttyACM interface
usb_path="/sys/bus/usb/devices/${dev_found}*/tty/*"
for tty_path in $usb_path; do
tty="$(basename "$tty_path")"
[ -c "/dev/${tty}" ] && {
uart="/dev/${tty}"
break
}
done
[ -n "$uart" ] || {
mcu_loge "failed to find ttyACM interface"
mcu_enable_pin_set "$gpio_path" "$gpio_off"
flock -u 9
return 1
}
mcu_sn_check_and_update "$sn" "$uart"
[ $? -ne 0 ] && {
mcu_enable_pin_set "$gpio_path" "$gpio_off"
flock -u 9
return 1
}
mcu_fw_check_and_update "$uart"
rc="$?"
[ "$rc" = "1" ] && {
mcu_enable_pin_set "$gpio_path" "$gpio_off"
flock -u 9
return 1
}
[ "$rc" = "0" ] && {
fw_type="$(uci -q get "mcu.${SECT}.firmware" | awk -F '__' '{print $2}')"
[ -n "$fw_type" -a -x "${MCU_HS_DIR}/${fw_type}.sh" ] && \
"${MCU_HS_DIR}/${fw_type}.sh" "$SECT"
}
flock -u 9
}
mcu_setup() {
local sn
local action
local fw_type
local disabled
local uart_baud
local uart_flow
local uart_path
local interface
local bootloader
local enable_pin
local gpio_path
local gpio_on="1"
local gpio_off="0"
SECT="$1"
MCU_SCRIPT_NAME="mcu-init.${SECT}"
action="$2"
MCU_SYSINFO_OUTPUT=""
MCU_IMGLIST_OUTPUT=""
# Section disabled?
[ "$action" = "start" ] && {
config_get_bool disabled "$SECT" disabled "0"
[ "$disabled" = "1" ] && {
mcu_logw "section is disabled in config"
return 0
}
}
config_get sn "$SECT" sn
config_get bootloader "$SECT" bootloader
config_get enable_pin "$SECT" enable_pin
config_get interface "$SECT" interface
config_get uart_path "$SECT" uart_path
config_get uart_baud "$SECT" uart_baud "115200"
config_get_bool uart_flow "$SECT" uart_flow "0"
# Stop related service
[ "$action" = "stop" ] && {
[ -n "$sn" -a -f "/var/run/mcu.${sn}.pid" ] && {
kill "$(cat "/var/run/mcu.${sn}.pid" 2>/dev/null)" \
> /dev/null 2>&1
rm -f "/var/run/mcu.${sn}.pid" > /dev/null 2>&1
}
}
# As for now, only 'mcuboot' bootloader is supported
case "$bootloader" in
"mcuboot")
command -v umcumgr > /dev/null 2>&1 || {
mcu_loge "missing 'umcumgr' tool"
return 1
}
;;
*)
mcu_loge "unsupported or unset 'bootloader' option"
return 1
;;
esac
# Verify 'enable_pin' option
if [ -z "$enable_pin" ]; then
# USB based MCU without GPIO based way for reset are fully
# handled by the hotplug script
[ "$interface" = "usb" ] && {
mcu_logw "'enable_pin' option is unset, ignoring"
return 0
}
[ "$interface" = "uart" ] && {
mcu_loge "'enable_pin' option is unset"
return 1
}
else
gpio_path="/sys/class/gpio/${enable_pin}"
[ -d "$gpio_path" ] || {
mcu_loge "invalid 'enable_pin' option"
return 1
}
[ "$(cat "${gpio_path}/active_low")" = "1" ] && {
gpio_on="0"
gpio_off="1"
}
# TODO: should we maybe bail out here if the MCU was took out
# of reset already before, by something/someone else?
[ "$(cat "${gpio_path}/value")" = "$gpio_on" ] && {
if [ "$action" = "start" ]; then
mcu_logw "MCU already enabled, resetting"
else
mcu_logi "disabling MCU"
fi
mcu_enable_pin_set "$gpio_path" "$gpio_off"
sleep 1
}
fi
[ "$action" = "stop" ] && return 0
# For now only 'usb' and 'uart' interfaces are supported
case "$interface" in
"uart")
[ -z "$uart_path" -o ! -c "$uart_path" ] && {
mcu_loge "invalid or unset 'uart_path' option"
return 1
}
mcu_setup_uart "$uart_path" "$uart_baud" "$uart_flow" \
"$gpio_path" "$gpio_on" "$sn"
;;
"usb")
mcu_setup_usb "$gpio_path" "$gpio_on" "$sn"
;;
*)
mcu_loge "unsupported or unset 'interface' option"
return 1
;;
esac
}
start() {
config_load mcu
config_foreach mcu_setup mcu "start"
return 0
}
stop() {
config_load mcu
config_foreach mcu_setup mcu "stop"
return 0
}