#!/bin/sh
# -*-Tcl-*- (for emacs) \
# This file invokes mlxburn tool \
tcl_lib_path=/usr/lib64/mft/tcl/lib:; \
tclsh=/usr/lib64/mft/tcl/bin/tclsh8.4; \
mbindir=/usr/bin; \
#@POST_MST_BIN_DIR@; \
# \
# The following lines should be updated the rpm post script to override the \
# tclsh and tcl_lib_path in case we install the MFT in a non default path. \
# PLEASE PAY ATTENTION TO UPDATE THE POST SCRIPT IF YOU CHANGE THE PARAMS tclsh AND tcl_lib_path \
#@POST_TCLSH@; \
#@POST_TCL_LIB_PATH@; \
# \
export LD_LIBRARY_PATH="${tcl_lib_path}$LD_LIBRARY_PATH"; \
export PATH="${mbindir}:$PATH"; \
exec $tclsh "$0" "$@"

set MFT_VERSION_STR "mft 4.1.0-28"
set TOOLS_GIT_SHA   "fc3edf0"
set BUILD_TIME      "Aug 19 2015, 17:06:49"
#MTL-HEADER##################################################################
#                 - Mellanox Confidential and Proprietary -
#
# Copyright (C) Jan. 2004, Mellanox Technologies Ltd.  ALL RIGHTS RESERVED.
#
# Except as specifically permitted herein, no portion of the information,
# including but not limited to object code and source code, may be reproduced,
# modified, distributed, republished or otherwise exploited in any form or by
# any means for any purpose without the prior written permission of Mellanox
# Technologies Ltd. Use of software subject to the terms and conditions
# detailed in the file "LICENSE.txt".
# End of legal section.
#############################################################################
# Id ..........$Id$
# Revision.....$Revision$
# Date.........$Date$
# Author.......$Author$
#EOH#########################################################################



################################################################################
################################################################################
##
## inifline TCL package (from sourceforge tcllib) is located 'inline'
## in this file to minimize external dependencies.
##
################################################################################
################################################################################



# ini.tcl --
#
#       Querying and modifying old-style windows configuration files (.ini)
#
# Copyright (c) 2003    Aaron Faupell <afaupell@users.sourceforge.net>
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# RCS: @(#) $Id$

# package provide inifile 1.0

namespace eval ini {
    set nexthandle 0
    set commentchar \;
}

proc ::ini::open {ini {mode r+}} {
    variable nexthandle

    if { ![regexp {^(w|r)\+?$} $mode] } {
        error "$mode is not a valid access mode"
    }

    ::set fh ini$nexthandle
    ::set tmp [::open $ini $mode]
    fconfigure $tmp -translation crlf

    namespace eval ::ini::$fh {
        array set data     {}
        array set comments {}
        array set sections {}
    }
    ::set ::ini::${fh}::channel $tmp
    ::set ::ini::${fh}::file    [_normalize $ini]
    ::set ::ini::${fh}::mode    $mode

    incr nexthandle
    if { [string match "r*" $mode] } {
        _loadfile $fh
    }
    return $fh
}

# close the file and delete all stored info about it
# this does not save any changes. see ::ini::commit

proc ::ini::close {fh} {
    _valid_ns $fh
    ::close [::set ::ini::${fh}::channel]
    namespace delete ::ini::$fh
}

# write all changes to disk

proc ::ini::commit {fh} {
    _valid_ns $fh
    namespace eval ::ini::$fh {
        if { $mode == "r" } {
            error "cannot write to read-only file"
        }
        set char $::ini::commentchar
        seek $channel 0 start
        foreach sec [array names sections] {
            if { [info exists comments($sec)] } {
                puts $channel "$char [join $comments($sec) "\n$char "]\n"
            }
            puts $channel "\[$sec\]"
            foreach key [lsort -dictionary [array names data [::ini::_globescape $sec]\000*]] {
                ::set key [lindex [split $key \000] 1]
                if {[info exists comments($sec\000$key)]} {
                    puts $channel "$char [join $comments($sec\000$key) "\n$char "]"
                }
                puts $channel "$key=$data($sec\000$key)"
            }
            puts $channel ""
        }
        catch { unset char sec key }
        close $channel
        set channel [::open $file r+]
        set mode r+
    }
    return
}

# internal command to read in a file
# see open and revert for public commands

proc ::ini::_loadfile {fh} {
    namespace eval ::ini::$fh {
        ::set cur {}
        ::set com {}
        set char $::ini::commentchar
        seek $channel 0 start

        foreach line [split [read $channel] "\n"] {
            if { [string match "$char*" $line] } {
                lappend com [string trim [string range $line [string length $char] end]]
            } elseif { [string match {\[*\]} $line] } {
                ::set cur [string range $line 1 end-1]
                if { $cur == "" } { continue }
                ::set sections($cur) 1
                if { $com != "" } {
                    ::set comments($cur) $com
                    ::set com {}
                }
            } elseif { [string match {*=*} $line] } {
                ::set line [split $line =]
                ::set key [string trim [lindex $line 0]]
                if { $key == "" || $cur == "" } { continue }
                ::set value [string trim [join [lrange $line 1 end] =]]
                if { [regexp "^(\".*\")\s+${char}(.*)$" $value -> 1 2] } {
                    set value $1
                    lappend com $2
                }
                ::set data($cur\000$key) $value
                if { $com != "" } {
                    ::set comments($cur\000$key) $com
                    ::set com {}
                }
            }
        }
        unset char cur com
        catch { unset line key value 1 2 }
    }
}

# internal command to escape glob special characters

proc ::ini::_globescape {string} {
    return [string map {* \\* ? \\? \\ \\\\ \[ \\\[ \] \\\]} $string]
}

# internal command to check if a section or key is nonexistant

proc ::ini::_exists {fh sec args} {
    if { ![info exists ::ini::${fh}::sections($sec)] } {
        error "no such section \"$sec\""
    }
    if { [llength $args] > 0 } {
        ::set key [lindex $args 0]
        if { ![info exists ::ini::${fh}::data($sec\000$key)] } {
            error "can't read key \"$key\""
        }
    }
}

# internal command to check validity of a handle

if { [package vcompare [package provide Tcl] 8.4] < 0 } {
    proc ::ini::_normalize {path} {
        return $path
    }
    proc ::ini::_valid_ns {name} {
        variable ::ini::${name}::data
        if { ![info exists data] } {
            error "$name is not an open INI file"
        }
    }
} else {
    proc ::ini::_normalize {path} {
        fix_file_path $path
    }
    proc ::ini::_valid_ns {name} {
        if { ![namespace exists ::ini::$name] } {
            error "$name is not an open INI file"
        }
    }
}

# return all section names

proc ::ini::sections {fh} {
    _valid_ns $fh
    return [array names ::ini::${fh}::sections]
}

# return boolean indicating existance of section or key in section

proc ::ini::exists {fh sec {key {}}} {
    _valid_ns $fh
    if { $key == "" } {
        return [info exists ::ini::${fh}::sections($sec)]
    }
    return [info exists ::ini::${fh}::data($sec\000$key)]
}

# return all key names of section
# error if section is nonexistant

proc ::ini::keys {fh sec} {
    _valid_ns $fh
    _exists $fh $sec
    ::set keys {}
    foreach x [array names ::ini::${fh}::data [_globescape $sec]\000*] {
        lappend keys [lindex [split $x \000] 1]
    }
    return $keys
}

# return all key value pairs of section
# error if section is nonexistant

proc ::ini::get {fh sec} {
    _valid_ns $fh
    _exists $fh $sec
    upvar 0 ::ini::${fh}::data data
    ::set r {}
    foreach x [array names data [_globescape $sec]\000*] {
        lappend r [lindex [split $x \000] 1] $data($x)
    }
    return $r
}

# return the value of a key
# error if key or section is nonexistant

proc ::ini::value {fh sec key} {
    _valid_ns $fh
    _exists $fh $sec $key
    return [::set ::ini::${fh}::data($sec\000$key)]
}

# set the value of a key
# new section or key names are created

proc ::ini::set {fh sec key value} {
    _valid_ns $fh
    ::set sec [string trim $sec]
    ::set key [string trim $key]
    if { $sec == "" || $key == "" } {
        error "section or key may not be empty"
    }
    ::set ::ini::${fh}::data($sec\000$key) $value
    ::set ::ini::${fh}::sections($sec) 1
    return $value
}

# delete a key or an entire section
# may delete nonexistant keys and sections

proc ::ini::delete {fh sec {key {}}} {
    _valid_ns $fh
    if { $key == "" } {
        array unset ::ini::${fh}::data [_globescape $sec]\000*
        array unset ::ini::${fh}::sections [_globescape $sec]
    }
    catch {unset ::ini::${fh}::data($sec\000$key)}
}

# read and set comments for sections and keys
# may comment nonexistant sections and keys

proc ::ini::comment {fh sec {key {}} args} {
    _valid_ns $fh
    upvar 0 ::ini::${fh}::comments comments
    if { $key == "" && [llength $args] == 0 } {
        if { $key == "" } {
            if { ![info exists comments($sec)] } { return {} }
            return $comments($sec)
        }
        if { ![info exists comments($sec\000$key)] } { return {} }
        return $comments($sec\000$key)
    }
    if { $key == "" } {
        eval [linsert $args 0 lappend comments($sec)]
    } else {
        eval [linsert $args 0 lappend comments($sec\000$key)]
    }
}

# return the physical filename for the handle

proc ::ini::filename {fh} {
    _valid_ns $fh
    return [::set ::ini::${fh}::file]
}

# reload the file from disk losing all changes since the last commit

proc ::ini::revert {fh} {
    _valid_ns $fh
    namespace eval ::ini::$fh {
        array set data     {}
        array set comments {}
        array set sections {}
    }
    if { ![string match "w*" $mode] } {
        _loadfile $fh
    }
}


################################################################################
################################################################################
##
## End of inifile package.
##
################################################################################
################################################################################


################################################################################
################################################################################
##
## g_puts local implementation
##
################################################################################
################################################################################


set G_PUTS_DEBUG   0
set G_PUTS_WARNING 0
set G_PUTS_INFORM  1

proc g_puts {str} {
   global G_PUTS_DEBUG
   global G_PUTS_WARNING
   global G_PUTS_INFORM

   set dbgLevel [string range $str 0 2]
   set allowPrint 0

   switch -- $dbgLevel {
      "-D-" { set allowPrint $G_PUTS_DEBUG   }
      "-W-" { set allowPrint $G_PUTS_WARNING }
      "-I-" { set allowPrint $G_PUTS_INFORM  }

      default { set allowPrint 1}

   }

   if {$allowPrint} {
      puts $str
   }
}

################################################################################
################################################################################
##
## End of g_puts package.
##
################################################################################
################################################################################



################################################################################
################################################################################
##
## getopt local implementation
##
################################################################################
################################################################################


set optind 0

proc getopt { argslist optstring optret argret } {
   global optind optindc
   upvar $optret retvar
   upvar $argret optarg

   # default settings for a normal return
   set optarg ""
   set retvar ""
   set retval 0

   # check if we are in a single char mode or support long
   # option string
   if {[string match "* *" $optstring]} {
      set longOptMode 1
   } else {
      set longOptMode 0
   }

   # check if we're past the end of the args list
   if { $optind < [ llength $argslist ] } then {

      # if we got -- or an option that doesn't begin with -, return (skipping
      # the --).  otherwise process the option arg.
      switch -glob -- [ set arg [ lindex $argslist $optind ]] {
         "--" {
            incr optind
         }

         "-" {
            set optarg "Illegal option: '-'"
            set retvar $optarg
            set retval -1
         }

         "-*" {
            # opt needs to return the name of the option
            regexp -- {-([^:]+):?} $arg d1 opt
            incr optind

            if {$longOptMode} {
               # options are defined as words optionaly containing ":"
               if { [ lsearch -regexp $optstring "^$opt:?\$" ] >= 0 } then {
                  set retvar $opt
                  set retval 1
                  if { [ lsearch -regexp $optstring "^$opt:\$" ] >= 0 } then {
                     if { $optind < [ llength $argslist ] } then {
                        set optarg [lindex $argslist $optind]
                        incr optind
                     } else {
                        set optarg "Option requires an argument -- $opt"
                        set retvar $optarg
                        set retval -1
                     }
                  }
               } else {
                  set optarg "Illegal option -- $opt"
                  set retvar $optarg
                  set retval -1
               }
            } else {
               # traditional single char options
               if { [ string match "*$opt*" $optstring ] } then {
                  set retvar $opt
                  set retval 1
                  if { [ string match "*$opt:*" $optstring ] } then {
                     if { $optind < [ llength $argslist ] } then {
                        set optarg [lindex $argslist $optind]
                        incr optind
                     } else {
                        set optarg "Option requires an argument -- $opt"
                        set retvar $optarg
                        set retval -1
                     }
                  }
               } else {
                  set optarg "Illegal option -- $opt"
                  set retvar $optarg
                  set retval -1
               }
            }
         }
      }
   }

   return $retval
}


################################################################################
################################################################################
##
## End of getopt package.
##
################################################################################
################################################################################

# Get an array key in teh form of Major:Minor format and return the major key
proc get_major_key {major_minor {separate "@"}} {
    return [lindex [split $major_minor $separate] 0]
}
#######################################
##
## VPD Access Functions:
##
#######################################

array set VPD_I2C_ACCESS {
   vpd_size              0x100
   vpd_offset            0x0
   log2_vpd_eeprom_size  15
   vpd_num_of_eeproms    1
   vpd_address           0x56
   vpd_i2c_16bit         true
   vpd_endian            little
}

proc load_i2c_setup { ini_file} {
   global VPD_I2C_ACCESS

   set sect "ADAPTER"

   set fh [::ini::open $ini_file r]

   foreach key [array names VPD_I2C_ACCESS] {
      if {[::ini::exists $fh $sect $key]} {
         g_puts "-D- i2c setup: Set Key $sect:$key from \"$VPD_I2C_ACCESS($key)\" to \"[::ini::value $fh $sect $key]\""
         set VPD_I2C_ACCESS($key) [::ini::value $fh $sect $key]
      }
   }

   ::ini::close $fh
}


proc vpd_i2c_setup {dev setup_file} {
   global VPD_I2C_ACCESS

   # check if the i2c executable is reachable
   # This is done by running with no parameters and checking the expected help string
   set e ""
   catch {set res [exec i2c]} e

   if {![regexp {All parameters are interpreted as hexa values} $e]} {
      error "i2c command not found. Cant access VPD via I2C"
   }


   # Load conf file (the FW's ini file)
   if {[llength $setup_file]} {
      foreach f $setup_file {
         load_i2c_setup $f
      }
   } else {
      # try to auto detect settings:

      # address width:
      # The heuristic is as follows:
      # First byte in vpd is 0x82. Second (the msb fo the ro data size) is
      # usually 0.
      # When reading from iaw of 1 eeprom using iaw of 2, the first byte is
      # duplicated. So I assume that if first 2 bytes are the same,
      # the ia2=2 setting is wrong.
      # Note: This is a heuristic. It can be overriden with the -vpd_conf
      #       true settings.
      # Try to read the first DW with i2c addres width (iaw) set to 2
      set w [vpd_read_bytes_i2c 0 4]
      if {[lindex $w 0] ==  [lindex $w 1]} {
         g_puts "-D- 2 first bytes of VPD are equal. Probably bad addr width -  trying to use i2c addr size = 1"
         set VPD_I2C_ACCESS(vpd_i2c_16bit) 0
      }
   }
}

array set VPD_PCI_ACCESS {
   slot     0
   address  0
   data     0
}

set lspci  /sbin/lspci
set setpci /sbin/setpci
set mellanox_vendor_id 15b3

proc lrun { cmd } {
   global errorInfo errorCode

   g_puts "-D- Running: $cmd"
   if {[catch {set res [eval "exec $cmd"]} e]} {
      set errc [lindex $errorCode end] ; set erri $errorInfo

      # g_puts "-D- errInfo=\"$errorInfo\" errCode=\"$errorCode\""
      if {$errc != "NONE" && $errc != 0 } {
          error "Failed running $cmd (returned $errc): $e"
      }
      set res $e
   }
   g_puts "-D- res=$res"
   return $res
}

proc vpd_pci_setup_windows {dev} {
    global setpci
    global VPD_PCI_ACCESS

    set setpci "win_mini_setpci.exe"

    if {![regexp {pciconf} $dev]} {
        error "VPD access is supported only by \"pciconfX\" devices"
    }

    set VPD_PCI_ACCESS(slot)     $dev
    set vpd_cap 48

    set VPD_PCI_ACCESS(address)  [format "%x" [expr "0x$vpd_cap" + 2]]
    set VPD_PCI_ACCESS(data)     [format "%x" [expr "0x$vpd_cap" + 4]]

    g_puts "-W- vpd_pci_setup_windows: using hard coded VPD CAP values"
}

proc vpd_pci_setup {dev_id dev_ix devid_is_slot} {
   global lspci setpci mellanox_vendor_id
   global VPD_PCI_ACCESS
   global tcl_platform


   # For linux, user = root check is done in the bash wrapper
   set default_path "/sbin"

   foreach f {lspci setpci} {
      if {[catch {set exe_path [exec which $f]} e]} {
         g_puts "-D- which $f: $e . Assuming $f path is $default_path"
         # be carefull - tricky stuff bellow note the "set $f" instead of "set f"
         set $f         "$default_path/$f"
         set exe_path   "$default_path/$f"
      } else {
         g_puts "-D- Path of $f: $exe_path"
         set $f $exe_path
      }

      if {![file executable $exe_path]} {
         error "Can't access VPD via PCI - Failed to find \"$f\" tool. Make sure it is in the path."
      }
   }


   if {$devid_is_slot} {
      set slot $dev_id
   } else {
      set dev_id_hex [format "%x" $dev_id]
      set cmd "$lspci -d $mellanox_vendor_id:$dev_id_hex"
      set res [lrun $cmd]

      set res [split $res "\n"]
      g_puts "-D- Found [llength $res] devices of type $dev_id on PCI"

      if {[llength $res] <= $dev_ix} {
         error "Device id $dev_id, index $dev_ix not found on PCI."
      }

      set slot [lindex [split [lindex $res $dev_ix] " "] 0]
      g_puts "-D- Using device [lindex $res $dev_ix] to access VPD (slot $slot)"
   }

   # Remove domain from slot if domain == 0.
   # On Suse9.3, lspci displays domain 0, but setpci does not support domains ???
   # regsub {^0000:} $slot {} slot


   set cmd "$lspci -v -s $slot"
   set res [lrun $cmd]

   if {![regexp {Capabilities:\s*\[(\S+)\]\s*Vital Product Data} $res d1 vpd_cap]} {
      error "The given device does not support VPD capability (or not ran as root)"
   }

   g_puts "-D- VPD Capabilities add offset $vpd_cap"

   set VPD_PCI_ACCESS(slot)     $slot
   set VPD_PCI_ACCESS(address)  [format "%x" [expr "0x$vpd_cap" + 2]]
   set VPD_PCI_ACCESS(data)     [format "%x" [expr "0x$vpd_cap" + 4]]
}

proc vpd_mst_setup { dev } {
    set cmd "mst status -vv"
    regsub {^0000:} $dev {} short_dev
    if {![g_exec $cmd status res]} {
        g_puts "-E- Failed to get mst devices info by the command $cmd"
        cexit 1
    }
    set pci_dev ""

    set start_index [expr [lsearch -regexp $res "PCI devices"] + 3]
    set next_list_index [lsearch -regexp -start $start_index $res "--------"]
    if { $next_list_index == -1} {
        set last_index [llength $res]
    } else {
        set last_index [expr $next_list_index - 2]
    }

    for {set i $start_index} { $i < $last_index} {incr i} {
        set current_info [regexp -inline -all -- {\S+} [lindex $res $i]]
        set current_info [string map {"," " "} $current_info]
        if {[lsearch -start 1 $current_info $dev] != -1} {
            set pci_dev [lindex $current_info 2]
            break
        }
        if {[lsearch -start 1 $current_info 0000:$dev] != -1} {
            set pci_dev [lindex $current_info 2]
            break
        }
        if {[lsearch -start 1 $current_info $short_dev] != -1} {
            set pci_dev [lindex $current_info 2]
            break
        }
    }
    if { $pci_dev != ""} {
        vpd_pci_setup $pci_dev 0 1
    } else {
        error "Cant access VPD through PCI using device \"$dev\", Unknown device name."
    }

}

proc vpd_access_setup { dev access_through setup_file } {
   global vpd_read_proc
   global vpd_write_proc
   global use_mstflint
   global tcl_platform

   switch $access_through {
      PCI {
        if {$use_mstflint} {
            vpd_pci_setup $dev 0 1
        } elseif {$tcl_platform(platform) == "windows" && [regexp {mt(\d+)_pci(conf|_cr)(\d)} $dev d1 devid pci_type dev_ix]} {
            if {[regexp {[:#]} $dev]} {
                error "VPD operations are not supported on remote devices"
            }
            vpd_pci_setup_windows $dev
        } else {
            if { $tcl_platform(platform) != "windows" } {
                vpd_mst_setup $dev
            } else {
                error "Cant access VPD through PCI using device \"$dev\". Bad device name format."
            }
        }
        set vpd_read_proc  vpd_read_bytes_pci
        set vpd_write_proc vpd_write_bytes_pci
        }

      I2C {
         vpd_i2c_setup $dev $setup_file
         set vpd_read_proc  vpd_read_bytes_i2c
         set vpd_write_proc vpd_access_i2c
      }

      default {
         error "Bad VPD access method ($access_through). Allowed: PCI, I2C"
      }
   }
}

proc vpd_pci_wait_ready {val op} {
   global setpci
   global VPD_PCI_ACCESS

   set    max_retries 200
   set    retries     0
   set    ready       [expr !$val]

   while {$retries < $max_retries && $ready != $val} {
      if {$retries > 0} {
         # Polling too fast causes the op to never be ready. Probably a FW bug. Sleeping 2ms between polls is a good WA for now.
         after 2
      }

      set cmd "$setpci -s $VPD_PCI_ACCESS(slot) $VPD_PCI_ACCESS(address).W"
      set res [lrun $cmd]

      regexp -line {^([a-fA-F0-9]+)$} $res d1 res
      set ready [expr "0x$res" >> 15]

      incr retries
   }

   if {$ready != $val} {
       error "VPD $op not finished after $max_retries retries"
   } elseif {$retries > 1} {
       g_puts "-D- vpd $op ready after $retries retries"
   }
}

proc vpd_write_bytes_pci {offset data {block 4}} {
   global setpci
   global VPD_PCI_ACCESS

   if {$block != 4} {
      error "Internal error: vpd_write_bytes_pci: got block of $block. Only 4 is allowed."
   }

   if {$offset % $block} {
      error "Internal error: vpd_write_bytes_pci: offset ($offset) is not $block bytes aligned"
   }

   g_puts "-D- vpd_write_bytes_pci: $offset , $data"

   if {($block) != [llength $data]} {
      error "Internal error: vpd_write_bytes_pci: Write data length ([llength $data]) does not match block size ($block)"
   }

   # set data [regsub -all {0x}  $data {}]
   # set data [regsub -all {\s+} $data {}]

   # Write data:
   set data_word 0
   for {set i 0} { $i < 4} {incr i} {
      #orenk
      if {[lindex $data $i] > 0xff} {
          error "Internal error: vpd_write_bytes_pci: data byte at offset $offset + $i == [lindex $data $i]"
      }
      set data_word [expr $data_word | ([lindex $data $i] << (8 * $i))]
   }
   set data_word [format "0x%x" $data_word]
   set cmd "$setpci -s $VPD_PCI_ACCESS(slot) $VPD_PCI_ACCESS(data).L=$data_word"
   set res [lrun $cmd]

   # Write command and address:
   # set vlag bit in VPD reg (for write)
   set offset [format "0x%x" [expr $offset | 0x8000]]

   set cmd "$setpci -s $VPD_PCI_ACCESS(slot) $VPD_PCI_ACCESS(address).W=$offset"
   set res [lrun $cmd]

   # Wait for write complete
   vpd_pci_wait_ready 0 "write"
}


# return a list of $block bytes
# error on error.
proc vpd_read_bytes_pci { offset {block 1} } {
   global setpci
   global VPD_PCI_ACCESS

   if {$block != 1 && $block != 4} {
      error "vpd_read_bytes_pci: got block of $block. Only 1 and 4 are allowed."
   }

   set cmd "$setpci -s $VPD_PCI_ACCESS(slot) $VPD_PCI_ACCESS(address).W=$offset"
   set res [lrun $cmd]

   # TODO: May check flag bit is set to 1 in control word
   vpd_pci_wait_ready 1 "read"

   set cmd "$setpci -s $VPD_PCI_ACCESS(slot) $VPD_PCI_ACCESS(data).L"
   set res [lrun $cmd]

   # fliter error meessages if any
   regexp -line {^([a-fA-F0-9]+)$} $res d1 res

   set ret_list ""
   for {set i 0} { $i < $block } { incr i} {
      lappend ret_list [format "%02x" [expr ("0x$res" >> ($i * 8)) & 0xff]]
   }


   g_puts "-D- VPD $offset=$res=$ret_list"

   return $ret_list
}

#
# vpd_access_XXX : Read / Write access to the VPD
# offset: byte offset in the VPD space.
# data:   For write op: list of bytes
# block:  Num of bytes requested to be read/written in current access.
#         Must match the data size for writes.
#

proc vpd_read_bytes_i2c { offset {block 1}} {
   set data {}
   return [vpd_access_i2c $offset $data $block]
}

proc vpd_access_i2c { offset data {block 1} } {
   global mst_dev
   global VPD_I2C_ACCESS

   if {[llength $data] == 0} {
      # read op
      set op r
   } else {
      set op w
      set data [regsub -all {0x}  $data {}]
      set data [regsub -all {\s+} $data {}]

      if {($block * 2) != [string length $data]} {
         error "Internal error: vpd_access_i2c ($op) : Write data length ([string length $data]) does not match block size ($block)"
      }
   }

   set address_width 1
   if {$VPD_I2C_ACCESS(vpd_i2c_16bit) == "true" || $VPD_I2C_ACCESS(vpd_i2c_16bit) == "1"  } {
      set address_width 2
   }

   # DEBUG
   # set address_width 1

   set slv_address $VPD_I2C_ACCESS(vpd_address)
   set offset      [format "0x%x" [expr $offset + $VPD_I2C_ACCESS(vpd_offset)]]

   set cmd "i2c -a $address_width -x $block $mst_dev $op $slv_address $offset $data"
   g_puts "-D- Running: $cmd"
   if {[catch {set res [eval "exec $cmd"]} e]} {
      error "Failed running $cmd: $e"
   }

   set ret_list ""

   if {$op == "r"} {
      for {set i 0} { $i < $block } { incr i} {
         lappend ret_list [string range $res [expr 2 * $i] [expr 2 * $i + 1]  ]
      }

      g_puts "-D- VPD $offset=$res=$ret_list"
   }

   return $ret_list
}


proc calc_checksum { hex_data } {

   set sum 0
   foreach ival $hex_data {
      set sum [expr $sum + $ival]
   }
   set sum [expr $sum % 0x100]
   if {$sum == 0} {
      return 0x00
   } else {
      #         return [format "0x%02x" [expr 0x100 - $sum]]
      return $sum
   }
}

proc vpd_check_field_name {fieldName} {
    return [regexp {^[A-Za-z1-9_][A-Za-z1-9_]$} $fieldName]
}

proc vpd_check_field_bin_data {fieldData} {
    return [expr ([string length $fieldData] % 2 == 0) && \
                  [regexp {^[0-9a-fA-F]*$} $fieldData]]
}

proc write_vpd_data {start_offset data } {
   global vpd_write_proc
   set block_size     4

   set pad_pre {}
   set pad_suf {}

   # In PCI VPD is written in DWORDS (4 bytes) chunks
   # if the data is not 4 bytes aligned - read-modify-write the
   # first and last words.

   set pre_pad_size [expr $start_offset % $block_size]
   set pre_pad_offs [expr $start_offset - $pre_pad_size]

   set suf_pad_offs [expr $start_offset + [llength $data]]
   set suf_pad_size [expr ((($suf_pad_offs + $block_size - 1) / $block_size ) \
                           * $block_size) - $suf_pad_offs]

   g_puts "-D- write_vpd_data: offs=$start_offset size=[llength $data] pre_pad=$pre_pad_size suf_pad=$suf_pad_size"

   g_puts "-D- pre  padded data (len = [llength $data]): $data"
   if {$pre_pad_size} {
      set pad  [read_vpd_data $pre_pad_offs $pre_pad_size]
      set data [concat $pad $data]
   }

   if {$suf_pad_size} {
      set pad  [read_vpd_data $suf_pad_offs $suf_pad_size]
      set data [concat $data $pad]
   }

   g_puts "-D- post padded data (len = [llength $data]): $data"

   # Now write the padded data:
   set dlen [llength $data]
   for {set i 0} {$i < $dlen} {incr i $block_size} {
      set cur_data [lrange $data $i [expr $i + $block_size - 1]]
      set offs [expr $pre_pad_offs + $i]
      g_puts "-D- Calling: $vpd_write_proc {$offs} {$cur_data} {$block_size} "
      $vpd_write_proc $offs $cur_data $block_size
   }
}


proc read_vpd_data { start_offset size } {

   global vpd_read_proc

   set block_size     4

   set start_offset_aligned [expr $start_offset - ($start_offset % $block_size)]
   set size_aligned [expr $size + ($start_offset % $block_size)]

   g_puts "-D- read_vpd_data $start_offset $size : start_offset_aligned = $start_offset_aligned"

   # read byte by byte into a list
   set vpd_read_hex ""
   for {set i 0} {$i < $size_aligned} {incr i $block_size} {
      set offset [format "0x%x" [expr $start_offset_aligned + $i]]
      set ibyte [$vpd_read_proc $offset $block_size]
      if {$ibyte == -1}  {
         exit 1
      }

      foreach b $ibyte {
         append vpd_read_hex " 0x$b"
      }
   }

   if {($start_offset % $block_size) || (($start_offset + $size) % $block_size) } {
      # More data than the user asked - Need to trim

      set start_idx [expr $start_offset - $start_offset_aligned]
      set end_idx   [expr $start_idx + $size - 1]

      g_puts "-D- vpd trim 0 - [llength $vpd_read_hex] to $start_idx - $end_idx"

      set vpd_read_hex [lrange $vpd_read_hex $start_idx $end_idx]
   }

   g_puts "-D- read_vpd_data $start_offset $size : $vpd_read_hex"
   return $vpd_read_hex
}


proc read_vpd_data_YYY { start_offset size } {
   global vpd_read_proc

   # read byte by byte into a list
   set vpd_read_hex ""
   for {set i 0} {$i < $size} {incr i} {
      set offset [format "0x%x" [expr $start_offset + $i]]
      set ibyte [$vpd_read_proc $offset]
      if {$ibyte == -1}  {
         exit 1
      }
      set hbyte [format "0x%s" $ibyte]
      append vpd_read_hex " $hbyte"
   }

   g_puts "-D- read_vpd_data $start_offset $size : $vpd_read_hex"


   return $vpd_read_hex
}

proc hex_to_char { hex_list } {

   set vpd_string ""
   for {set i 0} {$i < [llength $hex_list]} {incr i} {
      set char [format "%c" [lindex $hex_list $i]]
      append vpd_string $char
   }
   return $vpd_string
}

proc hex_to_bin { hex_list } {
   set vpd_string ""
   for {set i 0} {$i < [llength $hex_list]} {incr i} {
      set char [format "%02x" [lindex $hex_list $i]]
      append vpd_string $char
   }
   return $vpd_string
}

#
# Read the full VPD tag and stor data as a list of hex bytes in
# vpd_data.
# Return length of tag (including header) on success.
#
proc vpd_read_tag {offset tag_id tag_name {max_len 0x100} } {
   global vpd_data

   set header [read_vpd_data  $offset  3]
   set id     [lindex $header 0]

   if {$id != $tag_id} {
      error "Failed to find tag \"$tag_name\" at vpd offset $offset (Expected tag id $tag_id , got $id)."
   }

   if {$id & 0x80} {
      set len [format "0x%x" [expr [lindex $header 1] + ([lindex $header 2] * 256)]]
   } else {
      # small resource
      set len "0x[expr $id & 0x7]"
      if {$id == 0x78} {
         set header $id
      } else {
         error "VPD: Unknown small resource header type ($id) at offset $offset"
      }
   }

   if {$len > $max_len} {
      error "VPD: $tag_name too long ($len bytes). Probably VPD access error or a corrupted VPD memory."
   }

   append vpd_data " $header [read_vpd_data [expr $offset + 3] $len]"

   return [format "0x%x" [expr $len + [llength $header]]]
}

proc toAscii { char } {
   scan $char %c value
   return $value
}

proc toBin { byte_str } {
   return [expr "0x$byte_str"]
}

proc read_vpd_to_vars {{tag_only 0}} {
   global VPD_R_TAGS VPD_RW_TAGS VPD_MISC_INFO
   global vpd_check_rw

   global vpd_data

   set tag_len [vpd_read_tag 0 0x82 "ID-TAG"]

   if {! $tag_len} {
      error "Bad VPD format - Can't find ID-TAG"
   }

   set VPD_R_TAGS(IDTAG:Offs)  0
   set VPD_R_TAGS(IDTAG:Id)    [lindex $vpd_data 0 ]
   set VPD_R_TAGS(IDTAG:Len)   [expr $tag_len - 3]
   set VPD_R_TAGS(IDTAG:Data)  [hex_to_char [lrange $vpd_data 3 [expr 2 + $VPD_R_TAGS(IDTAG:Len)]]]


   # VPD-RTAG

   set VPDRbase $tag_len
   set tag_len [vpd_read_tag $VPDRbase 0x90 "VPD-RTAG"]

   if {! $tag_len} {
      error "Bad VPD format - Can't find VPD-RTAG section"
   }
   set VPD_R_TAGS(VPDRTAG:Offs) [expr $VPDRbase]
   set VPD_R_TAGS(VPDRTAG:Id)   [lindex $vpd_data       $VPDRbase]

   set tag_len_hi     [lindex $vpd_data [expr $VPDRbase + 2]]
   set tag_len_lo     [lindex $vpd_data [expr $VPDRbase + 1]]

   set VPD_R_TAGS(VPDRTAG:Len) [format "0x%04x" [expr $tag_len_hi * 256 + $tag_len_lo]]
   set VPD_MISC_INFO(VPDRTAG:Status) "OK"

   set vpd_r_end      [expr $VPD_R_TAGS(VPDRTAG:Len) + $VPD_R_TAGS(VPDRTAG:Offs) + 2]

   # loop that read the first two characters
   # and enter the values to the relevent variables
   # (${first2char}:Id ,${first2char}:Len,${first2char}:Data)
   # the loop stop when it find "RV"

   set base [expr $VPDRbase+3]
   set fieldName [hex_to_char [lrange $vpd_data $base [expr $base + 1]]]
   set TagNames ""
   while { ![regexp "^RV$" $fieldName] } {
      set VPD_R_TAGS($fieldName:Offs) $base
      set VPD_R_TAGS($fieldName:Id)   $fieldName
      set VPD_R_TAGS($fieldName:Len)  [lindex $vpd_data [expr $base+2]]
      set VPD_R_TAGS($fieldName:Data) [hex_to_char [lrange $vpd_data [expr $base+3] [expr $base+3+$VPD_R_TAGS(${fieldName}:Len)-1]]]

      set base [expr $base+$VPD_R_TAGS($fieldName:Len)+3]
      if { $base > $vpd_r_end } {
         error "Bad VPD format - Can't find RV keyword"
      }
      set fieldName [hex_to_char [lrange $vpd_data $base [expr $base + 1]]]

   }

   set VPD_R_TAGS(RV:Id)    $fieldName
   set VPD_R_TAGS(RV:Len)  [lindex $vpd_data [expr $base+2]]
   set VPD_R_TAGS(RV:Data) [lrange $vpd_data [expr $base+3] [expr $base + 3 + $VPD_R_TAGS(RV:Len) - 1]]

   set vpd_hex_data [lrange $vpd_data 0 [expr $base+2]]
   set VPD_MISC_INFO(CS)   [expr [expr [calc_checksum $vpd_hex_data]+$VPD_R_TAGS(RV:Data)]%256]

   if {$VPD_MISC_INFO(CS)} {
      puts "Warning: VPD checksum is not zero ($VPD_MISC_INFO(CS)) . VPD is corrupted."
   }

   if {!$vpd_check_rw} {
      return
   }

   # RW section
   set base [expr $base+4]

   set tag_len [vpd_read_tag $base 0x91 "VPD-WTAG" 4096]
   if {! $tag_len } {
      error "VPD: Can't find VPD-WTAG"
   }

   vpd_read_tag [expr $base + $tag_len] 0x78 "VPD-END"

   set VPD_RW_TAGS(VPDWTAG:Offs) $base
   set VPD_RW_TAGS(VPDWTAG:Id)   [lindex $vpd_data $base]
   set VPD_RW_TAGS(VPDWTAG:Len)  [format "0x%04x" [expr $tag_len - 3]]

   set VPD_MISC_INFO(VPDWTAG:Status) "OK"

   set vpd_rw_end [expr $VPD_RW_TAGS(VPDWTAG:Offs) + $tag_len]

   if {$tag_only} {
      return
   }

   set base [expr $base+3]
   set fieldName [hex_to_char [lrange $vpd_data $base [expr $base + 1]]]
   while { ![regexp "^RW$" $fieldName] } {
      g_puts "-D- Checking keyword \"$fieldName\". Offs=$base"
      set VPD_RW_TAGS($fieldName:Offs) $base
      set VPD_RW_TAGS($fieldName:Id)   $fieldName
      set VPD_RW_TAGS($fieldName:Len)  [lindex $vpd_data [expr $base+2]]
      set VPD_RW_TAGS($fieldName:Data) [hex_to_char [lrange $vpd_data [expr $base+3] [expr $base + 2 + $VPD_RW_TAGS($fieldName:Len)]]]
      set VPD_RW_TAGS($fieldName:Bin)  [hex_to_bin  [lrange $vpd_data [expr $base+3] [expr $base + 2 + $VPD_RW_TAGS($fieldName:Len)]]]

      set base [expr $base+$VPD_RW_TAGS(${fieldName}:Len)+3]
      if { $base > $vpd_rw_end } {
         error "Failed to find RW keyword in VPD-W. Probably VPD access error or a corrupted VPD memory."
      }
      set fieldName [hex_to_char [lrange $vpd_data $base [expr $base + 1]]]
      if {![vpd_check_field_name $fieldName]} {
         error "Bad keyword name \"$fieldName\" found in VPD-W tag, offset $base.  Probably VPD access error or a corrupted VPD memory."
      }
   }

   set VPD_RW_TAGS(RW:Id)  $fieldName
   set VPD_RW_TAGS(RW:Len) [lindex $vpd_data [expr $base+2]]

   if { [lindex $vpd_data [expr $base+3+$VPD_RW_TAGS(RW:Len)]] == 0x78 } {
      set VPD_MISC_INFO(RW:Status) "OK"
      set VPD_MISC_INFO(ENDTAG:Id) [lindex $vpd_data [expr $base+3+$VPD_RW_TAGS(RW:Len)]]
   } else {
      set VPD_MISC_INFO(RW:Status) "FAIL"
      set VPD_MISC_INFO(ENDTAG:Id) [lindex $vpd_data 255]
   }

    #puts "-D- debug"
    #local_parray VPD_RW_TAGS
    #exit

}

proc prepare_vpd_tags {arrName flds} {
   upvar 1 $arrName arr
   if {![array exists arr]} {
      error "\"$arrName\" isn't an array"
   }

   set rw_bytes {}

   foreach f $flds {
    set fdat $arr($f:Data)

      for {set i 0} {$i < 2} {incr i} {
         lappend rw_bytes [toAscii [string range $f $i $i]]
      }

      if {$arr($f:IsBin)} {
        # Binary field
        set flen [expr [string length $fdat] / 2]
        lappend rw_bytes $flen
        for {set i 0} {$i < $flen * 2} {incr i 2} {
            lappend rw_bytes [toBin [string range $fdat $i [expr $i + 1]]]
         }
      } else {
	 # ASCII field
	 set flen [string length $fdat]
	 lappend rw_bytes $flen
         for {set i 0} {$i < $flen} {incr i} {
            lappend rw_bytes [toAscii [string range $fdat $i $i]]
         }
      }
   }

   set rw_hex_bytes {}
   foreach f $rw_bytes {
      lappend rw_hex_bytes [format "0x%02x" $f]
   }

   return $rw_hex_bytes
}

proc processVpdSetLine {arrName sLine} {
   upvar 1 $arrName arr1
   if {![array exists arr1]} {
      error "processVpdSetLine: \"$arrName\" isn't an array"
   }

   if {[regexp {^\s*([^\s=]+)\s*=(.*)$} $sLine d1 name val]} {
      set val [string trim $val]
      if {[regexp {([^:]+):BIN} $name d1 newName]} {
	 set name $newName
         set arr1($name:IsBin) 1
      } else {
         set arr1($name:IsBin) 0
      }

      set arr1($name:Id) $name
      set arr1($name:Data) $val
      lappend keys $name
    } else {
         error "Bad line format (expecting \"keyword = value\")."
    }

    return $name
}

proc loadConfFile {arrName fn} {
   upvar 1 $arrName arr
   if {![array exists arr]} {
      error "\"$arrName\" isn't an array"
   }

   array unset arr
   array set arr {}

   # Keys: list of the original order of keys in the conf file
   set keys {}

   set f [open $fn r]
   set ln 0
   while {[gets $f sLine] >= 0} {
      incr ln

      if {[regexp {^\s*[\#]} $sLine] || [regexp {^\s*$} $sLine]} {
         continue
      } else {
         if {[catch "lappend keys [processVpdSetLine arr $sLine]" e]} {
	     global errorInfo
	     g_puts "-D- processVpdSetLine error: $errorInfo"
             error "$fn:$ln: $e"
         }
      }
   }
   close $f

   return $keys
}

proc checkVpdRwFlds {data_file} {
   global NEW_VPD_RW_DATA

   set mustHaveFlds    {}
   set mustNotHaveFlds {RW}

   set msg "Fix \"$data_file\" file and retry."
   foreach f $mustHaveFlds {
      if {![info exists NEW_VPD_RW_DATA($f:Id)]} {
         error "Keyword \"$f\" must be assigned in the VPD-W tag. $msg"
      }
   }

   if {[info exists NEW_VPD_RW_DATA(RW:Id)]} {
      error "\"RW\" keyword in the VPD-W tag must not be assigned (it is generated automatically by this tool). $msg"
   }

   foreach f_id [array names NEW_VPD_RW_DATA *:Id] {
      set f [get_major_key $f_id ":"]

      if {![vpd_check_field_name $f]} {
         error "Bad VPD-W keyword name ($f) - keyword names must have 2 alpha-numeric characters exactly. $msg"
      }

      if {$NEW_VPD_RW_DATA($f:IsBin) && ![vpd_check_field_bin_data $NEW_VPD_RW_DATA($f:Data)]} {
	  error "In binary VPD keyword \"$f\": data must be an even length hex string"
      }

      set max_len 255
      if {$NEW_VPD_RW_DATA($f:IsBin)} {
          set max_len 510
      }

      if {[string length $NEW_VPD_RW_DATA($f:Data)] > $max_len} {
          error "VPD keyword \"$f\": Data too long"           
      }
   }
}

proc vpd_add_rw_fld {fldsLen rwResourceLen} {
   set rwSizeLeft [expr $rwResourceLen - $fldsLen]
   g_puts "-D- Adding RW section of size $rwSizeLeft bytes"

   # For CX3 4KB eeprom , there are multiple RW kewords in the RW tag.
   # Logic of filling:
   # - Add RW keywords of size 253 (so it takes 256 bytes with the header)
   # - stop when < 256 size is left.
   # - Special case - if only 1 or 2 bytes remaining at end of RW tag (no room for last RW keword),
   #   remove 2 bytes from the first RW tag.

   set rwKeyMaxSize 253
   if {($rwSizeLeft % 256) == 1 || ($rwSizeLeft % 256) == 2} {
       set rwKeyMaxSize 250
   }

   set rwFld {}
   while {$rwSizeLeft > 0} {
       if {$rwSizeLeft > $rwKeyMaxSize + 3} {
	   set currSize $rwKeyMaxSize
       } else {
           set currSize [expr $rwSizeLeft - 3]
       }

       # Compensation is only in teh first iteration (see algo above)
       set rwKeyMaxSize 253

       lappend rwFld [toAscii R]
       lappend rwFld [toAscii W]

       lappend rwFld $currSize

       for {set i 0} {$i < $currSize} {incr i} {
	  lappend rwFld 0
       }

       set rwSizeLeft [expr $rwSizeLeft - ($currSize + 3)]
   }

   set rwHexFld {}
   foreach f $rwFld {
      lappend rwHexFld [format "0x%02x" $f]
   }
   return $rwHexFld
}

proc verify_vpd {rwOffs rw_bytes} {

   set len [llength $rw_bytes]
   set rd_bytes [read_vpd_data $rwOffs $len]
   for {set i 0} {$i < $len} {incr i} {
      set wb [lindex $rw_bytes $i]
      set rb [lindex $rd_bytes $i]

      if {$wb != $rb} {
         error [format "VPD-W programming verification at offset 0x%x: expected 0x%2x, got 0x%2x" \
                [expr $rwOffs + $i] $wb $rb]
      }
   }
}

proc confirmWrite {keys full_write} {
   global NEW_VPD_RW_DATA
   global VPD_RW_TAGS
   global force

   set pp "to"
   if {$full_write} {
       set op "write"
   } elseif {[info exist VPD_RW_TAGS([lindex $keys 0]:Id)]} {
       set op "set"
       set pp "in"
   } else {
       set op "add"
   }


   g_puts "-I- Going to $op the following keyword(s) $pp the VPD-W tag:\n"
   set fmt "  %-25s %s"
   puts [format $fmt "VPD-KEYWORD" "VALUE"]
   puts [format $fmt "-----------" "-----"]
   foreach f $keys {
      if {$NEW_VPD_RW_DATA($f:IsBin)} {
	 set from_old_arr "Bin"
	 set bin_mark     "(BIN)"
      } else {
	 set from_old_arr "Data"
	 set bin_mark     ""
      }

      if {[info exist VPD_RW_TAGS($f:Id)]} {
          set old_val "$VPD_RW_TAGS($f:$from_old_arr) --> "
      } else {
          set old_val ""
      }

      set desc "N/A"
      puts [format $fmt "$f$bin_mark$desc" "$old_val$NEW_VPD_RW_DATA($f:Data)"]
      g_puts "-D- Length = [string length $NEW_VPD_RW_DATA($f:Data)]"
   }

   puts ""
   puts -nonewline "-?- Continue write ? \[y/n\] "
   flush stdout
   if {![info exist force] || $force == 0} {
      set ans [gets stdin]
   } else {
      set ans "yes"
      puts $ans
   }

   if {!([string equal -nocase $ans "y"] || [string equal -nocase $ans "yes"])} {
      error "Aborted by user"
   }
}


#
# This function writes the fld=data values from the given conf file
# to the VPD-W data.
# It assumes read_vpd_to_vars was allready called - VPD_RW_TAGS is filled.
#
proc write_vpd_rw {data_file single_keyword} {
   global NEW_VPD_RW_DATA
   global VPD_RW_TAGS

   array set NEW_VPD_RW_DATA {}

   if {![info exist VPD_RW_TAGS(VPDWTAG:Len)]} {
      error "VPD-W resource length is not initialized. VPD read, or internal error"
   }

   if {![info exist VPD_RW_TAGS(VPDWTAG:Offs)]} {
      error "VPD-W resource offset is not initialized. VPD read, or internal error"
   }

   set rwLen  $VPD_RW_TAGS(VPDWTAG:Len)
   set rwOffs [expr $VPD_RW_TAGS(VPDWTAG:Offs) + 3]
   # The 3 is the minimal place for the RW place holder which
   # is added automatically
   set maxFldsLen [expr $rwLen - 3]

   if {$data_file != ""} {
       g_puts "-I- Loading VPD-W keywords file ($data_file)"
       set flds [loadConfFile NEW_VPD_RW_DATA $data_file]
       set all_flds $flds
       set full_write 1
   } else {
       #keyword:
       loadNewVpdRwFromCurrent
       set flds [processVpdSetLine NEW_VPD_RW_DATA $single_keyword]
       foreach f_id [array names NEW_VPD_RW_DATA *:Id] {
	   lappend all_flds [get_major_key $f_id ":"]
       }
       set full_write 0
   }

   checkVpdRwFlds $data_file
   set rw_bytes [prepare_vpd_tags NEW_VPD_RW_DATA $all_flds]

   set fieldsLen [llength $rw_bytes]

   if {$fieldsLen > $maxFldsLen} {
      set e    "The total length of the VPD keywords (and headers) to write is "
      append e "[llength $rw_bytes] bytes, which is bigger than the available VPD-W "
      append e " section size ($maxFldsLen bytes). Please re-edit $data_file file."

      error $e
   }

   confirmWrite $flds $full_write

   g_puts "-D- Adding RW section"
   set rw_bytes [concat $rw_bytes [vpd_add_rw_fld [llength $rw_bytes] $rwLen]]

   g_puts "-I- Writing data to VPD (Keywords length: $fieldsLen bytes, VPD-W size: $maxFldsLen bytes) ..."

   write_vpd_data $rwOffs $rw_bytes

   g_puts "-I- Verifying ..."

   # for I2C access, need to wait between read and write.
   after 10
   verify_vpd $rwOffs $rw_bytes

   g_puts "-I- VPD-W tag programming completed."
}


#
# This proc gets the pxe port enable string in form of
# coma separated port assignments.
# "port1=enable,port2=disable"
# Also accept on right side: enable|1 or disable|0
# Also accept on left  side: port1|1
#
# it processes the assignmet string to the V4 keyword bitmask, and then
# write it to VPD RW section.

proc set_vpd_pxe {assignment_str} {
    set assignment_list [split $assignment_str ","]

    set port_mask 0
    foreach a $assignment_list {
	if {![regexp {^(port|p)*(\d)=(enable|1|disable|0)} $a d1 d2 port_num value]} {
	    error "Bad PXE port assignment format ($a). Expected \"port(1|2)=enable|disable\""
	}

	if {$value == "enable" || $value == "1"} {
	    set value 1
	} else {
	    set value 0
	}

	set port_mask [expr "$port_mask | ($value << ($port_num - 1))"]
    }

    write_vpd_rw "" [format "V4=%04x" $port_mask]
}


proc loadNewVpdRwFromCurrent {} {
    global VPD_RW_TAGS
    global NEW_VPD_RW_DATA

    # Pass the current VPD-RW values to teh new array (as bin data)
    foreach f_id [array names VPD_RW_TAGS *:Id] {
	set f [get_major_key $f_id ":"]
	if {$f == "RW" || $f == "VPDWTAG"} {
	    continue
	}
	set NEW_VPD_RW_DATA($f:Id)   $f
	set NEW_VPD_RW_DATA($f:Data) $VPD_RW_TAGS($f:Bin)
	set NEW_VPD_RW_DATA($f:IsBin) 1
    }
}


array set VPD_RO_TAGS_DES {
   IDTAG       "Board Id"
   PN          "Part Number"
   SN          "Serial Number"
   EC          "Revision"
   RV          "Checksum Complement"
   CS          "Actual Checksum"
   MN          "Manufacture ID"
   V0          "Misc Info" 
}
set IBMEZZ_VPD_ID "MTL-0001"

proc local_parray {arr} {
     upvar 1 $arr loc

     if {[catch {set ns [array names loc]}]} {
         g_puts "-E- $_arr is not an array"
         return 1
     }
     if {[llength $ns] == 0} {
         g_puts "-D- $arr : EMPTY ARRAY"
     }
     foreach n [lsort $ns] {
         g_puts "-D- [format %-20s $arr\($n\)] = $loc($n)"
     }
     return 1
}

proc print_VPD {} {
   global VPD_R_TAGS VPD_RW_TAGS VPD_MISC_INFO
   global vpd_check_rw
   global VPD_RO_TAGS_DES
   global env

   global G_PUTS_DEBUG

   set na "N/A"
   if {$G_PUTS_DEBUG} {
      # Prinv VPD arrays raw content and exit

      puts "\n VPD READ ONLY TAG DATA:"
      local_parray VPD_R_TAGS

      if {$vpd_check_rw} {
         puts "\n VPD READ/WRITE TAG DATA:"
         local_parray VPD_RW_TAGS
      }

      puts "\n VPD MISC TAG INFO:"
      local_parray VPD_MISC_INFO
   }

   set fmt "  %-14s %-20s  %s"

   puts ""

   puts [format $fmt "VPD-KEYWORD" "DESCRIPTION" "VALUE"]
   puts [format $fmt "-----------" "-----------" "-----"]

   puts "Read Only section:\n"

   foreach {key value} [array get VPD_R_TAGS] {
        set name [lindex [split $key ":"] 0]
        set type [lindex [split $key ":"] 1]
        if {$type == "Data"} {
            if {[info exist VPD_RO_TAGS_DES($name)]} {
                set des $VPD_RO_TAGS_DES($name)
            } else {
                set des $na
            }            
            puts [format $fmt $name $des $value]
        }        
   }
   puts ""
   # READ WRITE SECTION

   if {!$vpd_check_rw} {
      return
   }


    puts "Read write section:\n"
    foreach {key value} [array get VPD_RW_TAGS] {
        set name [lindex [split $key ":"] 0]
        set type [lindex [split $key ":"] 1]
        if {$type == "Data"} {       
            if {[string is print $value]} {
                puts [format $fmt $name $na $value]
            } else {
                puts [format $fmt $name "$na\(BIN\)" $VPD_RW_TAGS($name:Bin)]
            }           
       }        
   }  
   puts ""
}

proc convertType2FwName {dev_type} {
    global g_dev_rev
    global g_dev_id
    global burner
    global mst_dev
    global additional_burn_flags
    global CX3_HW_ID
    global CX3_PRO_HW_ID

    g_puts "-D- convertType2FwName: g_dev_rev: $g_dev_rev"
    set fw_name "Unknown"
    if {$dev_type == "47396"} {
        set fw_name "IS3FW.BIN"
    } elseif {[is_is4 $dev_type]} {
        set fw_name "fw-IS4.mlx"
    } elseif {[is_sx $dev_type]} {
        set fw_name "fw-sx.mlx"
    } elseif {[is_switchib $dev_type]} {
        set fw_name "fw-SwitchIB.mlx"
    } elseif {[is_spectrum $dev_type]} {
        set fw_name "fw-Spectrum.mlx"
    } elseif {[is_bridgex $dev_type]} {
        set fw_name "fw-BridgeX-rel.mlx"
    } elseif {[is_connectx $dev_type]} {
        if {$g_dev_id == $CX3_HW_ID} {
            # ConnetX2-Pro device
            set fw_name "fw-ConnectX3-rel.mlx"
        } elseif  {$g_dev_id == $CX3_PRO_HW_ID} {
            # CX3 device
            set fw_name "fw-ConnectX3Pro-rel.mlx"
        } else {
            if {$g_dev_rev == 0xb0} {
                set fw_name "fw-ConnectX2-rel.mlx"
            } else {
                set fw_name "fw-25408-rel.mlx"
            }
        }
    } elseif {[is_connectib $dev_type]} {
        set fw_name "fw-ConnectIB.mlx"
    }  elseif {[is_connectx4lx $dev_type]} {
        set fw_name "fw-ConnectX4Lx.mlx"
    }  elseif {[is_connectx4 $dev_type]} {
        set fw_name "fw-ConnectX4.mlx"
    } elseif {[is_infinihost $dev_type]} {
        set fw_name "fw-23108-a1-rel.mlx"
    } elseif {$dev_type == "25218" || [is_infinihost_iii_lx $dev_type]} {
        set fw_name "fw-$dev_type-rel.mlx"
    } elseif {$dev_type == "25208"} {
        # The sw devs of InfiniHostIIIEx have the same HW ID.
        array set dev_qry {}
        set cmd "$burner -d $mst_dev $additional_burn_flags q"
        if {![g_exec $cmd status output]} {
            g_puts "-E- Failed to query device $dev: [lindex $status end]"
            cexit 1
        }
        parse_query_report $output dev_qry
        set fw_name "fw-$dev_qry(Device_ID)-rel.mlx"
    }
    g_puts "-D- convertType2FwName: fw_name = $fw_name"
    return $fw_name
}

#
# VPD Funcs end
###########################

# input: numerical value in base 16, 8 or 10
# return a number in decimal format
# Throws if bad conversion
# prefix for num ("0x" , "0") have precedence over specified base
proc my_strtoul {num {base 0}} {
    switch -exact -- $base {
         0 -
         10 { }
         16 {
                if {![regexp {^0[xX]} $num]} {
                    set num "0x$num"
                }
            }
         8  {
                if {![regexp {^0} $num]} {
                    set num "0$num"
                }
            }
         default {
             error "Unsupported base for my_strtoul ($base)"
         }
     }
     return [expr $num]
}


proc is_valid_hex {hex} {
    if {[catch "expr $hex"]} {
        return 0
    } else {
        return 1
    }
}

# Mellanox guid2mac - Remove 2 middle bytes of the guids to gen 6 bytes mac
proc guid2mac {guid} {
    return [expr (($guid & 0xffffff) | (($guid >> 16) & 0xffffff000000))]
}

#
# This function derives macs and guids based on teh ConnectIB default method:
# Port1 guid:  base_guid      to (base_guid + 7)
# Port2 guid: (base_guid + 8) to (base_guid + 15
#
# it returns a list of uids: (port1_guid,port2_guid,port1_mac,port2_mac)
proc get_conectib_uids {base_guid} {
    return [list $base_guid [expr $base_guid + 8] [guid2mac $base_guid] [guid2mac [expr $base_guid + 8]]]
}

# This function gets a list of uids  (port1_guid,port2_guid,port1_mac,port2_mac)
# Returns the relevant ConnectIB mic params assignment
proc connectib_uids2mic_cmd {uid_list} {
    set param_base_names {"guids.guids\\[0\\].uid"
                          "guids.guids\\[1\\].uid"
                          "guids.macs\\[0\\].uid"
                          "guids.macs\\[1\\].uid"}

    #print "Assigned UIDs:"
    #print "  Port 1 GUID: %16lx" % (uid_list[0])
    #print "  Port 2 GUID: %16lx" % (uid_list[1])
    #print "  Port 1 MAC:  %16lx" % (uid_list[2])
    #print "  Port 2 MAC:  %16lx" % (uid_list[3])

    set mic_str ""
    foreach node {"mfg_info" "device_info"} {
        for {set i 0} {$i < 4} {incr i} {
             append mic_str [format "-%s.%s.hi=0x%08x " $node [lindex $param_base_names $i] [expr [lindex $uid_list $i] >> 32]]
             append mic_str [format "-%s.%s.lo=0x%08x " $node [lindex $param_base_names $i] [expr [lindex $uid_list $i]  & 0xffffffff]]
        }
    }
    return $mic_str
}

#
# Returns teh ConnectIB GUIDs paramenter assignment as derived from the base GUID.
#
#
proc get_connectib_guids {base_guid} {
    return [connectib_uids2mic_cmd [get_conectib_uids $base_guid]]
}

# this function sets base_guid and base_mac for ConnectX4
proc get_connectx4_uids {base_guid base_mac} {
    set param_base_names {"guids.guids.uid"
                          "guids.macs.uid"}
    set uid_list {$base_guid $base_mac}
    set mic_str ""
    foreach node {"mfg_info" "device_info"} {
        for {set i 0} {$i < 2} {incr i} {
             append mic_str [format "-%s.%s.hi=0x%08x " $node [lindex $param_base_names $i] [expr [lindex $uid_list $i] >> 32]]
             append mic_str [format "-%s.%s.lo=0x%08x " $node [lindex $param_base_names $i] [expr [lindex $uid_list $i]  & 0xffffffff]]
        }
    }
    return $mic_str
}

#
# Generate image:
#
proc generate_image {{only_print_version 0}} {
   global additional_mic_flags
   global additional_burn_flags
   global nofs_img
   global G_PUTS_WARNING
   global format
   global fw_file
   global fw_dir
   global bin_file
   global conf_file
   global conf_dir
   global input_img
   global exp_rom
   global user_data
   global vpd_r_file
   global base_guid
   global base_mac
   global tmp_dir
   global mst_dev
   global dev_type
   global burner
   global t2a
   global errorCode
   global files_to_delete
   global exp_rom_dir
   global hash_file
   global prof_file
   global g_dev_rev
   global CX_MAIN_ID
   global tcl_platform
   global must_burn_flags
   global conf_dir_list
   global MORE_PSID_MSG
   #
   # Default format - BINARY for HCAs, IMAGE for switches
   #
   if {$tcl_platform(platform) == "unix"} {
       foreach f {mic t2a} {
          if {[catch {set exe_path [exec which $f]} e]} {
             g_puts "-E- Image generation is not supported (can not find $f tool). This may be due to an MFT installation using --without-image-generation option or a corrupted installation."
             cexit 1
          }
       }
   }
   if {$format == ""} {
      if {[is_flash $dev_type]} {
         set format "BINARY"
      } else {
         set format "IMAGE"
      }
      g_puts "-D- Format not given - using $format"
   } elseif {$format != "IMAGE" && $format != "BINARY"} {
      g_puts "-E- Bad format given ($format). Allowed formats: IMAGE, BINARY."
      cexit 1
   }

   if {[is_flash $dev_type] && $format != "BINARY" && $mst_dev != ""} {
      g_puts "-E- The given format ($format) is not supported for HCA burn. Please use BINARY format (default) when burning."
      cexit 1
   }

   # patch for ConnectX - All connectx devids get a single FW id ver:
   if {[is_connectx $dev_type]} {
       set dev_type $CX_MAIN_ID
   }

   if {![is_fs3_dev $dev_type] && ($base_guid != "" || $vpd_r_file != "" || $base_mac != "")} {
       g_puts "-E- vpd_r_file, base_guid and base_mac options are applicable only for FS3 images."
       cexit 1
   }
   
   if {([is_connectib $dev_type] || [is_switchib $dev_type]) && ($base_mac != "")} {
       g_puts "-E- base_mac is only applicable for ConnectX4/ConnectX4Lx/Spectrum compatible images."
       cexit 1
   }
   if {([is_connectx4 $dev_type] || [is_connectx4lx $dev_type] || [is_spectrum $dev_type]) && ((($base_mac != "") && ($base_guid == "")) || (($base_mac == "") && ($base_guid != "")))} {
       g_puts "-E- Both base_guid and base_mac must be specified."
       cexit 1
   }

   set dev $mst_dev

   # Get the fw file
   if {$fw_dir != ""} {
       set caught [catch {
           g_puts "-D- dev_type: $dev_type"
           set mlx_list [glob -nocomplain -directory $fw_dir *rel\.mlx | *IS4\.mlx | *IS3FW\.BIN | *sx.mlx | *IB.mlx | *SwitchIB.mlx |  *ConnectX4.mlx | *ConnectX4Lx.mlx | *Spectrum.mlx]
           set exp_fw_name [convertType2FwName $dev_type]
           set auto_detect_failed 1
           g_puts "-D- fw dir File: $mlx_list"
           foreach mlx_file $mlx_list {
               set file_name [file tail $mlx_file]
               g_puts "-D- Looking for suitable fw file, file_name: \"$file_name\", exp_file_name: \"$exp_fw_name\""
               if {$file_name == $exp_fw_name} {
                   set fw_file $mlx_file
                   set auto_detect_failed 0
                   break;
               }
           }
           if {$auto_detect_failed == 1} {
               error "Failed to find fw file in directory \"$fw_dir\" for device: $dev_type"
           }

       } e ]

       if {$caught} {
           g_puts "-E- Can't auto detect fw file: $e . Please specify fw (mlx) file using -fw flag ."
           cexit 1
       }

   }
   #
   # Transtate mlx to xml (t2a) if needed
   #

   if {[file extension $fw_file] == ".mlx"} {
      # Store t2a output in a tmp directory
      set mlx_file [open $fw_file]
      set first_line [gets $mlx_file]
      close $mlx_file
      if {[regexp {<!-- MT[0-9]{3,5} Firmware image} $first_line]} {
          set xml_file $fw_file
      } else {

          set xml_file [file tail $fw_file]
          regsub {.mlx$} $xml_file ".[pid].xml" xml_file
          set xml_file "$tmp_dir/$xml_file"

          # Run t2a

          set cmd "$t2a MT$dev_type \"$fw_file\" \"$xml_file\""

          #g_puts "-I- Translating $fw_file to xml format ..."
          g_puts "-D- Running $cmd"

          if {[catch {set res [eval "exec $cmd"]} e]} {
             # Warnings that are printed to stderr can cause the tcl error
             # Check exit status to see if it's a real error.
             g_puts "-D- errorCode = \"$errorCode\". e = \"$e\""

             if {[lindex $errorCode 0] == "CHILDSTATUS"} {
                g_puts "-E- Translation failed: $e"
                cexit 1
             } else {
                g_puts "-W- t2a: $e"
             }
          }

          lappend files_to_delete $xml_file
      }

   } elseif {[file extension $fw_file] == ".xml" || [file extension $fw_file] == ".BIN"} {
      set xml_file $fw_file
   } else {
      g_puts "-E- Illegal FW file extension (\"$fw_file\"), Supported extensions are: .xml, .mlx, .BIN"
      exit 1
   }

   set mic_cmd "mic -format $format"

   if {$G_PUTS_WARNING == 0} {
      append mic_cmd " -nowarn"
   }

   if {$nofs_img} {
      append mic_cmd " -nofs"
   }

   if {$dev_type == 47396} {
      # Add ugly hack because of FW internal check:
      append mic_cmd " -Special.Node_GUID=1"
   }

   if {$only_print_version} {
      append mic_cmd " -fw $xml_file -fwver"
      g_puts "-D- Running: $mic_cmd"
      if {[catch {set res [eval "exec $mic_cmd"]} e]} {
        g_puts "-E- Image version query failed: $e"
        cexit 1
      }
      return $res
   }


   if {$conf_file == "" || $exp_rom == "AUTO"} {
      set caught [catch {
         g_puts "-I- Querying device ..."

         array set dev_qry {}

         set status {}; set output {}
         set cmd "$burner -d $dev $additional_burn_flags $must_burn_flags  q"
         if {![g_exec $cmd status output]} {
            g_puts "-E- Failed to query device $dev: [lindex $status end]"
            cexit 1
         }

         parse_query_report $output dev_qry
      } e]

      if {$caught} {
         g_puts "-E- Can't auto detect fw configuration file: $e . Please specify configuration (ini) file using -conf flag and expansion rom file (if needed) using -exp_rom flag."
         cexit 1
      }
   }

   #
   # Get INI file
   #
   if {$conf_file == ""} {
        if { $conf_dir_list == "" } {
            if {[string match "*MLXBURN-AUTO" $conf_dir]} {
                set conf_dir [file dirname $fw_file]
            }
            set dirs_list $conf_dir
        } else {
            set dirs_list $conf_dir_list
        }

        set err_msg ""
        foreach curr_conf_dir $dirs_list {
            set caught [catch {
                set psid $dev_qry(PSID)
                if {[regexp {^\s*$} $psid] || [regexp {^N.A$} $psid]} {
                    error "Invalid PSID on device $mst_dev ($psid)"
                }
                g_puts "-D- curr_conf_dir = $curr_conf_dir"
                set conf_file [get_matching_conf_file $psid $curr_conf_dir]
            } e]

            if {!$caught} {
                break
            }
            if {[regexp "(.)*${MORE_PSID_MSG}(.)*" $e]} {
                set err_msg $e
                break;
            }
            lappend err_msg $e
        }
        if {$conf_file == ""} {
            g_puts "-E- Can't auto detect fw configuration file: $err_msg . Please specify configuration (ini) file using -conf flag ."
            cexit 1
        }

    g_puts "-I- Using auto detected configuration file: $conf_file (PSID = $psid)"
   }

   #
   # Get hash file
   #
   if {[is_is4 $dev_type]} {

       set hash_dir [file dirname $fw_file]
       set exp_hash_file "$hash_dir/hash.csv"

       if {![file exist $exp_hash_file]} {
          g_puts "-W- Can't auto detect hash.csv file on $hash_dir ."
       } else {
          set hash_file $exp_hash_file
          g_puts "-I- Using auto detected hash file: $hash_file"
       }
   }


   # Auto detect exp rom:
   if {$exp_rom_dir == ""} {
       set exp_rom_dir [file join [file dirname $fw_file] "exp_rom"]
   }
   if {$exp_rom == "AUTO"} {
        if {$dev_qry(Rom_Info) == ""} {
            set exp_rom "NONE"
        } elseif {$dev_qry(Rom_Info) == "N/A"} {
            g_puts "-E- Can not auto detect expansion ROM to use. Please specify expansion ROM using -exp_rom flag."
            cexit 1
        } else {
            set exp_rom [select_exp_rom $dev_qry(Rom_Info) $exp_rom_dir]
        }
        g_puts "-I- Using auto detected expansion ROM file: $exp_rom"
   }
   if {$exp_rom == "NONE"} {
        set exp_rom ""
   }
   #
   # run mic to create bin image file
   #
   if {$bin_file == ""} {
       set bin_file [file tail $fw_file]
       regsub {.BIN$} $bin_file ".[pid].img" bin_file
       regsub {.mlx$} $bin_file ".[pid].bin" bin_file
       set bin_file [fix_file_path "$tmp_dir/$bin_file"]
       g_puts "-D- Using tmp image file: $bin_file"

       lappend files_to_delete $bin_file
   }
   append mic_cmd " -fw \"$xml_file\"  -conf \"$conf_file\" -wrimage \"$bin_file\" $additional_mic_flags"

   # expansion rom:
   if {$exp_rom != ""} {
      append mic_cmd " -exp_rom \"$exp_rom\""
   }

   #user data
   if {$user_data != ""} {
      append mic_cmd " -user_data \"$user_data\""
   }

   # hash file
   if {$hash_file != ""} {
       append mic_cmd " -hash_file \"$hash_file\""
   }

   if {$prof_file != ""} {
       append mic_cmd " -prof_file \"$prof_file\""
   }

   if {[is_fs3_dev $dev_type]} {
       if {$vpd_r_file != ""} {
          append mic_cmd " -vpd_r $vpd_r_file"
       }

       if {$base_guid != ""} {
          if {[catch {set valid_base_guid [my_strtoul $base_guid 16]} e]} {
              g_puts "-E- Bad base_guid ($base_guid). Must be a valid hex number."
              cexit 1
          }

          append mic_cmd " "
          if {[is_connectx4 $dev_type] || [is_connectx4lx $dev_type] || [is_spectrum $dev_type]} {
              # we append it when we deal with base_mac
          } else {
              append mic_cmd [get_connectib_guids $valid_base_guid]
          }
       }

       if {$base_mac != ""} {
          if {!([is_connectx4 $dev_type] || [is_connectx4lx $dev_type] || [is_spectrum $dev_type])} {
          g_puts "-E- -base_mac flag is supported on ConnectX-4/ConnectX-4LX/Spectrum only."
          }

          if {[catch {set valid_base_mac [my_strtoul $base_mac 16]} e]} {
              g_puts "-E- Bad base_mac ($base_mac). Must be a valid hex number."
              cexit 1
          }

          append mic_cmd " "
          append mic_cmd  [get_connectx4_uids $valid_base_guid $valid_base_mac]
       }
   }

   g_puts "-I- Generating image ..."
   g_puts "-D- Running $mic_cmd"

   if {[catch {eval "exec $mic_cmd"} e]} {
      if {[string first ".guids.guids.uid.hi" $e] != -1} {
       set e "[lindex [split $e \n] 0] this might indicate an outdated mlx file format."
      }
      g_puts "-E- Image generation failed: $e"
      cexit 1
   }

   #
   # Post process:
   #
   # if {$format == "IMAGE" && ![is_hca $dev_type] && $remove_directives} {
   #   g_puts "-D- Post processing: remove_img_directives"
   #   remove_img_directives $bin_file
   # }
   return $bin_file
}

proc get_quick_query {} {
    global additional_burn_flags

    if {[regexp {(.)*(\-qq)(.)*} $additional_burn_flags]} {
        return "-qq"
    }
    return ""
}

proc generate_bin_db {img_dir} {
    global BIN_FILE_DB
    global burner
    global must_burn_flags

    # BIN_FILE_DB:
    # Major key: filename. Minor Keys: flint Query fields
    set quick_query [get_quick_query]
    set bin_files [glob -nocomplain "$img_dir/*.bin"]
    array set qry_arr {}
    foreach f $bin_files {
        set cmd "$burner -i \"$f\" $quick_query $must_burn_flags  q"
        if {![g_exec $cmd status output]} {
            g_puts "-D- Failed to query file $f: [lindex $status end]."
            g_puts "-W- Failed to query file $f. Ignoring."
            continue
        }

        array unset qry_arr
        if {[parse_query_report $output qry_arr]} {
            set BIN_FILE_DB($f@FNAME) $f
            foreach n [array names qry_arr] {
                set BIN_FILE_DB($f@$n) $qry_arr($n)
            }
        }
    }
}

proc generate_exprom_db {exprom_dir} {
    global EXPROM_DB
    global burner
    global G_PUTS_DEBUG
    global must_burn_flags

    # BIN_FILE_DB:
    # Major key: filename. Minor Keys: rom Query fields
    set bin_files [glob -nocomplain "$exprom_dir/*" ]
    array set qry_arr {}
    foreach f $bin_files {
        set cmd "$burner -i \"$f\" $must_burn_flags qrom"
        if {![g_exec $cmd status output]} {
            g_puts "-D- Failed to query file $f: [lindex $status end]."
            g_puts "-W- Failed to query file $f. Ignoring."
            continue
        }

        array unset qry_arr
        array unset exprom_arr
        if {[parse_query_report $output qry_arr]} {
            if {[parse_exprom_query $qry_arr(Rom_Info) exprom_arr]} {
                set EXPROM_DB($f@FNAME) $f
                foreach n [array names exprom_arr] {
                    set EXPROM_DB($f@$n) $exprom_arr($n)
                }
            }
        }
    }
    if {$G_PUTS_DEBUG} {
        local_parray EXPROM_DB
    }
}

proc select_binary_file {dev img_dir} {
    global BIN_FILE_DB
    global burner
    global force
    global must_burn_flags

    generate_bin_db $img_dir

    array set dev_qry {}

    set quick_query [get_quick_query]
    set status {}; set output {}
    set cmd "$burner -d $dev $quick_query $must_burn_flags q"
    if {![g_exec $cmd status output]} {
        g_puts "-E- Failed to query device $dev: [lindex $status end]"
        cexit 1
    }
    parse_query_report $output dev_qry

    set dev_psid $dev_qry(PSID)
    if {$dev_psid == ""} {
        g_puts "-E- Can not auto detect FW file. Device $dev PSID may not be configured."
    }

    set match_file ""
    foreach f_k [array names BIN_FILE_DB] {
        set f [get_major_key $f_k]
        if {$BIN_FILE_DB($f@PSID) == $dev_psid} {
            set match_file $f
        }
    }

    if {$match_file == ""} {
        g_puts "-E- Directory $img_dir does not contain an image that matches the device FW (No matching PSID)"
        cexit 1
    }

    return $match_file
}

proc select_exp_rom {dev_rom_info exp_rom_dir} {
    global EXPROM_DB

    array set dev_exprom {}
    if {![parse_exprom_query $dev_rom_info dev_exprom]} {
        cexit 1
    }

    generate_exprom_db $exp_rom_dir

    #
    # EXP ROM SELECTION RULES:
    # exp_rom type and device id must have exact match between new and old roms
    # PORT: accepts exact match, or if the new rom is port independant.
    #       No auto down-grade from port independant to a specific port.
    # PROTO: accepts exact match or moving from ETH or IB to VPI.
    #
    # Selection takes the first matching file. It is assumed only a single
    # matching file will be put in the exp_rom_dir

    set match_file ""
    foreach f_k [array names EXPROM_DB] {
        set f [get_major_key $f_k]
        g_puts "-D- Checking matching of Exp ROM file: $f"
        if {$EXPROM_DB($f@type)  == $dev_exprom(type) &&
            $EXPROM_DB($f@devid) == $dev_exprom(devid)} {
            if {$EXPROM_DB($f@port) == $dev_exprom(port) ||
                $EXPROM_DB($f@port) == 0} {

                if {$EXPROM_DB($f@proto) == $dev_exprom(proto) ||
                    ($EXPROM_DB($f@proto) == "VPI" && $dev_exprom(proto) == "IB") ||
                    ($EXPROM_DB($f@proto) == "VPI" && $dev_exprom(proto) == "ETH") } {
                    set match_file $f
                }
            }
        }
    }

    if {$match_file == ""} {
        g_puts "-E- Directory $exp_rom_dir does not contain an expansion ROM image that matches the device. You can specify expansion ROM manually using the -exp_rom flag"
        cexit 1
    }

    return $match_file
}

proc burn_image {_pat} {
    global nofs
    global additional_burn_flags
    global bin_file
    global mst_dev
    global burn_cmd
    global burner
    global force
    global dev_type
    global must_burn_flags

    set nofs_flag ""
    if {$nofs} {
       set nofs_flag "-nofs"
    }

    if {$force} {
       append additional_burn_flags " -y"
    }

    set cmd "$burner -d $mst_dev $nofs_flag -i \"$bin_file\" $additional_burn_flags $must_burn_flags $burn_cmd"

    # g_puts "-D- Sleep before running $cmd"
    # after 10000
    g_puts "-D- Running $cmd"

    if {[catch {eval "exec $cmd >&@ stdout <@stdin"} e]} {
       global errorCode
       if {[lindex $errorCode 2] == 7} {
           g_puts "\n-I- Image burn skipped - Image is already up to date."
       } else {
           g_puts "-E- Image burn failed: $e"
           cexit 1
       }
    } else {
        g_puts "-I- Image burn completed successfully."
    }
}


proc parse_exprom_query {qry_str _arr} {
    upvar 1 $_arr QUERY_ARR

    # Rom Info:        type=GPXE version=0.9.5 devid=25418 port=1
    array set QUERY_ARR {
        type    ""
        version ""
        devid   ""
        proto   ""
        port    0
    }

    foreach exp $qry_str {
        set param_val [split $exp "="]
        if {[llength $param_val] != 2} {
            g_puts "-E- Bad Rom Info query format: $qry_str"
            return 0
        }
        set param [lindex $param_val 0]
        set val   [lindex $param_val 1]

        if {$param != "" && [array names QUERY_ARR $param] != ""} {
            set QUERY_ARR($param) $val
        }
    }
    return 1
}


#Query:
#Node GUID              0x000000ae1bc1edf0
#System Image GUID      0x0000000100000001
#Node Description       MT47396 Infiniscale-III Mellanox Technologies
#Board Serial Number    NO_BSN
#PSID                   MT_024000000
proc parse_query_report {_spark_output _arr} {
    upvar 1 $_arr QUERY_ARR

    array set QUERY_ARR {
        FW_Version          ""
        Node_GUID           ""
        System_Image_GUID   ""
        Node_Description    ""
        Board_Serial_Number ""
        PSID                ""
        Device_ID           ""
        GUIDs               ""
        Rom_Info            ""
    }

    set parameterLine ""
    foreach line $_spark_output {

        set param ""
        if {$line == "Query:"} {
            continue
        }
        set param [string trim [lindex [split $line :] 0]]

        # remove dots if there are.
        regsub -all "\\." $param "" param
        # replace space with under line
        regsub -all " " $param "_" param

        set val [string trim [lindex [split $line :] 1]]

        if {$param != "" && [array names QUERY_ARR $param] != ""} {
            set QUERY_ARR($param) $val
        }
    }
    return 1
}

proc query_image_hca {} {
    global mst_dev
    global burner
    global additional_burn_flags
    global must_burn_flags

    set retVal 0
    set status {}; set output {}
    if {![g_exec "$burner -d $mst_dev $additional_burn_flags $must_burn_flags q" status output]} {
        set retVal 1
        g_puts "-E- $burner query failed : [lindex $status end]"
    }
    foreach line $output {
        g_puts "-I- $line"
    }
    return $retVal
}

proc query_image_switch {_geo_addr {_print_header 0}} {
    global nofs
    global additional_burn_flags
    global bin_file
    global mst_dev
    global burn_cmd
    global burner
    global force

    if {$force} {
       append additional_burn_flags " -y"
    }

    set status {}; set output {}
    if {![g_exec "$burner -d $mst_dev q" status output]} {
        g_puts "-E- $_geo_addr : query error : [lindex $status end]"
    } else {
        array set QARR {}
        parse_query_report $output QARR
        if {$_print_header == 1} {
            g_puts "-I- Location FW Version  Node GUID           System Image GUID   BSN                 PSID                "
        }
        g_puts "-I- [format %-8s $_geo_addr] [format %-11s $QARR(FW_Version)] [format %-19s $QARR(Node_GUID)] [format %-18s $QARR(System_Image_GUID)] \
                [format %-19s $QARR(Board_Serial_Number)] [format %-18s $QARR(PSID)]"
    }
}

proc g_exec {_cmdLine {_statusByName _NO_STATUS} {_outputByName _NO_OUTPUT} {_silent 0}} {
    global errorCode

    if {$_statusByName != "_NO_STATUS"} {
        upvar 1 $_statusByName status
    }

    if {$_outputByName != "_NO_OUTPUT"} {
        upvar 1 $_outputByName output
    }

    set retVal 1
    set execError 0
    set output {} ; set status {}

    set pref ""
    g_puts "-D- Command: $_cmdLine"

    if {[catch {set output [eval "exec $_cmdLine"]} e]} {
        set execError 1
    }

    set output [split $output \n]
    foreach line $output {
        g_puts "-D- output: $line"
    }

    if {$execError} {
        set retVal 0
        set status $errorCode
        lappend status $e
    } else {
        set status {normal}
    }

    foreach line [split $status \n] {
        g_puts "-D- status: $line"
    }

    return $retVal
}

proc format_revision {tool_name} {
   global MFT_VERSION_STR
   global TOOLS_GIT_SHA
   global BUILD_TIME

   return "$tool_name, $MFT_VERSION_STR, built on $BUILD_TIME. Git SHA Hash: $TOOLS_GIT_SHA"
}

array set Device_IDs {
    CONNECTX {25408 25418 26418 26428 25448 26448 26468 26478 25458 26458 26438 26488
              4097 4099 4101 4103 4105 4107 4109 4111}
    ConnectIB {4113}
    CONNECTX4 {4115}
    CONNECTX4LX {4117}
    INFINIHOST_III_LX    {24204 25204}
    IS4      {48436 48437 48438}
    BRIDGEX  {64102 64122 1016}
    INFINIHOST_III_EX    {25208 25218}
    INFINIHOST    {23108}
    IS3      {47396}
    SX       {51000}
    SwitchIB {52000}
    Spectrum {52100}
}

proc get_devs_id_str {pre_str HCAS} {

    set hca_devs_num [llength $HCAS]
    set devs "              $pre_str"
    for {set i 0} {$i < $hca_devs_num} {incr i} {
        set hca_dev [lindex $HCAS $i]
        append devs " $hca_dev"

        if {$i == [expr $hca_devs_num - 1]} {
            append devs ".\n"
        } else {
            append devs ","
            if {$i != 0 && [expr ($i + 1) % 7] == 0} {
                append devs "\n                        "
            }
        }
    }
    return $devs
}

set HCAS [concat $Device_IDs(INFINIHOST) $Device_IDs(INFINIHOST_III_EX) $Device_IDs(CONNECTX) $Device_IDs(INFINIHOST_III_LX) $Device_IDs(ConnectIB) $Device_IDs(CONNECTX4) $Device_IDs(CONNECTX4LX)]
set switch_devs  [concat $Device_IDs(IS3) $Device_IDs(IS4) $Device_IDs(SX) $Device_IDs(SwitchIB) $Device_IDs(Spectrum)] 
set bridgex_devs $Device_IDs(BRIDGEX)

set hca_devs_str     [get_devs_id_str  "HCAs/NICs:" $HCAS]
set switch_devs_str  [get_devs_id_str  "Switches: " $switch_devs]
set bridgex_devs_str [get_devs_id_str  "Bridges:  " $bridgex_devs]

set G_VSD_LEN 208
set version "[format_revision mlxburn]"

set ToolUsage {[-h][-v] <-dev mst-device|-wrimage fw-image>
   <-fw mellanox-fw-file|-image fw-image|-img_dir img_direcory|-fw_dir fw_dir>
   [-conf fw-conf-file][-nofs][-nofs_img][-striped_image][-format BINARY|IMAGE][-dev_type device-type]
   [-exp_rom <exp_rom_file>][-exp_rom_dir <exp_rom_dir>][-force][-conf_dir <conf_dir>][-fwver]
   [-vpd][-vpd_rw][-vpd_prog_rw <rw-keywords-file>][-vpd_set_keyword <keyword-assignment>]
   [-set_pxe_en <(port1|port2)=(enable|disable)>] [-prof_file <profiles file>]
   [-query] [-conf_dir_list <dir1,dir2,...,dirn>]}

set ToolUsage "Usage: mlxburn $ToolUsage"

set ToolHelp {
NAME
       mlxburn

DESCRIPTION

       Burn or generate FW image for Mellanox devices.
       Please see the mlxburm man page (1) for more info.

OPTIONS
       -dev <mst-dev>
              Burn the image using the given MST device.

       -fw <mellanox-fw-file>
              Specify Mellanox FW released Firmware File  to  use
              (file extension is .mlx)

       -image <fw-image-file>
              Do not  generate  image.  Use  the  given  fw  image
              instead.

       -img_dir <image directory>
              Do not generate image. Select the image to burn from
              the *.bin in the given directory.

       -conf <parameter-set-file>
              FW configuration file (.ini).  Needed  when
              generating  image  (not using -dev flag) or if con-
              figuration auto detection fails.

       -wrimage <fw-image-file>
              Write the image to the given file.

       -exp_rom <exp-rom-file>
              Integrate the given expansion rom file to the FW
              image.
              If the exp-rom-file is set to "AUTO", expansion
              rom file is auto detected from the files rom in the
              exp_rom_dir (see below).
              NOTE: Exp rom auto detection is done for devices that
              are already burned with an exp-rom image.
              If "-exp_rom AUTO" is specified for a device with no
              exp-rom, it would be burnt with no exp rom.
              To add exp-rom to a device, manually supply the exp rom
              file to use.

       -exp_rom_dir <exp_rom_dir>
              The directory in which to look for expansion rom file
              when "-exp_rom AUTO" is specified. By default, exp-rom
              files are searched in <fw file directory>/exp_rom/*

       -fwver
              When a device is given: Display current loaded firmware version.
              When a FW file is given (-fw flag): Display the file FW version.

       -query
              Query mode. Query the HCA or Switch device FW image.

       -vpd
              Display the read only section of the PCI VPD (Vital
              Product Data) of the given device.
              NOTE: VPD feature may not be supported on certain board types.

       -vpd_rw
              Display also the read/write section of the PCI VPD of the
              given device.

ADVANCED OPTIONS
       -nofs
              When specified, burn process will not be failsafe.

       -nofs_img
              When  specified,  generated image will not be fail-
              safe, and burn process will not be failsafe.

       -striped_image
              When specified, generated image will be in striped
              format, and will indicate that the image is in striped
              format when queried.

       -force
              None interactive mode. Assume "yes" for all user
              questions.

       -format <BINARY|IMAGE>
              Specify which image format to use. Can be specified
              only with the -wrimage flag.  Default is BINARY.

       -conf_dir <dir>
              When specified, the auto detected configuration files
              will be looked for in the given directory, instead of
              in the firmware file directory.
              Applicable for burn operation.

       -conf_dir_list <dir1,dir2,...,dirn>
              When specified, the auto detected configuration files
              will be looked for in the given directories, instead of
              in the firmware file directory.
              Applicable for burn operation.

       -fw_dir <dir>
              When specified, the auto detected fw files
              will be looked for in the given directory.
              Applicable for burn operation.

       -dev_type <mellanox-device-number>
              mlxburn  must know the device type in order to work
              properly.  Use this flag if device type auto-detec-
              tion fails.
              Example: -dev_type 23108

              Supported Mellanox device types:
}

append ToolHelp $hca_devs_str $switch_devs_str $bridgex_devs_str

append  ToolHelp {

       -vpd_prog_rw <rw-keywords-file>
              Program the VPD-W tag (the writable section of the
              VPD) with the data given in the rw-keywords-file.
              File lines format: "KEYWORD = VALUE".
              In order to set binary data to a keyword, add ":BIN"
              to the keyword name. in this case, the data is a hexa-
              decimal string of even length. Example file:
                 V1 = MY-ASCII-KEYWORD
                 V2:BIN = 1234abcd

              White spaces before and after VALUE are trimmed.

       -vpd_set_keyword <keyword-assignment>
              Add or change a keyword value in the VPD-W tag (the
              writable section of the VPD) with the data given in
              the keyword-assignment string. The string format is
              identical to a line in the rw-keywords-file described
              above. Other keywords in the VPD-W tag are not affected
              by this operation.

       -query
              Query the HCA/Switch device for firmware details, e.g.
              Firmware Version, GUIDs etc.

       -vsd <string>
              Write this string, of up to 208 characters, to VSD section.

       In addition to the above flags, Mlxburn can also accept the following
       flags/options, which are passed to the underlying burning tool:

              -banks      -use_image_ps  -skip_is
              -mac(s)     -guid(s)       -sysguid
              -ndesc      -bsn           -use_image_guids
              -pe_i2c     -se_i2c        -is3_i2c
              -no         -qq            -uid(s)
              -log        -blank_guids   -flash_params
              -allow_psid_change         -no_flash_verify
              -use_image_rom             -override_cache_replacement
              -ocr


       See the flint tool documentation for HCA/4th gen switches/Bridge burning options.

PRODUCTION OPTIONS
       These options are applicable for Mellanox ConnectIB HCA only.
       These options are relevant when generating an image for initial burn. The image contains
       the VPD and the GUIDs that are in a read-only area on flash.

       -vpd_r_file <vpd_r_file>
              Embed the given VPD Read-Only section in the generated image. The vpd_r_file should
              contain the vpd read only section and the first dword of the vpd write-able section.
              The file is in binary format, and its size must be a multiple of 4 bytes. Please
              refer to PCI base spec for VPD structure info.

       -base_guid <GUID>
              Set the given GUID as the image base GUID. The base GUID is used to derive GUIDs and MACs
              for the HCA ports. It is assumes that 16 GUIDs (base_guid to base_guid + 15) are reserved
              for the card.
              *On ConnectX4: only GUIDs will be derrived according to the HCA's configuration.

       -base_mac <MAC>
              Set the given MAC as the image base MAC. the base MAC is used to derrvie MACs for the HCA
              ports according to the device configuration (ConnectX4 only).

INFO OPTIONS
       -h
              display a short help text

       -V <INFORM|WARNING|DEBUG>
              Set verbosity level. Default is WARNING.

       -v
              Print version info and exit.
}

set IS4_HW_ID          435
set CX_MAIN_ID         25408
set SX_HW_ID           581
set CX3_HW_ID          501
set CX3_PRO_HW_ID      503
set CX_HW_ID           400
set CIB_HW_ID          511
set CX4_HW_ID          521
set CX4LX_HW_ID        523
set SWITCHIB_HW_ID     583
set SPECTRUM_HW_ID     585

proc is_is4 {dev} {
   global Device_IDs
   global IS4_HW_ID
   if {$dev == $IS4_HW_ID || [lsearch $Device_IDs(IS4) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_sx {dev} {
   global Device_IDs
   global SX_HW_ID
   if {$dev == $SX_HW_ID || [lsearch $Device_IDs(SX) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_switchib {dev} {
   global Device_IDs
   global SWITCHIB_HW_ID
   if {$dev == $SWITCHIB_HW_ID || [lsearch $Device_IDs(SwitchIB) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_spectrum {dev} {
   global Device_IDs
   global SPECTRUM_HW_ID
   if {$dev == $SPECTRUM_HW_ID || [lsearch $Device_IDs(Spectrum) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_bridgex {dev} {
   global Device_IDs
   if {$dev == 6100 || [lsearch $Device_IDs(BRIDGEX) $dev] != -1} {
       return 1
   }
   return 0
}

proc is_connectx {dev} {
   global Device_IDs
   global CX_HW_ID
   global CX3_HW_ID
   global CX3_PRO_HW_ID
   if {$dev == $CX_HW_ID || $dev == $CX3_HW_ID || $dev == $CX3_PRO_HW_ID || [lsearch $Device_IDs(CONNECTX) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_infinihost_iii_lx {dev} {
   global Device_IDs
   if {[lsearch $Device_IDs(INFINIHOST_III_LX) $dev] != -1} {
       return 1
   }
   return 0
}

proc is_infinihost {dev} {
    global Device_IDs
    if {[lsearch $Device_IDs(INFINIHOST) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_infinihost_iii_ex {dev} {
    global Device_IDs
    if {[lsearch $Device_IDs(INFINIHOST_III_EX) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_connectib {dev} {
    global CIB_HW_ID
    global Device_IDs
    if {$dev == $CIB_HW_ID || [lsearch $Device_IDs(ConnectIB) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_connectx4 {dev} {
    global CX4_HW_ID
    global Device_IDs
    if {$dev == $CX4_HW_ID || [lsearch $Device_IDs(CONNECTX4) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_connectx4lx {dev} {
    global CX4LX_HW_ID
    global Device_IDs
    if {$dev == $CX4LX_HW_ID || [lsearch $Device_IDs(CONNECTX4LX) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_fs3_dev {dev} {
    if {[is_connectx4lx $dev] || [is_connectx4 $dev] || [is_connectib $dev] || [is_switchib $dev] || [is_spectrum $dev]} {
        return 1
    }
    return 0
}

proc is_flash {dev} {
    if {[is_connectx  $dev] ||
        [is_is4       $dev] ||
        [is_bridgex   $dev] ||
        [is_infinihost_iii_lx     $dev] ||
        [is_infinihost_iii_ex     $dev] ||
        [is_sx        $dev] ||
        [is_fs3_dev   $dev] ||
        [is_infinihost     $dev]} {

            return 1
    }
    return 0
}

proc Usage {} {
   global ToolUsage
   puts $ToolUsage
}

proc Help {} {
   global ToolHelp

   Usage
   puts $ToolHelp
}

proc read_psid_from_dev {mst_dev burner} {
   set cmd "$burner -d $mst_dev q"

   set res [eval "exec -keepnewline $cmd"]

   # Flint query return format:
   # Board ID: <VSD>    (<PSID>)

   # Spark query format:
   # PSID    <PSID>
   if {[regexp -lineanchor {^Board ID:.+\((.+)\)} $res d1 psid]} {

   } elseif {[regexp -lineanchor {PSID:?\s*:?\s(\S+)} $res d1 psid] } {

   } else {
      set psid ""
   }

   g_puts "-D- Read PSID \"$psid\" from dev $mst_dev"

   return $psid
}

proc get_psid_from_ini {f} {

   set fh [::ini::open $f r]

   if       {[::ini::exists $fh  "PSID"    "PSID"]} {
      set psid [::ini::value $fh "PSID"    "PSID"]
   } elseif {[::ini::exists $fh  "ADAPTER" "PSID"]} {
      set psid [::ini::value $fh "ADAPTER" "PSID"]
   } elseif {[::ini::exists $fh  "image_info" "psid"]} {
      set psid [::ini::value $fh "image_info" "psid"]
   } else {
      error "Can't find psid assignment in file $f ."
   }

   ::ini::close $fh

   return $psid
}

#
# Read PSID from or .ini file
#

proc read_psid_from_conf {f} {
   set psid ""
   switch [file extension $f] {
      .ini -
      .INI {
         set psid [get_psid_from_ini $f]
      }
      default {
         error "Don't know how to read PSID for file with unsupported $f."
      }
   }

   return $psid

}


#
# get_matching_conf_file:
#
# Search in the directory of the FW file a ini file tha assigns the given PSID.
# An error is issued if no file is found, or if more than 1 file is found.
#
# Returns: The matching conf (ini) file.
#
set MORE_PSID_MSG "Defined in more than one file"
proc get_matching_conf_file {psid search_dir} {
    global MORE_PSID_MSG
   # get all conf file in the directory:

   set conf_file_in_path     [glob "$search_dir/*.ini" "$search_dir/*.INI" ]

   if {[llength $conf_file_in_path] == 0} {
      error "No FW configuration files found in $search_dir ."
   }

   set conf_file ""

   foreach f $conf_file_in_path {
      if {[catch {set curr_psid [read_psid_from_conf $f]} e]} {
         g_puts "-W- Can't extract PSID for file $f: $e ."
      } else {
         g_puts "-D- get_matching_conf_file: File $f --> PSID: $curr_psid"

         if {$curr_psid == $psid} {
            # Match! - Now check that there's no previous match.
            # Allow .ini file to override file with the same PSID

            if {$conf_file == ""} {
               set conf_file $f
            } else {
               if { $conf_file !=  $f } {
                  error "PSID \"$psid\" $MORE_PSID_MSG: $conf_file and $f"
               }
            }
         }
      }
   }

   if {$conf_file == ""} {
      error "No configuration file found for PSID \"$psid\" in $search_dir"
   }

   return $conf_file
}


proc remove_img_directives {img_file} {
   set tmp_file "$img_file.tmp"

   set cmd "egrep -v IMAGE|FW_VER|DEVICE_TYPE|DEVMAP $img_file > $tmp_file"

   if {[catch {eval "exec $cmd"} e]} {
      g_puts "-E- Image post processing failed: $e"
      cexit 1
   }

   file rename -force $tmp_file $img_file
}



proc cr_read {dev addr {i2c_addr ""}} {
   global errorCode errorInfo
   global use_mstflint

   # Try the hard coded mst path
   set mread  "/usr/mst/bin/mcra"

   if {![file executable $mread]} {
      # if not found , assume it's in the path
      set mread "mcra"
   }

   if {$use_mstflint} {
       set mread "mstmcra"
   }

   if {$i2c_addr != ""} {
       set i2c_addr  "-s $i2c_addr"
   }

   set cmd "$mread $i2c_addr $dev $addr"

    g_puts "-D- cr_read: Running $cmd"
   set e ""
   if {[catch {set reg [eval "exec $cmd"]} e]} {
         error "Can't access device $dev: $e"
   }

   g_puts "-D- cr read: $e . "
   return $reg
}

proc i2c_cr_read {dev addr i2c_addr i2c_addr_width} {
   global errorCode errorInfo

   # Try the hard coded mst path
   set i2c  "/usr/mst/bin/i2c"

   if {![file executable $i2c]} {
      # if not found , assume it's in the path
      set i2c "i2c"
   }

   set e ""

   g_puts "-D- $i2c -a $i2c_addr_width -d 4 $dev r $i2c_addr $addr"
   if {[catch {set devid [exec $i2c -a $i2c_addr_width -d 4 $dev r $i2c_addr $addr]} e]} {
      error "Can't access device $dev: $e"
   }

   g_puts "-D- i2c cr read: $devid . "

   return "0x$devid"
}

proc prepare_dev_info_list {dev_id rev} {
    set dev_list {}

    lappend dev_list    $dev_id
    lappend dev_list    $rev
    return $dev_list
}

proc get_dev_id {dev} {
    set hca_err ""
    set is3_err ""

    # Check if HCA
    if {![catch {set hdev_id [cr_read $dev 0xf0014  ]} hca_err]} {
       set rev [format %#x [expr (($hdev_id & 0xff0000) >> 16)]]
       set hdev_id   [expr $hdev_id & 0xffff]
       g_puts "-D- Read HCA dev id: $hdev_id, rev: $rev"

       if {[is_flash $hdev_id]} {
           return [prepare_dev_info_list $hdev_id $rev]
       }
    }

    # Check if IS3
    after 5
    if {![catch {set sdev_id [cr_read $dev 0x60014 0x6c]} is3_err]} {
       set rev [format %#x [expr (($sdev_id & 0xff0000) >> 16)]]
       set sdev_id [expr $sdev_id & 0xffff]

       g_puts "-D- Read IS3 dev id: $sdev_id"

       if {$sdev_id == 47396} {
           return [prepare_dev_info_list $sdev_id $rev]
       }
    }

    if {$hca_err != "" && $is3_err != ""} {
        # If all 3 cr reads failed - Assume there's a general error with the device  -arbitrarily display the first error
        error $hca_err
    }

    error "read unknown device id for $dev"
}

proc get_fw_ver {mst_dev} {

   set dev_id [lindex [get_dev_id $mst_dev] 0]

    if {[is_flash $dev_id]} {
        if {[is_connectx $dev_id]} {
            set rev_base 0x1f064
        } elseif {[is_is4 $dev_id] || [is_sx $dev_id]} {
            set rev_base 0x60040
        } elseif {[is_bridgex $dev_id]} {
            set rev_base 0x60044
        } elseif {[is_fs3_dev $dev_id]} {
            error "-fwver is not supported on FS3 devices."
        } else {
            set rev_base 0x82478
        }
        set maj     [cr_read $mst_dev $rev_base]
        set min_sub [cr_read $mst_dev [expr $rev_base + 4]]

        set maj [expr $maj >> 16]
        set min [expr $min_sub >> 16]
        set sub [expr $min_sub & 0xffff]
    } elseif {$dev_id == 47396} {
        # IS3
        set rev_base 0x3ffa0

        set rev_word [cr_read $mst_dev $rev_base 0x6c]

        set maj [expr ($rev_word >> 16) & 0xff]
        set min [expr ($rev_word >>  8) & 0xff]
        set sub [expr ($rev_word)       & 0xff]
    } else {
        error "-fwver is not supported for device MT$dev_id"
    }

    return "$maj.$min.$sub"
}

# clean exit
proc cexit { { ret_val 0 } } {
   global files_to_delete
   g_puts "-D- Deleting: $files_to_delete "
   foreach f $files_to_delete {
      file delete $f
   }

   exit $ret_val
}

proc get_absolute_path { relative_dir } {
    global tcl_platform
    if { $tcl_platform(platform) != "unix" } {
        return $relative_dir
    }

    exec cd $relative_dir
    set absolute_dir [pwd]
    exec cd -
    return $absolute_dir
}

proc fix_file_path {file_path} {
    set out_file [file normalize $file_path]
    # set out_file [string map { \{ \\\{  \} \\\} } $out_file]
    return $out_file
}

# on linux if symbolic link followed by .. normalize go crazy
proc fix_normalize_bug {dir_name} {
    global tcl_platform
    if {$tcl_platform(platform) == "windows"} {
        return $dir_name
    }
    set splited_dir_name [split $dir_name "/"]
    set output ""
    foreach dir $splited_dir_name {
	set backup $output
        if { [ string length $dir ] == 0 } {
            set output  "${output}/"
            continue
        } else {
            set output "${output}${dir}/"
        }
        set link ""
        if {![catch { set link [ file readlink $output ] } e]} {
	    if {[string index $link 0 ] == "/"} {
		set output $link
	    } else {
		set output "${backup}${link}/"
	    }
        }
        set output [ file normalize $output ]
        set output "${output}/"
    }
    return $output
}

proc fix_dir_path {dir_name} {

    set dir_name [string trim $dir_name]
    if { $dir_name == "" } {
        return $dir_name
    }
    if { [file isfile $dir_name] } {
        g_puts "-E- The given dir \"$dir_name\" is not a directory"
        cexit 1
    }

    set tmp_dir_name $dir_name
    set dir_name [fix_normalize_bug $dir_name]
    set dir_name [file normalize $dir_name]

    if {[catch {set dir_name [glob -type d $dir_name]} e]} {
        if {[catch {set dir_name [file dirname $dir_name]} e]} {
            g_puts "-E- Could not extract dir from :\"$dir_name\""
            cexit 1
        }
    }

    if {[llength $dir_name] > 1} {
        g_puts "-E- Expected dir, got list of directories \"$tmp_dir_name\""
        cexit 1
    }
    catch {set dir_name [file link $dir_name]}

    return $dir_name
}

proc fix_list_dir_path {dir_name} {

    set dir_name [string trim $dir_name]
    if { [file isfile $dir_name] } {
        g_puts "-E- The given dir \"$dir_name\" is not a directory"
        cexit 1
    }

    set dir_name [fix_normalize_bug $dir_name]
    set dir_name [file normalize $dir_name]
    if {[catch {set dir_name [glob -type d $dir_name]} e]} {
        if {[catch {set dir_name [file dirname $dir_name]} e]} {
            g_puts "-E- Could not extract dir from :\"$dir_name\""
            cexit 1
        }
    }

    set new_list_dir ""
    foreach dir $dir_name {
        if {[catch {set new_list_dir [concat $new_list_dir [fix_dir_path $dir]]} e]} {
            set new_list_dir [concat $new_list_dir $dir]
        }
    }
    set dir_name $new_list_dir

    return $dir_name
}
######################################################################
#
# MAIN
#
######################################################################


#
# Get params
#
set fw_file               ""
set fw_dir                ""
set mst_dev               ""
set conf_file             ""
set conf_dir              "MLXBURN-AUTO"
set conf_dir_list         ""
set bin_file              ""
set img_dir               ""

set prof_file             ""
set hash_file             ""
set hash_dir              ""

set input_img             ""

set additional_burn_flags ""
set additional_mic_flags  ""
set must_burn_flags       ""
set format                ""
set img_args              ""
set burn_args             ""
set dev_type              ""
set g_dev_rev             ""
set g_dev_id              ""
set exp_rom               ""
set exp_rom_dir           ""
set user_data             ""
set vpd_r_file            ""
set base_guid             ""
set base_mac              ""
set nofs                  0
set nofs_img              0
set force                 0
set burn_cmd              "b"
set remove_directives     1
set show_fwver            0
set gen_tmp_img           ""

set show_vpd              0

set vpd_access            "PCI"
set vpd_conf              ""
set vpd_check_rw          0
set vpd_prog_file         ""
set vpd_set_keyword       ""
set vpd_set_pxe           ""

set sw_sys_mode           0
set query_mode            0
set pattern               ""
set got_pattern           0
set got_range             0
set r_from                0x6c
set r_to                  0x6c
set list_only_mode        0
set use_ibspark           0
set use_mstflint          0


set files_to_delete {}

# package require getopt

#
#set ToolUsage {[-h] <-dev  mst-device|-wrimage  fw-image>  <-fw Mellanox-fw-release|-image fw-image>
#    [-conf fw-conf-file][-nofs][-format BINARY|IMAGE][-img_args args][-burn_args args][-dev_type device-type]}
#

#options
# Misc options are handled by mlxburn.
set misc_opts "h v V: tmp_img: fwver nofs_img vpd_conf: d: dev: f: fw: fw_dir: conf: c: conf_dir: conf_dir_list: image: i: wrimage: dev_type: format: img_args: burn_args: org_img exp_rom: exp_rom_dir: prof_file: user_data: force sw_sys: s: pattern: p: list l range: r: query q inband ul img_dir: vpd_r_file: base_guid: base_mac:"
set vpd_opts  "vpd vpd_prog_rw: vpd_access: vpd_rw vpd_set_keyword: set_pxe_en:"
# burn options are sent to the burning tool
set common_opts    "striped_image blank_guids"
set mic_opts       "no_vsd_swap"
set burn_opts      "allow_psid_change nofs  byte_mode use_image_ps skip_is no qq use_image_rom use_image_guids no_flash_verify ocr override_cache_replacement"
set burn_opts_flag "banks: uid: log: uids: mac: macs: guid: guids: sysguid: vsd: ndesc: bsn: pe_i2c: pe: se_i2c: se: is3_i2c: flash_params:"
set all_opts       "$misc_opts $vpd_opts $burn_opts $burn_opts_flag $mic_opts $common_opts"

# for tclchecker warning masking:
set opt ""
set arg ""

array set args {}

while { [ set err [ getopt $argv $all_opts opt arg ]] } {
   if { $err < 0 } then {
      puts "-E- Wrong usage: $opt"
      Usage
      exit 2
   } else {
       g_puts "-D- additional_mic_flags = $additional_mic_flags"

      switch -exact -- $opt {
         h {Help; exit 0}
         v {puts $version; exit 0}
         V {
            set verbosemode "-V $arg"
            switch -- $arg {
               DEBUG {
                  set G_PUTS_DEBUG 1
                  set G_PUTS_WARNING 1
                  set G_PUTS_INFORM 1
               }
               WARNING {
                  set G_PUTS_WARNING 1
                  set G_PUTS_INFORM 1
               }
               INFORM {
                  set G_PUTS_WARNING 0
                  set G_PUTS_INFORM 1
               }
               default {
                  puts ""
                  puts "-E- Wrong verbose mode ($arg). Supported modes: DEBUG, WARNING, INFORM."
                  puts ""
                  exit 1
               }
            }
         }
         f -
         fw        {set fw_file    $arg}
         fw_dir    {set fw_dir     $arg}
         dev -
         d         {set mst_dev    $arg}
         c -
         conf      {set conf_file   $arg}
         conf_dir  {set conf_dir    $arg}
         conf_dir_list {set conf_dir_list [split $arg ','];}
         i -
         image     {set input_img  $arg}
         img_dir   {set img_dir    $arg}
         wrimage   {set bin_file   $arg}
         format    {set format     $arg}
         prof_file {set prof_file   $arg}
         img_args  {append additional_mic_flags  " $arg"}
         burn_args {append additional_burn_flags " $arg"}
         dev_type  {set dev_type   $arg}
         nofs      {set nofs       1}
         force     {set force      1}
         nofs_img  {set nofs       1; set nofs_img  1}
         org_img   {set remove_directives 0}
         exp_rom     {set exp_rom     $arg}
         exp_rom_dir {set exp_rom_dir $arg}
         user_data   {set user_data  $arg}
         vpd_r_file  {set vpd_r_file $arg}
         base_guid   {set base_guid  $arg}
         base_mac    {set base_mac  $arg}
         fwver       {set show_fwver 1}
         tmp_img     {set gen_tmp_img $arg}

         vpd         {set show_vpd   1}
         vpd_access  {set vpd_access $arg; set show_vpd   1}
         vpd_conf    {set vpd_conf   $arg; set show_vpd   1}
         vpd_rw      {set vpd_check_rw  1; set show_vpd   1}
         vpd_prog_rw {set vpd_check_rw  1; set show_vpd   1; set vpd_prog_file  $arg}
        vpd_set_keyword {set vpd_check_rw 1; set show_vpd   1; set vpd_set_keyword $arg}
	    set_pxe_en      {set vpd_check_rw 1; set show_vpd   1; set vpd_set_pxe $arg}


         # The mic flags
         no_vsd_swap  { append additional_mic_flags  " -$opt" }

         # The flags that are used by both of the mic and the burner
         blank_guids -
         striped_image { append additional_burn_flags " -$opt"; append additional_mic_flags  " -$opt"}
         vsd {  set vsd_len [string length $arg]
                if { ${vsd_len} > ${G_VSD_LEN}} {
                    g_puts "-E- size of vsd ($vsd_len) is too long , it can be up to ${G_VSD_LEN}"
                    exit 1
                }
                append additional_burn_flags " -$opt \"$arg\""
                global vsd_str
                set vsd_str $arg
                # append the correct mic flag after getting the image format
             }

         # The burner flags
         nofs -
         byte_mode -
         allow_psid_change -
         skip_is -
         no -
         qq -
         use_image_ps -
         use_image_rom -
         no_flash_verify -
         use_image_guids -
         uid -
         uids -
         log -
         banks -
         guid -
         guids -
         mac -
         macs -
         sysguid -
         ndesc -
         bsn -
         pe_i2c -
         pe -
         se_i2c -
         se -
         flash_params -
         is3_i2c { append additional_burn_flags " -$opt $arg" }

         # The flags that should be used in all the 'flint' commands
         ocr -
         override_cache_replacement  { append must_burn_flags " -$opt" }

         # The sys flags
         sw_sys -
         s {set sw_sys_mode 1; set sw_sys_name $arg}
         pattern -
         p {set got_pattern 1; set pattern $arg}
         list -
         l {
            set list_only_mode 1
         }
         range -
         r {
            set range [split $arg :];
            if {[llength $range] != 2} {
                puts "Invalid range: $arg , syntax is from:to"
                exit 1
            }
            set r_from [lindex $range 0];
            set r_to [lindex $range 1];
            set got_range 1;
         }
         query -
         q {set query_mode 1}

         inband {
            set use_ibspark 1
         }

         ul {
            set use_mstflint 1
         }

         default {

            puts "Wrong usage: $opt"
            puts $ToolUsage
            exit 1
         }
      }

      set args($opt) $arg
   }
}

set left_args [ lrange $argv $optind end ]
if {[llength $left_args]} {
   puts "-E- illegal parameter(s) used : $left_args"
   Usage
   exit 1
}

if {$tcl_platform(platform) == "windows"} {
     if { $use_mstflint } {
        g_puts "-E- -ul flag is not supported on Windows"
        exit 1
     }
}

if {$mst_dev != ""} {
    if { $tcl_platform(platform) == "unix" } {
        if {[catch {set user_id [eval "exec id -u"]} e]} {
            # Support for ESXi
            if { $tcl_platform(user) != "root" } {
                g_puts "-E- Failed get the user ID: $e."
            } else {
                set user_id 0
            }
        } else {
            if {$user_id != 0} {
                puts "-E- Only root can access the MST device by this application"
                exit 1
            }
        }
    }
}


#
# Check params:
#
if {$use_ibspark} {
    g_puts "-E- The \"-inband\" flag is deprecated. To perform inband burn, simply specify the inabnd device name after the \"-d\" flag."
    exit 1

}

if {($got_pattern || $list_only_mode) && !$sw_sys_mode} {
    g_puts "-E- -pattern and -range are allowed in switch system mode only, use -sw_sys flag."
    exit 1
}
if {$query_mode && ($fw_file != "" || $fw_dir != "")} {
    g_puts "-E- -fw/fw_dir flags are not allowed in this mode."
    exit 1
}
if {$sw_sys_mode} {

    if {!$got_pattern} {
        set pattern \\*
    }
    if {$mst_dev == ""} {
       g_puts "-E- -dev flag must be provided in switch system mode."
       Usage
       exit 1
    }
}

# If mst_dev is BDF (Bus-Device-Function)
# Normlize it 0000:1a:00:0 ==> 1a:00:0
set orig_mst_dev $mst_dev
if {$mst_dev != ""} {
    set bdfPattern {\w{4}:\w{2}:\w{2}.\w{1}}
    if {[regexp $bdfPattern $mst_dev]} {
        g_puts "-D- Removing domain from the PCI address if it's zeros ... $mst_dev"
        regsub {^0000:} $mst_dev {} mst_dev
    }
}

if        {$show_fwver} {
    if {$fw_file == "" && $mst_dev == ""} {
       g_puts "-E- Either -dev or -fw flags must be specified when -fwver flag is given"
       Usage
       exit 1
    }
    if {$fw_file != "" && $mst_dev != ""} {
       g_puts "-E- Only one of -dev or -fw flags can be specified when -fwver flag is given"
       Usage
       exit 1
    }
}


if        {$show_vpd} {
    if {$mst_dev == ""} {
       g_puts "-E- -dev must be specified for VPD operations."
       Usage
       exit 1
    } else {
       if {$vpd_set_keyword != ""} {
	  set op "set_keyword"
	  set tag_only 0
       } elseif {$vpd_set_pxe != ""} {
	  set op "set_pxe_en"
          set tag_only 0
       } elseif {$vpd_prog_file != ""} {
	  set op "program"
          set tag_only 1
       } else {
          set op "read"
          set tag_only 0
       }

       set in ""
       if {[catch {
          vpd_access_setup $orig_mst_dev $vpd_access $vpd_conf
          read_vpd_to_vars $tag_only
          if {$op == "program"} {
             write_vpd_rw $vpd_prog_file ""
          } elseif {$op == "set_keyword"} {
	     set in " in"
             write_vpd_rw "" $vpd_set_keyword
          } elseif {$op == "set_pxe_en"} {
             set in " in"
	     set_vpd_pxe $vpd_set_pxe
	  } else {
             print_VPD
          }
       } e] } {
          g_puts "-E- Failed to $op$in VPD: $e"
	  g_puts "-D- errInfo=\"$errorInfo\" errCode=\"$errorCode\""
          exit 1
       }
       exit 0
    }
}

if       {($fw_file != "" || $input_img != "") && $list_only_mode} {
   g_puts "-E- -fw and -image flags cannot be specified in List mode (-list)."
   Usage
   exit 1
}

if       {$got_range && !$list_only_mode} {
    g_puts "-E- -range flag can only be specified in List mode, use the -list flag."
    Usage
    exit 1
}

if       {($fw_file == "" && $input_img == "" && $img_dir == "" && $fw_dir == "") && !$list_only_mode && !$query_mode && !$show_fwver} {
   g_puts "-E- Either a FW file, an image file, a FW directory or an image directory must be provided"
   Usage
   exit 1
} elseif {($fw_file != "" || $fw_dir != "") && ($input_img != "" || $img_dir != "")} {
   g_puts "-E- -fw/-fw_dir and -image/-img_dir flags are mutually exclusive."
   Usage
   exit 1
}

if {$input_img != "" && $img_dir != ""} {
    g_puts "-E- -image and -img_dir flags are mutually exclusive."
    exit 1
}

if {$fw_file != "" && $fw_dir != ""} {
    g_puts "-E- -fw and -fw_dir flags are mutually exclusive."
    exit 1
}

if {$conf_dir_list != "" && $conf_dir != "" && $conf_dir != "MLXBURN-AUTO"} {
    g_puts "-E- -conf_dir and -conf_dir_list flags are mutually exclusive."
    exit 1
}

if {$fw_file != "" && ![file readable $fw_file]} {
   g_puts "-E- FW file specified ($fw_file) not found."
   exit 1
}

if {$fw_dir != "" && ![file readable $fw_dir]} {
   g_puts "-E- FW Directory specified ($fw_dir) not found."
   exit 1
}

if {$fw_dir != "" && ![file isdirectory $fw_dir]} {
   g_puts "-E- FW Directory specified ($fw_dir) is not directory."
   exit 1
}

if       {$mst_dev == "" && $bin_file == "" && !$show_fwver} {
   g_puts "-E- Either MST device or output file must be provided."
   Usage
   exit 1
} elseif {$mst_dev != "" && $bin_file != ""} {
   g_puts "-E- -dev and -wrimage flags are mutually exclusive."
   Usage
   exit 1
}

if {$bin_file != "" && $fw_file == ""} {
   g_puts "-E- A fw file (xml/mlx) must be provided when generating image file."
   Usage
   exit 1
}

if {$mst_dev == "" && $conf_file == "" && !$show_fwver} {
   g_puts "-E- A fw configuration file (ini) must be provided when generating image file."
   Usage
   exit 1
}
if {$mst_dev == "" && ($exp_rom == "AUTO" || $exp_rom_dir != "")} {
   g_puts "-E- Cannot autodetect exprom from a directory when generating image file."
   exit 1
}

if {$input_img != "" && $mst_dev == ""} {
   g_puts "-E- Both input and output image specified - nothing to do ..."
   exit 1
}

if {$input_img != "" && $exp_rom != ""} {
    g_puts "-E- Expansion ROM can not be used with a binary image file. A FW file (given \
with the -fw flag) should be provided in order to embed the ROM in the FW image."
    exit 1
}

if {$exp_rom_dir != "" && $exp_rom != "AUTO"} {
	g_puts "-E- you should also use \"-exp_rom AUTO\", if you want to look for ROM in a specified directory"
	exit 1
}

#
# Get the burnt device type
# Get the device ID
if {$dev_type == ""} {

   # Getting device ID from the fw file name
   if {$fw_file != "" && $mst_dev == ""} {
      if {[regexp {^fw-(\d\d\d\d\d)} [file tail $fw_file] d1 dev_type]} {
         # hca dev type extracted
      } elseif {[regexp {^IS3FW} [file tail $fw_file]]} {
         set dev_type "47396"
      } elseif {[regexp -nocase {^fw-ConnectX2} [file tail $fw_file]]} {
         set dev_type  "$CX_MAIN_ID"
         set g_dev_rev 0xb0
      } elseif {[regexp -nocase {^fw-ConnectX3Pro} [file tail $fw_file]]} {
         set dev_type  "$CX_MAIN_ID"
         set g_dev_id  "$CX3_PRO_HW_ID"
      } elseif {[regexp -nocase {^fw-ConnectX3} [file tail $fw_file]]} {
         set dev_type  "$CX_MAIN_ID"
         set g_dev_id  "$CX3_HW_ID"
      }  elseif {[regexp -nocase {^fw-is4} [file tail $fw_file]]} {
         set dev_type "$IS4_HW_ID"
      } elseif {[regexp -nocase {^fw-sx} [file tail $fw_file]]} {
         set dev_type "$SX_HW_ID"
      } elseif {[regexp -nocase {^fw-SwitchIB} [file tail $fw_file]]} {
         set dev_type "$SWITCHIB_HW_ID"
      } elseif {[regexp -nocase {^fw-SwitchEN} [file tail $fw_file]]} {
         set dev_type "$SPECTRUM_HW_ID"
      } elseif {[regexp -nocase {^fw-Spectrum} [file tail $fw_file]]} {
         set dev_type "$SPECTRUM_HW_ID"
      } elseif {[regexp -nocase {^fw-bridgex} [file tail $fw_file]] || [regexp -nocase {^fw-PhyX} [file tail $fw_file]]} {
        set dev_type "6100"
      } elseif {[regexp -nocase {^fw-ConnectIB} [file tail $fw_file]]} {
         set dev_type "$CIB_HW_ID"
      } elseif {[regexp -nocase {^fw-ConnectX4Lx} [file tail $fw_file]]} {
         set dev_type "$CX4LX_HW_ID"
      } elseif {[regexp -nocase {^fw-ConnectX4} [file tail $fw_file]]} {
         set dev_type "$CX4_HW_ID"
      } else {
         set file_ext [file extension $fw_file]
         if { !($file_ext == ".xml") && !($file_ext == ".mlx") && !($file_ext == ".BIN") } {
            g_puts "-E- Illegal FW file extension (\"$fw_file\"), Supported extensions are: .xml, .mlx"
         } else {
            g_puts "-E- Can't extract burnt device type from fw file $fw_file. Please specify device type explicitly. (E.G. -dev_type 23108)"
         }

         exit 1
      }

   # Getting the device ID from the HW by reding address 0xf0014 on the cr-space.
   } else {
        if {[regexp "mlnxsw-" $mst_dev]} {
            g_puts  "-W- Cr-space read is not supported over 'mlnxsw' device, assuming it's an SX device..."
            set dev_type  ${SX_HW_ID}
            set g_dev_rev 0
        } else {
            if {[catch {set dev_list [get_dev_id $mst_dev]} e]} {
                if {$e == "0xbad0cafe"} {
                    g_puts "-E- Can not auto detect device type: CR-space is locked."
                } else {
                    g_puts "-E- Can not auto detect device type: $e. Please check the given device."
                }
                exit 1
            }
            set dev_type  [lindex $dev_list 0]
            set g_dev_id  $dev_type
            set g_dev_rev [lindex $dev_list 1]
        }
   }
}
g_puts "-D- dev_type: $dev_type"

# Map given dev type to the types used by MIC
if {$dev_type != ""} {
    if {[is_is4 $dev_type]} {
        set dev_type $IS4_HW_ID
    }
    if {[is_infinihost_iii_lx $dev_type]} {
        set dev_type 25204
    }
    if {[is_sx $dev_type]} {
        set dev_type $SX_HW_ID
    }

    if {[is_switchib $dev_type]} {
        set dev_type $SWITCHIB_HW_ID
    }
    
    if {[is_spectrum $dev_type]} {
        set dev_type $SPECTRUM_HW_ID
    }
    
    if {[is_connectib $dev_type]} {
        set dev_type $CIB_HW_ID
    }

    if {[is_connectx4 $dev_type]} {
        set dev_type $CX4_HW_ID
    }

    if {[is_connectx4lx $dev_type]} {
        set dev_type $CX4LX_HW_ID
    }
}

if {$dev_type == 43132} {
   g_puts "-E- Device type MT43132 (InfiniScale) is no longer supported by this tool version."
   exit 1
}

if {![is_flash $dev_type] && $dev_type != 47396 && !$show_fwver} {
   g_puts "-E- Device type $dev_type is not a Mellanox device."
   exit 1
}

# set the correct mic vsd parameter if vsd flag was specified

if {([info exists vsd_str]) && ($vsd_str != "")} {
    if {[is_fs3_dev $dev_type]} {
        append additional_mic_flags " -image_info.vsd \"$vsd_str\""
    } else {
        append additional_mic_flags " -ADAPTER.adapter_vsd \"$vsd_str\""
    }
}

if {[is_flash $dev_type]} {
   set burner "flint"
   if {$use_mstflint} {
       set burner "mstflint"
   }
} else {
   set burner "spark"
}

#
# platform specific:

#
if { $tcl_platform(platform) == "windows" } {
   if {[info exist env(TEMP)]} {
      set tmp_dir [fix_dir_path $env(TEMP)]
   } else {
      set tmp_dir "c:/temp"
   }
   set t2a "t2a"

   # Normalize paths:
   set fw_file    [fix_file_path $fw_file   ]
   set bin_file   [fix_file_path $bin_file  ]
   set conf_file  [fix_file_path $conf_file ]
   set input_img  [fix_file_path $input_img ]

   set img_dir    [fix_dir_path $img_dir ]

   if { $exp_rom != "AUTO" } {
       set exp_rom    [fix_file_path $exp_rom   ]
   }
   set user_data  [fix_file_path $user_data ]
   if { $conf_dir != "MLXBURN-AUTO" } {
       set conf_dir   [fix_dir_path $conf_dir  ]
   }
   set fw_dir     [fix_dir_path $fw_dir    ]
   set exp_rom_dir    [fix_dir_path $exp_rom_dir   ]


} else {
   set tmp_dir "/tmp"
   set t2a "t2a"

    if { $conf_dir != "MLXBURN-AUTO" && $conf_dir != ""} {
	set conf_dir [fix_dir_path $conf_dir]
    }
    if { $fw_dir != "" } {
	set fw_dir [fix_dir_path $fw_dir]
    }
    
    if { $exp_rom_dir != "" } {
	set exp_rom_dir    [fix_dir_path $exp_rom_dir   ]
    }
}

g_puts "-D- tmp_dir = $tmp_dir"

if { $conf_dir_list != "" } {
    set new_conf_dir ""
    foreach dir $conf_dir_list {
	catch {set new_conf_dir [concat $new_conf_dir [fix_list_dir_path $dir] ]}
    }
    set conf_dir_list $new_conf_dir
}

if {!$sw_sys_mode} {
    if {$show_fwver} {
        if {$fw_file != ""} {
           set fwver [generate_image 1]
        } else {
           if {[catch {set fwver [get_fw_ver $mst_dev]} e]} {
               g_puts "-E- Failed to get FW version: $e"
               cexit 1
           }
        }
        g_puts "-I- FW Version: $fwver"
        cexit 0
    }

    if {$query_mode} {
        cexit [query_image_hca]
    }

    if {$img_dir != ""} {
        set bin_file [select_binary_file $mst_dev $img_dir]
        g_puts "-I- Using auto detected image file : $bin_file"
    } elseif {$input_img == ""} {
        set bin_file [generate_image]
    } else {
        set bin_file $input_img
    }
    
    if {$gen_tmp_img != ""} {
        g_puts "-I- Copy the generated image into $gen_tmp_img"
        if {[catch {file copy -force $bin_file $gen_tmp_img} cpyError]} {
            g_puts "-E- Failed to copy generated image: $cpyError"
            cexit 1
        }
        cexit 0
    }
    	
    if {$mst_dev == ""} {
       # Generate only - no burn
       g_puts "-I- Image generation completed successfully."
       cexit 0
    } else {
       burn_image $pattern
    }
} else {
    set status {}; set output {}

    #check if the system name provided exists in isw db.
    if {![g_exec "isw -d $mst_dev -s $sw_sys_name -n" status output]} {
       g_puts "-E- Failed routing switch system I2C bus: [lindex $status end]."
       cexit 1
    }
    set swSystemDesc [lindex $output 0]
    g_puts "-I- $swSystemDesc"

    set sharkMode   [regexp {MTS3600} $swSystemDesc]

    if {!$got_range} {
        if {$sharkMode} {
            set r_from 0x48
            set r_to   0x48
        }
    }

    #check if the pattern is legal.
    if {![g_exec "isw -d $mst_dev -s $sw_sys_name -p $pattern -l 0x6c" status output]} {
       g_puts "-E- Failed routing switch system I2C bus: [lindex $status end]."
       cexit 1
    }
    #check if the range is legal.
    if {![g_exec "isw -d $mst_dev -s $sw_sys_name -p $pattern -l $r_from $r_to" status output]} {
       g_puts "-E- Failed routing switch system I2C bus: [lindex $status end]."
       cexit 1
    }
    set target_boards [lsort -dictionary $output]
    if {$list_only_mode} {
        if {$sharkMode} {
            set devI2cAddr "48"
        } else {
            set devI2cAddr "6c"
        }
        if {[format %x $r_from] == $devI2cAddr && [format %x $r_to] == $devI2cAddr} {
            g_puts "-I- List of switch devices detected:"
            foreach slave $target_boards {
                g_puts "-I- [lindex $slave 0]"
            }
        } else {
            g_puts "-I- List only mode - I2C slaves detected:"
            foreach slave $target_boards {
                g_puts "-I- $slave"
            }
        }
        cexit 0
    }

    g_puts "-D- Pattern matches [llength $target_boards] devices."
    set header 1
    foreach slave $target_boards {
        set pat [lindex $slave 0]
        #g_puts "-I- $pat"
        if {![g_exec "isw -d $mst_dev -s $sw_sys_name -p $pat" status output]} {
           g_puts "-E- Failure reaching Switch device at $pat : [lindex $status end]"
           cexit 1
        }

        if {$show_fwver} {
            if {[catch {set fwver [get_fw_ver $mst_dev]} e]} {
                g_puts [format "-E- %-6s  Failed to get FW version: $e" $pat]
            } else {
                g_puts [format "-I- %-6s  $fwver" $pat]
            }
        } elseif {$query_mode} {
            query_image_switch $pat $header
            if {$header} {
                set header 0
            }
        } else {

            puts ""
            if {$pat != ""} {
               g_puts "-I- Burning switch device at $pat ..."
            }
            if {$input_img == ""} {
                set bin_file [generate_image]
                #set conf_file ""
            } else {
                set bin_file $input_img
            }
            burn_image $pat
        }
    }
}

cexit 0
