#!/bin/bash
#
# vpd_show - show the contents of VPD in human-readable form
#
# This script is tailored to IBM BladeCenter platforms.
# The functions in the first part of the script are fairly generic, but the
# farther you go into the script, the more IBM-specific they become.
#

VPD_READ=vpd_read_live

declare -a VPD
TMP_VPD=/tmp/vpd.$$

function vpd_array_from_file_line {
  declare -i BASE=0x${1%:}
  declare -i END=${BASE}+$#-1
  declare -i n
  for ((n=BASE;n<END;++n))
  do
    shift
    echo "VPD[$n]=$1" >> $TMP_VPD
  done
}

function vpd_array_from_file {
  sed -e '/embeddedVpdRead/d' -e '/^[[:blank:]]*$/d' -e '/^[[:blank:]]*;/d' $1 |
  while read LINE
  do
    vpd_array_from_file_line $LINE
  done
  VPD_READ=vpd_read_array
  source $TMP_VPD
  rm -f $TMP_VPD
}

function vpd_read_array {
  declare -i BLOCK=0x400*$1
  declare -i BEGIN=${BLOCK}+0x$2
  declare -i LENGTH=$3
  declare -i END=${BEGIN}+${LENGTH}
  declare -i v
  for ((v=BEGIN;v!=END;++v))
  do
    printf " %s" "${VPD[$v]}"
  done
  printf "\n"
}

function hex {
  printf "%x" $1
}

function vpd_read_live {
  /fabos/share/vpdRead -offset $(hex $((0x400*$1+0x$2))) -size $(hex $3) |
  sed -e '/embeddedVpdRead/d' -e 's/^....: //' -e 's/[[:blank:]]*$//'
}

# vpd_read block_number offset_hex length_decimal
function vpd_read {
  $VPD_READ $1 $2 $3
}

function check_for_zero {
  declare -i offset=0
  for x in $(vpd_read $1 $2 $3)
  do
    declare -i X=0x$x
    if [ $X -ne 0 ]
    then
      printf "nonzero byte %s at offset %d" $x $offset
      return
    fi
  done
  printf "OK"
}

function show_rsrvd {
  declare OK=$(check_for_zero $1 $2 $3)
  printf "${FRMT_1234}%s\n" $1 $2 $3 "reserved, must be zero" "$OK"
}

function show_n_a {
  declare OK=$(check_for_zero $1 $2 $3)
  printf "${FRMT_1234}%s\n" $1 $2 $3 "N/A ($4)" "$OK"
}

function big_endian_number {
  declare -i number=0
  while [ $# != 0 ]
  do
    declare -i digit=0x$1
    let "number *= 256"
    let "number += digit"
    shift
  done
  echo $number
}

function show_decimal {
  declare -i n=$(big_endian_number $(vpd_read $1 $2 $3))
  printf "${FRMT_1234}%d\n" $1 $2 $3 "$4" $n
}

function show_hex {
  declare h=$(vpd_read $1 $2 $3 | tr '[:lower:]' '[:upper:]' | tr -d '\n[:blank:]')
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "$h"
}

function major_minor {
  declare -i maj=0x$1
  declare -i min=0x$2
  printf "%d.%.2d\n" $maj $min
}

function show_version {
  declare vsn=$(major_minor $(vpd_read $1 $2 $3))
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "$vsn"
}

function ascii_from_hex_1_byte {
  declare -i n=0x$1
  if [ $n -gt 31 -a $n -lt 127 ]
  then
    printf "%b" `printf "\\%o" $n`
  else
    printf "."
  fi
}

function ascii_from_hex_n_bytes {
  declare -i len=0x$1
  for ((i=0;i!=len;++i))
  do
    shift
    ascii_from_hex_1_byte $1
  done
}

function ascii_from_hex {
  for h
  do
    ascii_from_hex_1_byte $h
  done
}

function show_ascii {
  declare str=$(ascii_from_hex $(vpd_read $1 $2 $3))
  declare -i nrem=$3-32
  printf "${FRMT_1234}\"%s\"\n" $1 $2 $3 "$4" "${str:0:32}"
  while [ $nrem -gt 0 ]
  do
    str="${str:32}"
    printf "${FRMT_1234}\"%s\"\n" " " "    " " " " " "${str:0:32}"
    let "nrem-=32"
  done
}

function format_macs {
  printf "%s:%s:%s:%s:%s:%s\n" $1 $2 $3 $4 $5 $6
  while [ $# -gt 6 ]
  do
    shift 6
    printf "${FRMT_____}%s:%s:%s:%s:%s:%s\n" $1 $2 $3 $4 $5 $6
  done
}

function show_macs {
  declare macs=$(format_macs $(vpd_read $1 $2 $3 | tr '[:lower:]' '[:upper:]'))
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "$macs"
}

function show_type {
  TYPE=$(vpd_read $1 $2 $3)
  declare t
  case $TYPE in
    11) t="Ethernet Switch" ;;
    12) t="Fibre Channel Switch" ;;
    1a) t="HSSM" ;;
    90) t="ScSM" ;;
    *)  t="Unknown" ;;
  esac
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "$t"
}

function show_typesub {
  TYPE_SUB=$(vpd_read $1 $2 $3)
  declare s="Unknown"
  case $TYPE in
  12)
      case $TYPE_SUB in
      20) s="Default" ;;
      esac
      ;;
  1a)
      case $TYPE_SUB in
      30) s="Ethernet" ;;
      40) s="Fibre Channel" ;;
      esac
      ;;
  90)
      case $TYPE_SUB in
      40) s="Ethernet" ;;
      50) s="Fibre Channel" ;;
      esac
      ;;
  esac
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "$s"
}

function trim_string {
  declare -i n=$#-1
  declare -a c=($*)
  while [ $n -gt 0 ]
  do
    if [ "${c[$n]}" = "20" -o "${c[$n]}" = "00" ]
    then
      unset c[$n]
    else
      break
    fi
    let "n--"
  done
  echo "${c[@]}"
}

function show_codelvl {
  declare lvl=$(ascii_from_hex $(trim_string $(vpd_read $1 $2 $3)))
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "$lvl"
}

function format_port {
  case $1 in
  00) printf "n/a" ;;
  20) printf "FC,"
      case $2 in
      10) printf "16G" ;;
      esac
      ;;
  *)  printf "unkown" ;;
  esac
}

function format_ports {
  declare v
  case $1 in
  10) v="Version 1" ;;
  *)  v="Unknown Version $1" ;;
  esac
  printf "%s\n" "$v"
  shift 16
  declare -i blade=1
  while [ $blade -le 14 ]
  do
    printf "${FRMT_____}Blade %-2d " $blade
    printf "%-6s " $(format_port $1 $2)
    shift 2
    printf "%-6s " $(format_port $1 $2)
    shift 2
    printf "%-6s " $(format_port $1 $2)
    shift 2
    printf "%s\n" $(format_port $1 $2)
    shift 2
    let "blade++"
  done
}

function show_intport {
  declare prt=$(format_ports $(vpd_read $1 $2 $3))
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "$prt"
}

function format_wwn {
  declare t
  case $1 in
  01) t="WWN" ;;
  *)  v="Unknown Type $1" ;;
  esac
  printf "%s %s:%s:%s:%s:%s:%s:%s:%s\n" $t $2 $3 $4 $5 $6 $7 $8 $9
}

function show_wwn {
  declare wwn=$(format_wwn $(vpd_read $1 $2 $3))
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "$wwn"
}

function format_one_portdrt {
  declare r
  case $1 in
  0) r="SDR" ;;
  *)  r=" ? " ;;
  esac
  printf "$r"
}

function format_portdrt {
  declare -i n
  for ((n=0;n!=28;++n))
  do
    declare L=$(format_one_portdrt ${1:0:1})
    declare R=$(format_one_portdrt ${1:1:1})
    printf "%s %s " "$L" "$R"
    if [ $((n%4)) -eq 3 -a $n -ne 27 ]
    then
      printf "\n${FRMT_____}"
    fi
    shift
  done
}

function show_portdrt {
  declare drt=$(format_portdrt $(vpd_read $1 $2 $3))
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "$drt"
}

function bits_from_bytes {
  declare input_byte
  for input_byte in $*
  do
    declare -i byte=0x$input_byte
    declare -i i
    for ((i=7;i>=0;--i))
    do
      printf "%d " $((1&(byte>>i)))
    done
  done
}

function bit_filter {
	LIST_SEPARATOR=
	while [ $# -gt 0 ]
	do
	  if [ "$1" = "1" ]
	  then
	    printf "${LIST_SEPARATOR}$2"
	    LIST_SEPARATOR=", "
	  fi
	  shift 2
	done
	if [ "$LIST_SEPARATOR" != ", " ]
	then
	  echo "Reserved"
	fi
}

function show_sm_cap {
  declare C="$(vpd_read $1 $2 $3)"
  declare X=$(echo $C | tr -d '\n[:blank:]' | tr '[:lower:]' '[:upper:]')
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "${X:0:32}"
  printf "${FRMT_____}%s\n" "${X:32}"
  declare -a B=($(bits_from_bytes $C))
  printf "${FRMT____1}%d\n"   0     "VPD blocks 4-7"           ${B[0]}
  printf "${FRMT____1}%d\n"   1     "power meter 1"            ${B[1]}
  printf "${FRMT____1}%d\n"   2     "power meter 2"            ${B[2]}
  printf "${FRMT____1}%d\n"   3     "HW Component Image Map"   ${B[3]}
  printf "${FRMT____1}%d\n"   4     "daughter cards"           ${B[4]}
  printf "${FRMT____1}%d\n"   5     "pluggable SFP"            ${B[5]}
  printf "${FRMT____1}%d\n"   6     "PB2 Status"               ${B[6]}
  printf "${FRMT____1}%d\n"   7     "SM_OT"                    ${B[7]}
  printf "${FRMT____1}%d\n"   8     "SFP reporting needs PD2"  ${B[8]}
  declare v
  case "${B[9]}${B[10]}" in
  01) v="WWN" ;;
  *)  v="? (${B[9]}${B[10]})" ;;
  esac
  printf "${FRMT____2}%s\n"   9 10  "WWN/GUID/SASID"           "$v"
  printf "${FRMT____1}%d\n"  11     "Thermal monitoring"       ${B[11]}
  printf "${FRMT____1}%d\n"  15     "Dual Fuse detection"      ${B[15]}
  if [ "${X:4:4}" = "0000" ]
  then
    v="not supported"
  else
    v=$(bit_filter ${B[16]} "power off" ${B[19]} "ext cfg" ${B[20]} "ext port" ${B[22]} "RFD" ${B[26]} "IP")
  fi
  printf "${FRMT____2}%s\n"  16 31  "PRM"                      "$v"
  printf "${FRMT____1}%d\n"  32     "STM"                      ${B[32]}
  printf "${FRMT____1}%d\n"  33     "POST timeout extension"   ${B[33]}
  printf "${FRMT____1}%d\n"  34     "standard POST"            ${B[34]}
  printf "${FRMT____1}%d\n"  35     "extended POST"            ${B[35]}
  printf "${FRMT____1}%d\n"  36     "full POST"                ${B[36]}
  printf "${FRMT____1}%d\n"  37     "internal MM port"         ${B[37]}
  printf "${FRMT____1}%d\n"  38     "reports current IP cnfg"  ${B[38]}
  printf "${FRMT____1}%d\n"  39     "EHCM-L3"                  ${B[39]}
  printf "${FRMT____1}%d\n"  64     "HTTP"                     ${B[64]}
  printf "${FRMT____1}%d\n"  65     "HTTPs"                    ${B[65]}
  printf "${FRMT____1}%d\n"  66     "Telnet CLI"               ${B[66]}
  printf "${FRMT____1}%d\n"  67     "SSH CLI"                  ${B[67]}
  printf "${FRMT____1}%d\n"  68     "SNMPv1"                   ${B[68]}
  printf "${FRMT____1}%d\n"  69     "SNMPv3"                   ${B[69]}
  printf "${FRMT____1}%d\n"  70     "Proprietary management"   ${B[70]}
  printf "${FRMT____1}%d\n"  71     "NTP client"               ${B[71]}
  printf "${FRMT____1}%d\n"  72     "NTP server"               ${B[72]}
  printf "${FRMT____1}%d\n"  73     "FTP client"               ${B[73]}
  printf "${FRMT____1}%d\n"  74     "FTP server"               ${B[74]}
  printf "${FRMT____1}%d\n"  75     "TFTP client"              ${B[75]}
  printf "${FRMT____1}%d\n"  76     "TFTP server"              ${B[76]}
  printf "${FRMT____1}%d\n"  77     "LDAP"                     ${B[77]}
  printf "${FRMT____1}%d\n"  78     "Radius"                   ${B[78]}
  printf "${FRMT____1}%d\n"  79     "TACAS"                    ${B[79]}
  printf "${FRMT____1}%d\n"  80     "Syslog"                   ${B[80]}
  printf "${FRMT____1}%d\n"  81     "LDAPs"                    ${B[81]}
  printf "${FRMT____1}%d\n"  82     "DHCP using MM Ethernet"   ${B[82]}
  printf "${FRMT____1}%d\n"  83     "CIM: CIM - XML"           ${B[83]}
  printf "${FRMT____1}%d\n"  84     "CIM: SMI-s"               ${B[84]}
  printf "${FRMT____1}%d\n"  85     "CIM: rCMPI"               ${B[85]}
  printf "${FRMT____1}%d\n"  86     "CIM: reserved"            ${B[86]}
  printf "${FRMT____1}%d\n"  87     "sFTP client"              ${B[87]}
  printf "${FRMT____1}%d\n"  88     "sFTP server"              ${B[88]}
  printf "${FRMT____1}%d\n"  89     "SLP"                      ${B[89]}
  printf "${FRMT____2}%s\n" 120 127 "CR(0:7)"\
    "${B[120]}${B[121]}${B[122]}${B[123]}${B[124]}${B[125]}${B[126]}${B[127]}"
  printf "${FRMT____2}%s\n" 128 135 "ECR(0:7)"\
    "${B[128]}${B[129]}${B[130]}${B[131]}${B[132]}${B[133]}${B[134]}${B[135]}"
  printf "${FRMT____2}%s\n" 136 143 "SR(0:7)"\
    "${B[136]}${B[137]}${B[138]}${B[139]}${B[140]}${B[141]}${B[142]}${B[143]}"
  printf "${FRMT____2}%s\n" 144 151 "ESR(0:7)"\
    "${B[144]}${B[145]}${B[146]}${B[147]}${B[148]}${B[149]}${B[150]}${B[151]}"
  printf "${FRMT____2}%s\n" 152 159 "reserved"\
    "${B[152]}${B[153]}${B[154]}${B[155]}${B[156]}${B[157]}${B[158]}${B[159]}"
  case "${B[160]}${B[161]}" in
  00) v="not designed for ISL" ;;
  01) v="wired, no function" ;;
  10) v="supported, configurable" ;;
  00) v="Reserved" ;;
  esac
  printf "${FRMT____2}%s\n" 160 161 "Inter-Switch Link (ISL)"  "$v"
  if [ "${B[162]}${B[163]}${B[164]}${B[165]}" = "0000" ]
  then
    v="not supported"
  else
    v=$(bit_filter ${B[162]} "L1 v1" ${B[163]}  "L1 v3" ${B[164]} "L2 v1" ${B[165]} "L2 v3")
  fi
  printf "${FRMT____2}%s\n" 162 165 "EHCM SNMP"               "$v"
  printf "${FRMT____1}%d\n" 166     "VLAN-4095"               ${B[166]}
  case "${B[167]}${B[168]}" in
  00) v="not supported" ;;
  01) v="one additional VLAN" ;;
  10) v="multiple additional VLANs" ;;
  00) v="Reserved" ;;
  esac
  printf "${FRMT____2}%s\n" 167 168 "Chassis Internal Netwrk" "$v"
  printf "${FRMT____1}%d\n" 169     "External Location LED"   ${B[169]}
  printf "${FRMT____1}%d\n" 170     "VCO & PD1 Reset"         ${B[170]}
  printf "${FRMT____1}%d\n" 171     "update firmware version" ${B[171]}
  case "${B[172]}${B[173]}" in
  00) v="legacy" ;;
  01) v="version 1" ;;
  *)  v="Reserved" ;;
  esac
  printf "${FRMT____2}%s\n" 172 173 "firmware version ID"     "$v"
  case "${B[174]}" in
  0) v="100Mbps" ;;
  1) v="1Gbps" ;;
  esac
  printf "${FRMT____1}%s\n" 174     "internal MM speed"       "$v"
  printf "${FRMT____1}%d\n" 175     "external serial port"    ${B[175]}
  case "${B[176]}${B[177]}${B[178]}${B[179]}" in
  0000) v="not supported" ;;
  0001) v="30 seconds" ;;
  *)    v="Reserved" ;;
  esac
  printf "${FRMT____2}%s\n" 176 179 "Power meter 1 period"    "$v"
  case "${B[180]}${B[181]}${B[182]}${B[183]}" in
  0000) v="not supported" ;;
  0001) v="1 second" ;;
  *)    v="Reserved" ;;
  esac
  printf "${FRMT____2}%s\n" 180 183 "Power meter 2 period"    "$v"
  case "${B[184]}${B[185]}${B[186]}${B[187]}" in
  0000) v="not valid" ;;
  0001) v="1%" ;;
  0010) v="2%" ;;
  0011) v="3%" ;;
  0100) v="4%" ;;
  0101) v="5%" ;;
  *)    v="Reserved" ;;
  esac
  printf "${FRMT____2}%s\n" 184 187 "Power meter accuracy"    "$v"
  case "${B[188]}${B[189]}${B[190]}${B[191]}" in
  0000) v="LIOM" ;;
  0001) v="SIOM" ;;
  0010) v="HSIOM" ;;
  *)    v="Reserved" ;;
  esac
  printf "${FRMT____2}%s\n" 188 191 "Secure IOM Type"         "$v"
  printf "${FRMT____1}%d\n" 193     "external Ethernet port"  ${B[193]}
  printf "${FRMT____1}%d\n" 194     "UI allows IP change"     ${B[194]}
  printf "${FRMT____1}%d\n" 195     "IPv4 Static"             ${B[195]}
  printf "${FRMT____1}%d\n" 196     "DHCPv4"                  ${B[196]}
  printf "${FRMT____1}%d\n" 197     "IPv6"                    ${B[197]}
  printf "${FRMT____1}%d\n" 208     "I2C fw update phys PROM" ${B[208]}
  printf "${FRMT____1}%d\n" 209     "I2C fw update virt PROM" ${B[209]}
}

function show_ehcmudp {
  declare -a pp=($(vpd_read $1 $2 $3))
  declare -i p1=$(big_endian_number ${pp[0]} ${pp[1]})
  declare -i p2=$(big_endian_number ${pp[2]} ${pp[3]})
  printf "${FRMT_1234}req: %d, trap: %d\n" $1 $2 $3 "$4" $p1 $p2
}

function show_t_loc {
  declare loc
  case "$(vpd_read $1 $2 $3)" in
  00) loc="inlet" ;;
  01) loc="exhaust" ;;
  02) loc="within ASIC" ;;
  03) loc="on heat sink" ;;
  ff) loc="not implemented" ;;
  *)  loc="undefined" ;;
  esac
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "$loc"
}

function show_t_thrsh {
  declare -a tt=($(vpd_read $1 $2 $3))
  declare -i t0=0x${tt[0]}
  declare -i t1=0x${tt[1]}
  declare -i t2=0x${tt[2]}
  declare -i t3=0x${tt[3]}
  printf "${FRMT_1234}%d, %d, %d, %d\n" $1 $2 $3 "$4" $t0 $t1 $t2 $t3
}

function show_ip_acq {
  declare v
  declare acq=$(vpd_read $1 $2 $3)
  case $acq in
  00) v="Disable IPv4" ;;
  01) v="Static IPv4" ;;
  a5) v="Use Change Mask" ;;
  e0) v="Enable EHCM-L2 SNMPv1 user" ;;
  e1) v="Disable EHCM-L2 SNMPv1 user" ;;
  e7) v="Enable EHCM-L2 SNMPv3 user" ;;
  e8) v="Disable EHCM-L2 SNMPv3 user" ;;
  *) v="Reserved" ;;
  esac
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "$v"
}

function show_ip_mask {
  declare -a b=($(bits_from_bytes $(vpd_read $1 $2 $3)))
  declare mask=$(bit_filter ${b[1]} "Static IPv4" ${b[2]} "DHCPv4"\
    ${b[9]} "Static IPv6" ${b[10]} "DHCPv6" ${b[11]} "Stateless Auto Cnfg")
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "$mask"
}

function format_ipv4 {
  declare -i v1=0x$1
  declare -i v2=0x$2
  declare -i v3=0x$3
  declare -i v4=0x$4
  printf "%d.%d.%d.%d" $v1 $v2 $v3 $v4
}

function show_ipv4 {
  declare addr=$(format_ipv4 $(vpd_read $1 $2 4))
  declare -i offset=4+0x$2
  declare subn=$(format_ipv4 $(vpd_read $1 $(hex $offset) 4))
  let "offset+=4"
  declare gate=$(format_ipv4 $(vpd_read $1 $(hex $offset) 4))
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "$addr"
  printf "${FRMT_____}%s\n" "$subn"
  printf "${FRMT_____}%s\n" "$gate"
}

# TBD: pretty print IPv6 addresses according to RFC 5952
function format_ipv6 {
  declare -i v1=0x$1$2
  declare -i v2=0x$3$4
  declare -i v3=0x$5$6
  declare -i v4=0x$7$8
  declare -i v5=0x$9${10}
  declare -i v6=0x${11}${12}
  declare -i v7=0x${13}${14}
  declare -i v8=0x${15}${16}
  printf "%x:%x:%x:%x:%x:%x:%x:%x" $v1 $v2 $v3 $v4 $v5 $v6 $v7 $v8
}

function show_ipv6 {
  declare addr=$(format_ipv6 $(vpd_read $1 $2 10))
  declare -i offset=16+0x$2
  declare -i prfx=$(vpd_read $1 $(hex $offset) 1)
  let "offset++"
  declare gtwy=$(format_ipv6 $(vpd_read $1 $(hex $offset) 10))
  printf "${FRMT_1234}%s/%d\n" $1 $2 $3 "$4" "$addr" $prfx
  printf "${FRMT_____}%s\n" "$gtwy"
}

function show_histptr {
  declare v
  declare -i p=0x$(vpd_read $1  $2 $3)
  if [ $p = "20" ]
  then
    v="no history"
  else
    v=$(printf "%x" $p)
  fi
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "$v"
}

function show_one_hist {
  declare FRMT_HIST_1x="            [%x]"
  declare FRMT_HIST_xx=" %-28s%s\n"
  declare FRMT_HIST_n="                %-28s"
  printf "${FRMT_HIST_1x}" $1
  shift
  if [ $1 = "20" -a ${26} = "20" ]
  then
    printf "${FRMT_HIST_xx}" "" "-"
  else
    declare uuid="${1}${2}${3}${4}${5}${6}${7}${8}${9}${10}${11}${12}${13}${14}${15}${16}"
    printf "${FRMT_HIST_xx}" "UUID" "$uuid"
    printf "${FRMT_HIST_n}%d\n" "slot ID" 0x${17}
    declare D
    case ${21} in
    01) D="Mon" ;;
    02) D="Tue" ;;
    03) D="Wed" ;;
    04) D="Thu" ;;
    05) D="Fri" ;;
    06) D="Sat" ;;
    07) D="Sun" ;;
    *)  D="-"   ;;
    esac
    printf "${FRMT_HIST_n}%d/%d/%d(%s) %.2d:%.2d:%.2d\n"\
      "timestamp (Y/M/D(W) H:M:S)"\
      0x${18} 0x${19} 0x${20} $D 0x${22} 0x${23} 0x${24}
    printf "${FRMT_HIST_n}%s, %s\n" "inform, subtype" ${25} ${26}
  fi
}

function show_histlog {
  printf "${FRMT_1234}\n" $1 $2 $3 "$4"
  declare -i x=0
  set $(vpd_read $1 $2 $3 | tr '[:lower:]' '[:upper:]')
  while [ $x -lt 16 ]
  do
    show_one_hist $x $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14}\
      ${15} ${16} ${17} ${18} ${19} ${20} ${21} ${22} ${23} ${24} ${25} ${26}
    shift 26
    let "++x"
  done
}

function show_mm_cap {
  declare C="$(vpd_read $1 $2 $3)"
  declare X=$(echo $C | tr -d '\n[:blank:]' | tr '[:lower:]' '[:upper:]')
  printf "${FRMT_1234}%s\n" $1 $2 $3 "$4" "${X}"
  declare -a B=($(bits_from_bytes $C))
  printf "${FRMT____1}%d\n"   0   "reserved"          ${B[0]}
  printf "${FRMT____1}%d\n"   1   "STM"               ${B[1]}
  printf "${FRMT____2}%s\n" 2 15  "reserved"\
    "${B[2]}${B[3]}${B[4]}${B[5]}${B[6]}${B[7]}${B[8]}${B[9]}${B[10]}${B[11]}${B[12]}${B[13]}${B[14]}${B[15]}"
  printf "${FRMT____1}%d\n"   16  "PM Permission(0)"  ${B[16]}
  printf "${FRMT____2}%s\n" 17 18 "reserved"         "${B[17]}${B[18]}"
  printf "${FRMT____1}%d\n"   19  "PM Permission(3)"  ${B[19]}
  printf "${FRMT____1}%d\n"   20  "PM Permission(4)"  ${B[20]}
  printf "${FRMT____1}%d\n"   21  "reserved"          ${B[21]}
  printf "${FRMT____1}%d\n"   22  "PM Permission(6)"  ${B[22]}
  printf "${FRMT____2}%s\n" 23 25 "reserved"         "${B[23]}${B[24]}${B[25]}"
  printf "${FRMT____1}%d\n"   26  "PM Permission(10)" ${B[26]}
  printf "${FRMT____2}%s\n" 27 31 "reserved"\
    "${B[27]}${B[28]}${B[29]}${B[30]}${B[31]}"
  printf "${FRMT____2}%s\n" 32 39 "CR(0:7)"\
    "${B[32]}${B[33]}${B[34]}${B[35]}${B[36]}${B[37]}${B[38]}${B[39]}"
  printf "${FRMT____2}%s\n" 40 47 "ECR(0:7)"\
    "${B[40]}${B[41]}${B[42]}${B[43]}${B[44]}${B[45]}${B[46]}${B[47]}"
  printf "${FRMT____2}%s\n" 48 55 "SR(0:7)"\
    "${B[48]}${B[49]}${B[50]}${B[51]}${B[52]}${B[53]}${B[54]}${B[55]}"
  printf "${FRMT____2}%s\n" 56 63 "ESR(0:7)"\
    "${B[56]}${B[57]}${B[58]}${B[59]}${B[60]}${B[61]}${B[62]}${B[63]}"
}

function show_chtlv {
  printf "${FRMT_1234}\n" $1 $2 $3 "$4"
  declare FRMT_TLV="            %-32s"
  declare -i x=0x$2
  declare -i max=0x$2+$3
  while [ $x -lt $max ]
  do
    declare T=$(vpd_read $1 $(hex $x) 1)
    if [ $T = "20" -o $T == "00" ]
    then
      return
    fi
    let "x += 1"
    declare -i L=0x$(vpd_read $1 $(hex $x) 1)
    let "x += 1"
    declare V=$(vpd_read $1 $(hex $x) $L)
    case $T in
    01) printf "${FRMT_TLV}%d\n" "midplane bay" 0x$V ;;
    02) printf "${FRMT_TLV}%d\n" "MSIM LSSM slot" 0x$V ;;
    03) printf "${FRMT_TLV}chassis: %s,%s MM: %s,%s\n" "chassis and MM type,subtype" $V
        ;;
    04) declare uuid=$(echo $V | tr '[:lower:]' '[:upper:]' | tr -d '\n[:blank:]')
        printf "${FRMT_TLV}%s\n" "UUID" $uuid
        ;;
    05) declare mtm=$(ascii_from_hex $V)
        printf "${FRMT_TLV}%s\n" "machine type model" "$mtm"
        ;;
    06) declare msn=$(ascii_from_hex $V)
        printf "${FRMT_TLV}%s\n" "machine serial number" "$msn"
        ;;
    07) printf "${FRMT_TLV}%s %s %s %s\n" "lane info (mx1 ix1 mx4 ix4)" $V ;;
    esac
    let "x += L"
  done
}

SCRIPT=${0##*/}

function usage {
  printf "usage:\n"
  printf "$SCRIPT [-f filename]|[-h|-v]\n"
  printf "           -f filename  (file) read from file instead of VPD\n"
  printf "           -h           (help) print this message and exit\n"
  printf "           -v           (version) print the VPD spec version and exit\n"
  exit 1
}

function version {
  printf "This program is consistent with "
  printf "\"Base Specification for Vital Product Data\"\n"
  printf "Version "
  printf "2.05 (Oct. 3, 2013) "
  printf "supplemented by "
  printf "\"Enhanced Configuration and\nManagement for IOMs\" Revision "
  printf "1.01c (11/07/11)"
#  printf "\nand \"IOM Security Provisioning\" Revision "
#  printf "1.00 (Apr. 12, 2011)"
  printf ".\n"
  exit 1
}

while getopts "f:hv" ARG
do
  case $ARG in
  f) vpd_array_from_file $OPTARG ;;
  v) version ;;
  h) usage ;;
  \?) usage ;;
  *) usage ;;
  esac
done

FRMT_1234="%s %s %-5s%-32s"
FRMT_____="                                            "
FRMT____1="            %3d     %-24s"
FRMT____2="            %3d:%-3d %-24s"

printf      "${FRMT_1234}%s\n" B Offs Leng Description Value
printf      "${FRMT_1234}%s\n" = ==== ==== =============================== ================
show_hex     0 0000 2   "(length of block 0)-2"
show_version 0 0002 2   "VPD version"
show_hex     0 0004 2   "Length of mfg. data block"
show_hex     0 0006 1   "Block ID"
show_rsrvd   0 0007 1
show_hex     0 0008 2   "VPD ID"
show_hex     0 000A 2   "POS ID extension"
show_hex     0 000C 2   "POS ID value"
show_hex     0 000E 7   "Machine Type/Model (N/A)"
show_ascii   0 0015 7   "Machine Serial #"
show_ascii   0 001C 32  "Asset ID"
show_ascii   0 003C 12  "Cart Part Number"
show_ascii   0 0048 12  "Card FRU Number"
show_ascii   0 0054 6   "Card Serial #"
show_ascii   0 005A 6   "Card Prefix Serial Number"
show_ascii   0 0060 4   "System Manufacturer ID"
show_hex     0 0064 1   "Hardware Revision Level Minor"
show_hex     0 0065 1   "Hardware Revision Level"
show_hex     0 0066 5   "Physical Characteristics"
show_ascii   0 006B 4   "Manufacturing Date Code (WWYY)"
show_macs    0 006F 48  "Ethernet MAC Addresses"
show_hex     0 009F 16  "UUID"
show_type    0 00AF 1   "Type Code"
show_hex     0 00B0 16  "Int. interf. char."
show_hex     0 00C0 8   "Ext. interf. char."
show_hex     0 00C8 2   "Block 1 base offset"
show_hex     0 00CA 2   "Block 2 base offset"
show_hex     0 00CC 2   "Block 3 base offset"
show_hex     0 00CE 2   "Block 4 base offset"
show_hex     0 00D0 2   "Block 5 base offset"
show_hex     0 00D2 2   "Block 6 base offset"
show_hex     0 00D4 2   "Block 7 base offset"
show_rsrvd   0 00D6 1
show_typesub 0 00D7 1   "Type Sub-Code"
show_decimal 0 00D8 4   "IANA Enterprise Number"
show_decimal 0 00DC 2   "Product ID"
show_rsrvd   0 00DE 18
show_decimal 0 00F0 2   "Maximum Power (in Watts)"
show_rsrvd   0 00F4 12
show_ascii   0 0100 4   "Subsys Manufacturing ID"
show_ascii   0 0104 10  "CLEI Code"
show_ascii   0 010E 128 "IBM Product Name (short:long)"
show_rsrvd   0 018E 242
show_n_a     0 0280 192 "OEM Base VPD"
show_n_a     0 0340 128 "OEM Extd VPD"
show_rsrvd   0 03C0 60
show_hex     0 03FC 4   "Block 0 CRC"
show_rsrvd   1 0000 2
show_hex     1 0002 2   "(length of block 1)-2"
show_hex     1 0004 1   "Block ID"
show_hex     1 0005 1   "Flag"
show_codelvl 1 0006 36  "Code Level 1 version"
show_codelvl 1 002A 36  "Code Level 2 version"
show_codelvl 1 004E 36  "Code Level 3 version"
show_codelvl 1 0072 36  "Code Level 4 version"
show_codelvl 1 0096 36  "Code Level 5 version"
show_codelvl 1 00BA 36  "Code Level 6 version"
show_ipv4    1 00DE 48  "Default IPv4 Configuration"
show_ip_acq  1 010E 1   "Current IP acquisition method"
show_ipv4    1 010F 48  "Current Static IPv4 Config"
show_rsrvd   1 013F 37
show_n_a     1 0164 1   "? Mgmt Port IP acq ?"
show_rsrvd   1 0165 9
show_ip_mask 1 016E 2   "Current IP change mask"
show_ipv6    1 0170 33  "Current IPv6 Static Config"
show_rsrvd   1 0191 33
show_version 1 01B2 2   "Base Specification Version"
show_rsrvd   1 01B4 12
show_intport 1 01C0 128 "Internal Port Interface Info"
show_wwn     1 0240 64  "Base Address MAC/WWN/GUID/SASID"
show_portdrt 1 0280 32  "Port Data Rate"
show_sm_cap  1 02A0 32  "SM Capability Bits"
show_hex     1 02C0 2   "SM PRM"
show_hex     1 02C2 1   "SM STM"
show_rsrvd   1 02C3 2
show_ehcmudp 1 02C5 4   "EHCM UDP Ports"
show_rsrvd   1 02C9 23
show_hex     1 02E0 1   "Cards Supported"
show_rsrvd   1 02E1 3
show_hex     1 02E4 4   "SFP Presence Detect"
show_hex     1 02E8 2   "Daughter Card Fault"
show_hex     1 02EA 2   "SFP Fault"
show_t_loc   1 02EC 1   "Temperature Sensor Location"
show_rsrvd   1 02ED 3
show_t_thrsh 1 02F0 4   "T warning"
show_t_thrsh 1 02F4 4   "T critical"
show_t_thrsh 1 02F8 4   "T control"
show_decimal 1 02FC 2   "Power Domain 2 Cycles"
show_decimal 1 02FE 2   "Max Power Domain 2 Cycles"
show_rsrvd   1 0300 128
show_n_a     1 0380 6   "Sys Mgmt Info Block"
show_n_a     1 0386 6   "H/W Component Image Map"
show_rsrvd   1 038C 116
show_rsrvd   2 0000 2
show_hex     2 0002 2   "(length of block 2)-2"
show_hex     2 0004 1   "Block ID"
show_hex     2 0005 1   "Flag"
show_ip_acq  2 0006 1   "Assigned IP acquisition method"
show_ipv4    2 0007 48  "Assigned Static IPv4 Config"
show_n_a     2 0037 16  "Boot Path"
show_rsrvd   2 0047 1
show_version 2 0048 2   "MM Base Specification Version"
show_rsrvd   2 004A 21
show_ip_mask 2 005F 2   "Assigned IP change mask"
show_ipv6    2 0061 33  "Assigned IPv6 Static Config"
show_hex     2 0082 1   "EHCM algorithm version #"
show_ascii   2 0083 12  "EHCM authentication passwd"
show_rsrvd   2 008F 97
show_ascii   2 00F0 16  "Service Processor Name"
show_rsrvd   2 0100 7
show_histptr 2 0107 1   "History Log Pointer"
show_histlog 2 0108 416 "History Log"
show_rsrvd   2 02A8 8
show_mm_cap  2 02B0 8   "MM Capability Bits"
show_rsrvd   2 02B8 4
show_hex     2 02BC 2   "MM PRM Permissions"
show_macs    2 02BE 6   "MM Internal Ethernet MAC"
show_rsrvd   2 02C4 6
show_n_a     2 02CA 4   "MM IPv4"
show_rsrvd   2 02CE 306
show_rsrvd   3 0000 2
show_hex     3 0002 2   "(length of block 3)-2"
show_hex     3 0004 1   "Block ID"
show_hex     3 0005 1   "Flag"
show_rsrvd   3 0006 250
show_chtlv   3 0100 160 "Chassis Information TLV Block"
show_rsrvd   3 01A0 96
show_n_a     3 0200 302 "Port Extended Data (PED)"
show_rsrvd   3 032E 209
show_rsrvd   4 0000 2
show_hex     4 0002 2   "(length of block 4)-2"
show_hex     4 0004 1   "Block ID"
show_hex     4 0005 1   "Flag"
show_rsrvd   4 0006 10
show_n_a     4 0010 240 "Mezz Card Connectivity"
show_n_a     4 0100 256 "Blade Cross Connect"
show_rsrvd   4 0200 512
show_rsrvd   5 0000 2
show_hex     5 0002 2   "(length of block 5)-2"
show_hex     5 0004 1   "Block ID"
show_hex     5 0005 1   "Flag"
show_rsrvd   5 0006 1018
show_rsrvd   6 0000 2
show_hex     6 0002 2   "(length of block 6)-2"
show_hex     6 0004 1   "Block ID"
show_hex     6 0005 1   "Flag"
show_rsrvd   6 0006 1018
show_rsrvd   7 0000 2
show_hex     7 0002 2   "(length of block 7)-2"
show_hex     7 0004 1   "Block ID"
show_hex     7 0005 1   "Flag"
show_rsrvd   7 0006 1018
