#!/bin/bash
#
# mst        This starts and stops mst (Mellanox Software tools) drivers
#
# chkconfig: 3 20 80
# description: Starts and stops IB drivers used to burn FW and do simple IO
#

#insmod_flags="-f"
prefix="/usr/mst"


if [ `uname -m` == "ppc" ]; then
        ENABLE_I2C_DEV=1
fi

# Source function library.
action() {
    STRING=$1
    echo -n "$STRING"
    shift
    $*
    rc=$?
    if test $rc -ne 0
    then
        echo " - Failure: $rc"
    else
        echo " - Success"
    fi
    return $rc
}
echo_success() {
    echo " - Success"
}
echo_failure() {
    echo " - Failure: $?"
}
failure() {
    echo -n "$* - Failure"
}

 
RETVAL=0

WITH_MSIX="with_msix"
WITH_UNKNOWN_ID="with_unknown"

# Mellanox dev directory
mdir="/dev/mst"        # Directory where MST devices created
mbindir=/usr/bin # Binary directory where MST-utils and modules are located
#@POST_MST_BIN_DIR@  # Update the bin dir by the post install script.

UNKNOWN_ID="UNKNWON_ID"

#if [ `uname -m` == "ppc" ]; then
#    pcidir="/var/mst_pci"
#else
#    pcidir="$prefix/etc/pci" # Directory where PCI info should be saved
#fi
# Use the var to save the pci slot files
pcidir="/var/mst_pci"

# Default permission
perm="666"

# Vendor / Device IDs
venid="15b3"         # Mellanox vendor ID

devid_pcurom="5a50"  # MT23108 PCIROM device ID


# Old devices
devid_pci_tavor=5a44 # MT23108 PCI device ID
devid_anafa=a87c     # MT21108 (Anafa)
devid_apci=6278      # MT25208 PCI device ID
devid_apci1=6282     # MT25218 PCI device ID
devid_s4pci=5e8c     # MT24204 PCI device ID
devid_s8pci=6274     # MT25204 PCI device ID


#    DevId              RstAddr   Description
dev_id_database=(\
    "${devid_anafa}     0x3010    MT21108(Anafa)"
    "${devid_pci_tavor} 0xf0010   MT23108 InfiniHost"
    "${devid_apci}      0xf0010   MT25208 InfiniHost III Ex (Tavor compatibility mode)"
    "${devid_apci1}     0xf0010   MT25208 [InfiniHost III Ex]"
    "${devid_s4pci}     0xf0010   MT25204 [InfiniHost III Lx HCA]"
    "${devid_s8pci}     0xf0010   MT25204 [InfiniHost III Lx HCA]"
    "6340               0xf0010   MT25408 [ConnectX VPI - IB SDR / 10GigE]"
    "634a               0xf0010   MT25418 [ConnectX VPI PCIe 2.0 2.5GT/s - IB DDR / 10GigE]"
    "6368               0xf0010   MT25448 [ConnectX EN 10GigE, PCIe 2.0 2.5GT/s]"
    "6372               0xf0010   MT25458 [ConnectX EN 10GigE 10GBaseT, PCIe 2.0 2.5GT/s]"
    "6732               0xf0010   MT26418 [ConnectX VPI PCIe 2.0 5GT/s - IB DDR / 10GigE]"
    "673c               0xf0010   MT26428 [ConnectX VPI PCIe 2.0 5GT/s - IB QDR / 10GigE]"
    "6750               0xf0010   MT26448 [ConnectX EN 10GigE, PCIe 2.0 5GT/s]"
    "675a               0xf0010   MT26458 [ConnectX EN 10GigE 10GBaseT, PCIe Gen2 5GT/s]"
    "676e               0xf0010   MT26478 [ConnectX EN 10GigE, PCIe 2.0 5GT/s]"
    "6746               0xf0010   MT26438 [ConnectX-2 VPI w/ Virtualization+]"
    "6764               0xf0010   MT26468 [Mountain top]"
    "bd34               0xf0010   IS4 IB SDR"
    "bd35               0xf0010   IS4 IB DDR"
    "bd36               0xf0010   IS4 IB QDR"
    "fa66               0xf0010   BridgeX VPI up, E/FC down"
    "fa7a               0xf0010   BridgeX EN up, E/FC down"
    "c738				0xf0010	  SwitchX - 36 40G/64 10Gports InfiniBand and Ethernet Layer 2/3+ Switch"
    "1001               0xf0010   TODO: CHECK IT"
    "1002               0xf0010   TODO: CHECK IT"
    "1003               0xf0010   MT27500 [ConnectX-3]"
    "1004               0xf0010   TODO: CHECK IT"
    "1005               0xf0010   TODO: CHECK IT"
    "1006               0xf0010   TODO: CHECK IT"
    "1007               0xf0010   TODO: CHECK IT"
    "1008               0xf0010   TODO: CHECK IT"
    "1009               0xf0010   TODO: CHECK IT"
    "100a               0xf0010   TODO: CHECK IT"
    "100b               0xf0010   TODO: CHECK IT"
    "100c               0xf0010   TODO: CHECK IT"
    "100d               0xf0010   TODO: CHECK IT"
    "100e               0xf0010   TODO: CHECK IT"
    "100f               0xf0010   TODO: CHECK IT"
    "1010               0xf0010   TODO: CHECK IT"
)

live_fish_id_database=(\
    "0191  0xf0010 MT25408 [ConnectX IB SDR Flash Recovery"
    "6279  0xf0010 MT25208 [InfiniHost III Ex HCA Flash Recovery]"
    "5e8d  0xf0010 MT25204 [InfiniHost III Lx HCA Flash Recovery]"
    "5a45  0xf0010 MT23108 [Infinihost HCA Flash Recovery]"
    "0246  0xf0010 SwitchX Flash recovery mode"
    "01F6  0xf0010 MT27500 [ConnectX-3 Flash Recovery]"
    "6275  0xf0010 TODO: CHECK IT"
    "1d75  0xf0010 TODO: CHECK IT"
)


# Title
prog="MST (Mellanox Software Tools) driver set"



PATH=${PATH}:/sbin:/usr/bin:/bin:${mbindir}

kver=`uname -r`
modir="/lib/modules/${kver}/kernel/drivers/mft"


if [ -r /etc/mst.conf ]; then
. /etc/mst.conf
fi


###
### PCI / PCICONF PCUROM
### --------------------
###

### create group of PCI devices per one instance of tavor (ddr,cr,uar)
### ------------------------------------------------------------------
create_pci_dev()
{
    ## For IA64 - do not create PCI devices
    #if [ "`uname -m`" = "ia64" ]; then
    #    return
    #fi

    local devname=$1
    local busdevfn=$2
    local devnum=$3
    local major=$4
    local minor=$5
#    local c_major=$6
#    local c_minor=$7
    local bar_step=$6
    shift 6

    local bar=0
    # PCI group of devices
    #     "_cr", "_uar"  and "_ddr" for Tavor
    #     ""     "_i2cm"            for Gamla
    for name
    do
        mknod -m ${perm} ${mdir}/${devname}pci$name${devnum} c ${major} ${minor}
        ${mbindir}/minit ${mdir}/${devname}pci$name${devnum} $busdevfn $bar
        if [ $? -ne 0 ]; then
            rm ${mdir}/${devname}pci$name${devnum}
        fi
        minor=$(( $minor + 1 ))
        bar=$(( $bar + $bar_step ))
    done

    # open mem access if closed.
    mem_byte=`setpci -s $busdevfn 4.B`
    let "mem_en = 0x$mem_byte & 0xf"
    if [ $mem_en -eq 0 ]; then
        let "mem_byte = 0x$mem_byte | 0x6"
        mem_byte=`printf "%x" $mem_byte`
        setpci -s $busdevfn 4.B=$mem_byte
    fi
    echo $minor
}

### create a PCICONF device per one instance of tavor
### -------------------------------------------------
create_pciconf_dev()
{
    local devname=$1
    local busdevfn=$2
    local devnum=$3
    local major=$4
    local minor=$5
    local name=$6

    # PCICONF
    mknod -m ${perm} ${mdir}/${devname}pciconf$name${devnum} c ${major} ${minor}
    ${mbindir}/minit ${mdir}/${devname}pciconf$name${devnum} ${busdevfn} 88 92
    if [ $? -ne 0 ]; then
        rm ${mdir}/${devname}pciconf$name${minor}
    fi
    minor=$((  $minor + 1 ))
}

#write all-ones to PCI Vital Product Data(VPD) capability register
#wait till top bit read as 0
clear_VPD()
{
   busdevfn=$1
   reg_vpd=0
   stat=`setpci -s $busdevfn 06.B`
   if (($stat & 0x10 ));then #if there is a capabilities list
     reg=`setpci -s $busdevfn 34.L`     #get 1st capability reg
     while (( $reg != 0 )); do
       regval=0x`setpci -s $busdevfn ${reg}.L`
       if (($((regval & 0xff)) == 0x03)); then
         reg_vpd=$reg
         break;
       fi
       reg=${regval:6:2}
     done
     #echo reg_vpd=$reg_vpd

     if (($reg_vpd)); then
         setpci -s $busdevfn ${reg_vpd}.L=ffffffff
         regval=0x`setpci -s $busdevfn ${reg_vpd}.L`
         while (( $regval & 0x80000000 )); do
           regval=`setpci -s $busdevfn ${reg_vpd}.L`
         done
     fi
   fi
}

is_gamla_anfa()
{
    if [ "$1" == "$devid_anafa" ]; then
        echo 1
    else
        echo 0
    fi
}

get_pci_dev_args()
{
    local with_msix=$1
    local devid=$2

    is_gamla_anafa_ind=$(is_gamla_anfa $devid)

    if [ $is_gamla_anafa_ind -eq 1 ]; then
        echo " 0 \"\" _i2cm"
    elif [ "$devid" == "$devid_pci_tavor" ] || [ "$devid" == "$devid_apci" ] || [ "$devid" == "$devid_s4pci" ]; then
        echo "2 _cr _ddr"
    elif [ "$devid" == "$devid_s8pci" ] || [ "$devid" == "$devid_apci1" ]; then
        echo "2 _cr"
    elif [ "$with_msix" == "1" ]; then
        echo "2 _cr _msix"
    else
        echo "2 _cr"
    fi
}

get_clear_vpd()
{
    local devid=$1
    is_gamla_anafa_ind=$(is_gamla_anfa $devid)
    if [ $is_gamla_anafa_ind -eq 1 ]; then
        echo 1
    else
        echo 0
    fi

}

get_conf_create_ind()
{
    local devid=$1
    is_gamla_anafa_ind=$(is_gamla_anfa $devid)
    if [ $is_gamla_anafa_ind -eq 1 ]; then
        echo 0
    else
        echo 1
    fi
}
get_dev_id()
{
    local oper=$1
    local devidarg=$2
    local devid_lspci_out=$3

    local devid=$devidarg

    if [ "$devidarg" == $UNKNOWN_ID ] ; then
        if [ "$oper" == "before" ]; then
            devid=""
        else
            devid=`echo $devid_lspci_out | cut -f2 -d"\""`
        fi
    fi
    echo $devid
}

get_dev_name()
{
    dev_id=$1
    dev_id_dec=`printf %d 0x$dev_id`
    devname="mt"$dev_id_dec"_"
    echo $devname
}

prepare_create_pci_dev()
{
    local devidarg=$1
    local pciminor=$2
    local pciconfminor=$3
    local pcimajor=$4
    local pciconfmajor=$5
    local with_msix=$6

    shift 6
    
    devid=$(get_dev_id "before" $devidarg)

    devnum=0
    new_minors=`(
    echo $pciminor $pciconfminor
    lspci -m -n -d ${venid}:$devid | sort | while read str
    do
        set -- $str
        busdevfn=$1

        devid=$(get_dev_id "after" $devidarg $5)

        devname=$(get_dev_name $devid)
        pci_dev_args=$(get_pci_dev_args $with_msix $devid )
        needs_clear_vpd=$(get_clear_vpd $devid)
        needs_conf_create=$(get_conf_create_ind $devid)


        #write all-ones to PCI Vital Product Data(VPD) capability register
        if [ $needs_clear_vpd -ne 0 ]; then
            clear_VPD $busdevfn
        fi

        next_pciminor=$(create_pci_dev $devname $busdevfn $devnum $pcimajor $pciminor $pci_dev_args)
        
        if [ $needs_conf_create -ne 0 ]; then
            create_pciconf_dev $devname $busdevfn $devnum $pciconfmajor $pciconfminor
            pciconfminor=$((  $pciconfminor + 1 ))
        fi

        devnum=$(( $devnum + 1 ))
        pciminor=$next_pciminor
        
        echo $pciminor $pciconfminor
    done
    )| tail -1`
    echo $new_minors
}


prepare_create_pci_dev_live_fish()
{
    local devidarg=$1
    local pciconfminor=$2
    local pciconfmajor=$3

    devnum=0

    devid=$(get_dev_id "before" $devidarg)
    
    new_pciconfminor=`(
    echo $pciconfminor
    lspci -m -n -d ${venid}:${devid} | sort | while read str
    do
        set -- $str
        busdevfn=$1
        devid=$(get_dev_id "after" $devidarg $5)

        devname=$(get_dev_name $devid)
        create_pciconf_dev $devname $busdevfn $devnum $pciconfmajor $pciconfminor

        devnum=$((  $devnum + 1 ))
        pciconfminor=$((  $pciconfminor + 1 ))
        echo $pciconfminor
    done
    )| tail -1`
    echo $new_pciconfminor
}


create_mtusb_devices()
{

    # Create MTUSB devices
    if [ `uname -m` == "ppc" ]; then
        return
    fi

    local dimax_vend=0x0abf
    local dimax_prod=0x3370

    local mst_usb_dev=""

    OLD_IFS=$IFS
    IFS=$'\n';
    
    for lsusb_out in `lsusb -d $dimax_vend:$dimax_prod`;
    do
        IFS=$OLD_IFS
        found_devs=0
        local bus=$(echo $lsusb_out | cut -f2 -d" ")
        local device=$(echo $lsusb_out | cut -f1 -d":" | cut -f4 -d " ")

        for usb_dir in /dev/bus/usb /proc/bus/usb;
        do

            if ! test -d $usb_dir; then
                continue
            fi
            usb_dev="$usb_dir/$bus/$device"
            if chmod 0666 $usb_dev 2> /dev/null; then
                if ${mbindir}/dimax_init $usb_dev > /dev/null ; then
                    found_devs=1
                    action "MTUSB-1 USB to I2C Bridge" ln -fs $usb_dev ${mdir}/${mst_usb_dev}mtusb-1 2> /dev/null
                    mst_usb_dev=X$mst_usb_dev
                else
                    echo "Can't initialize MTUSB-1 USB to I2C Bridge"
                fi   
            fi
            # If devices were found on first dir, don't search the other dir (may be doplicated)
            if [ "$found_devs" == "1" ]; then 
                break;
            fi
        done

    done
    return
}

create_pci_devices()
{
    local with_misx=$1
    local with_unknown=$2

    # ------------------------------------
    # Determine PCI/PCICONF major numbers.
    # Initialize PCI/PCICONF minor numbers.
    # ------------------------------------

    mstr=`cat /proc/devices | grep 'mst_pci$'`
    if [ $? -ne 0 ]; then
        echo
        echo "mst_pci driver not found"
        return 1
    fi
    set -- $mstr
    pcimajor=$1
    mstr=`cat /proc/devices | grep 'mst_pciconf$'`
    if [ $? -ne 0 ]; then
        echo
        echo "mst_pciconf driver not found"
        return 1
    fi
    set -- $mstr
    pciconfmajor=$1
    pciminor=0
    pciconfminor=0


    if [ "$with_unknown" == "1" ]; then
        pci_pciconf_minor=$(prepare_create_pci_dev $UNKNOWN_ID $pciminor $pciconfminor $pcimajor $pciconfmajor $with_msix)
        set -- $pci_pciconf_minor
        pciminor=$1
        pciconfminor=$2
    else
        element_count=${#dev_id_database[@]}
        index=0
        while [ "$index" -lt "$element_count" ]; do
            set -- ${dev_id_database[$index]}
            devid=$1
            pci_pciconf_minor=$(prepare_create_pci_dev $devid $pciminor $pciconfminor $pcimajor $pciconfmajor $with_msix)
            set -- $pci_pciconf_minor
            pciminor=$1
            pciconfminor=$2

            ((index++))
        done
    fi

    element_count=${#live_fish_id_database[@]}
    index=0
    while [ "$index" -lt "$element_count" ]; do
        set -- ${live_fish_id_database[$index]}
        devid=$1
        pci_pciconf_minor=$(prepare_create_pci_dev_live_fish $devid $pciconfminor $pciconfmajor)
        ((index++))
    done
    return

}

# create all devices
create_devices()
{
    echo "$1"
    create_pci_devices $2 $3
    create_mtusb_devices
    return
}

is_module()
{
local RC

    /sbin/lsmod | grep -w "$1" > /dev/null 2>&1
    RC=$?

return $RC
}
load_module()
{
    mod_name=$1
    mod_file_path=$2
    load_cmd=$3
    load_cmd_flags=$4
    load_cmd_string=$5
    
    if is_module ${mod_name}
    then 
        echo "[warn] ${mod_name} is already loaded, skipping"
    else 
            action "${load_cmd_string}" "${load_cmd}" "${load_cmd_flags}" "${mod_file_path}"        
    fi

}

# Main function
start()
{
    local with_msix=0
    local with_unknwon_id=0

    while (( "$#" )); do
        if [ "$1" == "--$WITH_MSIX" ]; then
            with_msix=1
        elif [ "$1" == "--$WITH_UNKNOWN_ID" ]; then
            with_unknwon_id=1
        else
            echo "-E- Unknown argument $1"
            exit 1
        fi

        shift
    done



    echo "Starting $prog"

    # Create empty MST devices directory
    rm -fr ${mdir}
    mkdir ${mdir}


    if [ "$ENABLE_I2C_DEV" == "1" ]; then
        if [ ! -r /dev/i2c-0 ]; then
            action "Loading I2C modules" modprobe i2c-dev
            sleep 0.1
        fi
        for f in /dev/i2c-[0-9]; do
                dev=`basename $f`
                I2C_DEV=${mdir}/dev-$dev
                rm -f $I2C_DEV
                ln -s $f $I2C_DEV
        done
    fi


    MST_PCI_MOD="mst_pci"
    MST_PCICONF_MOD="mst_pciconf"        
    MST_PCI_NAME="${modir}/${MST_PCI_MOD}"
    MST_PCI_CONF_NAME="${modir}/${MST_PCICONF_MOD}"
    
    if [ -a "${MST_PCI_NAME}.o" ]; then
        MST_PCI_EXT="o"
    else
        MST_PCI_EXT="ko"
    fi

    if [ `uname -m` == "ppc" ]; then
        load_module "${MST_PCI_MOD}"     "${MST_PCI_MOD}"     "modprobe" "${insmod_flags}" "Loading MST PCI module"       
        load_module "${MST_PCICONF_MOD}" "${MST_PCICONF_MOD}" "modprobe" "${insmod_flags}" "Loading MST PCI configuration module"     
    else
        MST_FULL_PCI_MOD="${MST_PCI_NAME}.${MST_PCI_EXT}"
        MST_FULL_PCICONF_MOD="${MST_PCI_CONF_NAME}.${MST_PCI_EXT}"        
       
        load_module "${MST_PCI_MOD}"     "${MST_FULL_PCI_MOD}"     "insmod" "${insmod_flags}" "Loading MST PCI module"       
        load_module "${MST_PCICONF_MOD}" "${MST_FULL_PCICONF_MOD}" "insmod" "${insmod_flags}" "Loading MST PCI configuration module"     
    fi


    # create all related devices
    create_devices "Create devices"  $with_msix $with_unknwon_id
    rc1=$?
    if [ $rc1 -eq 0 ] && ls ${mdir}| grep -e "_cr" > /dev/null 2>&1
    then :
    else
        action "Unloading MST PCI module (unused)" rmmod mst_pci
    fi
    if [ $rc1 -eq 0 ] && ls ${mdir}| grep -e "conf" > /dev/null 2>&1
    then :
    else
        action "Unloading MST PCI configuration module (unused)" rmmod mst_pciconf
    fi


    if [ ! -e /dev/i2c-0 ]; then
       if lsmod | grep "i2c_dev" > /dev/null ; then
          action "Unloading I2C module (unused)" modprobe -r i2c-dev
       fi
    fi

}

stop()
{
    echo "Stopping $prog"

    serv_stop

    for dev in ${mdir}/*
    do
        if [ -e "$dev" ]; then
          ${mbindir}/mstop "$dev" >/dev/null 2>&1
        fi
    done

    if  lsmod | grep mst_pciconf > /dev/null
    then
        action "Unloading MST PCI configuration module" rmmod mst_pciconf
    fi
    if  lsmod | grep mst_pci | grep -v mst_pciconf> /dev/null
    then
        action "Unloading MST PCI module" rmmod mst_pci
    fi


    if lsmod | grep i2c_dev > /dev/null
    then
         action "Unloading i2c driver" rmmod i2c_dev
    fi

    rm -fr ${mdir}
}

print_chip_rev_internal()
{
    export MTCR_REMOTE_WARN=1
    local val=`${mbindir}/mread $1 0xF0014`
    unset MTCR_REMOTE_WARN
    local str=${val:20:2}
    str=`echo $str | tr a-z A-Z`
    echo "                                   Chip revision is: $str"
}

print_chip_rev()
{
    local dev=$1
    if expr match "$dev" ".*_pci_cr" > /dev/null 2>&1
    then
        print_chip_rev_internal $dev
    fi
    if expr match "$dev" ".*_pciconf" > /dev/null 2>&1
    then
        print_chip_rev_internal $dev
    fi
}


function ignore_cr_devs()
{
    set dev=$1

    if expr match "$dev" ".*_pci_cr"  > /dev/null 2>&1; then
        if expr match $kver ".*460ex" > /dev/null 2>&1; then
            if [ `uname -m` == "ppc" ]; then
                echo "YES"
                return
            fi
        fi
    fi

    echo "NO"
}



print_status()
{
    
    # Check modules
    echo "MST modules:"
    echo "------------"
    if  is_module mst_pci
    then
        echo "    MST PCI module loaded"
    else
        echo "    MST PCI module is not loaded"
    fi
    if  is_module mst_pciconf
    then
        echo "    MST PCI configuration module loaded"
    else
        echo "    MST PCI configuration module is not loaded"
    fi

    if  cat /proc/devices | grep mst_ppc > /dev/null
    then
       echo  "    MST PPC Bus module loaded"
    fi

    # Devices
    devcnt=0
    rcnt=0
    rdevs=""
    ibcnt=0
    ibdevs=""
    echo
    echo "MST devices:"
    echo "------------"
    for dev in ${mdir}/*
    do
        if [ -r "$dev" ]; then
            if expr $dev : '.*:' > /dev/null 2>&1
            then
                rdevs=$rdevs" $dev"
                rcnt=$((  $rcnt + 1 ))
            elif expr match "$dev" ".*lid-0x[0-9A-Fa-f]*" > /dev/null 2>&1
            then
                ibdevs=$ibdevs" $dev"
                ibcnt=$((  $ibcnt + 1 ))
            elif expr match "$dev" ".*lid-[0-9]*" > /dev/null 2>&1
            then
                ibdevs=$ibdevs" $dev"
                ibcnt=$((  $ibcnt + 1 ))
            elif expr match "$dev" ".*usb.*" > /dev/null 2>&1
            then
                    echo -e "$dev:\t\t - USB to I2C adapter as I2C master"
            elif expr match "$dev" ".*dev-i2c.*" > /dev/null 2>&1
            then
                    echo -e "$dev:\t\t - Embedded I2C master"
            else
                ignore_dev=$(ignore_cr_devs $dev)
                if [ "$ignore_dev" == "NO" ]; then
                    cat $dev
                    if expr match "$dev" ".*_pci_cr" > /dev/null 2>&1
                    then
                        print_chip_rev $dev
                    fi
                    if expr match "$dev" ".*_pciconf" > /dev/null 2>&1
                    then
                        print_chip_rev $dev
                    fi
                fi
            fi
            devcnt=$((  $devcnt + 1 ))
       fi
    done
    if [ ${rcnt} -ne 0 ]; then
        echo
        echo "Remote MST devices:"
        echo "-------------------"
        for dev in $rdevs
        do
            echo $dev
            print_chip_rev $dev
        done
    fi

    if [ ${ibcnt} -ne 0 ]; then
        echo
        echo "Inband devices:"
        echo "-------------------"
        for dev in $ibdevs
        do
            echo $dev
        done
    fi

    if [ ${devcnt} -eq 0 ]; then
        echo "    No MST devices found"
    fi
}



# return the matching slot file name in the PCI fs
# Check if domain need to be added.
get_pci_file()
{
    slot=$1

    local prefix=/proc/bus/pci/

    if echo $slot | grep ":..:"  > /dev/null 2>&1
    then
        dombus=`echo $slot | cut -f1,2 -d:`
        devfn=`echo  $slot | cut -f3   -d:`
    else
        dombus=`echo $slot | cut -f1   -d:`
        devfn=`echo  $slot | cut -f2   -d:`

        if [ -d "$prefix/0000:$dombus" ]
        then
            # Try to add zero domain to the name if domain not explicitly given
            dombus="0000:$dombus"
        fi
    fi

    echo "$prefix/$dombus/$devfn"
}


save_pci()
{
    rm -fr $pcidir
    mkdir -p $pcidir
    lspci -m | grep Mellanox | while read str
    do
        slot=`echo $str | cut -f1 -d' '`
        pci_file=`get_pci_file $slot`
         
        echo -n "Saving configuration for PCI device $slot"
        
        # Address 0x58 contains the last cr-space address read by the conf device,
        # this address may be a semaphore, GW lock or any dangerous address, 
        # so we will save the address 0xf0014 always, to protect from any unsafe read 
        # or write in the save and load commands.
        setpci -s $slot 0x58.L=0xf0014 > /dev/null 2>&1 
        
        if cp $pci_file $pcidir/$slot > /dev/null 2>&1
        then
            echo_success
        else
            echo_failure
        fi
    done
}

load_pci()
{
    lspci -m | grep Mellanox | while read str
    do
        slot=`echo $str | cut -f1 -d' '`
        pci_file=`get_pci_file $slot`
        
        echo -n "Restoring configuration for PCI device $slot"
        if cp $pcidir/$slot $pci_file > /dev/null 2>&1
        then
            echo_success
        else
            echo_failure
        fi
        echo
    done
}

get_reset_addr()
{

    #### A trick to get the array that was passed to the function as an argument ####
    OLD_IFS=$IFS; IFS=''

    local array_string="$1[*]"
    local data_base_arr=(${!array_string})

    IFS=$OLD_IFS
    ###################################################################################\


    local raddr=0x0


    local element_count=${#data_base_arr[@]}
    local index=0

    while [ "$index" -lt "$element_count" ]; do
        set -- ${data_base_arr[$index]}
        devid=$1
        rst_addr=$2
        dev_expr=".*$(get_dev_name $devid)pci"

        if expr $device : $dev_expr > /dev/null 2>&1
        then
            raddr=$rst_addr
            break;
        fi

        ((index++))
    done

    echo $raddr
}

reset_pci()
{
    local orig_device=$1
    local device=$1
    local raddr=0x0

    if [ ! -e $device ]; then
        device="$mdir/$device"
        if [ ! -e $device ]; then
            echo "Device \"$orig_device\" (or \"$device\") doesn't exist"
            return 1
        fi
    fi

    raddr=$(get_reset_addr dev_id_database)
    if [ "$raddr" == "0x0" ]; then
        raddr=$(get_reset_addr live_fish_id_database)
        if [ "$raddr" == "0x0" ]; then
            echo "$device is a wrong device to reset"
            return 1
        fi
    fi

    echo -n "Reset device $device"
     
    if ${mbindir}/mwrite $device $raddr 1
    then
        sleep 1
        echo_success
        echo
        return 0
    else
        echo_failure
        echo
        return 1
    fi

    return 0
}

radd()
{
    local host=$1
    local port=23108
    if expr $host : '.*:' > /dev/null 2>&1
    then
        set -- $(IFS=:; set -- $host; echo "$@")
        host=$1
        port=$2
    fi
    mkdir -p $mdir
    local devs=`${mbindir}/mremote $host:$port L`
    for dev in $devs
    do
      if ! echo $dev | grep ':' > /dev/null 2>&1; then
        local fname=${mdir}/$host:$port,`echo $dev | sed -e 's/\//@/g'`
        touch $fname
      fi
    done
}

rdel()
{
    local host=$1
    local port=23108
    if expr $host : '.*:' > /dev/null 2>&1
    then
        set -- $(IFS=:; set -- $host; echo "$@")
        host=$1
        port=$2
    fi
    rm -f ${mdir}/$host:$port,*
}

G_TT_DIAGNET="diagnet"
G_TT_NETDISCOVER="netdiscover"


g_ibdiagnet_tmp_path="/opt/bin/ibdiagnet"
g_out_file="/tmp/mft_discover.out"

g_ibdiag2_id="ibdiagnet2" 
g_new_ibdiag_id="new_ibdiagnet"
g_old_ibdiag_id="old_ibdiagnet"
g_ibnetdiscover="ibnetdiscover"
g_ibdiagnet_tool="ibdiagnet"
g_ibdiagnet2_lst_file="/var/tmp/ibdiagnet2/ibdiagnet2.lst"

g_tools_database=(\
    "${g_ibdiag2_id}    ${g_ibdiagnet_tmp_path} ${G_TT_DIAGNET}     i p 0 ${g_ibdiagnet2_lst_file}"
    "${g_new_ibdiag_id} ${g_ibdiagnet_tool}     ${G_TT_DIAGNET}     i p 0 ${g_ibdiagnet2_lst_file}"
    "${g_old_ibdiag_id} ${g_ibdiagnet_tool}     ${G_TT_DIAGNET}     i p 1 /tmp/ibdiagnet.lst"
    "${g_ibnetdiscover} ${g_ibnetdiscover}      ${G_TT_NETDISCOVER} C P 0 ${g_out_file}"
)

get_ib_tool_index() {
    ID=$1   
    element_count=${#g_tools_database[@]}
    index=0
    while [ "$index" -lt "$element_count" ]; do
        set -- ${g_tools_database[$index]}
        mem_id=$1
        if [ "${ID}" == "${mem_id}" ]; then
            return $index
        fi 
        ((index++))
    done
    echo "-E- Unknown discover tool \"${ID}\", to get the supported tool list run: 'mst help'"
    exit 1
}

function is_tool_existing() {
    cmd_exists=`which $1 2> /dev/null`
    if [ "$cmd_exists" == "" ]; then    
        echo "0"
    else
        echo "1"
    fi
}
function is_ibdiagnet_new() {
    ibdiagnet_tool=$1
    version="`$ibdiagnet_tool -V 2> /dev/null| head -1`"
    new_ver_regexp=\-I\-\ IBDIAGNET\ [0-9]\.[0-9]   
    if [[ "${version}" =~ ${new_ver_regexp} ]]; then
        echo "1"
    else 
        echo "0"
    fi
}

function get_ib_tools_info_index()
{
    tool_to_use=$1    
    if [ "${tool_to_use}" == "" ]; then
        if [ -f ${g_ibdiagnet_tmp_path} ] && [ `is_ibdiagnet_new ${g_ibdiagnet_tmp_path}` == "1" ]; then
            get_ib_tool_index ${g_ibdiag2_id}; return $?
        elif [ `is_ibdiagnet_new ${g_ibdiagnet_tool}` == "1" ]; then
            get_ib_tool_index ${g_new_ibdiag_id}; return $?
        elif [ `is_tool_existing ${g_ibnetdiscover}` == "1" ]; then
			get_ib_tool_index ${g_ibnetdiscover}; return $?
        elif [ `is_tool_existing ${g_ibdiagnet_tool}` == "1" ]; then
            get_ib_tool_index ${g_old_ibdiag_id}; return $?
        else 
            echo "-E- Failed to find a tool to discover the fabric (neither ${g_ibdiagnet_tool} nor ${g_ibnetdiscover} is installed on this machine)"
            exit 1
        fi
    else 
        if [ "${tool_to_use}" ==  "${g_ibdiagnet_tool}" ]; then
            if [ `is_ibdiagnet_new ${g_ibdiagnet_tool}` == "1" ]; then
                tool_to_use=${g_new_ibdiag_id}
            else 
                tool_to_use=${g_old_ibdiag_id}
            fi
        fi
        get_ib_tool_index ${tool_to_use}; return $?
    fi
 }

function get_index_for_old_diagnet() 
{
    hca_id=$1
    if [ "${hca_id}" == "" ]; then 
        return ${hca_id} 
    fi
    hca_idx=`ibv_devinfo | grep hca_id: | grep -n $hca_id | cut -f1 -d:`
    if [ "$hca_idx" == "" ]; then
        echo "-E- Failed to get hca index for hca \"$hca_id\""
        exit 1
    fi
    return ${hca_idx}
}

g_discover_tool_opt="--discover-tool"
g_topo_file_opt="--topo-file"
g_add_non_mlnx="--add-non-mlnx"

function check_arg() {
    if [ -z $2 ] || [[ "$2" == -* ]]; then
        echo "-E- Missing parameter after \"$1\" switch."
    exit 1
    fi
}

function ib_add()
{            
    tool_to_use=""
    topo_file=""
    added_nodes_type="only_mlnx"
    
    # Get the parameters
    while [ "$1" ]; do
        # Stop when we have a non flag paramater (an argument that doesn't start with -)
        if ! [[ "$1" =~ ^\- ]]; then 
            break;  
        fi
        case $1 in
            "${g_discover_tool_opt}")
                check_arg $1 $2
                tool_to_use=$2
                shift
                ;;
                
            "${g_add_non_mlnx}")
                added_nodes_type="all"
                shift
                ;;
                
            "${g_topo_file_opt}")
                check_arg $1 $2
                topo_file=$2
                shift
                ;;
            *)
            echo "-E- Bad switch \"$1\" for mst ib add, please run mst help for more details."
            exit 1
        esac         
        shift
    done        
    hca_id=$1
    ib_port=$2
    
    if [ "$topo_file" == "" ]; then
        get_ib_tools_info_index ${tool_to_use}; index=$?
        set -- ${g_tools_database[index]}
        g_tool_path=$2; g_tool_type=$3; ca_flag=$4; p_flag=$5; g_old_indexing=$6; topo_file=$7 ;
               
        cmd="$g_tool_path"
        hca_idx=${hca_id}
        
        if [ "${g_tool_type}" == "${G_TT_DIAGNET}" ]; then
            # Convert from hca name (mlx4_0,mthca0,...) to index (0,1..) using ibv_devinfo.        
            if [ ${g_old_indexing} == "1" ]; then
                get_index_for_old_diagnet ${hca_id}; hca_idx=$?
            fi                   
            cmd="$cmd -skip all"
        fi
 
        if [ "$hca_id" != "" ]; then
            cmd="$cmd -${ca_flag} $hca_idx"
            if [ "$ib_port" != "" ]; then
                cmd="$cmd -${p_flag} $ib_port"
            fi
        fi
        
        echo "-I- Discovering the fabric - Running: $cmd"
        rm -f ${topo_file}
        $cmd &> ${g_out_file}; RC=$?
        if [ "$RC" != "0" ]; then
            echo "-E- Command: \"$cmd\" failed (rc: $RC), for more details see: ${g_out_file}"
            exit 1
        fi
    else 
        if [ "${tool_to_use}" == "" ]; then 
            echo "-E- You should specify which tool you used to generate the given topofile by \"${g_discover_tool_opt} <tool>\""
            exit 1
        fi
        get_ib_tools_info_index ${tool_to_use}; index=$?
        set -- ${g_tools_database[index]}; g_tool_type=$3
    fi
 
    if [ ! -f ${topo_file} ]; then
        echo "-E- File ${topo_file} not found."
        return
    fi
    
    ibcnt=0
    mkdir -p $mdir
    for d in `${mbindir}/mst_ib_add.tcl ${topo_file} ${added_nodes_type} ${g_tool_type} $hca_id $ib_port`; do
        touch $mdir/$d
        ibcnt=$((  $ibcnt + 1 ))
    done
 
    echo "-I- Added $ibcnt in-band devices"
}

serv_start()
{
    local port=$1
    ${mbindir}/mtserver -p $port &
}

serv_stop()
{
    ps -efa | grep ${mbindir}/mtserver | grep -v grep | while read str
    do
        set -- $str
        kill $2
    done
}

# See how we were called.
case "$1" in
    help)
        cat <<END

        MST (Mellanox Software Tools) service
        =====================================

   This script is used to start MST service, to stop it,
   and for some other operations with Mellanox devices
   like reset or enabling remote access.

   The mst commands are:
   -----------------------

   mst start [--$WITH_MSIX] [--$WITH_UNKNOWN_ID]

       Create special files that represent Mellanox devices in
       directory ${mdir}. Load appropriate kernel modules and
       saves PCI configuration headers in directory ${pcidir}.
       After successfully completion of this command the MST driver
       is ready to work and you can invoke other Mellanox
       tools like Infiniburn or tdevmon.

       Options:
       --$WITH_MSIX : Create the msix device.
       --$WITH_UNKNOWN_ID : Do not check if the device ID is supported.

   mst stop

       Stop Mellanox MST driver service, remove all special files/directories
       and unload kernel modules.

   mst restart [--$WITH_MSIX] [--$WITH_UNKNOWN_ID]

       Just like "mst stop" followed by  "mst start [--$WITH_MSIX] [--$WITH_UNKNOWN_ID]"

   mst server start [port]
       Start MST server to allow incoming connection.
       Default port is 23108

   mst server stop
       Stop MST server.

   mst remote add <hostname>[:port]
       Establish connection with specified host on specified port
       (default port is 23108). Add devices on remote peer to local
       devices list. <hostname> may be host name as well as an IP address.

   mst remote del <hostname>[:port]
       Remove all remote devices on specified hostname. <hostname>[:port] should
       be specified exactly as in the "mst remote add" command.

   mst ib add [OPTIONS] [local_hca_id] [local_hca_port] 
       Add devices found in the IB fabric for inband access.
       Requires OFED installation and an active IB link.
           If local_hca_id and local_hca_port are given, the IB subnet connected
           to the given port is scanned. Otherwise, the default subnet is scanned.
       OPTIONS:
            ${g_discover_tool_opt} <discover-tool>: The tool that is used to discover the fabric.
                                             Supported tools: $g_ibnetdiscover, $g_ibdiagnet_tool. default: ${g_ibdiagnet_tool}
            ${g_add_non_mlnx} : Add non Mellanox nodes.
            ${g_topo_file_opt} <topology-file>: A prepared topology file which describes the fabric.
            NOTE: if a topology file is specified, device are taken from it.
                  Otherwise, a discover tool is run to discover the fabric.

   mst ib del
       Remove all inband devices.

   mst status

       Print current status of Mellanox devices

   mst save

       Save PCI configuration headers in directory ${pcidir}.
       Invoked automatically by "mst reset"

   mst load

        Load PCI configuration headers from directory ${pcidir}.
        Invoked automatically by "mst reset"

   mst reset <device_name>

       Reset device <device_name> and then do "mst load".
       For list of currently available Mellanox devices
       type "ls ${mdir}" or "mst status". Note that it
       may hang your device/computer when used inappropriately.

END
        ;;
    save)
        if [ `id -u` -ne 0 ]; then
            echo "You must be root to do that"
            exit 1
        fi
        save_pci
        ;;
    load)
        if [ `id -u` -ne 0 ]; then
            echo "You must be root to do that"
            exit 1
        fi
        load_pci
        ;;
    reset)
        # We are expecting for device name
        if [ $# -lt 2 ]; then
            echo "Please specify device name"
            devcnt=0
            echo
            echo "Available devices are:"
            echo "----------------------"
            echo
            for dev in ${mdir}/*
            do
                if [ -r "$dev" ]; then
                    if ! expr "$dev" : '.*\(vtop\|ddr\|uar\)' > /dev/null 2>&1
                    then
                        cat $dev | head -n 1
                        devcnt=$((  $devcnt + 1 ))
                    fi
                fi
            done
            if [ ${devcnt} -eq 0 ]; then
                echo "    Sorry, no available devices found."
            fi
            exit 1
        fi

        # Check that we are root
        if [ `id -u` -ne 0 ]; then
            echo "You must be root to do that"
            exit 1
        fi
        if save_pci 
        then
            if reset_pci $2
            then
                load_pci
            else
                echo "-E- Failed to reset the pci device $2"
                exit 1
            fi
        else 
            echo "-E- Failed to save the pci configuration headres before resetting $2."
        fi
        ;;
    server)
        # We are expecting subsommand here
        if [ $# -lt 2 ]; then
            echo "Please specify subcommand (start or stop)"
            exit 1
        fi
        case "$2" in
            start)
                cat /proc/devices | grep 'mst' > /dev/null
                if [ $? -ne 0 ]; then
                    mst start || exit 1
                fi

                if [ $# -lt 3 ]; then
                    serv_start 23108
                else
                    serv_start $3
                fi
            ;;
            stop)
                serv_stop
            ;;
            *)
                echo "Subcommand should be start or stop"
            ;;
        esac
        ;;
    remote)
        # We are expecting command and host name
        if [ $# -lt 3 ]; then
            echo "Please specify subcommand (add or del) and remote host name or its IP address"
            exit 1
        fi
        case "$2" in
            add)
                radd $3
            ;;
            del)
                rdel $3
            ;;
            *)
                echo "Subcommand should be add or del"
            ;;
        esac
        ;;
    ib)
        # We are expecting subsommand here
        if [ $# -lt 2 ]; then
            echo "Please specify subcommand (add or del)"
            exit 1
        fi
        case "$2" in
            add)
                shift 2
                ib_add $@
            ;;
            del)
                rm -f $mdir/SW_* $mdir/CA_*
            ;;
            *)
                echo "Subcommand should be add or del"
            ;;
        esac
        ;;
    start)
        if [ `id -u` -ne 0 ]; then
            echo "You must be root to do that"
            exit 1
        fi
        start $2 $3
        ;;
    stop)
        if [ `id -u` -ne 0 ]; then
            echo "You must be root to do that"
            exit 1
        fi
        stop
        rm -fr $pcidir
        ;;
    status)
        print_status
        ;;
    restart)
        if [ `id -u` -ne 0 ]; then
            echo "You must be root to do that"
            exit 1
        fi
        stop
        start $2 $3
        ;;
    *)
        echo "Usage:"
        echo "    $0 {start|stop|status|remote|server|restart|save|load|reset|help}"
        echo
        echo "Type \"$0 help\" for detailed help"
        RETVAL=1
esac
exit $RETVAL
