#!/bin/bash
# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
# Upgrade Hi1822 NIC card firmware
# Usage1: upgrade all matching devices
# 1. chmod -R +x *
# 2. ./install.sh upgrade
#
# Usage2: specified device bdf upgrade specified device
# 1. chmod -R +x *
# 2. ./install.sh upgrade BDF "0000:xx:xx.x,0000:yy:yy.y" or "xx:xx.x,yy:yy.y"
# Note: use the bdf value of device port 1,one or more devices can be specified.
set -e
# get work path
path=$(dirname "$0")
path="${path/\./$(pwd)}"
pwd="${path}"
cd "${pwd}"

# delete history files
if [ -f "${pwd}/work.log" ]; then
  rm -f "${pwd}/work.log" 2>/dev/null
fi

# define device number
dev_num=0
# define upgrade result flag,0-success,1-failure
flag_fail=0
# define result for result.xml,OK or Fail
result=""
# define error_code for result.xml,0-OK,1-Fail,4-No device,5-no version.xml,6-no tools
error_code=""
# define the upgrade result description
description=""

# create result.xml to record the upgrade result
create_result_xml() {
  echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>" >"${pwd}/result.xml"
  echo "<ConfigureResult>" >>"${pwd}/result.xml"
  echo "    <FwUpgrade>" >>"${pwd}/result.xml"
  echo "        <Result>${result}</Result>" >>"${pwd}/result.xml"
  echo "        <ErrorCode>${error_code}</ErrorCode>" >>"${pwd}/result.xml"
  echo "        <Description>${description}</Description>" >>"${pwd}/result.xml"
  echo "    </FwUpgrade>" >>"${pwd}/result.xml"
  echo "</ConfigureResult>" >>"${pwd}/result.xml"
}

# read version xml to obtaining upgrade package information
read_version_xml() {
  if [ ! -f "${pwd}/version.xml" ]; then
    # version xml not found
    result="Fail"
    error_code="5"
    description="version.xml not found"
    create_result_xml
    exit 5
  fi

  # get fw bin file name
  fw_file=$(cat "${pwd}/version.xml" | grep -i "<FileName>" | awk 'BEGIN{FS=">"}{print $2}' | awk 'BEGIN{FS="<"}{print $1}')
  # get fw version
  fw_version=$(cat "${pwd}/version.xml" | grep -i "<Version>" | awk 'BEGIN{FS=">"}{print $2}' | awk 'BEGIN{FS="<"}{print $1}')
  # get fw 4-tuple information
  fw_uid=$(cat "${pwd}/version.xml" | grep -i "<SupportModelUID>" | awk 'BEGIN{FS=">"}{print $2}' | awk 'BEGIN{FS="<"}{print $1}' | sed 's/;/ /g')

  # check file name
  if [ $(ls | grep -w ${fw_file} | wc -l) -eq 0 ]; then
    # firmware file error
    result="Fail"
    error_code="5"
    description="FileName Error"
    create_result_xml
    exit 5
  fi
}

#get OS type of the firmware upgrade environment
get_os_info()
{	
	#for euler as the firmware package environment
	if [ $(cat /etc/*-release|grep -i "EulerOS"|wc -l) -ge 1 ]; then	
		os_pretty_name=`uname -r| awk -F '.' '{print $8 "." $9}'`
	fi

}

# preparing hinic driver and hinicadm tool
prepare_tools() {
  # defines whether the tool exists,0: yes,1: no
  local tool_flag=0
  
  #get os_pretty_name
  get_os_info
  
  # load hinic3 driver
  if [ -d "${pwd}/drivers/${os_pretty_name}" ]; then
	# if OS has installed a hinic3 rpm package,uninstall it
	if [ $(rpm -qa | grep -i hinic3 | grep -v hinicadm3 | wc -l) -eq 1 ]; then
        hinic3_rpm_name=$(rpm -qa | grep -i hinic3 | grep -v hinicadm3)
        rpm -e ${hinic3_rpm_name} >>"${pwd}/work.log" 2>/dev/null
	fi
	
	# if OS has installed a hinic rpm package,uninstall it
	if [ $(rpm -qa | grep -i hinic | grep -v hinicadm | wc -l) -eq 1 ]; then
        hinic_rpm_name=$(rpm -qa | grep -i hinic | grep -v hinicadm)
        rpm -e ${hinic_rpm_name} >>"${pwd}/work.log" 2>/dev/null
    fi
	rpm -ivh --ignoresize ${pwd}/drivers/${os_pretty_name}/*.rpm --force >>"${pwd}/work.log" 2>/dev/null
  fi

  chmod -R +x * 

  # install hinicadm3 tool and modprobe hinic3
  if [ -d "${pwd}/tools/" ]; then
    # check whether the RPM file exists.
    if [ -f ${pwd}/tools/hinicadm3 ]; then
      # if os has installed a hinicadm3 tool,uninstall it ,then install the hinicadm3 tool from packeage
      #			if [ $(rpm -qa|grep -i hinicadm*|wc -l) -ge 1 ]; then
      #				rpm -e hinicadm* >>"${pwd}/work.log" 2>/dev/null
      #			fi

      # synchronizing information to work.log
      ${pwd}/tools/hinicadm3 -v >>"${pwd}/work.log" 2>/dev/null
      rmmod hisdk3 >/dev/null 2>&1 | true
      rmmod hinic3 >/dev/null 2>&1 | true
      modprobe hisdk3 >>"${pwd}/work.log" 2>/dev/null
      modprobe hinic3 >>"${pwd}/work.log" 2>/dev/null
      # synchronizing information to work.log
      modinfo hisdk3 | grep -i version >>"${pwd}/work.log"
      modinfo hinic3 | grep -i version >>"${pwd}/work.log"
    elif [ -f ${pwd}/tools/*.rpm ]; then
      # if os has installed a hinicadm tool,uninstall it ,then install the hinicadm tool from packeage
      if [ $(rpm -qa | grep -i hinicadm | wc -l) -ge 1 ]; then
        rpm -e hinicadm >>"${pwd}/work.log" 2>/dev/null
      fi
      rpm -ivh --ignoresize ${pwd}/tools/*.rpm >>"${pwd}/work.log" 2>/dev/null
      # synchronizing information to work.log
      hinicadm -v >>"${pwd}/work.log" 2>/dev/null
      rmmod hinic >/dev/null 2>&1 | true
      modprobe hinic 2>/dev/null
      # synchronizing information to work.log
      modinfo hinic | grep -i version >>"${pwd}/work.log" 2>/dev/null
    else
      echo 1
      tool_flag=1
    fi
  else
    echo 2
    tool_flag=1
  fi

  # failed to prepare the tool. exit the upgrade process.
  if [ ${tool_flag} == 1 ]; then
    result="Fail"
    error_code="6"
    description="Prepare tools failed"
    create_result_xml
    exit 6
  fi
}

# obtaining 1822 NIC card information in the environment,and collect the list of cards that match the firmware package.
get_device_info() {
  # get hinic device info and synchronizing info to work.log
  if [ -f ${pwd}/tools/hinicadm3 ]; then
    ${pwd}/tools/hinicadm3 info >device_info.txt
  else
    hinicadm info >device_info.txt
  fi
  # synchronizing information to work.log
  cat device_info.txt >>work.log
  # if no any Hi1822 NIC card,exit 4
  if [ $(cat device_info.txt | grep -i "Fail to open" | wc -l) -ge 1 ]; then
    result="Fail"
    error_code="4"
    description="No available device found"
    create_result_xml
    exit 4
  fi

  # 1822 device list
  if [ -f ${pwd}/tools/hinicadm3 ]; then
	device_list=$(cat device_info.txt | grep -o hinic.* | awk -F "(" '{print $1}')
  else
	device_list=$(cat device_info.txt | grep ETH_ | grep -o hinic.* | awk -F "(" '{print $1}')
  fi 
  # define the number of devices that do not need to be upgraded.
  local fw_dev=0
  # define the number of devices that match the firmware package.
  local num_index=0
  # check whether the 1822 device matches the firmware package.
  for dev in ${device_list}; do
    # define hinic deivce line to get the bdf line
	if [ -f ${pwd}/tools/hinicadm3 ]; then
		local line_hinic=$(cat -n device_info.txt | grep ${dev} | awk '{print $1}')
    else
		local line_hinic=$(cat -n device_info.txt | grep ETH_ | grep ${dev} | awk '{print $1}')
	fi
    # get the bdf line
    local line_bdf=$(expr ${line_hinic} + 1)
    local pci_info=$(cat device_info.txt | sed -n ''${line_bdf}'p' | grep -o "0000:..:...0")
    # for upgrade by bdf arg
    local bus_num_tmp=$(cat device_info.txt | sed -n ''${line_bdf}'p' | awk 'BEGIN{FS=":"}{print $2}')
    local device_num_tmp=$(cat device_info.txt | sed -n ''${line_bdf}'p' | awk 'BEGIN{FS="."}{print $1}' | awk 'BEGIN{FS=":"}{print $3}')
    local function_num_tmp=$(cat device_info.txt | sed -n ''${line_bdf}'p' | awk 'BEGIN{FS="."}{print $2}' | awk 'BEGIN{FS="("}{print $1}')

    # run the lspci command to query device information
    lspci -s ${pci_info} -xx >base_info.txt
    vendor_id=$(sed -n '2p' base_info.txt | awk 'BEGIN{FS=" "}{print $3}')$(sed -n '2p' base_info.txt | awk 'BEGIN{FS=" "}{print $2}')
    device_id=$(sed -n '2p' base_info.txt | awk 'BEGIN{FS=" "}{print $5}')$(sed -n '2p' base_info.txt | awk 'BEGIN{FS=" "}{print $4}')
    sub_vid=$(sed -n '4p' base_info.txt | awk 'BEGIN{FS=" "}{print $15}')$(sed -n '4p' base_info.txt | awk 'BEGIN{FS=" "}{print $14}')
    sub_did=$(sed -n '4p' base_info.txt | awk 'BEGIN{FS=" "}{print $17}')$(sed -n '4p' base_info.txt | awk 'BEGIN{FS=" "}{print $16}')

    # check whether the device matches.
    if [[ ${fw_uid} =~ "0x${vendor_id}.0x${device_id}.0x${sub_vid}.0x${sub_did}" ]]; then
      # the number of matched devices increases by 1.
      let num_index=${num_index}+1
      # synchronizing information to work.log
      echo -e >>work.log
      cat base_info.txt >>work.log
    else
      # the card does not match the firmware package. upgrade is not supported. skip this step.
      continue
    fi
    # obtains the firmware version information of the matching device.
	if [ -f ${pwd}/tools/hinicadm3 ]; then
		${pwd}/tools/hinicadm3 version -i ${dev} >dev_info.txt
    else
		hinicadm version -i ${dev} >dev_info.txt
	fi 
    # synchronizing information to work.log
    cat dev_info.txt >>work.log
    # check whether the current firmware version of the device is consistent with the target version
    cur_fw="$(cat dev_info.txt | grep -i "ucode" | awk '{print $2}')"
    if [ "${cur_fw}" == "${fw_version}" ]; then
      let fw_dev=${fw_dev}+1
      # the firmware version is the same as the target version and does not need to be upgraded.
      continue
    fi

    # need upgrade device list
    device_index[${dev_num}]=${dev}

    bus_num[${dev_num}]=${bus_num_tmp}
    device_num[${dev_num}]=${device_num_tmp}
    function_num[${dev_num}]=${function_num_tmp}

    #init bdf flag,the default value is 0, indicating that the device need to upgrade
    bdf_select[${dev_num}]=0
    let dev_num=${dev_num}+1

  done

  # delete tmp files
  if [ -f "${pwd}/device_info.txt" ]; then
    rm -f "${pwd}/device_info.txt" 2>/dev/null
  fi
  if [ -f "${pwd}/base_info.txt" ]; then
    rm -f "${pwd}/base_info.txt" 2>/dev/null
  fi
  if [ -f "${pwd}/dev_info.txt" ]; then
    rm -f "${pwd}/dev_info.txt" 2>/dev/null
  fi

  # the current version of all device are the same as the target version,no upgrade
  if [ ${fw_dev} -eq ${num_index} ] && [ ${num_index} != 0 ]; then
    result="OK"
    error_code="0"
    description="already upgrade"
    create_result_xml
    exit 0
  fi

  # no matching device exists. exit the upgrade process.
  if [ ${dev_num} == 0 ]; then
    result="Fail"
    error_code="4"
    description="No available device found"
    create_result_xml
    exit 4
  fi

}

# upgrade firmware command
upgrade_by_tools() {
  ${pwd}/tools/hinicadm3 updatefw -i ${device_index[$index]} -f "${pwd}/${fw_file}" -x 0 -or >"${pwd}/result.txt"
  # synchronizing Information to work.log
  echo -e >>"${pwd}/work.log"
  echo "${device_index[$index]} upgrade info" >>"${pwd}/work.log"
  cat "${pwd}/result.txt" >>"${pwd}/work.log"
  # checking the upgrade result
  if [ $(cat result.txt | grep -i "firmware image succeed" | wc -l) -ge 1 ]; then
    :
  else
    flag_fail=1
  fi
}

# upgrade firmware command
upgrade_by_tools() {
	if [ -f ${pwd}/tools/hinicadm3 ]; then
		${pwd}/tools/hinicadm3 updatefw -i ${device_index[$index]} -f "${pwd}/${fw_file}" -x 0 -or >"${pwd}/result.txt"
	else
		hinicadm updatefw -i ${device_index[$index]} -f "${pwd}/${fw_file}" >"${pwd}/result.txt"
	fi 
  # synchronizing Information to work.log
  echo -e >>"${pwd}/work.log"
  echo "${device_index[$index]} upgrade info" >>"${pwd}/work.log"
  cat "${pwd}/result.txt" >>"${pwd}/work.log"
  # checking the upgrade result
  if [ $(cat result.txt | grep -i "firmware image succeed" | wc -l) -ge 1 ]; then
    :
  else
    flag_fail=1
  fi
}

# upgrade device firmware
upgrade_firmware() {
  # upgrade in loop
  for ((i = 0; i < ${dev_num}; i++)); do
    index=${i}
    # if device is not in select bdfs,then skipping upgrade
    if [ ${bdf_select[${index}]} == 1 ]; then
      continue
    fi
    upgrade_by_tools
    # if upgrade failed,stop upgrade,and the result.xml file is recorded.
    if [ ${flag_fail} == 1 ]; then
      # delete tmp file
      if [ -f "${pwd}/result.txt" ]; then
        rm -f "${pwd}/result.txt" 2>/dev/null
      fi
      result="Fail"
      error_code="1"
      description="Upgrade failed"
      create_result_xml
      exit 1
    fi
  done

  # delete tmp file
  if [ -f "${pwd}/result.txt" ]; then
    rm -f "${pwd}/result.txt" 2>/dev/null
  fi
  # the loop exits normally, and the result.xml file is recorded.
  if [ ${flag_fail} == 0 ]; then
    result="OK"
    error_code="NA"
    description="NA"
    create_result_xml
    exit 0
  fi
}

# filter devices by bdf
filter_by_bdf() {
  # defines the filtering flag. The default value is 0, indicating that the entered bdf does not match.
  flag_filter=0
  # check bdf number
  for ((i = 0; i < ${dev_num}; i++)); do
    # skip if bdf is empty
    if [ -z "${bus_num[$i]}" ] && [ -z "${device_num[${i}]}" ] && [ -z "${function_num[${i}]}" ]; then
      continue
    fi
    # check whether the filtered bdf device information is within the range of the card
    if [[ ${select_bdfs} =~ "${bus_num[${i}]}:${device_num[${i}]}.${function_num[${i}]}" ]]; then
      # indicates that the bdf needs to be upgraded.
      flag_filter=1
    else
      # if the mismatch flag is 1, the upgrade is not performed.
      bdf_select[${i}]=1
    fi
  done

  # no device found by filter,Error input or the bdf card no need to upgrade
  if [ ${flag_filter} == 0 ]; then
    result="Fail"
    error_code="2"
    description="Error input"
    create_result_xml
    exit 2
  fi
}

# set select bdf
select_bdfs=""
if [ -n "$2" ] && [ "$2" == "BDF" ]; then
  # $3 bdf format: "0000:xx:xx.x,0000:yy:yy.y" or "xx:xx.x,yy:yy.y"
  select_bdfs=$(echo $3 | sed 's/;/ /g')
fi

case $1 in
upgrade)
  # read version xml
  read_version_xml
  # preparing the mst and flint upgrade tools
  prepare_tools
  # get device info by tools
  get_device_info
  # filter bdfs by input
  if [ -n "${select_bdfs}" ]; then
    filter_by_bdf
  fi
  # upgrade firmware
  upgrade_firmware
  ;;
*) ;;

esac

exit 0
