#!/bin/sh
#*******************************************************************************
#   COPYRIGHT (C) 2013-2014 PMC-SIERRA, INC. ALL RIGHTS RESERVED.
# -----------------------------------------------------------------------------
#  This software embodies materials and concepts which are proprietary and
#  confidential to PMC-Sierra, Inc.  PMC-Sierra distributes this software to
#  its customers pursuant to the terms and conditions of the Software License
#  Agreement contained in the text file software.lic that is distributed along
#  with the software. This software can only be utilized if all terms and
#  conditions of the Software License Agreement are accepted. If there are any
#  questions, concerns, or if the Software License Agreement text file,
#  software.lic, is missing please contact PMC-Sierra for assistance.
# -----------------------------------------------------------------------------
# $RCSfile: fw_tool.sh,v $
#
# $Date: 2014-05-27 17:34:46 +0800 (Tue, 27 May 2014) $
#
# $Revision: 93898 $
#
# DESCRIPTION:  The script provides following functions:
#                 1) Update SAS expander's firmware by Write Buffer Mode 07h
#                 2) Update SAS expander's CPLD
#
# NOTES:  None.
#
#*****************************************************************************/

# ------------------------------------------------------------------------------
# Function Name: usage
# 
# Description: This function prints usage message.
# 
# Arguments:  None 
#
# Return value:
#      0  ----  succsess
# ------------------------------------------------------------------------------
function usage () { 
    echo
    echo " Usage:"
    echo "    `basename $0` [--help] [--fw_update] [--cpld_update] DEVICE"
    echo          
    echo " where:"
    echo "    -h, --help                             Print out usage message"
    echo "    -f, --fw_update  firmware_image        Update SAS Expander's firmware"
    echo "    -c, --cpld_update  cpld_image          Update SAS Expander's CPLD"
    echo 
    echo " Example:" 
    echo "    fw_tool  -h"
    echo "    fw_tool  -f  firmware_image.bin  /dev/sg1"
    echo "    fw_tool  -c  cpld_image.bin  /dev/sg1"
    echo 
    echo " Notes:"
    echo "    DEVICE can be got by 'sg_scan -i' on Linux."
    echo 
    return 0
}

# -----------------------------------------------------------------------------
# Function Name: file_check
# 
# Description: This function checks the file
#
# Arguments: 
#    $1 --- file name
#    $2 --- the extension of file
#
# Return value:
#    0 --- Success
#    1 --- Fail
# -----------------------------------------------------------------------------
function file_check () {

    local file_name=$1
    local file_extension=$2

    if [ "$file_name" == "" ]; then
        echo "No file argument is given."
        return 1
    fi 
         
    if [ ! -f "$file_name" ]; then
        echo "$file_name" does not exist.
        return 1
    fi
     
    if [[ "$file_name" != *"$file_extension" ]]; then
        echo "$file_name" is not a file with the extension "$file_extension".
        return 1
    fi

    return 0
}

# -----------------------------------------------------------------------------
# Function name: device_check
#
# Description: This function checks the device name
#
# Arguments:
#     $1 --- device name
#
# Return value:
#     0 --- Success
#     1 --- Fail
# -----------------------------------------------------------------------------
function device_check () {

    local device_name=$1

    if [ "$device_name" == "" ]; then
        echo No device argument is given.
        return 1
    fi

    if [ ! -e "$device_name" ]; then
        echo "$device_name" does not exist.
        return 1
    fi

    sg_inq "$device_name" | egrep  "$device_type" > /dev/null
    if  [  $? != 0 ]; then
        echo "$device_name" is not "$device_type".
        return 1
    fi
    
    return 0
}

# -----------------------------------------------------------------------------
# Function Name: fw_update
#
# Description: This function downloads firmware by Write Buffer mode 07h.
#
# Arguments: 
#     $1 --- firmware image file 
#     $2 --- device name
#
# Return value:
#     0 --- Success
#     1 --- Fail
# -----------------------------------------------------------------------------
function fw_update () {

    local file_name=$1
    local device_name=$2

    local offset=0
    local buffer_length=1024
    local is_last=0

    if [ "$file_name" != "" ]; then
        file_check $file_name .bin
        if [ $? != 0 ]; then
            return 1
        fi
    else 
        echo "No file argument is given."
        return 1
    fi

    device_check $device_name
    if [ $? != 0 ]; then
        return 1
    fi

    local file_length=`stat -c %s "$file_name"`

    # check the device is ready or not
    if which sg_turs > /dev/null; then
        sg_turs "$device_name" > /dev/null
        sg_turs "$device_name" || (echo "sg_turs $device failed." && return 1)
    fi

    while [ "$is_last" == "0" ]; do
        if ((buffer_length>=file_length-offset)); then
            is_last=1
            ((buffer_length=file_length-offset))
        fi
        # Download by write buffer command
        sg_write_buffer -m 7 -I $file_name -l $buffer_length -o $offset -s $offset "$device_name" 
        if [ $? != 0 ]; then
            echo "Firmware update fail, sg_write_buffer error"
            return 1
        fi
        # Update file_off_set
        ((offset+=buffer_length))
        echo -ne "Finish: $(($offset*100/$file_length))%\r"
    done

    echo "Firmware update finished!"
    return 0
}

# -----------------------------------------------------------------------------
# Function name: cpld_update
#
# Description: This function download CPLD image by SES string out page.
#
# Arguments:
#    $1 --- CPLD image file
#    $2 --- Device name
# Return value: 
#    0 --- Success
#    1 --- Fail
# -----------------------------------------------------------------------------
function cpld_update () {

    # Get the input parameters
    local cpld_image_name=$1
    local device_name=$2

    if [ "$cpld_image_name" != "" ]; then
         file_check $cpld_image_name .bin
         if [ $? != 0 ]; then
             return 1
         fi
    else 
         echo "No CPLD image argument is given."
         return 1
    fi

    device_check $device_name
    if [ $? != 0 ]; then
         return 1
    fi

    local cpld_image_length=`stat -c %s "$cpld_image_name"`
    
    # check the device is ready or not
    if which sg_turs > /dev/null; then
        sg_turs "$device_name" > /dev/null
        sg_turs "$device_name" || (echo "sg_turs $device_name failed" && return 1)
    fi

    finish_flag=0
    # The first 6 bytes are CPLD image's head.
    cpld_head_length=6
    cpld_length=`expr $cpld_image_length - $cpld_head_length`
    image_offset=$cpld_head_length
    download_bytes=0
    rw_bytes=1024

    command_field="05"
    cdb_offset="00 00 00 00"
    data_size="00 00"
    remain_size=0

    # Init data_size
    if [ $cpld_length -lt $rw_bytes ]; then
        data_size=`printf "%04x\n" $cpld_length | sed "s/\(..\)/\1 /g"`
    else
        data_size=`printf "%04x\n" $rw_bytes | sed "s/\(..\)/\1 /g"`
    fi

    while [ $finish_flag -eq 0 ]; do
        # Read 1024 bytes from image
        dd if=$cpld_image_name of=temp.bin bs=1 count=$rw_bytes skip=$image_offset 2>/dev/null
        if [ $? != 0 ]; then
             echo "Error is reported by tool dd."
             return 1
        fi
        # Convert  binary file to string of ASCII hex bytes
        od -A n -t x1 -v temp.bin > temp.txt
        if [ $? != 0 ]; then
             echo "Error is reported by tool od."
             return 1
        fi
        #
        rm -f temp.bin
        
        # Populate the SES string out page
        echo -n $command_field $cdb_offset $data_size | cat - temp.txt > download_temp
        # Download by SES string out page
        sg_ses --page=0x04 --control --data=- $device_name  < download_temp  > /dev/null
        if [ $? != 0 ]; then
             echo "Error is reported by tool sg_ses."
             return 1
        fi

        image_offset=`expr $image_offset + $rw_bytes`
        download_bytes=`expr $image_offset - $cpld_head_length`
        if [ $cpld_length -lt $download_bytes ]; then
            finish_flag=1
            download_bytes=`expr $cpld_length - 1`
        fi
        remain_size=`expr $cpld_length - $download_bytes`
        echo -ne "Finish: $(($download_bytes*100/$cpld_length))%\r"
        # Update cdb_offset filed
        cdb_offset=`printf "%08x\n" $download_bytes | sed "s/\(..\)/\1 /g"`
        
        # Update data_size filed if it is last page
        if [ "$remain_size" -lt $rw_bytes ]; then
            data_size=`printf "%04x\n" $remain_size | sed "s/\(..\)/\1 /g"`
        fi

        if [ "$cpld_length" -lt "$download_bytes" ]; then
            finish_flag=1
        fi
    done

    # Send a complete flag page
    cdb_offset="ff ff ff ff"
    data_size="00 06"
    # Read 6 bytes from image
    dd if=$cpld_image_name of=temp.bin bs=1 count=6 skip=0 2>/dev/null
    if [ $? != 0 ]; then
         echo "Error is reported by tool dd."
         return 1
    fi
    # Convert  binary file to string of ASCII hex bytes
    od -A n -t x1 -v temp.bin > temp.txt
    if [ $? != 0 ]; then
         echo "Error is reported by tool od."
         return 1
    fi
    # Populate the SES string out page
    echo -n $command_field $cdb_offset $data_size | cat - temp.txt > download_temp
    sg_ses --page=0x04 --control --data=- $device_name  < download_temp >/dev/null
    if [ $? != 0 ]; then
        echo "CPLD update fail: Error is reported by tool sg_ses."
        return 1
    fi

    echo CPLD update finished!
    rm -f temp.bin temp.txt download_temp
}

# -----------------------------------------------------------------------------
# Function name: fw_tool
#
# Description:  This function is the main function 
#
# Arguments: None
#
# Return value:
#    0 --- Success
#    1 --- Fail
# -----------------------------------------------------------------------------
function  fw_tool () {
    if [ "$1" == "" ] ; then
        usage;
        return 1
    else
        case "$1" in
          -f | --fw_update)
             fw_update  $2  $3
             if [ $? != 0 ]; then
                 return 1
             fi
             ;;
          -c | --cpld_update)
             cpld_update  $2  $3
             if [ $? != 0 ]; then
                 return 1
             fi
             ;;
          -h | --help)
             usage
             if [ $? != 0 ]; then
                 return 1
             fi
             ;;
          *)
             echo $1 is not a correct argument.
             usage
             return 1
             ;;
           esac
    fi
}

device_type="RES3FV288|RES3FV288-R|RES3TV360|AEC-82885T|AEC-83600"
fw_tool $1 $2 $3
