#!/bin/bash

# Copyright 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

SCRIPT="$(readlink -f "$0")"
SCRIPT_DIR="$(dirname "$SCRIPT")"

EC_DIR="$(readlink -f "${SCRIPT_DIR}/..")"
if [[ "$(basename "${EC_DIR}")" != "ec" ]]; then
	EC_DIR=
fi

# Loads script libraries.
. "/usr/share/misc/shflags" || exit 1

# Redirects tput to stderr, and drop any error messages.
tput2() {
	tput "$@" 1>&2 2>/dev/null || true
}

error() {
	tput2 bold && tput2 setaf 1
	echo "ERROR: $*" >&2
	tput2 sgr0
}


info() {
	tput2 bold && tput2 setaf 2
	echo "INFO: $*" >&2
	tput2 sgr0
}

warn() {
	tput2 bold && tput2 setaf 3
	echo "WARNING: $*" >&2
	tput2 sgr0
}

die() {
	[ -z "$*" ] || error "$@"
	exit 1
}


# Include a board name to the BOARDS_${EC_CHIP} array below ONLY IF servod is
# not aware of its 'ec_chip'. If servod becomes able to answer 'ec_chip'
# for the board, remove it from BOARDS_XXXX array below.
BOARDS_IT83XX=(
	adlrvpm_ite
	adlrvpp_ite
	glkrvp_ite
	it83xx_evb
	jslrvp_ite
	reef_it8320
	tglrvpu_ite
	tglrvpu_ite_tcpmv1
	tglrvpy_ite
	tglrvpy_ite_tcpmv1
)

BOARDS_IT83XX_SPI_PROGRAMMING=(
	it8xxx2_evb
	it8xxx2_pdevb
)

BOARDS_STM32=(
	bloonchipper
	chell_pd
	coffeecake
	chocodile_bec
	chocodile_vpdmcu
	dartmonkey
	glados_pd
	hatch_fp
	jerry
	minimuffin
	nami_fp
	nocturne_fp
	oak_pd
	pit
	plankton
	rainier
	samus_pd
	strago_pd
	zinger
)
BOARDS_STM32_PROG_EN=(
	plankton
)

BOARDS_STM32_DFU=(
	c2d2
	dingdong
	hoho
	twinkie
	discovery
	servo_v4
	servo_v4p1
	servo_micro
	sweetberry
	polyberry
	stm32f446e-eval
	tigertail
	fluffy
)

BOARDS_NPCX_5M5G_JTAG=(
	npcx_evb
	npcx_evb_arm
)

BOARDS_NPCX_5M6G_JTAG=(
)

BOARDS_NPCX_7M6X_JTAG=(
)

BOARDS_NPCX_7M7X_JTAG=(
	npcx7_evb
)

BOARDS_NPCX_SPI=(
	glkrvp
)

BOARDS_SPI_1800MV=(
	coral
	reef
)

BOARDS_RAIDEN=(
	coral
	eve
	fizz
	flapjack
	kukui
	nami
	nautilus
	poppy
	rammus
	reef
	scarlet
	soraka
)

DEFAULT_PORT="${SERVOD_PORT:-9999}"
BITBANG_RATE="57600"  # Could be overwritten by a command line option.

# Flags
DEFINE_integer bitbang_rate "${BITBANG_RATE}" \
	"UART baud rate to use when bit bang programming, "\
"standard UART rates from 9600 to 57600 are supported."
DEFINE_string board "${DEFAULT_BOARD}" \
	"The board to run debugger on."
DEFINE_string chip "" \
	"The chip to run debugger on."
DEFINE_boolean dry_run "${FLAGS_FALSE}" \
	"Print the commands to be run instead of actually running them."
DEFINE_string image "" \
	"Full pathname of the EC firmware image to flash."
DEFINE_string logfile "" \
	"Stm32 only: pathname of the file to store communications log."
DEFINE_string offset "0" \
	"Offset where to program the image from."
DEFINE_integer port "${DEFAULT_PORT}" \
	"Port to communicate to servo on."
DEFINE_boolean raiden "${FLAGS_FALSE}" \
	"Use raiden_debug_spi programmer"
DEFINE_string read "" "Pathname of the file to store EC firmware image."
DEFINE_boolean ro "${FLAGS_FALSE}" \
	"Write only the read-only partition"
DEFINE_boolean servo_micro_uart_rx_rework "${FLAGS_FALSE}" \
	"The servo micro for flashing has b/143163043#comment3 rework"
DEFINE_integer timeout 600 \
	"Timeout for flashing the EC, measured in seconds."
DEFINE_boolean verbose "${FLAGS_FALSE}" \
	"Verbose hw control logging"
DEFINE_boolean verify "${FLAGS_FALSE}" \
	"Verify EC firmware image after programming."

# Parse command line
FLAGS_HELP="usage: $0 [flags]"
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"
if [[ $# -gt 0 ]]; then
	die "invalid arguments: \"$*\""
fi

# Error messages
MSG_PROGRAM_FAIL="Failed to flash EC firmware image"
MSG_READ_FAIL="Failed to read EC firmware image"
MSG_VERIFY_FAIL="Failed to verify EC firmware image."

set -e

DUT_CONTROL_CMD=( "dut-control" "--port=${FLAGS_port}" )
DUT_CTRL_PREFIX=""

# Run a command which mutates the state of an attached device.
# This is used by the --dry_run flag, when enabled, the command will
# only print.  When disabled, the command will run without printing
# any message.
function print_or_run() {
	if [ "${FLAGS_dry_run}" = "${FLAGS_TRUE}" ]; then
		local arg
		local -a quoted_args

		# Quote each of the arguments so it can easily be
		# copy-pasted into a shell.
		for arg in "$@"; do
			quoted_args+=("$(printf "%q" "${arg}")")
		done

		tput2 bold && tput2 setaf 4
		echo "DRY RUN:" "${quoted_args[@]}" >&2
		tput2 sgr0
	else
		"$@"
	fi
}

function dut_control() {
	local DUT_CTRL_CML=( "${DUT_CONTROL_CMD[@]}" )

	for p in $@ ; do
		# Only add the prefix if the arg is a control name.
		if [[ ${p} != -* ]] ; then
			p="${DUT_CTRL_PREFIX}${p}"
		fi
		DUT_CTRL_CML+=( "$p" )
	done

	if [ "${FLAGS_verbose}" = ${FLAGS_TRUE} ]; then
		echo "${DUT_CTRL_CML[*]}" 1>&2
	fi

	print_or_run "${DUT_CTRL_CML[@]}" >/dev/null
}

function dut_control_or_die {
	dut_control "$@" || die "command exited $? (non-zero): dut-control $*"
}

function dut_control_get() {
	if [ $# -gt 1 ]; then
		error "${FUNCNAME[0]} failed: more than one argument: $@"
		return 1
	fi

	local ARGS DUT_GETVAL RETVAL
	ARGS=( "${DUT_CONTROL_CMD[@]}" "--value_only" "${DUT_CTRL_PREFIX}$1" )
	RETVAL=0
	# || statement is attached to avoid an exit if error exit is enabled.
	DUT_GETVAL=$( "${ARGS[@]}" ) || RETVAL="$?"
	if (( "${RETVAL}" )) ; then
		warn "${FUNCNAME[0]} failed: ${ARGS[*]} returned ${RETVAL}."
		return "${RETVAL}"
	fi

	echo "${DUT_GETVAL}"
}

function dut_control_get_or_die {
	dut_control_get "$@" || \
	    die "command exited $? (non-zero): dut-control --value_only $*"
}

: ${BOARD:=${FLAGS_board}}

in_array() {
	local n=$#
	local value=${!n}

	for (( i=1; i<$#; i++ )) do
		if [ "${!i}" == "${value}" ]; then
			return 0
		fi
	done
	return 1
}

declare -a SUPPORTED_CHIPS

if $(in_array "${BOARDS_STM32[@]}" "${BOARD}"); then
	SUPPORTED_CHIPS+=("stm32")
fi

if $(in_array "${BOARDS_STM32_DFU[@]}" "${BOARD}"); then
	SUPPORTED_CHIPS+=("stm32_dfu")
fi

if $(in_array "${BOARDS_NPCX_5M5G_JTAG[@]}" "${BOARD}"); then
	SUPPORTED_CHIPS+=("npcx_5m5g_jtag")
fi

if $(in_array "${BOARDS_NPCX_5M6G_JTAG[@]}" "${BOARD}"); then
	SUPPORTED_CHIPS+=("npcx_5m6g_jtag")
fi

if $(in_array "${BOARDS_NPCX_7M6X_JTAG[@]}" "${BOARD}"); then
	SUPPORTED_CHIPS+=("npcx_7m6x_jtag")
fi

if $(in_array "${BOARDS_NPCX_7M7X_JTAG[@]}" "${BOARD}"); then
	SUPPORTED_CHIPS+=("npcx_7m7x_jtag")
fi

if $(in_array "${BOARDS_NPCX_SPI[@]}" "${BOARD}"); then
	SUPPORTED_CHIPS+=("npcx_spi")
fi

if $(in_array "${BOARDS_IT83XX[@]}" "${BOARD}"); then
	SUPPORTED_CHIPS+=("it83xx")
fi

if $(in_array "${BOARDS_IT83XX_SPI_PROGRAMMING[@]}" "${BOARD}"); then
	SUPPORTED_CHIPS+=("ite_spi")
fi

if [[ ${#SUPPORTED_CHIPS[@]} -eq 0 && -n "${FLAGS_chip}" ]]; then
	SUPPORTED_CHIPS+="${FLAGS_chip}"
fi

if [[ ${#SUPPORTED_CHIPS[@]} -eq 0 ]]; then
	SERVO_EC_CHIP="$(dut_control_get ec_chip)"
	SERVO_EC_CHIP="${SERVO_EC_CHIP,,}"
	if [[ "${SERVO_EC_CHIP}" =~ "unknown" || -z "${SERVO_EC_CHIP}" ]]; then
		die "Please check that servod is running or," \
		"manually specify either --board or --chip."
	fi
	SUPPORTED_CHIPS+=("${SERVO_EC_CHIP}")
fi

if [[ ${#SUPPORTED_CHIPS[@]} -eq 0 ]]; then
	# This happens if ${FLAGS_board} is not known in this flash_ec yet,
	# ${FLAGS_chip} is not given, and servod doesn't know ec_chip.
	# In this case, '--chip' should be specified in the command line.
	die "board '${BOARD}' not supported." \
	    "Please check that servod is running, or manually specify --chip."
elif [[ ${#SUPPORTED_CHIPS[@]} -eq 1 ]]; then
	CHIP="${SUPPORTED_CHIPS[0]}"
elif [ -n "${FLAGS_chip}" ]; then
	if $(in_array "${SUPPORTED_CHIPS[@]}" "${FLAGS_chip}"); then
		CHIP="${FLAGS_chip}"
	else
		die "board ${BOARD} only supports (${SUPPORTED_CHIPS[@]})," \
			"not ${FLAGS_chip}."
	fi
else
	# Ideally, ec_chip per servo_type should be specified in servo overlay
	# file, instead of having multiple board-to-chip mapping info in this
	# script. Please refer to crrev.com/c/1496460 for example.
	die "board ${BOARD} supports multiple chips" \
	"(${FILTERED_CHIPS[@]}). Use --chip= to choose one."
fi

if [ -n "${FLAGS_chip}" -a "${CHIP}" != "${FLAGS_chip}" ]; then
	die "board ${BOARD} doesn't use chip ${FLAGS_chip}"
fi

if [[ "${CHIP}" = "stm32_dfu" ]]; then
	NEED_SERVO="no"
fi

# Servo variables management
case "${BOARD}" in
	chocodile_bec ) MCU="usbpd" ;;
	oak_pd|samus_pd|strago_pd ) MCU="usbpd" ;;
	chell_pd|glados_pd ) MCU="usbpd" ;;
	bloonchipper|dartmonkey|hatch_fp|nami_fp|nocturne_fp ) MCU="fpmcu" ;;
	dingdong|hoho|twinkie ) DUT_CONTROL_CMD=( "true" ); MCU="ec" ;;
	*) MCU="ec" ;;
esac

case "${CHIP}" in
	"stm32"|"npcx_spi"|"npcx_int_spi"|"it83xx"|"npcx_uut"|"ite_spi"| \
			"ite_spi_ccd_i2c")
		;;
	*)
		if [[ -n "${FLAGS_read}" ]]; then
			die "The flag is not yet supported on ${CHIP}."
		fi

		# If verification is not supported, then show a warning message.
		# Keep it running however.
		if [[ "${FLAGS_verify}" == ${FLAGS_TRUE} ]]; then
			warn "Ignoring '--verify'" \
			"since read is not supported on ${CHIP}."
		fi
		;;
esac

SERVO_TYPE="$(dut_control_get servo_type || :)"

if [[ "${SERVO_TYPE}" =~ ^servo_v4(p1)?_with_.*$ ]]; then
	ACTIVE_DEVICE="$(dut_control_get active_dut_controller)"
else
	ACTIVE_DEVICE="${SERVO_TYPE}"
fi

if [[ "${SERVO_TYPE}" =~ ^servo_v4(p1)?_with_.*_and_.*$ ]]; then
	# If there are two devices, servo v4 type will show both devices. The
	# default device is first. The other device is second.
	# servo_type:servo_v4{p1}_with_servo_micro_and_ccd_cr50
	SECOND_DEVICE="${SERVO_TYPE#*_and_}"
	# servo_v4p1 can shared the same type with servo_v4, since there's no
	# difference handling the flash.
	SERVO_TYPE="servo_v4_with_${ACTIVE_DEVICE}"
	# Controls sent through the default device don't have a prefix. The
	# second device controls do. If the default device isn't active, we
	# need to use the second device controls to send commands. Use the
	# prefix for all dut control commands.
	if [[ "${SECOND_DEVICE}" = "${ACTIVE_DEVICE}" ]]; then
		DUT_CTRL_PREFIX="${ACTIVE_DEVICE}."
	fi
fi

servo_is_ccd() {
	[[ "${SERVO_TYPE}" =~ "ccd_cr50" ]] || \
	[[ "${SERVO_TYPE}" =~ "ccd_ti50" ]]
}

servo_has_warm_reset() {
	dut_control -i warm_reset >/dev/null 2>&1
}

servo_has_cold_reset() {
	dut_control -i cold_reset >/dev/null 2>&1
}

servo_has_dut_i2c_mux() {
	dut_control -i dut_i2c_mux >/dev/null 2>&1
}

servo_ec_hard_reset_or_die() {
	dut_control_or_die cold_reset:on
	dut_control_or_die cold_reset:off

	# Cold reset on C2D2 is H1 reset, which will double reset the EC
	# We need to wait a little bit to ensure we catch final EC reset
	if [[ "${SERVO_TYPE}" =~ "c2d2" ]]; then
		print_or_run sleep 0.2
	fi
}

servo_ec_hard_reset() {
	dut_control cold_reset:on
	dut_control cold_reset:off
}

c2d2_ec_hard_reset() {
	# This is an H1-level reset (instead of just an EC-level reset)
	dut_control cold_reset:on
	dut_control cold_reset:off
}

servo_usbpd_hard_reset() {
	dut_control usbpd_reset:on sleep:0.5 usbpd_reset:off
}

servo_fpmcu_hard_reset() {
	dut_control fpmcu_reset:on sleep:0.5 fpmcu_reset:off
}

servo_sh_hard_reset() {
	dut_control sh_reset:on
	dut_control sh_reset:off
}

ec_reset() {
	local stype

	if [[ "${SERVO_TYPE}" =~ "servo" ]] || servo_is_ccd; then
		stype="servo"
	else
		stype=${SERVO_TYPE}
	fi

	if [[ -n "${stype}" ]]; then
		eval ${stype}_${MCU}_hard_reset
	fi
}

ccd_ec_boot0() {
	local on_value="${1}"
	local boot_mode="${2}"

	info "Using CCD ${boot_mode}."

	if [[ "${on_value}" == "on" ]] && [[ "${boot_mode}" == "uut" ]] ; then
		# Ti50 requires EC reset to be asserted before UUT mode can be
		# enabled, Cr50 should not mind.
		dut_control cold_reset:on
	fi

	dut_control "ccd_ec_boot_mode_${boot_mode}":"${on_value}"
}

servo_micro_ec_boot0() {
	# Some devices (e.g. hatch) control the RX UART pin via an on-board
	# circuit that is controlled by the EC_FLASH_ODL pin. For those boards,
	# we want to continue to drive the EC_FLASH_ODL if they do not have the
	# servo micro rework listed below.
	if [[ "${FLAGS_servo_micro_uart_rx_rework}" == ${FLAGS_TRUE} ]]; then
		info "Servo micro $2 mode: $1 (using rx_rework)"

		# Setting the test point allows the EC_TX_SERVO_RX line
		# to be driven by the servo for 'on' value. 'off' value
		# lets the EC drive the EC_TX_SERVO_RX net.
		#
		# HW Rework (b/143163043#comment3):
		#   - Disconnect U45.1 from ground
		#   - Connected U45.1 to TP1 pad
		dut_control tp1:$1
		dut_control servo_micro_ec_boot_mode_$2:$1
	else
		info "Servo micro $2 mode: $1 (using FW_UP_L)"
		dut_control ec_boot_mode:$1
	fi
}

servo_ec_boot0() {
	dut_control ec_boot_mode:$1
}

c2d2_ec_boot0() {
	dut_control ec_boot_mode_uut:$1
}

servo_usbpd_boot0() {
	dut_control usbpd_boot_mode:$1
}

servo_fpmcu_boot0() {
	dut_control fpmcu_boot_mode:"$1"
}

servo_micro_fpmcu_boot0() {
	servo_fpmcu_boot0 "$@"
}

servo_micro_usbpd_boot0() {
	servo_usbpd_boot0 "$@"
}

servo_sh_boot0() {
	dut_control sh_boot_mode:$1
}

ec_switch_boot0() {
	local on_value=$1
	# Enable programming GPIOs
	if $(in_array "${BOARDS_STM32_PROG_EN[@]}" "${BOARD}"); then
		servo_save_add "prog_en"

		dut_control prog_en:yes
	fi
	if servo_is_ccd ; then
		stype=ccd
	elif [[ "${SERVO_TYPE}" =~ "servo_micro" ]] ; then
		stype=servo_micro
	elif [[ "${SERVO_TYPE}" =~ "servo" ]] ; then
		stype=servo
	else
		stype=${SERVO_TYPE}
	fi
	eval ${stype}_${MCU}_boot0 "${on_value}" $2
}

ec_enable_boot0() {
	ec_switch_boot0 "on" $1
}

ec_disable_boot0() {
	ec_switch_boot0 "off" $1
}

# Returns 0 on success (if on beaglebone)
on_servov3() {
	grep -qs '^CHROMEOS_RELEASE_BOARD=beaglebone_servo' /etc/lsb-release
}

# Returns 0 on success (if raiden should be used instead of servo)
error_reported=  # Avoid double printing the error message.
on_raiden() {
	if [[ "${SERVO_TYPE}" =~ "servo_v4" ]] || \
	   servo_is_ccd || \
	   [[ "${SERVO_TYPE}" =~ "servo_micro" ]]; then
		return 0
	fi
	if [ -z "${BOARD}" ]; then
		[ "${FLAGS_raiden}" = ${FLAGS_TRUE} ] && return 0 || return 1
	fi
	if [ "${FLAGS_raiden}" = ${FLAGS_TRUE} ]; then
		if in_array "${BOARDS_RAIDEN[@]}" "${BOARD}"; then
			return 0
		fi
		if [ -z "${error_reported}" ]; then
			error_reported="y"
			die "raiden mode not supported on ${BOARD}" >&2
		fi
	fi
	return 1
}

declare -a DELETE_LIST 	# Array of file/dir names to delete at exit

# Put back the servo and the system in a clean state at exit
FROZEN_PIDS=""
cleanup() {
	for pid in ${FROZEN_PIDS}; do
		info "Sending SIGCONT to process ${pid}!"
		kill -CONT "${pid}"
	done

	# Delete all files or directories in DELETE_LIST.
	for item in "${DELETE_LIST[@]}"; do
		if [[ -e "${item}" ]]; then
			print_or_run rm -rf "${item}" &> /dev/null
		fi
	done

	if [ "${CHIP}" = "it83xx" ]; then
		if [ "${SERVO_TYPE}" = "servo_v2" ]; then
			info "Reinitializing ftdi_i2c interface"
			# Ask servod to close its FTDI I2C interface because it
			# could be open or closed at this point.  Using
			# ftdii2c_cmd:close when it's already closed is okay,
			# however ftdii2c_cmd:open when it's already open
			# triggers an error.
			#
			# If there is a simple, reliable way to detect whether
			# servod FTDI I2C interface is open or closed, it would
			# be preferable to check and only re-initialize if it's
			# closed.  Patches welcome.
			dut_control ftdii2c_cmd:close
			dut_control ftdii2c_cmd:init
			dut_control ftdii2c_cmd:open
			dut_control ftdii2c_cmd:setclock
		fi
	fi

	servo_restore

	if [ "${CHIP}" = "stm32" -o "${CHIP}" = "npcx_uut" ]; then
		dut_control "${MCU}"_boot_mode:off
	fi

	if servo_is_ccd; then
		dut_control ccd_ec_boot_mode_uut:off
		dut_control ccd_ec_boot_mode_bitbang:off
	fi

	if ! on_raiden || servo_has_cold_reset; then
		ec_reset
	fi
}
trap cleanup EXIT

# Possible default EC images
if [ "${FLAGS_ro}" = ${FLAGS_TRUE} ] ; then
	EC_FILE=ec.RO.flat
else
	EC_FILE=ec.bin
fi

LOCAL_BUILD=
if [[ -n "${EC_DIR}" ]]; then
	if [ "${FLAGS_ro}" = ${FLAGS_TRUE} ] ; then
		LOCAL_BUILD="${EC_DIR}/build/${BOARD}/RO/${EC_FILE}"
	else
		LOCAL_BUILD="${EC_DIR}/build/${BOARD}/${EC_FILE}"
	fi
fi

# Get baseboard from build system if present
BASEBOARD=

# We do not want to exit script if make call fails; we turn -e back on after
# setting BASEBOARD
set +e
if [[ -n "${EC_DIR}" ]]; then
	BASEBOARD=$(make --quiet -C ${EC_DIR} BOARD=${BOARD} print-baseboard \
		2>/dev/null)
elif [[ -d "${HOME}/trunk/src/platform/ec" ]]; then
	BASEBOARD=$(make --quiet -C ${HOME}/trunk/src/platform/ec \
		BOARD=${BOARD} print-baseboard 2>/dev/null)
else
	info "Could not find ec build folder to calculate baseboard."
fi
if [ $? -ne 0 ]; then
	info "EC build system didn't recognize ${BOARD}. Assuming no baseboard."
fi
set -e

if [[ -n "${BASEBOARD}" ]]; then
	EMERGE_BUILD=/build/${BASEBOARD}/firmware/${BOARD}/${EC_FILE}
else
	EMERGE_BUILD=/build/${BOARD}/firmware/${EC_FILE}
fi

# Find the EC image to use
function ec_image() {
	# No image specified on the command line, try default ones
	if [[ -n "${FLAGS_image}" ]] ; then
		if [ -f "${FLAGS_image}" ] || \
		   [ "${FLAGS_image}" == "-" ]; then
			echo "${FLAGS_image}"
			return
		fi
		die "Invalid image path : ${FLAGS_image}"
	else
		if [ -f "${LOCAL_BUILD}" ]; then
			echo "${LOCAL_BUILD}"
			return
		fi
		if [ -f "${EMERGE_BUILD}" ]; then
			echo "${EMERGE_BUILD}"
			return
		fi
	fi
	die "no EC image found : build one or specify one."
}

# Get the correct UART for flashing. The resulting string is concatenated with
# the various UART control suffixes, such as "_pty", "_en", "_parity", etc.
function servo_ec_uart_prefix() {
	if [[ "${MCU}" == "fpmcu" ]]; then
		# The FPMCU has multiple UARTs. Use the platform UART since it's a
		# bootloader capable UART on all devices. See
		# http://go/cros-fingerprint-reference-designs#uart-console.
		echo "fpmcu_platform_uart"
		return
	fi

	echo "${MCU}_uart"
}

# Find the EC UART provided by servo.
function servo_ec_uart() {
	SERVOD_FAIL="Cannot communicate with servod. Is servod running?"
	local EC_UART_PREFIX
	EC_UART_PREFIX="$(servo_ec_uart_prefix)"
	PTY=$(dut_control_get "raw_${EC_UART_PREFIX}_pty" ||
	      dut_control_get "${EC_UART_PREFIX}_pty")
	if [[ -z "${PTY}" ]]; then
	    die "${SERVOD_FAIL}"
	fi
	echo $PTY
}

# Not every control is supported on every servo type.  Therefore, define which
# controls are supported by each servo type.
servo_v2_VARS=( "cold_reset" )
servo_micro_VARS=( "cold_reset" )
servo_v4_with_ccd_cr50_VARS=( "cold_reset" )
c2d2_VARS=( "cold_reset" )

# Some servo boards use the same controls.
servo_v3_VARS=( "${servo_v2_VARS[@]}" )
servo_v4_with_servo_micro_VARS=( "${servo_micro_VARS[@]}" )

declare -a save

#######################################
# Store DUT control value to restore in LIFO order.
# Arguments:
#   $1: Control name
#   $2: (optional) Value to restore for the name at exit.
#######################################
function servo_save_add() {
	local CTRL_RESULT=
	case $# in
		1) CTRL_RESULT="$( "${DUT_CONTROL_CMD[@]}" \
				"${DUT_CTRL_PREFIX}$@" )"
		   if [[ -n "${CTRL_RESULT}" ]]; then
			# Don't save the control with the prefix, because
			# dut_control will add the prefix again when we restore
			# the settings.
			save=( "${CTRL_RESULT#$DUT_CTRL_PREFIX}" "${save[@]}" )
		   fi
		   ;;
		2) save=( "$1:$2" "${save[@]}" )
		   ;;
		*) die "${FUNCNAME[0]} failed: arguments error"
		   ;;
	esac
}

function servo_save() {
	local SERVO_VARS_NAME="${SERVO_TYPE}_VARS[@]"
	for ctrl in "${!SERVO_VARS_NAME}"; do
		servo_save_add "${ctrl}"
	done

	if [[ "${SERVO_TYPE}" == "servo_v2" ]]; then
		servo_save_add "i2c_mux_en"
		servo_save_add "i2c_mux"

		dut_control i2c_mux_en:on
		dut_control i2c_mux:remote_adc
	fi
}

function servo_restore() {
	info "Restoring servo settings..."
	for ctrl in "${save[@]}"; do
		if [[ -n "${ctrl}" ]]; then
			dut_control "${ctrl}"
		fi
	done
}

function claim_pty() {
	if grep -q cros_sdk /proc/1/cmdline; then
		die "You must run this tool in a chroot that was entered with" \
		    "'cros_sdk --no-ns-pid' (see crbug.com/444931 for details)"
	fi

	if [[ -z "$1" ]]; then
		warn "No parameter passed to claim_pty()"
		return
	fi

	# Disconnect the EC-3PO interpreter from the UART since it will
	# interfere with flashing.
	servo_save_add "${MCU}_ec3po_interp_connect"

	dut_control ${MCU}_ec3po_interp_connect:off || \
	    warn "hdctools cannot disconnect the EC-3PO interpreter from" \
	    "the UART."

	pids=$(lsof -FR 2>/dev/null -- $1 | grep -v '^f' | tr -d 'pR')
	FROZEN_PIDS=""

	# reverse order to SIGSTOP parents before children
	for pid in $(echo ${pids} | tac -s " "); do
		if ps -o cmd= "${pid}" | grep -qE "(servod|/sbin/init)"; then
			info "Skip stopping servod or init: process ${pid}."
		else
			info "Sending SIGSTOP to process ${pid}!"
			FROZEN_PIDS+=" ${pid}"
			sleep 0.02
			kill -STOP ${pid}
		fi
	done
}

# Returns the serialnumber of the specified servo.
function get_serial() {
	if [[ "${SERVO_TYPE}" =~ ^servo_v4(p1)?_with_servo_micro ]]; then
		if [[ -z "${BOARD}" ]]; then
			sn_ctl="servo_micro_"
		elif [[ "$(dut_control_get "servo_micro_for_${BOARD}_serialname")" =~ \
				"unknown" ]] ; then
			# Fall back to servo_micro_ if S/N is uknown.
			sn_ctl="servo_micro_"
		else
			sn_ctl="servo_micro_for_${BOARD}_"
		fi
	elif [[ "${SERVO_TYPE}" =~ "_with_ccd" ]] ; then
		sn_ctl="ccd_"
	else
		# If it's none of the above, the main serialname will do.
		sn_ctl=""
	fi

	dut_control_get "${sn_ctl}serialname"
}

function c2d2_wait_for_h1_power_on_or_reset() {
	# Ensure we have the latest c2d2 fw and hdctools. This could
	# be removed eventually (estimate removal 2020/06/01)
	dut_control h1_vref_present || die "Need to kill servod and run:
repo sync && sudo emerge hdctools servo-firmware && sudo servo_updater -b c2d2"

	# Handle the case when flash_ec starts before DUT power is
	# applied. Otherwise just use h1-level reset.
	if [[ "$(dut_control_get h1_vref_present)" = "off" ]] ; then
		info "Please attach C2D2 to DUT and power DUT now!"
		# Waits ~40 seconds for Vref presence before timeout
		local LOOP_COUNTER=100
		while [[ "$(dut_control_get h1_vref_present)" = "off" \
			&& "${LOOP_COUNTER}" -gt 1 ]] ; do
			sleep 0.1
			let LOOP_COUNTER=LOOP_COUNTER-1
		done
		# If we ran out of time, then just die now
		if [[ "${LOOP_COUNTER}" -eq 1 ]] ; then
			die "H1 Vref not present after waiting"
		fi
	else
		# Ensure DUT is in clean state with H1 Reset
		dut_control cold_reset:on
		dut_control cold_reset:off
	fi
}

# Board specific flashing scripts

# helper function for using servo v2/3 with openocd
function flash_openocd() {
	OCD_CFG="servo.cfg"
	if [[ -z "${EC_DIR}" ]]; then
		# check if we're on beaglebone
		if [[ -e "/usr/share/ec-devutils" ]]; then
			OCD_PATH="/usr/share/ec-devutils"
		else
			die "Cannot locate openocd configs"
		fi
	else
		OCD_PATH="${EC_DIR}/util/openocd"
	fi

	servo_save_add "jtag_buf_on_flex_en"
	servo_save_add "jtag_buf_en"

	dut_control jtag_buf_on_flex_en:on
	dut_control jtag_buf_en:on

	print_or_run sudo timeout -k 10 -s 9 "${FLAGS_timeout}" \
		openocd -s "${OCD_PATH}" -f "${OCD_CFG}" -f "${OCD_CHIP_CFG}" \
		-c "${OCD_CMDS}" || \
	die "Failed to program ${IMG}"
}

# helper function for using servo with flashrom
function flash_flashrom() {
	TOOL_PATH="${EC_DIR}/build/${BOARD}/util:$PATH:/usr/sbin"
	FLASHROM=$(PATH="${TOOL_PATH}" which flashrom)

	if on_servov3; then
		FLASHROM_ARGS="-p linux_spi"
	elif on_raiden; then
	    if [[ "${SERVO_TYPE}" =~ "servo_micro" ]]; then
		# Servo micro doesn't use the "target" parameter.
		FLASHROM_ARGS="-p raiden_debug_spi:"
	    else
		FLASHROM_ARGS="-p raiden_debug_spi:target=EC,"
	    fi
	else
		FLASHROM_ARGS="-p ft2232_spi:type=google-servo-v2,port=B,"
	fi

	if [ ! -x "$FLASHROM" ]; then
		die "no flashrom util found."
	fi

	if ! on_servov3; then
		SERIALNAME=$(get_serial)
		if [[ "$SERIALNAME" != "" ]] ; then
			FLASHROM_ARGS+="serial=${SERIALNAME}"
		fi
	fi

	if ! on_raiden || [[ "${SERVO_TYPE}" =~ "servo_micro" ]] ; then
		if $(in_array "${BOARDS_SPI_1800MV[@]}" "${BOARD}"); then
			SPI_VOLTAGE="pp1800"
		elif [[ "${CHIP}" == "ite_spi" || "${CHIP}" == "ite_spi_ccd_i2c" ]]; then
			SPI_VOLTAGE="pp1800"
		else
			SPI_VOLTAGE="pp3300"
		fi

		dut_control cold_reset:on

		# If spi flash is in npcx's ec, enable gang programer mode
		if [[ "${CHIP}" == "npcx_int_spi" ]]; then
			servo_save_add "fw_up" "off"

			# Set GP_SEL# as low then start ec
			dut_control fw_up:on
			sleep 0.1
			dut_control cold_reset:off
		fi

		# Enable SPI programming mode.
		if [[ "${CHIP}" == "ite_spi" || "${CHIP}" == "ite_spi_ccd_i2c" ]]; then
			# Set hardware strap pin (GPG6) of SPI programming as low then start ec
			dut_control fw_up:on
			sleep 0.1
			dut_control cold_reset:off
			sleep 0.1
			# We have to release the HW strap pin because it also SPI clock pin.
			dut_control fw_up:off
		fi

		servo_save_add "spi1_vref" "off"
		servo_save_add "spi1_buf_en" "off"

		# Turn on SPI1 interface on servo for SPI Flash Chip
		dut_control spi1_vref:${SPI_VOLTAGE} spi1_buf_en:on
		if [[ ! "${SERVO_TYPE}" =~ "servo_micro" ]]; then
			# Servo micro doesn't support this control.
			servo_save_add "spi1_buf_on_flex_en" "off"
			dut_control spi1_buf_on_flex_en:on
		fi
	else
		if [[ "${CHIP}" == "npcx_int_spi" ]]; then
			servo_save_add "fw_up" "off"

			# Set GP_SEL# as low then start ec
			dut_control cold_reset:on
			dut_control fw_up:on
			# sleep 0.1
			dut_control cold_reset:off
		else
			# Assert EC reset.
			dut_control cold_reset:on
		fi

		# Temp layout
		L=/tmp/flash_spi_layout_$$
		DELETE_LIST+=( "${L}" )

		[[ -z "${FLAGS_read}" ]] && dump_fmap -F "${IMG}" > "${L}"

		FLASHROM_OPTIONS="-i EC_RW -i WP_RO -l "${L}" --noverify-all"
	fi

	# Generate the correct flashrom command base.
	FLASHROM_CMDLINE="${FLASHROM} ${FLASHROM_ARGS}"
	if [[ -z "${FLAGS_read}" ]]; then
		# Program EC image.
		# flashrom should report the image size at the end of the output.
		local FLASHROM_GETSIZE="sudo ${FLASHROM_CMDLINE} --flash-size"
		if [[ "${FLAGS_verbose}" == ${FLAGS_TRUE} ]]; then
			info "Running flashrom:" 1>&2
			echo " ${FLASHROM_GETSIZE}" 1>&2
		fi
		SPI_SIZE=$(${FLASHROM_GETSIZE} | grep -oe '[0-9]\+$' |
			   tail -n1) || die "Failed to determine chip size!"
		[[ ${SPI_SIZE} -eq 0 ]] && die "Chip size is 0!"

		PATCH_SIZE=$((${SPI_SIZE} - ${IMG_SIZE}))

		# Temp image
		T=/tmp/flash_spi_$$
		DELETE_LIST+=( "${T}" )

		if [[ "${CHIP}" =~ ^npcx(|_int)_spi$ ]] || \
		   [[ "${CHIP}" =~ "ite_spi_ccd_i2c" ]] ||
		   [[ "${CHIP}" =~ "ite_spi" ]] ; then
		{	# Patch temp image up to SPI_SIZE
			cat "$IMG"
			if [[ ${IMG_SIZE} -lt ${SPI_SIZE} ]] ; then
				dd if=/dev/zero bs=${PATCH_SIZE} count=1 | \
					tr '\0' '\377'
			fi
		} > $T
		else
		{	# Patch temp image up to SPI_SIZE
			if [[ ${IMG_SIZE} -lt ${SPI_SIZE} ]] ; then
				dd if=/dev/zero bs=${PATCH_SIZE} count=1 | \
					tr '\0' '\377'
			fi
			cat "$IMG"
		} > $T
		fi

		info "Programming EC firmware image."
		local FLASHROM_WRITE="${FLASHROM_CMDLINE} ${FLASHROM_OPTIONS}"
		if [[ "${FLAGS_verbose}" == ${FLAGS_TRUE} ]]; then
			info "Running flashrom:" 1>&2
			echo " ${FLASHROM_WRITE} -w ${T}" 1>&2
		fi
		print_or_run sudo timeout -k 10 -s 9 "${FLAGS_timeout}" \
			${FLASHROM_WRITE} -w "${T}" \
			|| die "${MSG_PROGRAM_FAIL}"
	else
		# Read EC image.
		info "Reading EC firmware image."
		if [[ "${FLAGS_verbose}" == ${FLAGS_TRUE} ]]; then
			info "Running flashrom:" 1>&2
			echo " ${FLASHROM_CMDLINE} -r ${FLAGS_read}" 1>&2
		fi
		print_or_run sudo timeout -k 10 -s 9 "${FLAGS_timeout}" \
			${FLASHROM_CMDLINE} -r "${FLAGS_read}" \
			|| die "${MSG_READ_FAIL}"
	fi
}

function flash_stm32() {
	local STM32MON
	local STM32MON_OPT

	if ! servo_has_cold_reset; then
		die "Cold reset must be available for STM32 programming"
	fi

	TOOL_PATH="${EC_DIR}/build/${BOARD}/util:$PATH"
	STM32MON=$(PATH="${TOOL_PATH}" which stm32mon)
	EC_UART="$(servo_ec_uart)"
	EC_UART_PREFIX="$(servo_ec_uart_prefix)"
	if [ ! -x "$STM32MON" ]; then
		die "no stm32mon util found."
	fi

	info "Using serial flasher : ${STM32MON}"
	info "${MCU} UART pty : ${EC_UART}"
	claim_pty ${EC_UART}
	STM32MON_OPT="-d ${EC_UART}"

	# Make sure EC reboots in serial monitor mode.
	ec_enable_boot0 "bitbang"

	# Pulse EC reset.
	ec_reset

	if ! on_raiden && [[ "${SERVO_TYPE}" =~ "servo" ]] ; then
		servo_save_add "${EC_UART_PREFIX}_en"

		dut_control "${EC_UART_PREFIX}_en:on"
	fi

	servo_save_add "${EC_UART_PREFIX}_parity"

	dut_control "${EC_UART_PREFIX}_parity:even"

	if servo_is_ccd ; then
		case "${FLAGS_bitbang_rate}" in
			(9600|19200|38400|57600) : ;;
			(*)
			    die "${FLAGS_bitbang_rate} is not a valid" \
			    "bit bang rate"
			    ;;
		esac
		info "Programming at ${FLAGS_bitbang_rate} baud"

		servo_save_add "${EC_UART_PREFIX}_baudrate"
		servo_save_add "${EC_UART_PREFIX}_bitbang_en"

		dut_control "${EC_UART_PREFIX}_baudrate:${FLAGS_bitbang_rate}"
		dut_control "${EC_UART_PREFIX}_bitbang_en:on"
	else
		servo_save_add "${EC_UART_PREFIX}_baudrate"

		dut_control "${EC_UART_PREFIX}_baudrate:115200"
	fi

	# Add a delay long enough for cr50 to update the ccdstate. Cr50 updates
	# ccdstate once a second, so a 2 second delay should be safe.
	if servo_is_ccd ; then
		sleep 2
		STM32MON_OPT+=" -c"
	fi

	if [ -n "${FLAGS_logfile}" ]; then
		info "Saving log in ${FLAGS_logfile}"
		STM32MON_OPT+=" -L ${FLAGS_logfile}"
	fi

	local IMG_READ="${FLAGS_read}"
	# Program EC image.
	if [[ -z "${IMG_READ}" ]]; then
		info "Programming EC firmware image."
		# Unprotect flash, erase, and write
		local STM32MON_COMMAND="${STM32MON} ${STM32MON_OPT} -U -u -e -w"
		if [[ "${FLAGS_verbose}" == ${FLAGS_TRUE} ]]; then
			echo "${STM32MON_COMMAND} ${IMG}"
		fi
		print_or_run timeout -k 10 -s 9 "${FLAGS_timeout}" \
			${STM32MON_COMMAND} "${IMG}" \
			|| die "${MSG_PROGRAM_FAIL}"

		# If it is a program-verify request, then make a temporary
		# directory to store the image
		if [[ "${FLAGS_verify}" == ${FLAGS_TRUE} ]]; then
			local TEMP_SUFFIX=".$(basename ${SCRIPT}).${CHIP}"
			local TEMP_DIR="$(mktemp -d --suffix="${TEMP_SUFFIX}")"

			IMG_READ="${TEMP_DIR}/ec.read.bin"
			DELETE_LIST+=( "${TEMP_DIR}" )
		fi
	fi

	# Read EC image.
	if [[ -n "${IMG_READ}" ]]; then
		info "Reading EC firmware image."
		local STM32MON_READ_CMD="${STM32MON} ${STM32MON_OPT} -U -r"
		if [[ "${FLAGS_verbose}" == ${FLAGS_TRUE} ]]; then
			echo "${STM32MON_READ_CMD} ${IMG_READ}"
		fi
		print_or_run timeout -k 10 -s 9 "${FLAGS_timeout}" \
			${STM32MON_READ_CMD} "${IMG_READ}" \
			|| die "${MSG_READ_FAIL}"
	fi

	# Verify the flash by comparing the source image to the read image,
	# only if it was a flash write request.
	if [[ -z "${FLAGS_read}" && "${FLAGS_verify}" == ${FLAGS_TRUE} ]]; then
		info "Verifying EC firmware image."
		if [[ "${FLAGS_verbose}" == ${FLAGS_TRUE} ]]; then
			echo "cmp -n ${IMG_SIZE} ${IMG} ${IMG_READ}"
		fi
		cmp -s -n "${IMG_SIZE}" "${IMG}" "${IMG_READ}" \
			|| die "${MSG_VERIFY_FAIL}"
	fi

	# Remove the Application processor reset
	# TODO(crosbug.com/p/30738): we cannot rely on servo_VARS to restore it
	if servo_has_warm_reset; then
		dut_control warm_reset:off
	fi
}

function flash_stm32_dfu() {
	DFU_DEVICE=0483:df11
	ADDR=0x08000000
	DFU_UTIL='dfu-util'
	which $DFU_UTIL &> /dev/null || die \
		"no dfu-util util found.  Did you 'sudo emerge dfu-util'"

	info "Using dfu flasher : ${DFU_UTIL}"

	dev_cnt=$(lsusb -d $DFU_DEVICE | wc -l)
	if [ $dev_cnt -eq 0 ] ; then
		die "unable to locate dfu device at $DFU_DEVICE"
	elif [ $dev_cnt -ne 1 ] ; then
		die "too many dfu devices (${dev_cnt}). Disconnect all but one."
	fi

	SIZE=$(wc -c ${IMG} | cut -d' ' -f1)
	# Remove read protection
	print_or_run sudo timeout -k 10 -s 9 "${FLAGS_timeout}" $DFU_UTIL -a 0 \
		-d "${DFU_DEVICE}" -s ${ADDR}:${SIZE}:force:unprotect -D "${IMG}"
	# Wait for mass-erase and reboot after unprotection
	print_or_run sleep 1
	# Actual image flashing
	print_or_run sudo timeout -k 10 -s 9 "${FLAGS_timeout}" $DFU_UTIL -a 0 \
		-d "${DFU_DEVICE}" -s ${ADDR}:${SIZE} -D "${IMG}"
}

function dut_i2c_dev() {
	if [ -n "$DUT_I2C_DEV" ]; then
		[ -e "$DUT_I2C_DEV" ] ||
			die "\$DUT_I2C_DEV is a non-existent path: $DUT_I2C_DEV"
		echo "$DUT_I2C_DEV"
		return
	fi

	local adap_num=
	adap_num="$(dut_control_get_or_die \
	    "${ACTIVE_DEVICE}_i2c_pseudo_adapter_num")"
	echo /dev/i2c-"$adap_num"
}

function flash_it83xx() {
	local TOOL_PATH="${EC_DIR}/build/${BOARD}/util:$PATH"
	local ITEFLASH_BIN=$(PATH="${TOOL_PATH}" which iteflash)

	if [[ ! -x "$ITEFLASH_BIN" ]]; then
		die "no iteflash util found."
	fi

	# We need to ensure that c2d2 and dut-side path are set up for i2c
	if [[ "${SERVO_TYPE}" =~ "c2d2" ]]; then
		c2d2_wait_for_h1_power_on_or_reset

		# Don't let the EC come out of reset after H1 reset
		dut_control ec_reset:on

		# Enable i2c bus on C2D2 at 400kbps
		servo_save_add "i2c_ec_bus_speed"
		dut_control_or_die i2c_ec_bus_speed:400

		# We need to swing the DUT-side muxes to I2C instead of UART.
		# This is done by convention with EC_FLASH_SELECT pin from H1
		dut_control_or_die ec_flash_select:on
	fi

	# Now the we have enabled the I2C mux on the servo to talk to the dut,
	# we need to switch the I2C mux on the dut to allow ec programing (if
	# there is a mux on the dut)
	if servo_has_dut_i2c_mux; then
		info "Switching DUT I2C Mux to ${CHIP}"
		servo_save_add "dut_i2c_mux"
		dut_control dut_i2c_mux:ec_prog
	fi

	# We need to send the special waveform very soon after the EC powers on
	if [[ "${SERVO_TYPE}" =~ "c2d2" ]]; then
		# The EC was held in reset above
		dut_control ec_reset:off
	elif servo_has_cold_reset; then
		servo_ec_hard_reset_or_die
	fi

	# Send the special waveform to the ITE EC.
	if [[ "${SERVO_TYPE}" =~ "servo_micro" || \
	      "${SERVO_TYPE}" =~ "c2d2" ]] ; then
		info "Asking servo to send the dbgr special waveform to ${CHIP}"
		dut_control_or_die enable_ite_dfu
	elif servo_is_ccd; then
		local CCD_I2C_CAP="$(dut_control_get ccd_i2c_en)"
		if [[ "${CCD_I2C_CAP,,}" != "always" ]]; then
			die "CCD I2C capability is not set as 'Always'" \
				": ${CCD_I2C_CAP}"
		fi

		info "Asking CR50 to send the dbgr special waveform to ${CHIP}"
		sleep 2
		dut_control_or_die cr50_i2c_ctrl:ite_debugger_mode
		sleep 3
	elif [[ "${SERVO_TYPE}" == "servo_v2" ]]; then
		info "Closing servod connection to ftdi_i2c interface"
		dut_control_or_die ftdii2c_cmd:close
	else
		die "This servo type is not yet supported: ${SERVO_TYPE}"
	fi

	# Build the iteflash command line.
	local ITEFLASH_ARGS=( "${ITEFLASH_BIN}" )

	if [[ "${SERVO_TYPE}" == "servo_v2" ]]; then
		ITEFLASH_ARGS+=( "--send-waveform=1" "--i2c-interface=ftdi" )
	else
		ITEFLASH_ARGS=( "sudo" "--" "${ITEFLASH_ARGS[@]}" \
			"--send-waveform=0" "--i2c-interface=linux" \
			"--i2c-dev-path=$(dut_i2c_dev)" )
		if servo_is_ccd; then
			ITEFLASH_ARGS+=( "--block-write-size=256" )
		fi
	fi

	local ERROR_MSG
	if [[ -n "${FLAGS_read}" ]]; then
		ITEFLASH_ARGS+=( "--read=${FLAGS_read}" )
		info "Reading EC firmware image using iteflash..."
		ERROR_MSG="${MSG_READ_FAIL}"
	else
		ITEFLASH_ARGS+=( "--erase" "--write=${IMG}" )
		info "Programming EC firmware image using iteflash..."
		ERROR_MSG="${MSG_PROGRAM_FAIL}"
	fi

	if [[ "${FLAGS_verify}" == "${FLAGS_FALSE}" ]]; then
		ITEFLASH_ARGS+=( "--noverify" )
	fi

	if [[ "${FLAGS_verbose}" == ${FLAGS_TRUE} ]]; then
		ITEFLASH_ARGS+=( "--debug" )
		echo "${ITEFLASH_ARGS[@]}"
	fi

	# Ensure the CR50 doesn't go into low power while flashing
	# otherwise the DUT side muxes will get cut. Note this also needs to
	# happen once the CR50 hooks have settled and H1 realizes that the
	# "AP [is] Off", since that over writes the idle action with sleep.
	if [[ "${SERVO_TYPE}" =~ "c2d2" ]]; then
		dut_control cr50_idle_level:active
	fi

	print_or_run "${ITEFLASH_ARGS[@]}" || die "${ERROR_MSG}"
}

function flash_ite_spi() {
	flash_flashrom
}

function flash_ite_spi_ccd_i2c() {
	if [[ "${SERVO_TYPE}" =~ "servo_micro" || \
	      "${SERVO_TYPE}" =~ "servo_v2" ]] ; then
		flash_flashrom
	else
		flash_it83xx
	fi
}

function flash_lm4() {
	OCD_CHIP_CFG="lm4_chip.cfg"
	OCD_CMDS="init; flash_lm4 ${IMG} ${FLAGS_offset}; shutdown;"

	flash_openocd

}

function flash_nrf51() {
	OCD_CHIP_CFG="nrf51_chip.cfg"
	OCD_CMDS="init; flash_nrf51 ${IMG} ${FLAGS_offset}; exit_debug_mode_nrf51; shutdown;"

	flash_openocd

	# waiting 100us for the reset pulse is not necessary, it takes ~2.5ms
	dut_control swd_reset:on swd_reset:off
}

function flash_npcx_jtag() {
	IMG_PATH="${EC_DIR}/build/${BOARD}"
	OCD_CHIP_CFG="npcx_chip.cfg"
	if [ "${FLAGS_ro}" = ${FLAGS_TRUE} ] ; then
		# Program RO region only
		OCD_CMDS="init; flash_npcx_ro ${CHIP} ${IMG_PATH} ${FLAGS_offset}; shutdown;"
	else
		# Program all EC regions
		OCD_CMDS="init; flash_npcx_all ${CHIP} ${IMG_PATH} ${FLAGS_offset}; shutdown;"
	fi

	# Reset the EC
	ec_reset

	flash_openocd
}

function flash_npcx_uut() {
	local TOOL_PATH="${EC_DIR}/build/${BOARD}/util:$PATH"
	local NPCX_UUT=$(PATH="${TOOL_PATH}" which uartupdatetool)
	local EC_UART="$(servo_ec_uart)"

	# Look for npcx_monitor.bin in multiple directories, starting with
	# the same path as the EC binary.
	local MON=""
	for dir in \
		"$(dirname "$IMG")/chip/npcx/spiflashfw" \
		"$(dirname "$IMG")" \
		"${EC_DIR}/build/${BOARD}/chip/npcx/spiflashfw" \
		"$(dirname "$LOCAL_BUILD")" \
		"$(dirname "$EMERGE_BUILD")" ;
	do
		if [ -f "$dir/npcx_monitor.bin" ] ; then
			MON="$dir/npcx_monitor.bin"
			break
		fi
	done
	if [ -z "${MON}" ] ; then
		echo "Failed to find npcx_monitor.bin"
		exit 1
	fi
	info "Using NPCX image : ${MON}"

	# The start address to restore monitor firmware binary
	local MON_ADDR="0x200C3020"

	if [ ! -x "$NPCX_UUT" ]; then
		die "no NPCX UART Update Tool found."
	fi

	info "Using: NPCX UART Update Tool"
	info "${MCU} UART pty : ${EC_UART}"
	claim_pty ${EC_UART}

	if [[ "${SERVO_TYPE}" =~ "ccd_cr50" ]] ; then
		# Ti50 does not yet support ccd_keepalive option which
		# requires ccdstate command on the GSC console.
		# TODO(b/161483597) remove the check when Ti50 CCD is on par.
		servo_save_add ccd_keepalive_en
		dut_control ccd_keepalive_en:on
	fi

	# C2D2 does not use waits and has to ensure that the EC does not come
	# out of reset after using a H1-level reset
	if [[ "${SERVO_TYPE}" =~ "c2d2" ]] ; then
		c2d2_wait_for_h1_power_on_or_reset

		# Don't let the EC come out of reset after H1 reset
		dut_control ec_reset:on

		# Force the EC to boot in UART update mode coming out of reset
		ec_enable_boot0 "uut"
		dut_control ec_reset:off

		# Ensure normal UART operation
		ec_disable_boot0 "uut"
	else
		# Force the EC to boot in UART update mode
		ec_enable_boot0 "uut"
		ec_reset

		# Have to wait a bit for EC boot-up
		print_or_run sleep 0.1

		# Ensure normal UART operation
		ec_disable_boot0 "uut"
		print_or_run sleep 0.1
	fi

	# Remove the prefix "/dev/" because uartupdatetool will add it.
	local UUT_ARGS=( "--port=${EC_UART#/dev/}" " --baudrate=115200" )
	local IMG_READ="${FLAGS_read}"

	# Program EC image.
	if [[ -z "${IMG_READ}" ]]; then
		info "Loading monitor binary."
		local UUT_MON=( "${NPCX_UUT}" "${UUT_ARGS[@]}"  \
				"--opr=wr" "--addr=${MON_ADDR}" \
				"--file=${MON}" )

		# Load monitor binary to address 0x200C3020
		if [[ "${FLAGS_verbose}" = ${FLAGS_TRUE} ]]; then
			echo "${UUT_MON[*]}"
		fi
		print_or_run timeout -k 10 -s 9 "${FLAGS_timeout}" \
			"${UUT_MON[@]}" || die "Failed to load monitor binary."

		info "Programming EC firmware image."
		local UUT_WR=( "${NPCX_UUT}" "${UUT_ARGS[@]}"      \
			       "--auto" "--offset=${FLAGS_offset}" \
			       "--file=${IMG}" )
		if [[ "${FLAGS_verbose}" = ${FLAGS_TRUE} ]]; then
			echo "${UUT_WR[*]}"
		fi
		print_or_run timeout -k 10 -s 9 "${FLAGS_timeout}" \
			"${UUT_WR[@]}" || die "${MSG_PROGRAM_FAIL}"

		# If it is a program-verify request, then make a temporary
		# directory to store the image.
		if [[ "${FLAGS_verify}" == ${FLAGS_TRUE} ]]; then
			local TEMP_SUFFIX=".$(basename ${SCRIPT}).${CHIP}.$$"
			local TEMP_DIR="$(mktemp -d --suffix="${TEMP_SUFFIX}")"

			IMG_READ="${TEMP_DIR}/ec.read.bin"
			DELETE_LIST+=( "${TEMP_DIR}" )
		fi
	fi

	# Read EC image.
	if [[ -n "${IMG_READ}" ]]; then
		info "Reading EC firmware image."

		local UUT_RD=( "${NPCX_UUT}" "${UUT_ARGS[@]}"   \
			      "--read-flash" "--file=${IMG_READ}" )

		if [[ "${FLAGS_verbose}" == ${FLAGS_TRUE} ]]; then
			echo "${UUT_RD[*]}"
		fi
		print_or_run timeout -k 10 -s 9 "${FLAGS_timeout}" \
			"${UUT_RD[@]}" || die "${MSG_READ_FAIL}"
	fi

	# Verify the flash by comparing the source image to the read image,
	# only if it was a flash write request.
	if [[ -z "${FLAGS_read}" && "${FLAGS_verify}" == ${FLAGS_TRUE} ]]; then
		info "Verifying EC firmware image."

		if [[ "${FLAGS_verbose}" == ${FLAGS_TRUE} ]]; then
			echo "cmp -n ${IMG_SIZE} ${IMG} ${IMG_READ}"
		fi

		print_or_run cmp -s -n "${IMG_SIZE}" "${IMG}" "${IMG_READ}" \
			|| die "${MSG_VERIFY_FAIL}"
	fi
}

function flash_npcx_5m5g_jtag() {
	flash_npcx_jtag
}

function flash_npcx_5m6g_jtag() {
	flash_npcx_jtag
}

function flash_npcx_7m6x_jtag() {
	flash_npcx_jtag
}

function flash_npcx_7m7x_jtag() {
	flash_npcx_jtag
}

function flash_npcx_spi() {
	flash_flashrom
}

function flash_npcx_int_spi() {
	flash_flashrom
}

function flash_mec1322() {
	flash_flashrom
}

info "Using ${SERVO_TYPE}."

# Only if it is a flash program request, call ec_image.
if [[ -z "${FLAGS_read}" ]]; then
	IMG="$(ec_image)"
	info "Using ${MCU} image : ${IMG}"
	IMG_SIZE=$(stat -c%s "${IMG}")
fi

if [ "${NEED_SERVO}" != "no" ] ; then
	servo_save
fi

info "Flashing chip ${CHIP}."
flash_${CHIP}
info "Flashing done."
