#!/bin/sh

if [ -z "${CURR_R_MTD}" ] || [ -z "${R_MTD}" ] || [ -z "${T_K_MTD}" ] || [ -z "${T_R_MTD}" ] ; then
	echo "no export variables CURR_R_MTD / R_MTD / T_K_MTD / T_R_MTD" > /dev/console
	exit 1
fi

UID="`grep '^Uid=' /tmp/sysinfo | cut -d = -f 2`"
if [ -z "$UID" ] ; then
	exit 1
fi

FW_JSON_FN="/tmp/test.json"
FW_IMAGE="/tmp/fw.bin"

if [ "${CURR_R_MTD}" == "${R_MTD}" ] ; then
#	DOWNLOAD_CMD_DFT="curl -k https://sfup.simpnic.com/S1-J/fwupgrade.json -o ${FW_JSON_FN}"
	DOWNLOAD_CMD_DFT="curl -k https://sfup.simpnic.com/S1-J/test.json -o ${FW_JSON_FN}"
	DOWNLOAD_FW_DFT="curl -k -o ${FW_IMAGE} https://sfup.simpnic.com/firmware/E_"
else
#	DOWNLOAD_CMD_DFT="ftpget -u firmware -p firmware -t 15 fup.simpnic.com ${FW_JSON_FN} json/fwupgrade.json"
	DOWNLOAD_CMD_DFT="ftpget -u firmware -p firmware -t 15 fup.simpnic.com ${FW_JSON_FN} json/test.json"
	DOWNLOAD_FW_DFT="ftpget -u firmware -p firmware -t 15 fup.simpnic.com ${FW_IMAGE} s3mnt/"
fi

FW_UPG_VER=
FW_STATUS=/tmp/fw_upgrade_status
FW_HEADER=/tmp/fw_header
VENDOR_LIST=/tmp/fw_vendor_list
PLATFORM_LIST=/tmp/fw_platform_list
PRODUCT_LIST=/tmp/fw_product_list
IMAGE_TYPE_LIST=/tmp/fw_type_list
OFFSET_LIST=/tmp/fw_offset_list
SIZE_LIST=/tmp/fw_size_list
TMP_IMAGE=/tmp/fw_burn_image


fw_is_upgrading()
{
	if [ -f ${FW_STATUS} ] ; then
		STATUS=`cat ${FW_STATUS}`
		if [ "$STATUS" == "1" ] || [ "$STATUS" == "2" ] ; then
			return 1
		fi
	fi

	return 0
}

fw_status_set()
{
	echo $1 > $FW_STATUS
}

download_fw_json_file()
{
	local DOWNLOAD_CMD

	DOWNLOAD_CMD="`fw_getenv fw_json_cmd 2> /dev/null | cut -d = -f 2-`"

	if [ -z "${DOWNLOAD_CMD}" ] ; then
		DOWNLOAD_CMD="${DOWNLOAD_CMD_DFT}"
	fi

	${DOWNLOAD_CMD} 2> /dev/null
	dos2unix ${FW_JSON_FN}
	if [ "$?" == "0" ] && [ `head -n 1 ${FW_JSON_FN} | grep -c '^{$'` -ge 1 ] ; then
		return 0
	else
		return 1
	fi
}

#
# calculate which firmware version to upgrade
#
# $1 - kernel is crashed or not, 0-healthy, 1-crashed
#
# return code:
#    0 - calculation successful
#    1 - calculation failed
#
calculate_fw_version()
{
	local CURR_VER
	local VERS
	local FROM_VER
	local UPG_VER

	if [ "${CURR_R_MTD}" == "${R_MTD}" ] ; then
		if [ "$1" == "0" ] ; then
			mkdir -p /tmp/emergency
			mount -t squashfs -o ro /dev/mtdblock${T_R_MTD} /tmp/emergency
			CURR_VER=`zcat /tmp/emergency/etc/config.gz | grep "^CONFIG_TARGET_IMAGE_VERSION=" | cut -d '"' -f 2`
			VERS=`fw_json_version ${FW_JSON_FN} emergency_firmware`
			umount /tmp/emergency
		else
			VERS=`fw_json_version ${FW_JSON_FN} emergency_firmware`
		fi
	else
		if [ "$1" == "0" ] ; then
			mkdir -p /tmp/rootfsX
			mount -t squashfs -o ro /dev/mtdblock${T_R_MTD} /tmp/rootfsX
			CURR_VER=`zcat /tmp/rootfsX/etc/config.gz | grep "^CONFIG_TARGET_IMAGE_VERSION=" | cut -d '"' -f 2`
			VERS=`fw_json_version ${FW_JSON_FN} firmware`
			umount /tmp/rootfsX
		else
			VERS=`fw_json_version ${FW_JSON_FN} firmware`
		fi
	fi

	if [ -z "${CURR_VER}" ] ; then
		VER=`echo $VERS | tr ' ' '\n' | tail -n 1`
		FW_UPG_VER="`echo $VER | cut -d : -f 2`"
		if [ -n "${FW_UPG_VER}" ] ; then
			return 0
		fi
	else
		for VER in $VERS ; do
			FROM_VER="`echo $VER | cut -d : -f 1`"
			UPG_VER="`echo $VER | cut -d : -f 2`"
			if [ "${CURR_VER}" \> "${FROM_VER}" -a "${CURR_VER}" \< "${UPG_VER}" ] || [ "${CURR_VER}" == "${FROM_VER}" ] ; then
				FW_UPG_VER="${UPG_VER}"
				return 0
			fi
		done
	fi

	return 1
}

download_fw()
{
	local DOWNLOAD_FW

	DOWNLOAD_FW="`fw_getenv fw_dl_cmd 2> /dev/null | cut -d = -f 2-`"

	if [ -z "${DOWNLOAD_FW}" ] ; then
		DOWNLOAD_FW="${DOWNLOAD_FW_DFT}${FW_UPG_VER}"
	fi

	${DOWNLOAD_FW} 2> /dev/null
	if [ "$?" == "0" ] && [ `hexdump -C -n 4 ${FW_IMAGE} | grep -c '27 05 19 56'` -ge 1 ] ; then
		return 0
	else
		return 1
	fi
}

#
# extract file from a multi-files image
#
# $1 - firmware name
#
# $? - 0-Ok, 1-wrong image or header
#
extract_fw_header()
{
	local IMG_SZ

	mkimage -l $1 > $FW_HEADER
	if [ "$?" != 0 ] ; then
		IMG_SZ=`wc -c $1 | cut -d ' ' -f 1`
		dd if=$1 of=$1.fix bs=$IMG_SZ count=1
		mv -f $1.fix $1
		mkimage -l $1 >& $FW_HEADER
		if [ "$?" != 0 ] ; then
			return 1
		fi
	fi

	grep "Name Field 1:" $FW_HEADER | tr -d ' ' | cut -d ':' -f2 > $VENDOR_LIST
	grep "Name Field 2:" $FW_HEADER | tr -d ' ' | cut -d ':' -f2 | tr "a-z" "A-Z" > $PLATFORM_LIST
	grep "Name Field 3:" $FW_HEADER | tr -d ' ' | cut -d ':' -f2 > $VERSION_LIST
	grep "Image Type:" $FW_HEADER > $IMAGE_TYPE_LIST
	echo -n > $SIZE_LIST
	echo -n > $OFFSET_LIST
	if [ `head -n 1 $IMAGE_TYPE_LIST | grep -c Multi` -ge 1 ] ; then
		echo 0 > $OFFSET_LIST
		echo 0 > $SIZE_LIST
	fi
	grep "Offset = " $FW_HEADER | awk '{ print $3 }' >> $OFFSET_LIST
	grep "Image .:" $FW_HEADER | awk '{ print $3 }' >> $SIZE_LIST
	grep "Name Field 4:" $FW_HEADER | tr -d ' ' | cut -d ':' -f2 > $SPCINFO_LIST
	grep "Prod List:" $FW_HEADER | tr -d ' ' | cut -d ':' -f2 > $PRODUCT_LIST

	if [ -z "`cat $OFFSET_LIST`" ] ; then
		echo 0 > $OFFSET_LIST
		IMG_SZ=`grep "Data Size:" $FW_HEADER | awk '{ print $3 }'`
		let IMG_SZ=$IMG_SZ+64
		echo $IMG_SZ > $SIZE_LIST
	fi

	return 0
}

#
# check if the firmware header valid
#
# $1 - firmware file name
#
# return: 0-VALID 1-INVALID
#
fw_check()
{
	local KEY

	if [ -z "$1" ] ; then
		return 1
	fi

	# generate firmware image header files
	extract_fw_header $1
	if [ "$?" != 0 ] ; then
		return 1
	fi

	# Image identification
	if [ "`grep -c 'Bad Magic Number' $FW_HEADER`" -ge 1 ] ; then
		echo "Bad Magic Number, this is not valid image."
		return 1
	fi

	# vendor name check
	if [ "`grep -c -vw ^CTS$ $VENDOR_LIST`" -ge 1 ] ; then
		echo "vendor name error, this is not valid image."
		return 1
	fi

	# vendor platform check
#	KEY=`/etc/init.d/sysinfo.sh Product | tr -d ' -_'`
#	if [ "`grep -c -vw ^${KEY}$ $PLATFORM_LIST`" -ge 1 ] ; then
#		echo "product name error, this is not valid image."
#		return 1
#	fi

	# vendor product ID check
	KEY=`/etc/init.d/sysinfo.sh ID`
	if [ "`grep -c -vw ${KEY} $PRODUCT_LIST`" -ge 1 ] ; then
		echo "product ID error, this is not valid image."
		return 1
	fi

	if [ `grep -c "Linux Kernel Image" $IMAGE_TYPE_LIST` -le 0 ] || [ `grep -c "Linux Filesystem Image" $IMAGE_TYPE_LIST` -le 0 ] ; then
		echo "No kernel or rootfs image."
		return 1
	fi

	return 0
}

#
# extract file from a multi-files image
#
# $1 - image size
# $2 - offset
# $3 - skip size
#
extract_file()
{
	local	portion_size
	local	portion
	local	skip_size
	local	skip_offset
	local	image_size
	local	offset
	local	count

	image_size=$1
	offset=$2
	skip_size=$3

	rm -f $TMP_IMAGE

	# Split Files
	let offset=$offset+$skip_size
	let portion_size=$offset%1024
	if [ $portion_size -gt 0 ] ; then
		let portion=1024-$portion_size
		let count=$portion/4
		let skip_offset=$offset/4
		dd if=$FW_IMAGE bs=4 count=$count skip=$skip_offset >> $TMP_IMAGE
		let skip_size=$skip_size+$portion
		let offset=$offset+$portion
	fi

	let count=$image_size/1024-1
	let skip_offset=$offset/1024
	dd if=$FW_IMAGE bs=1024 count=$count skip=$skip_offset >> $TMP_IMAGE
	let skip_size=$count*1024+$skip_size
	let offset=$count*1024+$offset

	let portion=$image_size-$skip_size
	if [ $portion -gt 0 ] ; then
		let count=$portion/4
		let skip_offset=$offset/4
		dd if=$FW_IMAGE bs=4 count=$count skip=$skip_offset >> $TMP_IMAGE
		let skip_offset=skip_offset*4+$count*4
		let count=$portion%4
		if [ $count -gt 0 ] ; then
			dd if=$FW_IMAGE bs=1 count=$count skip=$skip_offset >> $TMP_IMAGE
		fi
	fi
}

#
# Image burning
#
# $1 - mtd id
# $2 - image size
# $3 - offset
# $4 - skip size
#
burn_image()
{
	local	MTD_IMAGE

	MTD_IMAGE=/dev/mtd/$1

	extract_file $2 $3 $4

	cat $TMP_IMAGE > $MTD_IMAGE

	rm -f $TMP_IMAGE
}

#
# Firmware burning
#
# $1 - image counter, 1-based
# $2 - mtd id
# $3 - skip
#
fw_burn_one()
{
	local	IMG_SZ
	local	OFFSET

	IMG_SZ=`sed -n ${1}p $SIZE_LIST`
	OFFSET=`sed -n ${1}p $OFFSET_LIST`
	OFFSET=`printf %lu 0x$OFFSET`

	burn_image $2 $IMG_SZ $OFFSET $3
}

#
# All firmware burning
#
fw_burn_all()
{
	local LINES
	local CNT

	if [ `grep -c "Linux Kernel Image" $IMAGE_TYPE_LIST` -ge 1 ] ; then
		flash_eraseall /dev/mtd/${T_K_MTD}
	fi

	if [ `grep -c "Linux Filesystem Image" $IMAGE_TYPE_LIST` -ge 1 ] ; then
		flash_eraseall /dev/mtd/${T_R_MTD}
	fi

	LINE=`grep "Image Type:" $IMAGE_TYPE_LIST | grep -n "Linux Filesystem Image" | cut -d : -f 1`
	if [ -n "$LINE" ] ; then
		fw_burn_one ${LINE} ${T_R_MTD} 64
	fi

	LINE=`grep "Image Type:" $IMAGE_TYPE_LIST | grep -n "Linux Kernel Image" | cut -d : -f 1`
	if [ -n "$LINE" ] ; then
		fw_burn_one ${LINE} ${T_K_MTD} 0
	fi

	LINE=`grep "Image Type:" $IMAGE_TYPE_LIST | grep -n "U-Boot Firmware" | cut -d : -f 1`
	if [ -n "$LINE" ] ; then
		flash_eraseall /dev/mtd/1
		fw_burn_one ${LINE} 1 64
	fi
}

#
# check if peer partitions are healthy or not
#
# return code:
#    0 - partitions are healthy
#    1 - partitions are corrupted
#
check_peer_partition_health()
{
	local DEV
	local TMP
	local SIZE

	DEV=/dev/mtdblock${T_K_MTD}

	dd if=$DEV of=/tmp/$$.tmp bs=64 count=1 # dump kimage header

		# get kimage size
	SIZE=`mkimage -L /tmp/$$.tmp | grep "^Data Size" | awk '{ print $3 }'`
	if [ -z "$SIZE" ] ; then
		rm -f /tmp/$$.tmp
		return 1
	fi
	let SIZE=SIZE+64 # mkimage header

	dd if=$DEV of=/tmp/$$.tmp bs=$SIZE count=1 # dump kimage image

	if [ "`mkimage -l /tmp/$$.tmp | grep -ci 'Linux Kernel Image'`" -ge 1 ] ; then
		rm -f /tmp/$$.tmp
		return 0
	else
		rm -f /tmp/$$.tmp
		return 1
	fi
}


main()
{
	fw_is_upgrading
	if [ "$?" -ne "0" ] ; then
		exit 1
	fi

	download_fw_json_file
	if [ "$?" != "0" ] ; then
		exit 1
	fi

	check_peer_partition_health

	calculate_fw_version $?
	if [ "$?" != "0" ] ; then
		exit 2
	fi

	download_fw
	if [ "$?" != "0" ] ; then
		exit 1
	fi

	fw_status_set 1

	fw_check ${FW_IMAGE}
	if [ "$?" != "0" ] ; then
		fw_status_set 0
		exit 1
	fi

	peripheral_ctrl_cli act 5 # LED show firmware upgrading

	fw_burn_all

	fw_setenv newimage 1

	zwave_app_cli cmd "evt_send node/sys/class/fw/act/status 0"

	fw_status_set 0

	peripheral_ctrl_cli act 21 # LED go back, use zwave_abort_ui(21) as go back
}

main

exit 0
