#
# Copyright (c) 2019-2025 Citrix Systems, Inc. All rights reserved.
# This software and documentation contain valuable trade
# secrets and proprietary property belonging to Citrix Systems, Inc.
# None of this software and documentation may be copied,
# duplicated or disclosed without the express
# written permission of Citrix Systems, Inc.
#
# 08/14/25
#
# This file contains shell script subroutines needed by the
# Mellanox firmware update script suite.
#
# usage:  NO USAGE!  This file cannot be called separately!
#

LOG_FILE="/var/log/mellanoxFw.log"

#
# Prints message, and logs it to the log file.
#
log_msg()
{
	echo -e "$*"
	echo -e "$*" >> $LOG_FILE
}

#
# Unconditionally power-cycle the NetScaler.
#
power_cycle_netscaler()
{
	# log_msg ">>> Power-cycling NetScaler!"

	# Change default power cycle "off" time to 30 seconds.
	/usr/local/sbin/ipmitool -I open raw 0x0 0x0b 0x1e 
	# Command the LOM to cycle NetScaler power off/on.
	/usr/local/sbin/ipmitool chassis power cycle
}

#
# Cleanly power cycle the NetScaler.
#
cleanly_power_cycle_netscaler()
{
	log_msg ">>> Power-cycling NetScaler... $(date)"
	sleep 6
	sync; sync
	power_cycle_netscaler
	sleep 14
}

#
# Returns 0 if the NetScaler has at least one Mellanox NIC
#
has_mellanox_nics()
{
	sysctl dev.mce.0.debug > /dev/null 2>&1
	return $?
}

#
# Returns 0 if this PCI DBSF represents the first port on a NIC.
#
is_mellanox_nic_port_0()
{
	local PCI_DBSF
	PCI_DBSF=$1

	echo ${PCI_DBSF} | grep -Eq "pci0:[0-9]*:[0-9]*:[0]+"
	return $?
}

#
# Returns ConnectX_n type string in CONNECTX_TYPE, if identifiable.
#
get_connectX_type()
{
	local MLX_PCID
	MLX_PSID=$1

	case "${MLX_PSID}" in
	"MT_2150110033" | \
	"MT_2600110035" | \
	"MT_2130110035" | \
	"MT_2610110035" )
		CONNECTX_TYPE="   CX_4"
		return
	;;
	esac

	case "${MLX_PSID}" in
	"MT_0000000224" )
		CONNECTX_TYPE="CX_6"
		return
	;;
	esac

	case "${MLX_PSID}" in
	"MT_0000000359" )
		CONNECTX_TYPE="CX_6 DX"
		return
	;;
	esac

	case "${MLX_PSID}" in
	"MT_0000000834" )
		CONNECTX_TYPE="CX_7 200G"
		return
	;;
	esac

	case "${MLX_PSID}" in
	"MT_0000000849" )
		CONNECTX_TYPE="CX_7 50G"
		return
	;;
	esac

	CONNECTX_TYPE=""
}


#
# Returns lists of the mce number/NetScaler interface name strings
# for any 100G, 50G and 25G Mellanox interfaces that may be present.
# Returns in VALUE_STRINGS_100, VALUE_STRINGS_50 and VALUE_STRINGS_25.
#
nic_mce_strings()
{
	VALUE_STRINGS_200=`sysctl -i dev.200/ | grep conf.device_name | \
	    grep mce | sed -e "s/ //"`
	VALUE_STRINGS_100=`sysctl -i dev.100/ | grep conf.device_name | \
	    grep mce | sed -e "s/ //"`
	VALUE_STRINGS_50=`sysctl -i dev.50/ | grep conf.device_name | \
	    grep mce | sed -e "s/ //"`
	VALUE_STRINGS_25=`sysctl -i dev.25/ | grep conf.device_name | \
	    grep mce | sed -e "s/ //"`
}

#
# Prints mce #, NS interface name, FW version, PCI DBSF,
# Mellanox name and PSID, and ConnectX_4/6/7 type
# for one Mellanox NIC.
# Also, records that string in the log file.
#
output_nic_info_if_port_0()
{
	local VALUE_STR
	VALUE_STR=$1

	MCE_NUM=`echo ${VALUE_STR} | sed -e "s/mce//" | sed -e "s/dev.*://"`
	DBSF=`sysctl dev.mce.${MCE_NUM}.%location | cut -f 4 -w | \
	    sed -e "s/dbsf=//"`

	# return if not port 0
	is_mellanox_nic_port_0 ${DBSF}
	if [ $? -ne 0 ]; then
		return
	fi

	NS_IF_NAME=$(echo "${VALUE_STR}" | sed -e "s/dev\.//" \
		| sed -e "s/\.conf\.device_name:mce[0-9]*//" \
		| sed -e "s/\.//" \
		| sed -e 's/^ *//;s/ *$//') # trim leading/trailing spaces

	FW_STR=$(mstflint -d "${DBSF}" q \
		| grep -Eio "FW Version:[[:space:]]*[0-9]+\.[0-9]+\.[0-9]+" \
		| sed -e "s/FW Version:[[:space:]]*//")

	PSID=$(mstflint -d "${DBSF}" q \
		| grep -i 'PSID:' \
		| sed -e "s/PSID:[[:space:]]*//")

	get_connectX_type ${PSID}

	log_msg "mce${MCE_NUM} (${NS_IF_NAME})\t${FW_STR} ${DBSF}\t ${PSID}" \
	    "${CONNECTX_TYPE}"
}

#
# Prints a list of mce #, NS interface name, FW version, PCI DBSF,
# Mellanox name and PSID, and ConnectX_4/6/7 type
# for each Mellanox NIC in the system.
# Also, records those strings in the log file.
#
output_all_nics_info()
{
	local VALUE_STRINGS_200
	local VALUE_STRINGS_100
	local VALUE_STRINGS_50
	local VALUE_STRINGS_25

	VALUE_STRINGS_200=""
	VALUE_STRINGS_100=""
	VALUE_STRINGS_50=""
	VALUE_STRINGS_25=""

	nic_mce_strings

	log_msg "mce#  NS if#      FW ver     PCI DBSF        PSID     " \
	    "      Type"
	log_msg "----- ------    ---------- ------------  -------------" \
	    "-----------------"

	# NetScaler 200/nn interfaces
	for VALUE_STR in ${VALUE_STRINGS_200}; do
		output_nic_info_if_port_0 ${VALUE_STR}
	done
	# NetScaler 100/nn interfaces
	for VALUE_STR in ${VALUE_STRINGS_100}; do
		output_nic_info_if_port_0 ${VALUE_STR}
	done
	# NetScaler 50/nn interfaces
	for VALUE_STR in ${VALUE_STRINGS_50}; do
		output_nic_info_if_port_0 ${VALUE_STR}
	done
	# NetScaler 25/nn interfaces
	for VALUE_STR in ${VALUE_STRINGS_25}; do
		output_nic_info_if_port_0 ${VALUE_STR}
	done
}

