#!/bin/bash
################################################################################
# This script update SSD FW at init, it is important to run when SSD is idle 

# otherwise SSD FW might get corrupted and get into brick mode.
#
################################################################################


#==============================================================================#
# FW version which require update
#Innodisk - M.2 (S42) 3IE3 - 16GB - MEM000145 - DHM24-16GD09BC1DC-92
INO_S16425i_VER=S16425i
INO_S16425i_MODEL="M.2 (S42) 3IE3"
INO_S16425i_UPDATE_TYPE=1                       # 1 - Innoidsk, 0 - hdparm
INO_S16425i_UPDATE_VER=S20728i
INO_S16425i_UPDATE_FILE=/usr/sbin/SYS_S_S20728i.bin

#Innodisk - M.2 (S42) 3IE3 - 16GB - MEM000145 - DHM24-16GD09BC1DC-92
INO_S19903Mi_VER=S19903Mi
INO_S19903Mi_MODEL="M.2 (S42) 3IE3"
INO_S19903Mi_UPDATE_TYPE=1                       # 1 - Innoidsk, 0 - hdparm
INO_S19903Mi_UPDATE_VER=S20728i
INO_S19903Mi_UPDATE_FILE=/usr/sbin/SYS_S_S20728i.bin

#Innodisk - M.2 (S42) 3ME3 - 16GB - MEM000109 - DEM24-16GD09BC1DC-92ME
INO_S15A19_VER=S15A19
INO_S15A19_MODEL="M.2 (S42) 3ME3"
INO_S15A19_UPDATE_TYPE=1                       # 1 - Innoidsk, 0 - hdparm
INO_S15A19_UPDATE_VER=S20728
INO_S15A19_UPDATE_FILE=/usr/sbin/SYS_S_S20728.bin

#Innodisk - M.2 (S42) 3ME3 - 16GB - MEM000109 - DEM24-16GD09BC1DC-92ME
INO_S16425M_VER=S16425M
INO_S16425M_MODEL="M.2 (S42) 3ME3"
INO_S16425M_UPDATE_TYPE=1                       # 1 - Innoidsk, 0 - hdparm
INO_S16425M_UPDATE_VER=S20728
INO_S16425M_UPDATE_FILE=/usr/sbin/SYS_S_S20728.bin

#Innodisk - M.2 (S42) 3ME3 - 16GB - MEM000109 - DEM24-16GD09BC1DC-92ME
INO_S19903M_VER=S19903M
INO_S19903M_MODEL="M.2 (S42) 3ME3"
INO_S19903M_UPDATE_TYPE=1                       # 1 - Innoidsk, 0 - hdparm
INO_S19903M_UPDATE_VER=S20728
INO_S19903M_UPDATE_FILE=/usr/sbin/SYS_S_S20728.bin

#Virtium - StorFly VSF302XC016G-MLX - 15.8GB - MEM000207 - StorFly VSF302XC016G-MLX1
VIRT_VSF302XC016GMLX_VER=0115-000
VIRT_VSF302XC016GMLX_MODEL="StorFly VSF302XC016G-MLX"
VIRT_VSF302XC016GMLX_UPDATE_TYPE=0                       # 1 - Innoidsk, 0 - hdparm
VIRT_VSF302XC016GMLX_UPDATE_VER=1210-000
VIRT_VSF302XC016GMLX_UPDATE_FILE=/usr/sbin/VSF302XC016G-MLX_ISP_1210.bin


#==============================================================================#


INO_DISK_UPDATE_TOOL=hdparm
INO_DISK_RST_CTRL_TOOL=/usr/sbin/mp_64
SWITCH_SSD_DEV=/dev/sda
BOOT_ID_1=1
BOOT_ID_2=2
TIME_STR=$(date +'%b %d %H:%M:%S')
HOSTNAME_STR=$(hostname)
APPNAME_STR="ssd_fw_service[555]: "
LOG_FILE="/var/log/messages"
LOG_HEADER="$TIME_STR $HOSTNAME_STR $APPNAME_STR"
CTRL_RESET_ISSUE="failed command: IDENTIFY DEVICE"
RAM_LOG=""
TRUE="0"
FALSE="1"
UPD_TYPE_VIRT=0
UPD_TYPE_INO=1

#==============================================================================#
#=  This function dump RAM log into LOG_FILE by mounting var partition         #
#=
function write_log {

    mount  /var
    if [ $? -ne 0 ]; then
        echo "=== Failed to mount var, probably mounted."
        echo "mount:"
        cat /proc/mounts | grep "dev/sda"
    fi

    echo -e "${RAM_LOG}" >> $LOG_FILE
    if [ $? -eq 0 ]; then
        echo "=== Log saved"
    else
        echo "=== Failed to save log to $LOG_FILE"
    fi
    sync
    umount -f /var

}

#==============================================================================#
#=  Logger function which append into buffer and print to screen the given str #
#=
function LOG_MSG {

    if [ $# -eq 1 ]; then
        LOG_STR=$1
        RAM_LOG+=${LOG_HEADER}${LOG_STR}'\n'
        echo "=== $LOG_STR"
    fi
}


#==============================================================================#
#=  This function checks if SSD FW update require, if so operate FW update.    #
#=
function update_ssd_fw {

    if [ $# -eq 0 ]; then
        LOG_MSG "Invalid ssd fw file and version were given!"
        exit_on_failure
    fi

    target_file=$1
    target_version=$2
    update_type=$3

    #check if SSD is mounted if so exit
    cat /proc/mounts | grep "dev/sd" > /dev/null 2>&1
    if [ $? -eq 0 ]; then
        LOG_MSG "SSD partitions mounted. FW upgrade must be done on unmount drive."
        LOG_MSG "Mounts:"
        LOG_MSG "$(cat /proc/mounts | grep "dev/sd")"
        exit_on_failure
    fi

    LOG_MSG "Updating SSD FW ($target_file)..."
    #$INO_DISK_UPDATE_TOOL -i $SWITCH_SSD_DEV
    SSD_FW_VER=""


    $INO_DISK_UPDATE_TOOL --yes-i-know-what-i-am-doing --please-destroy-my-drive --fwdownload-mode7 $target_file $SWITCH_SSD_DEV

    if [ "$update_type" == "$UPD_TYPE_INO" ]; then
        # this command is for Innodisk SSD only!
        LOG_MSG "Resetting SSD controller..."
        sleep 1
        $INO_DISK_RST_CTRL_TOOL -d $SWITCH_SSD_DEV -c 1 -m -k
        # Innodisk require 10 seconds after reset ctrl
        sleep 7
        #######
    fi

    sleep 3
    get_ssd_fw_version SSD_FW_VER

    ret_val=$(do_string_exists_in_dmesg "$CTRL_RESET_ISSUE")
    if [[ "$ret_val" == "$TRUE" ]]; then
        echo "SSD identification error, retry..."
        sleep 5
        get_ssd_fw_version SSD_FW_VER
    fi

    if [[ "$SSD_FW_VER" != "$target_version" ]]; then
        LOG_MSG "SSD FW update failed. FW version is:$SSD_FW_VER"
        exit_on_failure
    fi

    LOG_MSG "SSD FW update succeed to ver:$SSD_FW_VER"

}

#==============================================================================#
# This function check if given string exists in dmesg and return 0 - success   #
# or 1 - fail.
#
function do_string_exists_in_dmesg {

    if [ $# -eq 0 ]; then
        LOG_MSG "Invalid msg given, fail to search in dmesg"
        echo "1"
    fi

    dmesg | grep "$1" > /dev/null

    echo $?

}



#==============================================================================#
# This function revert the boot partition to the second partition.             #
#
function change_boot_order {

    AIG_NEXT_BOOT_ID=
    AIG_THIS_BOOT_ID=$(aiget.sh | grep -Po "AIG_THIS_BOOT_ID=\K[^,]")

    # if failed to get boot ID no point in exit_on_failure, just power cycle.
    if [ $? -ne 0 ]; then
        LOG_MSG "Failed to get boot ID - Power cycling in 10 seconds..."
        sleep 10
        do_power_cycle
    fi


    if [[ "$AIG_THIS_BOOT_ID" == "$BOOT_ID_1" ]]; then
        AIG_NEXT_BOOT_ID=$BOOT_ID_2
    else
        AIG_NEXT_BOOT_ID=$BOOT_ID_1
    fi

    #echo "=== System boot from:$AIG_THIS_BOOT_ID Setting next boot to:$AIG_NEXT_BOOT_ID"

    aiset.sh -i -l $AIG_NEXT_BOOT_ID

    if [ $? -ne 0 ]; then
        LOG_MSG "Failed to set image boot next - Power cycling in 10 seconds..."
        sleep 10
        do_power_cycle
    fi

    sync
    LOG_MSG "Boot order changed to:$AIG_NEXT_BOOT_ID"
}

#==============================================================================#
# This function operates power cycle - off / on as reboot doesn't support FW   #
# update.
#
function do_power_cycle {

    write_log
    echo "=== Power cycling the system..."
    sleep 3
    /usr/sbin/mlnx_shutdown.sh -s

    exit 0
}
#==============================================================================#
# This function is called for failure modes, where it returns back to previous #
# OS partition.
#
function exit_on_failure {

    LOG_MSG "Error: SSD FW update script failed to complete, resetting boot order and power cycling..."
    change_boot_order
    do_power_cycle
}

#==============================================================================#
# This function return SSD fw version using haprm utility                      #
#
function get_ssd_fw_version {

#    SSD_FW_VER=$(hdparm -i $SWITCH_SSD_DEV | grep -Po "FwRev=\K[^,]+")
#    echo "$SSD_FW_VER"
    local _ssd_fw_version
    _ssd_fw_version=$(hdparm -i $SWITCH_SSD_DEV | grep -Po "FwRev=\K[^,]+")
    eval $1='$_ssd_fw_version'
}


#==============================================================================#
# This function return SSD fw model using smart utility                      #
#
function get_ssd_device_model {

    local _device_model_name
    _device_model_name=$(hdparm -i $SWITCH_SSD_DEV | grep -Po "Model=\K[^,]+")
    eval $1='$_device_model_name'
}



################################################################################
#  MAIN                                                                        #
################################################################################
echo "=== SSD FW Update service"

SSD_DEVICE_MODEL=""
SSD_FIRMWARE_VERSION=""


get_ssd_device_model SSD_DEVICE_MODEL
get_ssd_fw_version SSD_FIRMWARE_VERSION


#SSD_FW_VER=$(get_ssd_fw_version)
LOG_MSG "SSD FW version:$SSD_FIRMWARE_VERSION"
LOG_MSG "SSD model type:$SSD_DEVICE_MODEL"

if [[ "$SSD_FIRMWARE_VERSION" == "$INO_S16425i_VER" ]]; then
    if [[ "$SSD_DEVICE_MODEL" == "$INO_S16425i_MODEL" ]]; then
        update_ssd_fw $INO_S16425i_UPDATE_FILE $INO_S16425i_UPDATE_VER $INO_S16425i_UPDATE_TYPE
    fi

elif [[ "$SSD_FIRMWARE_VERSION" == "$INO_S19903Mi_VER" ]]; then
    if [[ "$SSD_DEVICE_MODEL" == "$INO_S19903Mi_MODEL" ]]; then
        update_ssd_fw $INO_S19903Mi_UPDATE_FILE  $INO_S19903Mi_UPDATE_VER $INO_S19903Mi_UPDATE_TYPE
    fi

elif [[ "$SSD_FIRMWARE_VERSION" == "$INO_S15A19_VER" ]]; then
    if [[ "$SSD_DEVICE_MODEL" == "$INO_S15A19_MODEL" ]]; then
        update_ssd_fw $INO_S15A19_UPDATE_FILE  $INO_S15A19_UPDATE_VER $INO_S15A19_UPDATE_TYPE
    fi

elif [[ "$SSD_FIRMWARE_VERSION" == "$INO_S16425M_VER" ]]; then
    if [[ "$SSD_DEVICE_MODEL" == "$INO_S16425M_MODEL" ]]; then
        update_ssd_fw $INO_S16425M_UPDATE_FILE  $INO_S16425M_UPDATE_VER $INO_S16425M_UPDATE_TYPE
    fi

elif [[ "$SSD_FIRMWARE_VERSION" == "$INO_S19903M_VER" ]]; then
    if [[ "$SSD_DEVICE_MODEL" == "$INO_S19903M_MODEL" ]]; then
        update_ssd_fw $INO_S19903M_UPDATE_FILE  $INO_S19903M_UPDATE_VER $INO_S19903M_UPDATE_TYPE
    fi

elif [[ "$SSD_FIRMWARE_VERSION" == "$VIRT_VSF302XC016GMLX_VER" ]]; then
    if [[ "$SSD_DEVICE_MODEL" == "$VIRT_VSF302XC016GMLX_MODEL" ]]; then
        update_ssd_fw $VIRT_VSF302XC016GMLX_UPDATE_FILE $VIRT_VSF302XC016GMLX_UPDATE_VER $VIRT_VSF302XC016GMLX_UPDATE_TYPE
    fi

else
    LOG_MSG "SSD FW version is valid, update is not needed."
fi

change_boot_order
do_power_cycle

# if you got to here, power cycle didn't take place and script exit
echo "=== ERR: Script failure"
exit 1
