#! /bin/bash
#
# Hemisphere GPS startup script for lpc firmware updates/status management.
#
### BEGIN INIT INFO
# Provides:          check_lpc_fw
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1
# Short-Description: Check for lpc updates and set boot flags on firmware images,
# Description: Firstly updates boot good flag on successful LPC startup. Then checks if new
#              firmware is available, and attempts an upgrade if so.
### END INIT INFO

#set -x

# first check init parameter
case $1 in

      start|restart|force-reload)
          echo -e "Attempting firmware status check.";
          ;;

      *)
          echo -e "Skipping lpc update check.";
          exit 1;
          ;;

esac

# remove init parameter from $*
shift;

# amount to wait before proceeding with fw check.
DELAY=0
# flag to indicate check is part of an fw package upgrade non-zero means we are doing package upgrade.
DOING_UPGRADE=0
# flag to remember if we need to restart celestia, as we shut it down.
RESTART_CELESTIA=0

while [ $# -gt 0 ]; do
    
    case $1 in

        -delay)
            # extract extra parameter
            DELAY=$2;
            shift;
            ;;

        -upgrade)
            DOING_UPGRADE=1;
	    ;;
        *)
            echo -e "Bad option!";
            exit 1;
            ;;
    esac

    shift

done

if [ $DELAY -gt 0 ]; then
    echo -e "Attempting firmware status check - waiting $DELAY";
    sleep $DELAY;
fi

NEW_LOADER_INSTALLED=0

eDriveXLoadTool --get_firmware_version 1

fw_version=$?

if [ $fw_version -eq 0 ]; then
    NEW_LOADER_INSTALLED=1
else
    NEW_LOADER_INSTALLED=0

fi

if [ $NEW_LOADER_INSTALLED -eq 0 ]; then
    if [ ! -e /proc/lpcusbcan2 ]; then
        # first double check driver module is loaded.
        if [ $(lsmod | grep lpcusbcan2 | wc -l) -eq 0 ]; then
            modprobe lpcusbcan2
        fi
        if [ ! -e /proc/lpcusbcan2 ]; then
            # still not there, just wait a bit to give driver a chance
            sleep 5;
            if [ ! -e /proc/lpcusbcan2 ]; then
                    # if we're still not there, then basically WTF ?
                    echo -e "ERROR: lpcusbcan2 driver not up !"
                    exit -1;
            fi
        fi
    fi
# We used to check for minors in /proc/lpcusbcan2 here, but now we do it for each chip individually in
# the main loop, so if one chip has failed, we can still update the other. Don't know if this
# makes sense exactly, but we get less reported problems this way.


# test for existence of device nodes.
    if [ \( ! -e /dev/lpcfw0 \) -o \( ! -e /dev/lpcfw1 \) ]; then
        make_lpc_devices.sh
    fi

# version is M.r.b-rsvn -> we want just the 'svn' bit.
    DRV_VER=$(grep "version =" /proc/lpcusbcan2 | sed -e "s/^.*version[[:space:]]*=.*r\([[:digit:]]\+\).*/\1/" )

    if [ -z "$DRV_VER" ]; then
        echo -e "ERROR: BAD Driver version.";
        exit -1;
    fi

    if [ $DRV_VER -le 284 ]; then
        echo -e "ERROR: driver version doesn't support firmware upgrades - install new driver."
        exit -1;
    fi
fi

#  stop celestia and can server here !
function KillApps
{
    invoke-rc.d --disclose-deny celestia stop
    if [ $? -eq 0 ]; then
        # if celestia was not running we won't get a success code, so we won't end up starting it.
	RESTART_CELESTIA=1
    fi
}

# $1 dev node
# $2 firmware hex file.
function DoFWUpgradeAndReset
{
    # calculate required new priority
    OLD_PRIORITY=$(lpcusbcan2_fw_tool.py -D $1 -s | grep RUNNING | sed -e "s/.*prior[[:space:]]\+\([^[:space:]]\+\).*/\1/")
    PRIORITY=$(($OLD_PRIORITY + 1))

    # in case celestia and/or can server are running, kill them, as the can devices are about to dissappear.
    # Doing this here means we don't kill them if we don't upgrade any firmware.
    KillApps;

    lpcusbcan2_fw_tool.py -D $1 --upgrade -f $2 --priority $PRIORITY --name "UPG $(basename $2)" --auto --countdown 3

    # We no longer reset the lpc - we have two cases -
    # 1. first check on install - in this case fw upgrade is unlikely to occur anyway. If fw
    # update is required, techs will know, so they will know to re-boot the ecu.
    # 2. field upgrade as part of usb-update - in this case, ecu will be halted at end of install, so a re-boot will happen.
}

# $1 chip no
# $2 firmware hex file.
function DoFWUpgradeAndReset2
{
    # in case celestia and/or can server are running, kill them, as the can devices are about to dissappear.
    # Doing this here means we don't kill them if we don't upgrade any firmware.
    KillApps;

    eDriveXLoadTool --prepare_for_update $1

    sleep 10

    eDriveXLoadTool --update_firmware $1 --filename $2

    # We no longer reset the lpc - we have two cases -
    # 1. first check on install - in this case fw upgrade is unlikely to occur anyway. If fw
    # update is required, techs will know, so they will know to re-boot the ecu.
    # 2. field upgrade as part of usb-update - in this case, ecu will be halted at end of install, so a re-boot will happen.
}

function CheckAndUpgradeFW2
{
    CHIP_NO=$1

    # firmware version is third column of hex numbers
    FW_VER=`eDriveXLoadTool --get_firmware_version $CHIP_NO`

    # ensure final "success" "resting" place dir exists
    if [ ! -d /lib/firmware/ags/eDriveX/LPC$1/verified ]; then
        mkdir /lib/firmware/ags/eDriveX/LPC$1/verified
    fi

    # ensure final "fail" "resting" place dir exists
    if [ ! -d /lib/firmware/ags/eDriveX/LPC$1/failed ]; then
        mkdir /lib/firmware/ags/eDriveX/LPC$1/failed
    fi

    if [ $DOING_UPGRADE -eq 0 ]; then
        # not doing a package install, so check for previous fw upgrade results.

        # check if current firmware completes an upgrade test.
        if [ -d /lib/firmware/ags/eDriveX/LPC$1/test2 ]; then
            cd /lib/firmware/ags/eDriveX/LPC$1/test2
            for F in *.h86; do
                if [ -e $F ]; then
                    if [ ${F%.h86} -eq  $FW_VER ]; then
                        # upgrade succeeded move file to verified status
                        mv $F ../verified/
                    else
                        # upgrade failed twice, move file to failed status.
                        mv $F ../failed/
                    fi
                fi
            done
            cd ..
            rmdir test2;
        fi;

        # check if current firmware completes an upgrade test.
        if [ -d /lib/firmware/ags/eDriveX/LPC$1/test1 ]; then
            cd /lib/firmware/ags/eDriveX/LPC$1/test1
            for F in *.h86; do
                if [ -e $F ]; then
                    if [ ${F%.h86} -eq  $FW_VER ]; then
                        # upgrade succeeded move file to verified status
                        mv $F ../verified/
                    else
                        # upgrade failed once, retry and move to 2nd chance dir.
                        if [ ! -d ../test2 ]; then
                            mkdir ../test2
                        fi
                        mv $F ../test2/
                        echo -e "Re-trying to replace current firmware $FW_VER on LPC$1 with ${F%.h86}"
                        DoFWUpgradeAndReset2 $CHIP_NO ../test2/$F
                    fi
                fi
            done
            cd ..
            rmdir test1;
        fi;
    fi

    # Try any "candidate" firmwares, oldest first - this way if there are more than 1 (which there
    # shouldn't be really), a series of re-boots will result in, eventually, newest firmware
    # having highest priority.
    FW_FILE=$( ls /lib/firmware/ags/eDriveX/LPC$1/candidate/*.h86 2> /dev/null | sort -n | head -1 );

    if [ ! -z "$FW_FILE" ]; then

        FILE_VERSION=$(basename ${FW_FILE%.h86})

        if [ $FILE_VERSION -eq $FW_VER ]; then
            # don't re-try firmware if version is the same !    
            echo -e "Firmware ${FW_FILE} already running - not uploading again.";
            mv $FW_FILE /lib/firmware/ags/eDriveX/LPC$1/verified/
        else
            if [ $FILE_VERSION -lt $FW_VER ]; then
                echo -e "Downgrading current firmware $FW_VER on LPC$1 to $FILE_VERSION"
            else
                echo -e "Upgrading current firmware $FW_VER on LPC$1 to $FILE_VERSION"
            fi

            if [ ! -d /lib/firmware/ags/eDriveX/LPC$1/test1 ]; then
                mkdir /lib/firmware/ags/eDriveX/LPC$1/test1
            fi
            mv $FW_FILE /lib/firmware/ags/eDriveX/LPC$1/test1/

            DoFWUpgradeAndReset2 $CHIP_NO /lib/firmware/ags/eDriveX/LPC$1/test1/$(basename $FW_FILE)
        fi

    fi
}

function CheckAndUpgradeFW
{
    CHIP_NO=$1

    case $CHIP_NO in

        1)
        DEV=/dev/lpcfw0
        MINOR=3
	;;

        2)
	DEV=/dev/lpcfw1
        MINOR=7
        ;;

        *)
        echo -e "ERROR: Bad chip no:" $CHIP_NO
        exit -1;
        ;;
    esac

    # check for existence of minor in driver output -
    if ! grep "^[[:space:]]\+${MINOR}" /proc/lpcusbcan2 ; then
	echo -e "ERROR: usb endpoint not detected for firmware upload device on LPC${CHIP_NO}".
	return 0;
    fi


    # firmware version is third column of hex numbers
    FW_VER=$(grep "^[[:space:]]\+$MINOR" /proc/lpcusbcan2 | sed -e "s/^[[:space:]]\+[^[:space:]]\+[[:space:]]\+[^[:space:]]\+[[:space:]]\+\([^[:space:]]\+\).*/\1/" )

    if [ -z "$FW_VER" ]; then 
      echo -e "ERROR: BAD firmware version !"
      return 0
    fi

    if [ $(( 0x$FW_VER ))  -lt 4 ]; then
      echo -e "ERROR: firmware version on LPC$1 doesn't support upgrades !"
      return 0
    fi

    # extract flags for running image
    OLD_FLAGS=$(lpcusbcan2_fw_tool.py -D $DEV -s | grep RUNNING | sed -e "s/.*flags[[:space:]]\+\([^[:space:]]\+\).*/\1/")
    # test if "ok" flag is set.
    OK_FLAG=$((0x${OLD_FLAGS} & 0x01))
    if [ $OK_FLAG -eq 0 ]; then
        # whatever firmware is running, it is good, so set "ok" flag, if it hasn't yet been set.
        lpcusbcan2_fw_tool.py -D $DEV --ok
    fi

    # ensure final "success" "resting" place dir exists
    if [ ! -d /lib/firmware/ags/eDriveX/LPC$1/verified ]; then
	mkdir /lib/firmware/ags/eDriveX/LPC$1/verified
    fi

    # ensure final "fail" "resting" place dir exists
    if [ ! -d /lib/firmware/ags/eDriveX/LPC$1/failed ]; then
	mkdir /lib/firmware/ags/eDriveX/LPC$1/failed
    fi

    if [ $DOING_UPGRADE -eq 0 ]; then
        # not doing a package install, so check for previous fw upgrade results.

        # check if current firmware completes an upgrade test.
	if [ -d /lib/firmware/ags/eDriveX/LPC$1/test2 ]; then
	    cd /lib/firmware/ags/eDriveX/LPC$1/test2
	    for F in *.hex; do
		if [ -e $F ]; then
		    if [ ${F%.hex} -eq  $(( 0x$FW_VER )) ]; then
                        # upgrade succeeded move file to verified status
			mv $F ../verified/
		    else
                        # upgrade failed twice, move file to failed status.
			mv $F ../failed/
		    fi
		fi
	    done
	    cd ..
	    rmdir test2;
	fi;

        # check if current firmware completes an upgrade test.
        if [ -d /lib/firmware/ags/eDriveX/LPC$1/test1 ]; then
	    cd /lib/firmware/ags/eDriveX/LPC$1/test1
	    for F in *.hex; do
		if [ -e $F ]; then
		    if [ ${F%.hex} -eq  $(( 0x$FW_VER )) ]; then
                        # upgrade succeeded move file to verified status
			mv $F ../verified/
		    else
                        # upgrade failed once, retry and move to 2nd chance dir.
			if [ ! -d ../test2 ]; then
			    mkdir ../test2
			fi
			mv $F ../test2/
			echo -e "Re-trying to replace current firmware $FW_VER on LPC$1 with ${F%.hex}"
			DoFWUpgradeAndReset $DEV ../test2/$F
		    fi
		fi
	    done
	    cd ..
	    rmdir test1;
	fi;
    fi

    # Try any "candidate" firmwares, oldest first - this way if there are more than 1 (which there
    # shouldn't be really), a series of re-boots will result in, eventually, newest firmware
    # having highest priority.
    FW_FILE=$( ls /lib/firmware/ags/eDriveX/LPC$1/candidate/*.hex 2> /dev/null | sort -n | head -1 );

    if [ ! -z "$FW_FILE" ]; then

	FILE_VERSION=$(basename ${FW_FILE%.hex})

	if [ $FILE_VERSION -eq $(( 0x$FW_VER )) ]; then
            # don't re-try firmware if version is the same !	
	    echo -e "Firmware ${FW_FILE} already running - not uploading again.";
            mv $FW_FILE /lib/firmware/ags/eDriveX/LPC$1/verified/
	else
	    if [ $FILE_VERSION -lt $(( 0x$FW_VER ))   ]; then
		echo -e "Downgrading current firmware $FW_VER on LPC$1 to $FILE_VERSION"
	    else
		echo -e "Upgrading current firmware $FW_VER on LPC$1 to $FILE_VERSION"
	    fi
            
	    if [ ! -d /lib/firmware/ags/eDriveX/LPC$1/test1 ]; then
		mkdir /lib/firmware/ags/eDriveX/LPC$1/test1
	    fi
	    mv $FW_FILE /lib/firmware/ags/eDriveX/LPC$1/test1/
	
	    DoFWUpgradeAndReset $DEV /lib/firmware/ags/eDriveX/LPC$1/test1/$(basename $FW_FILE)
	fi

    fi
}


for i in 1 2 ; do
    if [ $NEW_LOADER_INSTALLED -eq 0 ]; then
     CheckAndUpgradeFW $i
    else
     CheckAndUpgradeFW2 $i
    fi
done

if [ $RESTART_CELESTIA -ne 0 ]; then
    # we stopped celestia, so restart it.
    invoke-rc.d celestia start
fi
