blob: f3a54216988f25d257d0f20f4513c41ea5f3d1b0 [file] [log] [blame]
#!/bin/bash
# Copyright 2019 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Disable check that incorrectly detects the "config_*" functions as unreachable.
# shellcheck disable=SC2317
shflags_path=""
for path in \
"/usr/share/misc/shflags" \
"/system/usr/share/misc/shflags"
do
if [ -f "${path}" ]; then
shflags_path="${path}"
break
fi
done
# shellcheck source=../../../scripts/lib/shflags/shflags
. "${SHFLAGS:-${shflags_path}}" || exit 1
readonly DEFAULT_RETRIES=${DEFAULT_RETRIES:-4}
readonly STM32MON_CONNECT_RETRIES=${STM32MON_CONNECT_RETRIES:-6}
readonly STM32MON_SERIAL_BAUDRATE=${STM32MON_SERIAL_BAUDRATE:-115200}
DEFINE_boolean 'read' "${FLAGS_FALSE}" 'Read instead of write' 'r'
# Both flash read and write protection are removed by default, but you
# can optionally enable them (for testing purposes).
DEFINE_boolean 'remove_flash_read_protect' "${FLAGS_TRUE}" \
'Remove flash read protection while performing command' 'U'
DEFINE_boolean 'remove_flash_write_protect' "${FLAGS_TRUE}" \
'Remove flash write protection while performing command' 'u'
DEFINE_integer 'retries' "${DEFAULT_RETRIES}" 'Specify number of retries' 'R'
DEFINE_integer 'baudrate' "${STM32MON_SERIAL_BAUDRATE}" 'Specify UART baudrate' 'B'
DEFINE_boolean 'hello' "${FLAGS_FALSE}" 'Only ping the bootloader' 'H'
DEFINE_boolean 'services' "${FLAGS_TRUE}" \
'Stop the biod service before flashing and then resume it afterwards' 's'
FLAGS_HELP="Usage: ${0} [flags] [ec.bin]"
# EXIT_SUCCESS=0
# EXIT_FAILURE=1
# EXIT_BASHBUILTIN=2
readonly EXIT_ARGUMENT=3
readonly EXIT_CONFIG=4
readonly EXIT_PRECONDITION=5
readonly EXIT_RUNTIME=6
# Process commandline flags
FLAGS "${@}" || exit "${EXIT_ARGUMENT}"
eval set -- "${FLAGS_ARGV}"
readonly CROS_EC_SPI_MODALIAS_STR="of:NcrfpTCgoogle,cros-ec-spi"
readonly CROS_EC_FP_SPI_MODALIAS_STR="spi:cros-ec-fp"
readonly CROS_EC_UART_MODALIAS_STR="of:NcrfpTCgoogle,cros-ec-uart"
# Clean up any remaining gpioset processes on exit.
trap 'kill "${GPIO_ACTIVE_PIDS[@]}" 2>/dev/null' EXIT
klog() {
echo "flash_fp_mcu: $*" > /dev/kmsg
}
# POSIX-compliant helper to check if a name is a defined function.
# Usage: is_function <function_name>
is_function() {
type "$1" 2>/dev/null | grep -q 'function'
}
os() {
if [[ -f "/system/build.prop" ]]; then
echo "android"
return 0
fi
if [[ -f "/etc/lsb-release" ]] && grep -q "CHROMEOS_RELEASE" "/etc/lsb-release" 2>/dev/null; then
echo "chromeos"
return 0
fi
echo "unknown"
return 1
}
check_spidev() {
if [[ -d "/sys/module/spidev" ]]; then
return 0
fi
if [[ "$(os)" == "android" ]]; then
if insmod /vendor/lib/modules/spidev.ko; then
return 0
fi
fi
echo "Failed to load spidev kernel module" >&2
exit "${EXIT_PRECONDITION}"
}
stop_services() {
if [[ "$(os)" == "android" ]]; then
echo "# Stopping vendor.biometrics.fingerprint-service.crosfp"
stop vendor.biometrics.fingerprint-service.crosfp
return 0
fi
if [[ "${FLAGS_services}" -eq "${FLAGS_TRUE}" ]]; then
echo "# Stopping biod"
stop biod
fi
echo "# Stopping timberslide"
stop timberslide LOG_PATH=/sys/kernel/debug/cros_fp/console_log
}
start_services() {
if [[ "$(os)" == "android" ]]; then
echo "# Restarting vendor.biometrics.fingerprint-service.crosfp"
start vendor.biometrics.fingerprint-service.crosfp
return 0
fi
if [[ "${FLAGS_services}" -eq "${FLAGS_TRUE}" ]]; then
echo "# Restarting timberslide"
start timberslide LOG_PATH=/sys/kernel/debug/cros_fp/console_log
echo "# Restarting biod"
start biod
fi
}
echo_service_warning() {
if [[ "$(os)" == "android" ]]; then
echo "Try 'stop vendor.biometrics.fingerprint-service.crosfp'" >&2
else
echo "Try 'stop biod' and" >&2
echo "'stop timberslide LOG_PATH=/sys/kernel/debug/cros_fp/console_log'" >&2
fi
echo "before running this script." >&2
}
check_hardware_write_protect_disabled() {
local hardware_write_protect_state
if ! hardware_write_protect_state="$(crossystem wpsw_cur)"; then
echo "Failed to get hardware write protect status" >&2
exit "${EXIT_PRECONDITION}"
fi
if [[ "${hardware_write_protect_state}" != "0" ]]; then
echo "Unable to proceed because hardware write protect is enabled."
echo "Please disable the hardware write protect, so that we can" \
"reboot the FPMCU into the ROM bootloader."
echo "See https://www.chromium.org/chromium-os/firmware-porting-guide/firmware-ec-write-protection"
exit "${EXIT_PRECONDITION}"
fi
}
# Get the gpiochip base number from sysfs that matches
# a device path
get_sysfs_gpiochip_base() {
local match="$1"
for chip in /sys/class/gpio/gpiochip*; do
if readlink "${chip}" | grep -q "${match}"; then
echo "${chip#/sys/class/gpio/gpiochip}"
return 0
fi
done
return 1
}
# Get the spiid for the fingerprint sensor based on the modalias
# string: https://crbug.com/955117
get_spiid() {
for dev in /sys/bus/spi/devices/*; do
case "$(cat "${dev}/modalias")" in
"${CROS_EC_SPI_MODALIAS_STR}"|"${CROS_EC_FP_SPI_MODALIAS_STR}")
basename "${dev}"
return 0
;;
esac
done
return 1
}
# Get the uartid for the fingerprint sensor based on the modalias
get_uartid() {
for dev in /sys/bus/serial/devices/*; do
if [ -f "${dev}/modalias" ]; then
if [[ "$(cat "${dev}/modalias")" == "${CROS_EC_UART_MODALIAS_STR}" ]]; then
basename "${dev}"
return 0
fi
fi
done
return 1
}
# Usage: gpio <unexport|export|in|out|0|1|get> <signal> [signal...]
_gpio_legacy() {
local cmd="$1"
shift
for signal in "$@"; do
case "${cmd}" in
unexport|export)
klog "Set gpio ${signal} to ${cmd}"
echo "${signal}" > "/sys/class/gpio/${cmd}"
;;
in|out)
local direction="${cmd}"
klog "Set gpio ${signal} direction to ${direction}"
echo "${direction}" > "/sys/class/gpio/gpio${signal}/direction"
;;
0|1)
local value="${cmd}"
klog "Set gpio ${signal} to ${value}"
echo "${value}" > "/sys/class/gpio/gpio${signal}/value"
;;
get)
local value="${cmd}"
klog "Get gpio ${signal}"
cat "/sys/class/gpio/gpio${signal}/value"
;;
*)
echo "Invalid gpio command: ${cmd}" >&2
exit "${EXIT_RUNTIME}"
;;
esac
done
}
# Usage: _gpioset_hold <gpio_chip> <signal> <value>
_gpioset_hold() {
local gpio_chip="$1"
local signal="$2"
local value="$3"
# Kill any gpioset processes for this GPIO before spawning a new one.
if [[ -n "${GPIO_ACTIVE_PIDS[${signal}]}" ]]; then
kill "${GPIO_ACTIVE_PIDS[${signal}]}"
unset "GPIO_ACTIVE_PIDS[${signal}]"
fi
# We need to run gpioset as a background process in order to hold the
# gpio at a specific value.
gpioset --mode=signal "${gpio_chip}" "${signal}=${value}" &
GPIO_ACTIVE_PIDS[${signal}]=$!
klog "Set and hold GPIO ${gpio_chip}/${signal} to ${value} (PID: ${GPIO_ACTIVE_PIDS[${signal}]})"
}
# Usage: _gpioset <gpio_chip> <signal> <value>
_gpioset() {
local gpio_chip="$1"
local signal="$2"
local value="$3"
gpioset "${gpio_chip}" "${signal}=${value}"
klog "Set GPIO ${gpio_chip}/${signal} to ${value}"
}
# Usage: gpio <unexport|export|in|out|0|1|get> <signal> [signal...]
_gpio_new() {
local cmd="$1"
shift
if [[ -z "${GPIO_CHIP}" ]]; then
echo "Error: GPIO_CHIP variable is not set." >&2
exit "${EXIT_CONFIG}"
fi
for signal in "$@"; do
case "${cmd}" in
0|1)
if [[ "${GPIOSET_SUPPORTS_HOLD}" -eq 1 ]]; then
_gpioset_hold "${GPIO_CHIP}" "${signal}" "${cmd}"
else
_gpioset "${GPIO_CHIP}" "${signal}" "${cmd}"
fi
;;
get)
klog "Get GPIO ${GPIO_CHIP}/${signal}"
gpioget "${GPIO_CHIP}" "${signal}"
;;
unexport|export|in|out)
# no-op
;;
*)
echo "Invalid gpio command: ${cmd}" >&2
exit "${EXIT_RUNTIME}"
;;
esac
done
}
# Usage: gpio <0|1|get> <signal> [signal...]
gpio() {
if [[ "${USE_LEGACY_GPIO_API}" -eq 1 ]]; then
_gpio_legacy "$@"
else
_gpio_new "$@"
fi
}
gpio_chip_exists() {
if [[ "${USE_LEGACY_GPIO_API}" -eq 1 ]]; then
[[ -e "/sys/class/gpio/${GPIO_CHIP}" ]]
return
fi
gpioinfo "${GPIO_CHIP}" > /dev/null 2>&1
}
# Usage: warn_gpio <signal> <expected_value> <msg>
warn_gpio() {
local signal=$1
local expected_value=$2
local msg=$3
local value
if ! value="$(gpio get "${signal}")"; then
echo "Error fetching gpio value ${signal}" >&2
exit "${EXIT_RUNTIME}"
fi
if [[ "${value}" != "${expected_value}" ]]; then
echo "${msg}" >&2
return 1
fi
}
# Taken verbatim from
# https://chromium.googlesource.com/chromiumos/docs/+/HEAD/lsb-release.md#shell
# This should not be used by anything except get_platform_name.
# See https://crbug.com/98462.
lsbval() {
local key="$1"
local lsbfile="${2:-/etc/lsb-release}"
if ! echo "${key}" | grep -Eq '^[a-zA-Z0-9_]+$'; then
return 1
fi
sed -E -n -e \
"/^[[:space:]]*${key}[[:space:]]*=/{
s:^[^=]+=[[:space:]]*::
s:[[:space:]]+$::
p
}" "${lsbfile}"
}
# Get the underlying board (reference design) that we're running on (not the
# FPMCU or sensor).
# This may be an extended platform name, like nami-kernelnext, hatch-arc-r,
# or hatch-borealis.
get_platform_name() {
local platform_name
case "$(os)" in
"chromeos")
# We used to use "cros_config /identity platform-name", but that is specific
# to mosys and does not actually provide the board name in all cases.
# cros_config intentionally does not provide a way to get the board
# name: b/156650654.
# If there was a way to get the board name from cros_config, it's possible
# that it could fail in the following cases:
#
# 1) We're running on a non-unibuild device (the only one with FP is nocturne)
# 2) We're running on a proto device during bringup and the cros_config
# settings haven't yet been setup.
#
# In all cases we can fall back to /etc/lsb-release. It's not recommended
# to do this, but we don't have any other options in this case.
echo "Getting platform name from /etc/lsb-release." 1>&2
platform_name="$(lsbval "CHROMEOS_RELEASE_BOARD")"
if [[ -z "${platform_name}" ]]; then
return 1
fi
;;
"android")
echo "Getting platform name from ro.product.device." 1>&2
platform_name="$(getprop ro.product.device 2>/dev/null)"
if [[ -z "${platform_name}" ]]; then
return 1
fi
;;
*)
echo "Unsupported operating system" 1>&2
return 1
;;
esac
echo "${platform_name}"
}
# Get the model name as a string.
get_model_name() {
local model_name
echo "Getting model name." 1>&2
model_name="$(cros_config / name)"
if [[ -z "${model_name}" ]]; then
return 1
fi
echo "${model_name}"
}
# Return the board version as a string. The board version corresponds to EVT,
# DVT, PVT, etc.
# https://chromium.googlesource.com/chromiumos/docs/+/HEAD/design_docs/cros_board_info.md#board_version
# NOTE: This doesn't work on all platforms and should only be used for
# short-term workarounds that are fixed in later hardware.
get_board_version() {
local board_version
echo "Getting board version." 1>&2
board_version="$(ectool cbi get 0 | grep -o '[0-9]\+' | head -n 1)"
if [[ -z "${board_version}" ]]; then
return 1
fi
echo "${board_version}"
}
power_cycle_fpmcu() {
local gpio_pwren="${1}"
if [[ "${gpio_pwren}" -gt 0 ]]; then
echo "Power cycling the FPMCU."
gpio export "${gpio_pwren}"
gpio out "${gpio_pwren}"
gpio 0 "${gpio_pwren}"
# Must outlast hardware soft start, which is typically ~3ms.
sleep 0.5
gpio 1 "${gpio_pwren}"
# Power enable line is externally pulled down, so leave as output-high.
gpio unexport "${gpio_pwren}"
fi
}
# Given a full platform name, extract the base platform.
#
# Tests are also run on modified images, like hatch-arc-r, hatch-borealis, or
# hatch-kernelnext. These devices still have fingerprint and are expected to
# pass tests. The full platform name reflects these modifications and might
# be needed to apply an alternative configuration (kernelnext). Other modified
# tests (arc-r) just need to default to the base platform config, which is
# identified by this function.
# See b/186697064.
#
# Examples:
# * platform_base_name "hatch-kernelnext" --> "hatch"
# * platform_base_name "hatch-arc-r" --> "hatch"
# * platform_base_name "hatch-borealis" --> "hatch"
# * platform_base_name "hatch" --> "hatch"
# * platform_base_name "strongbad64" --> "strongbad"
#
# Usage: platform_base_name <platform_name>
platform_base_name() {
local platform_name="$1"
# We remove any suffix starting at the first '-'.
local base_name="${platform_name%%-*}"
# Remove any numbers from the base name.
echo "${base_name//[0-9]/}"
}
get_fp_mcu_board() {
local board
case "$(os)" in
"chromeos")
board="$(cros_config /fingerprint board)"
;;
"android")
board="$(dev_config Fingerprint board)"
;;
*)
echo "Unsupported operating system" 1>&2
exit "${EXIT_CONFIG}"
;;
esac
echo "${board}"
}
get_chip() {
local board
board="$(get_fp_mcu_board)"
case "${board}" in
bloonchipper|dartmonkey|nocturne_fp|nami_fp)
echo "STM32"
;;
buccaneer|gwendolin|helipilot|rosalia)
echo "NPCX"
;;
*)
echo "Error - Unknown board '${board}'." >&2
return "${EXIT_CONFIG}"
;;
esac
}
get_firmware_dir() {
if [[ "$(os)" == "android" ]]; then
echo "/apex/com.android.hardware.fingerprint.crosfp/etc/firmware"
else
echo "/opt/google/biod/fw"
fi
}
get_default_fw() {
local board
board="$(get_fp_mcu_board)"
# If cros_config returns "", that is okay assuming there is only
# one firmware file on disk.
local tmp_file
tmp_file="$(mktemp)"
find "$(get_firmware_dir)" -maxdepth 1 -name "${board}*.bin" 2>/dev/null > "${tmp_file}"
local num_fw_files
num_fw_files="$(wc -l < "${tmp_file}")"
local fw_file
fw_file="$(head -n 1 "${tmp_file}" 2>/dev/null)"
rm -f "${tmp_file}"
if [[ "${num_fw_files}" -ne 1 ]]; then
return 1
fi
echo "${fw_file}"
}
# Find processes that have the named file, active or deleted, open.
#
# Deleted files are important because unbinding/rebinding cros-ec
# with biod/timberslide running will result in the processes holding open
# a deleted version of the files. Issues can arise if the process continue
# to interact with the deleted files (ex kernel panic) while the raw driver
# is being used in flash_fp_mcu. The lsof and fuser tools can't seem to
# identify usages of the deleted named file directly, without listing all
# files. This takes a large amount out time on Chromebooks, thus we need this
# custom search routine.
#
# Usage: proc_open_file [file_pattern]
proc_open_file() {
local file_pattern="$1"
find /proc/ -maxdepth 3 -type l -path '*/fd/*' -exec ls -l {} + 2>/dev/null | \
awk -v pattern="${file_pattern}" '
{
proc_fd_col = ""
filename_col = 0
for (i = 1; i <= NF; i++) {
if ($i ~ /^\/proc\//) {
proc_fd_col = $i
}
if ($i == "->") {
filename_col = i + 1
}
}
if (proc_fd_col == "" || filename_col == 0) {
next
}
if ($(filename_col) ~ pattern) {
split(proc_fd_col, p, "/")
print "PID", p[3], "->", $(filename_col), $(filename_col + 1)
found = 1
}
}
END {
# 0 if found, 1 if not found.
exit !found
}
'
return $?
}
flash_fp_mcu_npcx() {
local transport="${1}"
local device="${2}"
local gpio_nrst="${3}"
local file="${4}"
local cmd_flags=""
if [[ "${transport}" == "SPI" ]]; then
cmd_flags+=" -p linux_spi:dev=${device},spispeed=250k"
if [[ "${FLAGS_read}" -eq "${FLAGS_TRUE}" ]]; then
# Read from FPMCU to file
if [[ -e "${file}" ]]; then
echo "Output file already exists: ${file}"
return "${EXIT_PRECONDITION}"
fi
echo "# Reading to '${file}' over ${transport}"
cmd_flags+=" -r ${file}"
else
# Write to FPMCU from file
if [[ ! -f "${file}" ]]; then
echo "Invalid image file: ${file}"
return "${EXIT_PRECONDITION}"
fi
echo "# Flashing '${file}' over ${transport}"
cmd_flags+=" -w ${file}"
fi
elif [[ "${transport}" == "UART" ]]; then
local port="${device#/dev/}"
cmd_flags+="--port=${port} --baudrate ${FLAGS_baudrate}"
fi
echo "# cmd flags= '${cmd_flags}'"
local attempt=0
local cmd_exit_status=1
local cmd=""
if [[ "${transport}" == "SPI" ]]; then
cmd="flashrom ${cmd_flags}"
elif [[ "${transport}" == "UART" ]]; then
# need to flash monitor first before flashing image over UART
cmd="uartupdatetool ${cmd_flags}"
local cmd_flash_monitor=${cmd}
cmd_flash_monitor+=" --opr=wr --addr=0x200c3020"
#TODO(b/294944527): Make sure this file is available in image
cmd_flash_monitor+=" \
--file /usr/local/share/npcx/npcx_monitor_$(get_fp_mcu_name).bin"
# Reset sequence to enter bootloader mode
gpio 0 "${gpio_nrst}"
sleep 0.01
# Release reset as the UART bus is now ready
gpio 1 "${gpio_nrst}"
# Print out the actual underlying command we're running and run it
echo "# ${cmd_flash_monitor}"
${cmd_flash_monitor}
cmd_exit_status=$?
if [[ "${cmd_exit_status}" -eq 0 ]]; then
echo "# NPCX: Flashing monitor PASSED"
else
# No point to continue if flashing the monitor failed
echo "# NPCX: Flashing monitor FAILED!"
return "${EXIT_RUNTIME}"
fi
if [[ "${FLAGS_read}" -eq "${FLAGS_TRUE}" ]]; then
# Read from FPMCU to file
cmd+=" --read-flash --file ${file}"
else
# Write to FPMCU from file
cmd+=" --auto --offset=0 --file ${file}"
fi
fi
for attempt in $(seq "${FLAGS_retries}"); do
# Reset sequence to enter bootloader mode
gpio 0 "${gpio_nrst}"
sleep 0.01
# Release reset as the SPI bus is now ready
gpio 1 "${gpio_nrst}"
# Keeping some margin, add delay of 100 ms to consider minimum bootloader
# startup time after the reset for npcx devices.
sleep 0.1
# Print out the actual underlying command we're running and run it
echo "# ${cmd}"
${cmd}
cmd_exit_status=$?
if [[ "${cmd_exit_status}" -eq 0 ]]; then
break
fi
echo "# Attempt ${attempt} failed."
echo
sleep 1
done
return "${cmd_exit_status}"
}
flash_fp_mcu_stm32() {
local transport="${1}"
local device="${2}"
local gpio_nrst="${3}"
local file="${4}"
local cmd_flags="-p --retries ${STM32MON_CONNECT_RETRIES}"
if [[ "${transport}" == "UART" ]]; then
cmd_flags+=" --baudrate ${FLAGS_baudrate} --device ${device}"
else
cmd_flags+=" -s ${device}"
fi
if [[ "${FLAGS_hello}" -eq "${FLAGS_FALSE}" ]]; then
if [[ "${FLAGS_remove_flash_write_protect}" -eq "${FLAGS_TRUE}" ]]; then
cmd_flags+=" -u"
fi
if [[ "${FLAGS_remove_flash_read_protect}" -eq "${FLAGS_TRUE}" ]]; then
cmd_flags+=" -U"
fi
if [[ "${FLAGS_read}" -eq "${FLAGS_TRUE}" ]]; then
# Read from FPMCU to file
if [[ -e "${file}" ]]; then
echo "Output file already exists: ${file}"
return "${EXIT_PRECONDITION}"
fi
echo "# Reading to '${file}' over ${transport}"
cmd_flags+=" -r ${file}"
else
# Write to FPMCU from file
if [[ ! -f "${file}" ]]; then
echo "Invalid image file: ${file}"
return "${EXIT_PRECONDITION}"
fi
echo "# Flashing '${file}' over ${transport}"
cmd_flags+=" -e -w ${file}"
fi
else
echo "# Saying hello over ${transport}"
fi
local attempt=0
local cmd_exit_status=1
local cmd="stm32mon ${cmd_flags}"
for attempt in $(seq "${FLAGS_retries}"); do
# Reset sequence to enter bootloader mode
gpio 0 "${gpio_nrst}"
sleep 0.01
# Release reset as the SPI bus is now ready
gpio 1 "${gpio_nrst}"
# As per section '68: Bootloader timings' from application note below:
# https://www.st.com/resource/en/application_note/cd00167594-stm32-microcontroller-system-memory-boot-mode-stmicroelectronics.pdf
# bootloader startup time is 16.63 ms for STM32F74xxx/75xxx and 53.975 ms
# for STM32H74xxx/75xxx. SPI needs 1 us delay for one SPI byte sending.
# Keeping some margin, add delay of 100 ms to consider minimum bootloader
# startup time after the reset for stm32 devices.
sleep 0.1
# Print out the actual underlying command we're running and run it
echo "# ${cmd}"
${cmd}
cmd_exit_status=$?
if [[ "${cmd_exit_status}" -eq 0 ]]; then
break
fi
echo "# Attempt ${attempt} failed."
echo
sleep 1
done
return "${cmd_exit_status}"
}
flash_fp_mcu_board() {
local transport="${1}"
local device="${2}"
local gpio_nrst="${3}"
local gpio_boot0="${4}"
local gpio_pwren="${5}"
local file="${6}"
local chip_type="${7}"
local deviceid=""
echo "# Flashing '${chip_type}' with '${file}' over ${transport}"
if [[ "${transport}" == "UART" ]]; then
if ! deviceid="$(get_uartid)"; then
# Failure to get Device ID when transport is UART is not critical.
# Cros-ec-uart driver was built on top of serial bus. Serial bus is
# created on top of UART driver (in our case it's dw-apb-uart).
# To get /dev/ttyS1 device, we need to unbind cros-ec-uart and rebind
# dw-apb-uart driver which creates ttyS1, but destroyes the serial bus.
# If we can't get the Device ID, it means that dw-apb-uart was rebound
# previously.
echo "Unable to find FP sensor UART device: ${CROS_EC_UART_MODALIAS_STR}"
fi
else
if ! deviceid="$(get_spiid)"; then
# Failure to get Device ID when transport is SPI is critical.
# It means that the FPMCU is missing on the device or it's not defined
# in ACPI properly.
echo "Unable to find FP sensor SPI device: ${CROS_EC_SPI_MODALIAS_STR}"
return "${EXIT_PRECONDITION}"
fi
fi
# Unbind cros-ec driver
if [[ -n "${deviceid}" ]]; then
klog "Unbinding cros-ec driver"
if [[ "${transport}" == "UART" ]]; then
echo "${deviceid}" > /sys/bus/serial/drivers/cros-ec-uart/unbind
else
echo "${deviceid}" > /sys/bus/spi/drivers/cros-ec-spi/unbind
fi
echo "Flashing ${transport} device ID: ${deviceid}"
fi
# Configure the MCU Boot0 and NRST GPIOs
gpio export "${gpio_boot0}" "${gpio_nrst}"
gpio out "${gpio_boot0}" "${gpio_nrst}"
# Reset sequence to enter bootloader mode
gpio 1 "${gpio_boot0}"
gpio 0 "${gpio_nrst}"
sleep 0.001
klog "Binding raw driver"
if [[ "${transport}" == "UART" ]]; then
# load AMDI0020:01 ttyS1
echo AMDI0020:01 > /sys/bus/platform/drivers/dw-apb-uart/unbind;
echo AMDI0020:01 > /sys/bus/platform/drivers/dw-apb-uart/bind;
else
echo spidev > "/sys/bus/spi/devices/${deviceid}/driver_override"
echo "${deviceid}" > /sys/bus/spi/drivers/spidev/bind
# The following sleep is a workaround to mitigate the effects of a
# poorly behaved chip select line. See b/145023809.
fi
sleep 0.5
# When using gpioset, we can only do this check if it's not holding the
# GPIO.
if [[ "${#GPIO_ACTIVE_PIDS[@]}" -eq 0 ]]; then
# We do not expect the drivers to change the pin state when binding.
# If you receive this warning, the driver needs to be fixed on this board
# and this flash attempt will probably fail.
warn_gpio "${gpio_boot0}" 1 \
"WARNING: One of the drivers changed BOOT0 pin state on bind attempt."
warn_gpio "${gpio_nrst}" 0 \
"WARNING: One of the drivers changed NRST pin state on bind attempt."
fi
if [[ ! -c "${device}" ]]; then
echo "Failed to bind raw device driver." >&2
return "${EXIT_RUNTIME}"
fi
local cmd_exit_status=1
if [[ "${chip_type}" == "STM32" ]]; then
flash_fp_mcu_stm32 \
"${transport}" \
"${device}" \
"${gpio_nrst}" \
"${file}"
cmd_exit_status=$?
elif [[ "${chip_type}" == "NPCX" ]]; then
flash_fp_mcu_npcx \
"${transport}" \
"${device}" \
"${gpio_nrst}" \
"${file}"
cmd_exit_status=$?
else
echo "# Unsupported chip type found: ${chip_type}"
return "${EXIT_CONFIG}"
fi
# unload device
if [[ "${transport}" != "UART" ]]; then
klog "Unbinding raw driver"
echo "${deviceid}" > /sys/bus/spi/drivers/spidev/unbind
fi
# Go back to normal mode
gpio 0 "${gpio_boot0}"
# Dartmonkey's RO has a flashprotect logic issue that forces reboot loops
# when SW-WP is enabled and HW-WP is disabled. It is avoided if a POR is
# detected on boot. We force a POR here to ensure we avoid this reboot loop.
# See to b/146428434.
if [[ "${gpio_pwren}" -gt 0 ]]; then
power_cycle_fpmcu "${gpio_pwren}"
else
echo "Reset the FPMCU."
gpio out "${gpio_nrst}"
gpio 0 "${gpio_nrst}"
# Make sure that we keep nRST line low long enough.
sleep 0.01
gpio 1 "${gpio_nrst}"
fi
# Give up GPIO control, unless we need to keep these driving as
# outputs because they're not open-drain signals.
# TODO(b/179839337): Make this the default and properly support
# open-drain outputs on other platforms.
if [[ "${PLATFORM_BASE_NAME}" != "strongbad" ]] &&
[[ "${PLATFORM_BASE_NAME}" != "zork" ]] &&
[[ "${PLATFORM_BASE_NAME}" != "rauru" ]] &&
[[ "${PLATFORM_BASE_NAME}" != "skyrim" ]] &&
[[ "${PLATFORM_BASE_NAME}" != "skywalker" ]]; then
gpio in "${gpio_boot0}" "${gpio_nrst}"
fi
gpio unexport "${gpio_boot0}" "${gpio_nrst}"
# Put back cros_fp driver if transport is SPI
if [[ "${transport}" != "UART" ]]; then
# wait for FP MCU to come back up (including RWSIG delay)
sleep 2
klog "Binding cros-ec driver"
echo "" > "/sys/bus/spi/devices/${deviceid}/driver_override"
echo "${deviceid}" > /sys/bus/spi/drivers/cros-ec-spi/bind
# Factory expects SWWP = 0 after flash_fp_mcu, so disable it, see
# b/341193125.
if [[ "${chip_type}" == "NPCX" ]]; then
cmd="ectool --name=cros_fp flashprotect disable"
echo "# ${cmd}"
${cmd}
cmd_exit_status=$?
if [[ "${cmd_exit_status}" -eq 0 ]]; then
echo "Failed to disable WP after flashing NPCX."
fi
fi
else
# FPMCU can still have ro_now protection (sector write protection) enabled
# e.g. when flash_fp_mcu was run with --read option. It is disabled when
# FPMCU boots with HW write protect disabled.
# Disabling sector write protection takes some time due to flash internal
# structures update. During that time we can't reset or power cycle FPMCU
# otherwise we will corrupt internal flash state which will brick FPMCU.
# This situation can occur if DUT is rebooted immediately after flash_fp_mcu
# finishes, so give some time to remove sector write protection. No need to
# wait if transport is SPI because later we query for version and reset
# flags.
sleep 3
fi
if [[ "${cmd_exit_status}" -ne 0 ]]; then
return "${EXIT_RUNTIME}"
fi
# Inform user to reboot if transport is UART.
# Display fw version is transport is SPI
if [[ "${transport}" == "UART" ]]; then
echo "Please reboot this device."
else
# Test it
klog "Query version and reset flags"
ectool --name=cros_fp version
# TODO(b/408245482): re-enable when implemented.
if [[ "$(os)" != "android" ]]; then
ectool --name=cros_fp uptimeinfo
fi
fi
}
config_hatch() {
readonly USE_LEGACY_GPIO_API=0
readonly TRANSPORT="SPI"
readonly DEVICE="/dev/spidev1.1"
# See
# third_party/coreboot/src/soc/intel/cannonlake/include/soc/gpio_soc_defs.h
# for pin name to number mapping.
# Examine `cat /sys/kernel/debug/pinctrl/INT34BB:00/gpio-ranges` on a hatch
# device to determine gpio number from pin number.
readonly GPIO_CHIP="gpiochip0"
# FPMCU RST_ODL is on GPP_A12 = 12
readonly GPIO_NRST=12
# FPMCU BOOT0 is on GPP_A22 = 22
readonly GPIO_BOOT0=22
# FP_PWR_EN is on GPP_C11 = 256 + 11 = 267
readonly GPIO_PWREN=267
}
config_nami() {
readonly USE_LEGACY_GPIO_API=0
readonly TRANSPORT="SPI"
readonly DEVICE="/dev/spidev1.0"
# See kernel/v5.4/drivers/pinctrl/intel/pinctrl-sunrisepoint.c
# for pin name and pin number.
readonly GPIO_CHIP="gpiochip0"
# FPMCU RST_ODL is on GPP_C9 = 57
readonly GPIO_NRST=57
# FPMCU BOOT0 is on GPP_D5 = 77
readonly GPIO_BOOT0=77
# FP_PWR_EN is on GPP_B11 = 35
readonly GPIO_PWREN=35
}
config_nocturne() {
readonly USE_LEGACY_GPIO_API=0
readonly TRANSPORT="SPI"
readonly DEVICE="/dev/spidev1.0"
# See kernel/v5.4/drivers/pinctrl/intel/pinctrl-sunrisepoint.c
# for pin name and pin number.
readonly GPIO_CHIP="gpiochip0"
# FPMCU RST_ODL is on GPP_C10 = 58
readonly GPIO_NRST=58
# FPMCU BOOT0 is on GPP_C8 = 56
readonly GPIO_BOOT0=56
# FP_PWR_EN is on GPP_A11 = 11
readonly GPIO_PWREN=11
}
config_rauru() {
readonly USE_LEGACY_GPIO_API=0
readonly TRANSPORT="SPI"
readonly DEVICE="/dev/spidev5.0"
readonly GPIO_CHIP="gpiochip0"
# FPMCU RST_ODL is $(gpiofind FP_RST_1V8_S3_L) is gpiochip0 26
GPIO_NRST=$(gpiofind FP_RST_1V8_S3_L|cut -f2 -d" ")
readonly GPIO_NRST
# FPMCU BOOT0 is $(gpiofind AP_FP_FW_UP_STRAP) is gpiochip0 27
GPIO_BOOT0=$(gpiofind AP_FP_FW_UP_STRAP|cut -f2 -d" ")
readonly GPIO_BOOT0
# EN FP RAILS is $(gpiofind EN_PWR_FP) is gpiochip0 173
GPIO_PWREN=$(gpiofind EN_PWR_FP|cut -f2 -d" ")
readonly GPIO_PWREN
}
config_strongbad() {
readonly USE_LEGACY_GPIO_API=0
readonly TRANSPORT="SPI"
readonly DEVICE="/dev/spidev10.0"
readonly GPIO_CHIP="gpiochip0"
# FPMCU RST_ODL is $(gpiofind FP_RST_L) is gpiochip0 22
GPIO_NRST=$(gpiofind FP_RST_L|cut -f2 -d" ")
readonly GPIO_NRST
# FPMCU BOOT0 is $(gpiofind FPMCU_BOOT0) is gpiochip0 10
GPIO_BOOT0=$(gpiofind FPMCU_BOOT0|cut -f2 -d" ")
readonly GPIO_BOOT0
# EN FP RAILS is $(gpiofind EN_FP_RAILS) is gpiochip0 74 (if available)
local en_fp_rails
if en_fp_rails=$(gpiofind EN_FP_RAILS); then
GPIO_PWREN=$(echo "${en_fp_rails}"|cut -f2 -d" ")
readonly GPIO_PWREN
else
readonly GPIO_PWREN=-1
fi
}
config_volteer() {
readonly USE_LEGACY_GPIO_API=0
readonly TRANSPORT="SPI"
readonly DEVICE="/dev/spidev1.0"
# See kernel/v5.4/drivers/pinctrl/intel/pinctrl-tigerlake.c
# for pin name and pin number.
# Examine `cat /sys/kernel/debug/pinctrl/INT34C5:00/gpio-ranges` on a
# volteer device to determine gpio number from pin number.
# For example: GPP_C23 is UART2_CTS which can be queried from EDS
# the pin number is 194. From the gpio-ranges, the gpio value is
# 920 + (194-171) = 943. The relative gpio value is 943 - 664 = 279.
readonly GPIO_CHIP="gpiochip0"
# FPMCU RST_ODL is on GPP_C23 = 256 + (194 - 171) = 279
readonly GPIO_NRST=279
# FPMCU BOOT0 is on GPP_C22 = 256 + (193 - 171) = 278
readonly GPIO_BOOT0=278
# FP_PWR_EN is on GPP_A21 = 64 + (63 - 42) = 85
readonly GPIO_PWREN=85
}
config_brya() {
readonly USE_LEGACY_GPIO_API=0
readonly TRANSPORT="SPI"
DEVICE="/dev/spidev0.0"
if [[ "$(os)" == "android" ]]; then
DEVICE="/dev/spidev1.0"
fi
readonly DEVICE
# See kernel/v5.10/drivers/pinctrl/intel/pinctrl-tigerlake.c
# for pin name and pin number.
# Examine `cat /sys/kernel/debug/pinctrl/INTC1055:00/gpio-ranges` on a
# brya device to determine gpio number from pin number.
# For example: GPP_D1 is ISH_GP_1 which can be queried from EDS
# the pin number is 100 from the pinctrl-tigerlake.c.
# From the gpio-ranges, the gpio value is 824 + (100-99) = 825
readonly GPIO_CHIP="gpiochip0"
# FPMCU RST_ODL is on GPP_D1 = 160 + (100 - 99) = 161
readonly GPIO_NRST=161
# FPMCU BOOT0 is on GPP_D0 = 160 + (99 - 99) = 160
readonly GPIO_BOOT0=160
# FP_PWR_EN is on GPP_D2 = 160 + (101 - 99) = 162
readonly GPIO_PWREN=162
}
config_brask() {
# Let's call the config_brya since brask follows the brya HW design
config_brya
}
config_ghost() {
# No changes to brya.
config_brya
}
config_rex() {
readonly USE_LEGACY_GPIO_API=0
readonly TRANSPORT="SPI"
readonly DEVICE="/dev/spidev0.0"
# See kernel/v6.1/drivers/pinctrl/intel/pinctrl-meteorlake.c
# for pin name and pin number.
# Examine `cat /sys/kernel/debug/pinctrl/INTC1083:00/gpio-ranges` on a
# rex device to determine gpio number from pin number.
# For example: GPP_B11, the pin number is 215 from the pinctrl-meteorlake.c.
# From the gpio-ranges, the gpio value is 925 + (215-204) = 936
readonly GPIO_CHIP="gpiochip0"
local model
model=$(cros_config / name)
case "${model}" in
(screebo | screebo4es)
# Screebo is based off Rex but with different pins for FPMCU
# FPMCU RST_ODL is on GPP_C21 = 64 + (50 - 29) = 85
readonly GPIO_NRST=85
# FPMCU BOOT0 is on GPP_C20 = 64 + (49 - 29) = 84
readonly GPIO_BOOT0=84
# FP_PWR_EN is on GPP_B08 = 352 + (212-204) - 573 = 360
readonly GPIO_PWREN=360
;;
*)
# FPMCU RST_ODL is on GPP_C23 = 64 + (52 - 29) = 87
readonly GPIO_NRST=87
# FPMCU BOOT0 is on GPP_C22 = 64 + (51 - 29) = 86
readonly GPIO_BOOT0=86
# FP_PWR_EN is on GPP_B11 = 352 + (215-204) = 363
readonly GPIO_PWREN=363
;;
esac
}
config_zork() {
readonly USE_LEGACY_GPIO_API=0
readonly TRANSPORT="UART"
readonly DEVICE="/dev/ttyS1"
readonly GPIO_CHIP="gpiochip0"
# FPMCU RST_ODL is on AGPIO 11
readonly GPIO_NRST=11
# FPMCU BOOT0 is on AGPIO 69
readonly GPIO_BOOT0=69
# FPMCU PWR_EN is on AGPIO 32
readonly GPIO_PWREN=32
}
config_guybrush() {
readonly USE_LEGACY_GPIO_API=0
readonly TRANSPORT="UART"
readonly DEVICE="/dev/ttyS1"
readonly GPIO_CHIP="gpiochip0"
# FPMCU RST_ODL is on AGPIO 11
readonly GPIO_NRST=11
# FPMCU BOOT0 is on AGPIO 144
readonly GPIO_BOOT0=144
# FPMCU PWR_EN is on AGPIO 3
readonly GPIO_PWREN=3
}
config_skyrim() {
readonly USE_LEGACY_GPIO_API=0
readonly TRANSPORT="UART"
readonly DEVICE="/dev/ttyS1"
readonly GPIO_CHIP="gpiochip0"
# FPMCU RST_ODL is on AGPIO 12
readonly GPIO_NRST=12
# FPMCU BOOT0 is on AGPIO 130
readonly GPIO_BOOT0=130
# FPMCU PWR_EN is on AGPIO 4
readonly GPIO_PWREN=4
}
config_brox() {
readonly USE_LEGACY_GPIO_API=0
readonly TRANSPORT="SPI"
readonly DEVICE="/dev/spidev0.0"
# Raptorlake uses Alderlake-P PCH and hence
# exports INTC1055 ACPI ID matching with tigerlake-pinctrl.c
# See kernel/v6.6/drivers/pinctrl/intel/pinctrl-tigerlake.c
# for pin name and pin number.
# Examine `cat /sys/kernel/debug/pinctrl/INTC1055:00/gpio-ranges` on a
# lotso device to determine gpio number from pin number.
# For example: GPP_E21, the pin number is 247 from the pinctrl-tigerlake.c.
# From the gpio-ranges, the gpio value is 832 + (247-226) = 853
readonly GPIO_CHIP="gpiochip0"
local model
model=$(cros_config / name)
case "${model}" in
(lotso)
# FPMCU RST_ODL is on GPP_E21 = 320 + 21 = 341
readonly GPIO_NRST=341
# FPMCU BOOT0 is on GPP_D9 = 160 + 9 = 169
readonly GPIO_BOOT0=169
# FP_PWR_EN is on GPP_E20 = 320 + 20 = 340
readonly GPIO_PWREN=340
;;
(jubilant|jubileum)
# FPMCU RST_ODL is on GPP_D15 = 160 + 15 = 175
readonly GPIO_NRST=175
# FPMCU BOOT0 is on GPP_D3 = 160 + 3 = 163
readonly GPIO_BOOT0=163
# FP_PWR_EN is on GPP_D2 = 160 + 2 = 162
readonly GPIO_PWREN=162
;;
(caboc)
# FPMCU RST_ODL is on GPP_D9 = 160 + 9 = 169
readonly GPIO_NRST=169
# FPMCU BOOT0 is on GPP_D8 = 160 + 8 = 168
readonly GPIO_BOOT0=168
# FP_PWR_EN is on GPP_D11 = 160 + 11 = 171
readonly GPIO_PWREN=171
;;
*)
echo "FPMCU not supported on model ${model}"
return "${EXIT_PRECONDITION}"
;;
esac
}
config_fatcat() {
readonly USE_LEGACY_GPIO_API=0
readonly TRANSPORT="SPI"
readonly DEVICE="/dev/spidev0.0"
# Examine `cat /sys/kernel/debug/pinctrl/INTC1083:00/gpio-ranges` on a
# fatcat device to determine gpio number from pin number.
readonly GPIO_CHIP="gpiochip0"
local model
model=$(cros_config / name)
case "${model}" in
(francka)
# FPMCU FPS_RST_N(RST_ODL) is on GPP_H17 = 626 + 17
readonly GPIO_NRST=17
# FPMCU FPMCU_FW_UPDATE(BOOT0) is on GPP_F20 = 560 + 20
readonly GPIO_BOOT0=20
# FPMCU_PWREN(FP_PWR_EN) is on GPP_H03 = 626 + 3
readonly GPIO_PWREN=3
;;
*)
# FPMCU FPS_RST_N(RST_ODL) is on GPP_C15 = 24 + (39 - 24) = 39
readonly GPIO_NRST=39
# FPMCU FPMCU_FW_UPDATE(BOOT0) is on GPP_E20 = 26 + (46 - 26) = 46
readonly GPIO_BOOT0=46
# FPMCU_PWREN(FP_PWR_EN) is on GPP_E19 = 26 + (45 - 26) = 45
readonly GPIO_PWREN=45
;;
esac
}
config_nissa() {
readonly USE_LEGACY_GPIO_API=0
readonly TRANSPORT="SPI"
readonly DEVICE="/dev/spidev1.0"
# Twinlake uses Alderlake-P PCH and hence
# exports INTC1057 ACPI ID matching with pinctrl-alderlake.c
# See kernel/v6.6/drivers/pinctrl/intel/pinctrl-alderlake.c
# for pin name and pin number.
# Examine `cat /sys/kernel/debug/pinctrl/INTC1057:00/gpio-ranges` on a
# pujjoniru device to determine gpio number from pin number.
# For example: GPP_E7, the pin number is 231 from the pinctrl-alderlake.c.
# From the gpio-ranges, the gpio value is 320 + (231 - 224) = 327
readonly GPIO_CHIP="gpiochip0"
local model
model=$(cros_config / name)
case "${model}" in
(pujjoniru | pujjoquince)
# FPMCU RST_ODL is on GPP_E7 = 320 + (231 - 224) = 327
readonly GPIO_NRST=327
# FPMCU BOOT0 is on GPP_D0 = 192 + (119 - 119) = 192
readonly GPIO_BOOT0=192
# FP_PWR_EN is on GPP_D2 = 192 + (121-119) = 194
readonly GPIO_PWREN=194
;;
*)
echo "FPMCU not supported on model ${model}"
return "${EXIT_PRECONDITION}"
;;
esac
}
config_skywalker() {
readonly USE_LEGACY_GPIO_API=0
readonly TRANSPORT="SPI"
readonly DEVICE="/dev/spidev1.0"
readonly GPIO_CHIP="gpiochip0"
# FPMCU RST_ODL is $(gpiofind FP_RST_1V8_S3_L) is gpiochip0 134
GPIO_NRST=$(gpiofind FP_RST_1V8_S3_L|cut -f2 -d" ")
readonly GPIO_NRST
# FPMCU BOOT0 is $(gpiofind AP_FP_FW_UP_STRAP) is gpiochip0 133
GPIO_BOOT0=$(gpiofind AP_FP_FW_UP_STRAP|cut -f2 -d" ")
readonly GPIO_BOOT0
# EN FP RAILS is $(gpiofind EN_PWR_FP) is gpiochip0 12
GPIO_PWREN=$(gpiofind EN_PWR_FP|cut -f2 -d" ")
readonly GPIO_PWREN
}
main() {
local filename="$1"
local chip_type
# print out canonical path to differentiate between /usr/local/bin and
# /usr/bin installs
readlink -f "$0"
echo "OS: $(os)"
if [[ "$(os)" == "android" ]]; then
USE_LEGACY_GPIO_API=0
readonly GPIOSET_SUPPORTS_HOLD=0
else
USE_LEGACY_GPIO_API=1
readonly GPIOSET_SUPPORTS_HOLD=1
fi
# The "platform name" corresponds to the underlying board (reference design)
# that we're running on (not the FPMCU or sensor). At the moment all of the
# reference designs use the same GPIOs. If for some reason a design differs in
# the future, we will want to add a nested check in the config_<platform_name>
# function. Doing it in this manner allows us to reduce the number of
# configurations that we have to maintain (and reduces the amount of testing
# if we're only updating a specific config_<platform_name>).
if ! PLATFORM_NAME="$(get_platform_name)"; then
echo "Failed to get platform name"
exit "${EXIT_CONFIG}"
fi
readonly PLATFORM_NAME
if ! PLATFORM_BASE_NAME="$(platform_base_name "${PLATFORM_NAME}")"; then
echo "Failed to get platform base name"
exit "${EXIT_CONFIG}"
fi
readonly PLATFORM_BASE_NAME
echo "Platform name is ${PLATFORM_NAME} (${PLATFORM_BASE_NAME})."
# Check that the config function exists
if is_function "config_${PLATFORM_NAME}"; then
readonly PLATFORM_CONFIG="${PLATFORM_NAME}"
elif is_function "config_${PLATFORM_BASE_NAME}"; then
readonly PLATFORM_CONFIG="${PLATFORM_BASE_NAME}"
else
echo "No config for platform ${PLATFORM_NAME}." >&2
exit "${EXIT_CONFIG}"
fi
echo "Using config for ${PLATFORM_CONFIG}."
if ! "config_${PLATFORM_CONFIG}"; then
echo "Configuration failed for platform ${PLATFORM_CONFIG}." >&2
exit "${EXIT_CONFIG}"
fi
# Help the user out with defaults, if no *file* was given.
if [[ "$#" -eq 0 ]]; then
# If we are actually reading, set to a timestamped temp file.
if [[ "${FLAGS_read}" -eq "${FLAGS_TRUE}" ]]; then
filename="/tmp/fpmcu-fw-$(date --iso-8601=seconds).bin"
else
# Assume we are "writing" the default firmware to the FPMCU.
if ! filename="$(get_default_fw)"; then
echo "Failed to identify a default firmware file" >&2
exit "${EXIT_CONFIG}"
fi
fi
fi
# Check that the gpiochip exists
if ! gpio_chip_exists; then
echo "Cannot find GPIO chip: ${GPIO_CHIP}"
exit "${EXIT_CONFIG}"
fi
# Ensure spidev module is loaded.
check_spidev
# Ensure hardware write-protect is disabled.
check_hardware_write_protect_disabled
stop_services
# If cros-ec driver isn't bound on startup, this means the final rebinding
# may fail.
if [[ ! -c "/dev/cros_fp" ]]; then
echo "WARNING: The cros-ec driver was not bound on startup." >&2
fi
if [[ -c "${DEVICE}" ]]; then
echo "WARNING: The raw driver was bound on startup." >&2
fi
local files_open
# Ensure no processes have cros_fp device or debug device open.
if files_open=$(proc_open_file "/dev/cros_fp|/sys/kernel/debug/cros_fp/.*")
then
echo "WARNING: Another process has a cros_fp device file open." >&2
echo "${files_open}" >&2
echo_service_warning
echo "See b/188985272." >&2
fi
# Ensure no processes are using the raw driver. This might be a wedged
# stm32mon process spawned by this script.
if files_open=$(proc_open_file "${DEVICE}"); then
echo "WARNING: Another process has ${DEVICE} open." >&2
echo "${files_open}" >&2
echo "Try 'fuser -k ${DEVICE}' before running this script." >&2
echo "See b/188985272." >&2
fi
if ! chip_type="$(get_chip)"; then
echo "Failed to get chip type." >&2
exit "${EXIT_CONFIG}"
fi
echo "Detected chip ${chip_type}."
local ret
flash_fp_mcu_board \
"${TRANSPORT}" \
"${DEVICE}" \
"${GPIO_NRST}" \
"${GPIO_BOOT0}" \
"${GPIO_PWREN}" \
"${filename}" \
"${chip_type}"
ret=$?
start_services
exit "${ret}"
}
main "$@"