From d602f4fb516292c297072bd924289eea37b45fa2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= <debian@jff.email>
Date: Wed, 15 Mar 2023 20:36:27 +0100
Subject: New upstream version 3.1.9

---
 util/Makefile.in   |    5 +-
 util/ievents.c     |    2 +-
 util/igetevent.c   |    2 +-
 util/ipmiutil.c    |    2 +-
 util/ireset.c      |    2 +
 util/isensor.c     |   10 +-
 util/isensor.c-old | 3907 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 util/mem_if.c      |   11 +-
 8 files changed, 3930 insertions(+), 11 deletions(-)
 create mode 100644 util/isensor.c-old

(limited to 'util')

diff --git a/util/Makefile.in b/util/Makefile.in
index 2909704..50f821c 100644
--- a/util/Makefile.in
+++ b/util/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -378,6 +378,7 @@ pkgconfigdir = @pkgconfigdir@
 prefix = @prefix@
 program_transform_name = @program_transform_name@
 psdir = @psdir@
+runstatedir = @runstatedir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
diff --git a/util/ievents.c b/util/ievents.c
index e5d34f7..17b41a4 100644
--- a/util/ievents.c
+++ b/util/ievents.c
@@ -80,7 +80,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 extern char *progver;  /*from ipmiutil.c*/
 static char * progname  = "ipmiutil events";
 #else
-static char *progver   = "3.18";
+static char *progver   = "3.19";
 static char *progname  = "ievents";
 #endif
 static char fsensdesc = 0;   /* 1= get extended sensor descriptions*/
diff --git a/util/igetevent.c b/util/igetevent.c
index f4c75d6..dd806c4 100644
--- a/util/igetevent.c
+++ b/util/igetevent.c
@@ -1183,7 +1183,7 @@ main(int argc, char **argv)
 	  ipath = getenv("ipmiutildir");  /*ipmiutil directory path*/
 	  if (ipath != NULL) {
 	     if (strlen(ipath)+12 < sizeof(idxfile)) {
-	        sprintf(idxfile,"%s\\%s",ipath,"\\",IDXFILE);
+	        sprintf(idxfile,"%s\\%s",ipath,IDXFILE);
 	     }
 	  }
        }
diff --git a/util/ipmiutil.c b/util/ipmiutil.c
index 980c57e..409cb9f 100644
--- a/util/ipmiutil.c
+++ b/util/ipmiutil.c
@@ -57,7 +57,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "ipmiutil.h"
 
 static char *progname  = "ipmiutil";
-char *progver   = "3.18";
+char *progver   = "3.19";
 // static char fdebug = 0;
 /*int ipmiutil(int argc, char **argv); */
 
diff --git a/util/ireset.c b/util/ireset.c
index c668996..d311142 100644
--- a/util/ireset.c
+++ b/util/ireset.c
@@ -631,11 +631,13 @@ main(int argc, char **argv)
       } else if (vendid == VENDOR_INTEL) {   /* Intel */
 	  if (prodid != 0x0100)  /* ia64 Itanium2 is different */
 	      platform = platIntel;  /* else handle as Intel Sahalee */
+#ifdef OBSOLETE	
 	  if (is_romley(vendid,prodid) || (prodid == 0x003E)) {  
 		/* Romley or Thurley/S5520UR */
 	      platform = platS5500;  /* not like Intel Sahalee */
 	      set_max_kcs_loops(URNLOOPS); /*longer KCS timeout*/
 	  }
+#endif
       } else if (vendid == VENDOR_KONTRON) {   /* Kontron */
           fignore_opterr = 1;  /* ignore boot options errors */
 	  /* supports Chassis Soft Power command 0x05, so not platIntel */
diff --git a/util/isensor.c b/util/isensor.c
index eded150..a3fc650 100644
--- a/util/isensor.c
+++ b/util/isensor.c
@@ -81,6 +81,7 @@
  * 01/25/08 ARCress  v2.7    allow float input with -u thresholds, 
  *                           add -p persist logic for -u thresholds.
  * 09/20/19 ARCress  v3.15   workaround for Pigeon Point bad sa in SDR
+ * 07/08/22 ARCress  v3.19   fix -i get_idx_range to show last idx in range
  */
 /*M*
 Copyright (c) 2002-2006 Intel Corporation. 
@@ -609,16 +610,17 @@ int get_idx_range(char *str)
 {
     // int i = 0;
     char *p;
+	char *p2;
     p = strchr(str,'-');
     if (p == NULL) p = strchr(str,',');
-    if (p != NULL) {
+    if (p != NULL) {  /*range*/
        *p = 0;
        p++;
        sensor_idx1 = parse_idx(str);
-       sensor_idxN = parse_idx(p);
-    } else {
+       sensor_idxN = parse_idx(p) + 1; /*end if >=, so + 1*/
+    } else {  /*single sensor*/
        sensor_idx1 = parse_idx(str);
-       sensor_idxN = sensor_idx1;
+       sensor_idxN = sensor_idx1; /*end if >=*/
     }
     return(0);
 }
diff --git a/util/isensor.c-old b/util/isensor.c-old
new file mode 100644
index 0000000..eded150
--- /dev/null
+++ b/util/isensor.c-old
@@ -0,0 +1,3907 @@
+/*
+ * isensor.c
+ *
+ * This tool reads the SDR records to return sensor information. 
+ * It can use either the Intel /dev/imb driver or VALinux /dev/ipmikcs.
+ *
+ * Author:  arcress at users.sourceforge.net
+ * Copyright (c) 2002-2006 Intel Corporation. 
+ * Copyright (c) 2009 Kontron America, Inc.
+ *
+ * 07/25/02 Andy Cress created
+ * 10/09/02 Andy Cress v1.1 added decodeValue(RawToFloat) routine 
+ * 10/11/02 Andy Cress v1.2 added expon routine 
+ * 10/30/02 Andy Cress v1.3 added SDR types 08 & 14
+ * 12/04/02 Andy Cress v1.4 changed dstatus descriptions
+ * 01/29/03 Andy Cress v1.5 added MV OpenIPMI driver support
+ *          Hannes Schulz <schulz@schwaar.com>
+ *                1) correct raw readings to floatval
+ *                2) allow extra SDR bytes returned from HP netserver 1000r
+ *          Guo Min <guo.min@intel.com>
+ *                add -l option for simpler list display
+ * 02/25/03 Andy Cress v1.6 misc cleanup
+ * 05/02/03 Andy Cress v1.7 add PowerOnHours
+ * 07/28/03 Andy Cress v1.8 added -t option for threshold values,
+ *                          added sample Discovery routine (unfinished),
+ *                          added ipmi_getdeviceid for completeness.
+ * 09/05/03 Andy Cress v1.9 show SDR OEM subtypes, 
+ *                          fix GetSDR multi-part get for OEM SDRs
+ *                          stop if SDR Repository is empty
+ * 09/23/03 Andy Cress v1.10 Add options to set thresholds
+ * 10/14/03 Andy Cress v1.11 Fixed sdr offset for ShowThreshold values
+ * 01/15/04 Andy Cress v1.12 Fixed SetThreshold to set hysteresis, 
+ *                           Fixed sens_cap testing in ShowThresh(Full)
+ * 01/30/04 Andy Cress v1.13 Changed field display order, added header,
+ *                           check for sdr sz below min, added WIN32.
+ * 02/19/04 Andy Cress v1.14 Added SDR type 3 parsing for mBMC
+ * 02/27/04 Andy Cress v1.15 Added check for superuser, more mBMC logic
+ * 03/11/04 Andy Cress v1.16 Added & removed private mBMC code for set 
+ *                           thresholds due to licensing issues
+ * 04/13/04 Andy Cress v1.17 Added -r to show raw SDRs also
+ * 05/05/04 Andy Cress v1.18 call ipmi_close before exit,
+ *                           fix sresp in GetSDR for WIN32.
+ * 07/07/04 Andy Cress v1.19 Added -a to reArm sensor,
+ *                           show debug raw reading values only in hex
+ * 08/18/04 Andy Cress v1.20 Added decoding for DIMM status
+ * 11/01/04 Andy Cress v1.21 add -N / -R for remote nodes,  
+ *                           added -U for remote username
+ * 11/19/04 Andy Cress v1.22 added more decoding for compact reading types,
+ *                           added -w option to wrap thresholds
+ * 11/24/04 ARCress  v1.23   added sens_type to display output
+ * 12/10/04 ARCress  v1.24   added support for device sdrs also,
+ *                           fixed sens_cap byte,
+ * 01/10/05 ARCress  v1.25   change ShowThresh order, highest to lowest,
+ *                           change signed exponent type in RawToFloat
+ * 01/13/05 ARCress  v1.26   added time display if fwrap
+ * 01/28/05 ARCress  v1.27   mod for Power Redundancy SDR status
+ * 02/15/05 ARCress  v1.28   added FloatToRaw for -h/-l threshold set funcs, 
+ *                           always take -n sensor_num as hex (like displayed)
+ * 03/07/05 ARCress  v1.29   added "LAN Leash Lost" decoding in decode_comp_
+ *                           added -v to show max/min & hysteresis.
+ * 03/22/05 ARCress  v1.30   added OEM subtype 0x60 for BMC TAM
+ * 03/26/05 ARCress  v1.31   added battery type to decode_comp_reading
+ * 04/21/05 ARCress  v1.32   added error message if -n sensor_num not found,
+ *                           added more decoding for Power Redund sensor
+ * 06/20/05 ARCress  v1.33   if GetSDRRepository cc=0xc1 switch fdevsdrs mode,
+ *                           also detect fdevsdrs better for ATCA.
+ * 07/28/05 ARCress  v1.34   check for Reading init state, 
+ *                           add extra byte to decode_comp_reading()
+ * 09/12/05 ARCress  v1.35   don't check superuser for fipmi_lan
+ * 01/26/06 ARCress  v1.36   added -i option to only show one sensor index
+ * 03/14/06 ARCress  v1.37   added -p option to save persistent thresholds
+ * 04/06/06 ARCress  v1.38   show auto/manual rearm
+ * 07/17/06 ARCress  v1.39   add -V, add -L, handle RepInfo rc=0xc1 
+ * 11/28/06 ARCress  v1.46   added -c -m for ATCA child MCs
+ * 08/15/07 ARCress  v1.58   filter display if -n sensor_num
+ * 08/29/07 ARCress  v1.59   fixed Battery sensor interpretation
+ * 10/31/07 ARCress  v2.3    retry GetSDR if cc=0xC5 (lost reservationID)
+ * 01/14/08 ARCress  v2.6    add -u param for setting unique thresholds, 
+ *                           always show float when setting thresholds,
+ *                           fixup in decoding Proc,PS Comp readings
+ * 01/25/08 ARCress  v2.7    allow float input with -u thresholds, 
+ *                           add -p persist logic for -u thresholds.
+ * 09/20/19 ARCress  v3.15   workaround for Pigeon Point bad sa in SDR
+ */
+/*M*
+Copyright (c) 2002-2006 Intel Corporation. 
+Copyright (c) 2009 Kontron America, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions are met:
+
+  a.. Redistributions of source code must retain the above copyright notice, 
+      this list of conditions and the following disclaimer. 
+  b.. Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation 
+      and/or other materials provided with the distribution. 
+  c.. Neither the name of Intel nor the names of its contributors 
+      may be used to endorse or promote products derived from this software 
+      without specific prior written permission. 
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *M*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>   // for:  double pow(double x, double y);
+#include <string.h>
+#include <time.h>
+#ifdef WIN32
+#include <windows.h>
+#include "getopt.h"
+#elif defined(DOS)
+#include <dos.h>
+#include "getopt.h"
+#else
+#include <sys/stat.h>
+#if defined(HPUX)
+/* getopt is defined in stdio.h */
+#elif defined(MACOS)
+/* getopt is defined in unistd.h */
+#include <unistd.h>
+#else
+#include <getopt.h>
+#endif
+#endif
+#if defined(LINUX)
+#include <unistd.h>
+#include <sys/types.h>
+#endif
+#include "ipmicmd.h"
+#include "isensor.h"
+
+#define PICMG_CHILD  1 /* show child MCs if -b */
+#define MIN_SDR_SZ  8 
+#define SZCHUNK 16    /* SDR chunksize was 8, now 16 */
+#define INIT_SNUM  0xff 
+#define N_SGRP   16
+#define THR_EMPTY  999
+
+/* prototypes */
+int get_LastError( void );  /* ipmilan.c */
+extern int use_devsdrs(int picmg);  /* ipmicmd.c */
+extern int get_MemDesc(int array, int dimm, char *desc, int *psz); /*mem_if.c*/
+extern char *get_sensor_type_desc(uchar stype); /*ievents.c*/
+#ifdef METACOMMAND
+#include "oem_intel.h"
+/*     void show_oemsdr_intel(uchar *sdr); in oem_intel.h */
+/*     int decode_sensor_intel();          in oem_intel.h */
+/*     int is_romley(int vend, int prod);  in oem_intel.h */
+extern int decode_sensor_kontron(uchar *sdr,uchar *reading,char *pstring,
+				int slen);  /*see oem_kontron.c*/
+extern int decode_sensor_fujitsu(uchar *sdr,uchar *reading,char *pstring,
+				int slen);  /*see oem_fujitsu.c*/
+extern int decode_sensor_sun(uchar *sdr,uchar *reading,char *pstring,
+				int slen);  /*see oem_sun.c*/
+extern int decode_sensor_supermicro(uchar *sdr,uchar *reading,char *pstring,
+				int slen, int fsimple, char fdbg);  /*see oem_supermicro.c*/
+extern int decode_sensor_quanta(uchar *sdr,uchar *reading,char *pstring,
+				int slen);  /*see oem_quanta.c*/
+extern int decode_sensor_dell(uchar *sdr,uchar *reading,char *pstring,
+				int slen);  /*see oem_dell.c*/
+extern int decode_sensor_lenovo(uchar *sdr,uchar *reading,char *pstring,
+				int slen);  /*see oem_lenovo.c*/
+extern int decode_sensor_asus(uchar *sdr,uchar *reading,char *pstring,int slen);
+extern int decode_sensor_hp(uchar *sdr,uchar *reading,char *pstring,
+				int slen);  /*see oem_hp.c*/
+extern void show_oemsdr_hp(uchar *sdr); 
+#else
+int is_romley(int vend, int prod) { 
+   if ((vend == VENDOR_INTEL) && ((prod >= 0x0048) && (prod <= 0x005e)))
+	return(1);
+   return(0); 
+}
+int is_grantley(int vend, int prod) { 
+   if ((vend == VENDOR_INTEL) && (prod == 0x0071))
+	return(1);
+   return(0); 
+}
+int is_thurley(int vend, int prod) { 
+   if ((vend == VENDOR_INTEL) && ((prod >= 0x003A) && (prod <= 0x0040)))
+	return(1);
+   return(0); 
+}
+#endif
+#ifdef ALONE
+#define NENTID  53
+static char *entity_id_str[NENTID] = {
+/* 00 */ "unspecified",
+/* 01 */ "other",
+/* 02 */ "unknown",
+/* 03 */ "processor",
+/* 04 */ "disk",
+/* 05 */ "peripheral bay",
+/* 06 */ "management module",
+/* 07 */ "system board",
+/* 08 */ "memory module",
+/* 09 */ "processor module",
+/* 10 */ "power supply",
+/* 11 */ "add-in card",
+/* 12 */ "front panel bd",
+/* 13 */ "back panel board",
+/* 14 */ "power system bd",
+/* 15 */ "drive backplane",
+/* 16 */ "expansion board",
+/* 17 */ "Other system board",
+/* 18 */ "processor board",
+/* 19 */ "power unit",
+/* 20 */ "power module",
+/* 21 */ "power distr board",
+/* 22 */ "chassis back panel bd",
+/* 23 */ "system chassis",
+/* 24 */ "sub-chassis",
+/* 25 */ "Other chassis board",
+/* 26 */ "Disk Drive Bay",
+/* 27 */ "Peripheral Bay",
+/* 28 */ "Device Bay",
+/* 29 */ "fan",
+/* 30 */ "cooling unit",
+/* 31 */ "cable/interconnect",
+/* 32 */ "memory device ",
+/* 33 */ "System Mgt Software",
+/* 34 */ "BIOS",
+/* 35 */ "Operating System",
+/* 36 */ "system bus",
+/* 37 */ "Group",
+/* 38 */ "Remote Mgt Comm Device",
+/* 39 */ "External Environment",
+/* 40 */ "battery",
+/* 41 */ "Processing blade",
+/* 43 */ "Processor/memory module",
+/* 44 */ "I/O module",
+/* 45 */ "Processor/IO module",
+/* 46 */ "Mgt Controller Firmware",
+/* 47 */ "IPMI Channel",
+/* 48 */ "PCI Bus",
+/* 49 */ "PCI Express Bus",
+/* 50 */ "SCSI Bus",
+/* 51 */ "SATA/SAS bus",
+/* 52 */ "Processor FSB"
+};
+char *decode_entity_id(int id) { 
+   if (id < NENTID) return (""); 
+   else return(entity_id_str[id]); }
+#else
+/* char *decode_entity_id(int id);  *isensor.h, from ievents.c*/
+#endif
+/************************
+ *  Global Data
+ ************************/
+#ifdef METACOMMAND
+extern char * progver;  /*from ipmiutil.c*/
+static char * progname  = "ipmiutil sensor";
+#else
+static char * progver   = "3.15";
+static char * progname  = "isensor";
+#endif
+#ifdef WIN32
+static char savefile[] = "%ipmiutildir%\\thresholds.cmd";
+#else
+static char savefile[] = "/var/lib/ipmiutil/thresholds.sh";
+// static char savefile[] = "/usr/share/ipmiutil/thresholds.sh";
+#endif
+extern char fdebug;      /*from ipmicmd.c*/
+int sens_verbose    = 0; /* =1 show max/min & hysteresis also */
+static int fdevsdrs = 0;
+static int fReserveOK = 1;
+static int fDoReserve = 1;
+static int fsimple     = 0; /*=1 simple, canonical output*/
+static int fshowthr = 0;   /* =1 show thresholds, =2 show thr in ::: fmt */
+static int fwrap    = 0;
+static int frawsdr  = 0;
+static int frearm   = 0;
+static int fshowidx = 0;   /* only show a specific SDR index/range */
+static int fshowgrp = 0;   /* =1 show group of sensors by sensor type */
+static int fdoloop  = 0;   /* =1 if user specified number of loops */
+static int fpicmg   = 0;
+static int fchild   = 0;   /* =1 show child SDRs */
+static int fset_mc  = 0;   /* =1 -m to set_mc   */
+static int fbadsdr  = 0;   /* =1 to ignore bad SDR mc  */
+static int fdump    = 0;  
+static int frestore = 0;  
+static int fjumpstart = 0;  
+static int fgetmem  = 0;  
+static int fprivset = 0;  
+static char fremote = 0;  
+static int  nloops  = 1;   /* num times to show repeated sensor readings */
+static int  loopsec  = 1;  /* wait N sec between loops, default 1 */
+static char bdelim = BDELIM;  /* delimiter for canonical output */
+static char tmpstr[20];    /* temp string */
+static char *binfile = NULL;
+static int   fsetthresh = 0;
+static int   fsavethresh = 0;
+static uchar  sensor_grps[N_SGRP] = {0, 0};  /*sensor type groups*/
+static ushort sensor_idx1 = 0xffff;
+static ushort sensor_idxN = 0xffff;
+static uchar  sensor_num = INIT_SNUM;
+static uchar  sensor_hi = 0xff;
+static uchar  sensor_lo = 0xff;
+static uchar  sensor_thr[6]  = {0,0,0,0,0,0};
+static double sensor_thrf[6] = {0,0,0,0,0,0};
+static double sensor_hi_f = 0;
+static double sensor_lo_f = 0;
+static int fmBMC = 0;
+static int fRomley = 0;
+static int fGrantley = 0;
+static char chEol = '\n';  /* newline by default, space if option -w */
+static uchar resid[2] = {0,0};
+static uchar g_bus = PUBLIC_BUS;
+static uchar g_sa  = 0;
+static uchar g_lun = BMC_LUN;
+static uchar g_addrtype = ADDR_SMI;
+static int vend_id = 0;
+static int prod_id;
+
+/* sensor_dstatus
+ * This is used to decode the sensor reading types and meanings.
+ * Use IPMI Table 36-1 and 36-2 for this.
+ */
+#define N_DSTATUS 101
+#define STR_CUSTOM     58
+#define STR_OEM        71
+#define STR_AC_LOST    76
+#define STR_PS_FAIL    77
+#define STR_PS_CONFIG  78
+#define STR_HSC_OFF    79
+#define STR_REBUILDING 80
+#define STR_OTHER      81
+static char oem_string[50] = "OEM";
+static char *sensor_dstatus[N_DSTATUS] = {
+/* 0 00h */ "OK  ",
+/* Threshold event states */
+/* 1 01h */ "Warn-lo",   // "Warning-lo",
+/* 2 02h */ "Crit-lo",   // "Critical-lo",
+/* 3 04h */ "BelowCrit", // "BelowCrit-lo",
+/* 4 08h */ "Warn-hi",   // "Warning-hi",
+/* 5 10h */ "Crit-hi",   // "Critical-hi",
+/* 6 20h */ "AboveCrit", // "AboveCrit-hi",
+/* 7 40h */ "Init ",  /*in init state, no reading*/
+/* 8 80h */ "OK* ",
+/*  Hotswap Controller event states, also Availability */
+/* 9 HSC */ "Present", /*present,inserted*/
+/*10 HSC */ "Absent", /*absent,removed,empty,missing*/
+/*11 HSC */ "Ready",
+/*12 HSC */ "Faulty",
+/*  Digital/Discrete event states */
+/*13 D-D */ "Asserted",
+/*14 D-D */ "Deassert",
+/*15 D-D */ "Predict ",
+/*  Availability event states */
+/*16 Avl */ "Disabled",
+/*17 Avl */ "Enabled ",
+/*18 Avl */ "Redundant",
+/*19 Avl */ "RedunLost",
+/*20 Avl */ "RedunDegr",
+/* ACPI Device Power States */
+/*21 ACPI*/ "Off    ", /*D3*/
+/*22 ACPI*/ "Working", /*D1*/
+/*23 ACPI*/ "Sleeping", /*D2/S2*/
+/*24 ACPI*/ "On",      /*D0*/
+/* Critical Interrupt event states */
+/*25 CrI */ "FP_NMI  ",
+/*26 CrI */ "Bus_TimOut",
+/*27 CrI */ "IOch_NMI",
+/*28 CrI */ "SW_NMI  ",
+/*29 CrI */ "PCI_PERR",
+/*30 CrI */ "PCI_SERR",
+/*31 CrI */ "EISA_TimOut",
+/*32 CrI */ "Bus_Warn ", /*Correctable*/
+/*33 CrI */ "Bus_Error", /*Uncorrectable*/
+/*34 CrI */ "Fatal_NMI",
+/*35 CrI */ "Bus_Fatal", /*0x0A*/
+/*36 CrI */ "Bus_Degraded", /*0x0B*/
+/* Physical Security event states */
+/*37 Phys*/ "LanLeashLost",
+/*38 Phys*/ "ChassisIntrus",
+/* Memory states  */
+/*39 Mem */ "ECCerror",
+/*40 Mem */ "ParityErr",
+/* Discrete sensor invalid readings (error or init state) */
+/*41 D-D */ "Unknown",
+/*42 D-D */ "NotAvailable",
+/* Discrete sensor OEM reading states */
+/*43 OEM */ "Enabled ",
+/*44 OEM */ "Disabled",
+/* Session Audit states */
+/*45 OEM */ "Activated ",
+/*46 OEM */ "Deactivated",
+/*47 HSC */ "Unused  ", 
+/* Processor event states */
+/*48 Proc*/ "IERR",
+/*49 Proc*/ "ThermalTrip",
+/*50 Proc*/ "FRB1Failure",
+/*51 Proc*/ "FRB2Failure",
+/*52 Proc*/ "FRB3Failure",
+/*53 Proc*/ "ConfigError",
+/*54 Proc*/ "SMBIOSError",
+/*55 Proc*/ "ProcPresent",
+/*56 Proc*/ "ProcDisabled",
+/*57 Proc*/ "TermPresent",
+/* Custom data string, 15 bytes */
+/*58 Custom*/ "CustomData12345", 
+/* Event Log */ 
+/*59 EvLog*/ "MemLogDisab",
+/*60 EvLog*/ "TypLogDisab",
+/*61 EvLog*/ "LogCleared",
+/*62 EvLog*/ "AllLogDisab",
+/*63 EvLog*/ "SelFull",
+/*64 EvLog*/ "SelNearFull",
+/* more Digital Discrete */ 
+/*65 D-D */  "Exceeded",
+/*66 Alert*/ "AlertPage",
+/*67 Alert*/ "AlertLAN",
+/*68 Alert*/ "AlertPET",
+/*69 Alert*/ "AlertSNMP",
+/*70 Alert*/ "None",
+/*71 OEM str*/ &oem_string[0],
+/* Version Change */
+/*72 Change*/ "HW Changed",
+/*73 Change*/ "SW Changed",
+/*74 Change*/ "HW incompatibility",
+/*75 Change*/ "Change Error",
+/* Power Supply event states */
+/*76 PS  */   "AC_Lost  ",
+/*77 PS  */   "PS_Failed",
+/* Power Supply event states */
+/*78 PS  */   "Config_Err",
+/*79 HSC */   "Offline",
+/*80 HSC */   "Rebuilding",
+/*81 other*/  " _ ",
+/*82 Avl */ "NonRedund_Sufficient",
+/*83 Avl */ "NonRedund_Insufficient",
+/*84 Usage */ "Idle",
+/*85 Usage */ "Active",
+/*86 Usage */ "Busy",
+/*87 Trans */ "Non-Critical",
+/*88 Trans */ "Critical",
+/*89 Trans */ "Non-Recoverable",
+/*90 Trans */ "Monitor",
+/*91 Trans */ "Informational",
+/*92 State */ "Running",
+/*93 State */ "In_Test",
+/*94 State */ "Power_Off",
+/*95 State */ "Online",
+/*96 State */ "Offline",
+/*97 State */ "Off_Duty",
+/*98 State */ "Degraded",
+/*99 State */ "PowerSave",
+/*100 State */ "InstallError"
+};
+
+static char *raid_states[9] = {  /*for sensor type 0x0d drive status */
+  "Faulty",
+  "Rebuilding",
+  "InFailedArray",  
+  "InCriticalArray",  
+  "ParityCheck",  
+  "PredictedFault",  
+  "Un-configured",  
+  "HotSpare",
+  "NoRaid" };
+
+#define NSENSTYPES   0x2a
+#ifdef OLD
+/* see ievents.c */
+static const char *sensor_types[NSENSTYPES] = {  /*IPMI 2.0 Table 42-3*/
+/* 00h */ "reserved",
+/* 01h */ "Temperature",
+/* 02h */ "Voltage",
+/* 03h */ "Current",
+/* 04h */ "Fan",
+/* 05h */ "Platform Chassis Intrusion",
+/* 06h */ "Platform Security Violation",
+/* 07h */ "Processor",
+/* 08h */ "Power Supply",
+/* 09h */ "Power Unit",
+/* 0Ah */ "Cooling Device",
+/* 0Bh */ "FRU Sensor",
+/* 0Ch */ "Memory",
+/* 0Dh */ "Drive Slot",
+/* 0Eh */ "POST Memory Resize",
+/* 0Fh */ "System Firmware",
+/* 10h */ "SEL Disabled",
+/* 11h */ "Watchdog 1",
+/* 12h */ "System Event",          /* offset 0,1,2 */
+/* 13h */ "Critical Interrupt",    /* offset 0,1,2 */
+/* 14h */ "Button",                /* offset 0,1,2 */
+/* 15h */ "Board",
+/* 16h */ "Microcontroller",
+/* 17h */ "Add-in Card",
+/* 18h */ "Chassis",
+/* 19h */ "Chip Set",
+/* 1Ah */ "Other FRU",
+/* 1Bh */ "Cable / Interconnect",
+/* 1Ch */ "Terminator",
+/* 1Dh */ "System Boot Initiated",
+/* 1Eh */ "Boot Error",
+/* 1Fh */ "OS Boot",
+/* 20h */ "OS Critical Stop",
+/* 21h */ "Slot / Connector",
+/* 22h */ "ACPI Power State",
+/* 23h */ "Watchdog 2",
+/* 24h */ "Platform Alert",
+/* 25h */ "Entity Presence",
+/* 26h */ "Monitor ASIC",
+/* 27h */ "LAN",
+/* 28h */ "Management Subsystem Health",
+/* 29h */ "Battery",
+};
+#endif
+
+#define NUNITS  30
+static char *unit_types[] = {
+/* 00 */ "unspecified",
+/* 01 */ "degrees C",
+/* 02 */ "degrees F",
+/* 03 */ "degrees K",
+/* 04 */ "Volts",
+/* 05 */ "Amps",
+/* 06 */ "Watts",
+/* 07 */ "Joules",
+/* 08 */ "Coulombs",
+/* 09 */ "VA",
+/* 10 */ "Nits",
+/* 11 */ "lumen",
+/* 12 */ "lux",
+/* 13 */ "Candela",
+/* 14 */ "kPa",
+/* 15 */ "PSI",
+/* 16 */ "Newton",
+/* 17 */ "CFM",
+/* 18 */ "RPM",
+/* 19 */ "Hz",
+/* 20 */ "microseconds",
+/* 21 */ "milliseconds",
+/* 22 */ "seconds",
+/* 23 */ "minutes",
+/* 24 */ "hours",
+/* 25 */ "days",
+/* 26 */ "weeks",
+/* 27 */ "mil",
+/* 28 */ "inches",
+/* 29 */ "feet",
+/* 42 */ "cycles"  
+};
+/* 68 * "megabit", */
+/* 72 * "megabyte", */
+/* 90 * "uncorrectable error" (last defined)*/
+static char *unit_types_short[] = {
+/* 00 */ "?", /*unknown, not specified*/
+/* 01 */ "C",
+/* 02 */ "F",
+/* 03 */ "K",
+/* 04 */ "V",
+/* 05 */ "A",
+/* 06 */ "W",
+/* 07 */ "J",
+/* 08 */ "Coul",
+/* 09 */ "VA",
+/* 10 */ "Nits",
+/* 11 */ "lumen",
+/* 12 */ "lux",
+/* 13 */ "Cand",
+/* 14 */ "kPa",
+/* 15 */ "PSI",
+/* 16 */ "Newton",
+/* 17 */ "CFM",
+/* 18 */ "RPM",
+/* 19 */ "Hz",
+/* 20 */ "usec",
+/* 21 */ "msec",
+/* 22 */ "sec",
+/* 23 */ "min",
+/* 24 */ "hrs",
+/* 25 */ "days",
+/* 26 */ "wks",
+/* 27 */ "mil",
+/* 28 */ "in",
+/* 29 */ "ft",
+/* 42 */ "cyc"  
+};
+
+ushort parse_idx(char *str)
+{
+    int i, n;
+    char istr[5];
+    if (strncmp(str,"0x",2) == 0) str += 2;
+    n = strlen_(str);
+    if (n == 4) {
+        i = (htoi(str) << 8) + htoi(&str[2]);
+    } else if (n == 3) {
+        istr[0] = '0';
+        memcpy(&istr[1],str,3);
+        i = (htoi(istr) << 8) + htoi(&istr[2]);
+    } else i = htoi(str);      /*was atoi()*/
+    printf("idx = 0x%x\n",i);
+    return((ushort)i);
+}
+
+int get_idx_range(char *str)
+{
+    // int i = 0;
+    char *p;
+    p = strchr(str,'-');
+    if (p == NULL) p = strchr(str,',');
+    if (p != NULL) {
+       *p = 0;
+       p++;
+       sensor_idx1 = parse_idx(str);
+       sensor_idxN = parse_idx(p);
+    } else {
+       sensor_idx1 = parse_idx(str);
+       sensor_idxN = sensor_idx1;
+    }
+    return(0);
+}
+
+char *get_unit_type(int iunits, int ibase, int imod, int fshort)
+{
+    char *pstr = NULL;
+    char **punittypes;
+    static char unitstr[32];
+    int jbase, jmod;
+    uchar umod;
+
+    punittypes = unit_types;
+    if (fshort) punittypes = unit_types_short;
+    if (fdebug) printf("get_unit_type(%x,%d,%d,%d)\n",iunits,ibase,imod,fshort);
+    umod = (iunits & 0x06) >> 1;
+    if (ibase < NUNITS) jbase = ibase;
+    else {
+	if (fdebug) printf("units base %02x > %d\n",ibase,NUNITS);
+        if (ibase == 42) jbase = NUNITS;  /*"cycles"*/
+	else jbase = 0; 
+    }
+    if (imod < NUNITS) jmod = imod;
+    else {
+	if (fdebug) printf("units mod %02x > %d\n",imod,NUNITS);
+	jmod = 0; 
+    }
+    switch (umod) {
+	case 2:
+	   snprintf(unitstr,sizeof(unitstr),"%s * %s",
+			punittypes[jbase],punittypes[jmod]);
+	   pstr = unitstr;
+	   break;
+	case 1:
+	   snprintf(unitstr,sizeof(unitstr),"%s/%s",
+			punittypes[jbase],punittypes[jmod]);
+	   pstr = unitstr;
+	   break;
+	case 0:
+	default:
+           pstr = punittypes[jbase];
+	   break;
+    }
+    if ((umod == 0) && (iunits > 0)) {
+	   /* special cases for other SensorUnits1 bits */
+	   if ((iunits & 0x01) != 0) {  /*percentage*/
+	      if (fshort) pstr = "%";
+	      else pstr = "percent";
+	   } else if (iunits == 0xC0) {  /*no analog reading*/
+	      pstr = "na";
+	   } else if (iunits == 0x18) {
+	      /* For Tyan fans:  base=42, units=24.(0x18) -> cycles/hour */
+	      snprintf(unitstr,sizeof(unitstr),"%s/hour",punittypes[jbase]);
+	      pstr = unitstr;
+	   } 
+    }
+    return(pstr);
+}
+
+char *decode_capab(uchar c)
+{
+   static char cstr[50];
+   char *arm;
+   char *thr;
+   char *evt;
+   // char *hys;
+   uchar b;
+   /* decode sens_capab bits */
+   if ((c & 0x40) == 0) arm = "man"; /*manual rearm*/
+   else arm = "auto";  /*automatic rearm*/
+   /* skip hysteresis bits (0x30) */
+   b = ((c & 0x0c) >> 2);
+   switch(b) {
+   case 0x00: thr = "none"; break;  /*no thresholds*/
+   case 0x01: thr = "read"; break;
+   case 0x02: thr = "write"; break; /*read & write*/
+   case 0x03: 
+   default:   thr = "fixed"; break; 
+   }
+   b = (c & 0x03) ;
+   switch(b) {
+   case 0x00: evt = "state"; break;  /*threshold or discrete state*/
+   case 0x01: evt = "entire"; break; /*entire sensor only*/
+   case 0x02: evt = "disab"; break;  /*global disable only*/
+   case 0x03:
+   default:   evt = "none"; break;   /*no events*/
+   }
+   sprintf(cstr,"arm=%s thr=%s evts=%s",arm,thr,evt);
+   return(cstr);
+}
+
+
+int get_group_id(char *pstr)
+{
+   int i, j, n, sz, len;
+   char *p;
+   int rv = -1;
+		   
+   sz = strlen_(pstr);
+   p = &pstr[0];
+   n = 0;
+   for (i = 0; i <= sz; i++) {
+      if (n >= N_SGRP) break;
+      switch(pstr[i]) {
+	 case ',':  /*delimiter*/
+	 case '\n':
+	 case '\0':
+	    pstr[i] = 0;  /*stringify this word*/
+            len = strlen_(p);
+	    for (j = 0; j < NSENSTYPES; j++) {
+		if (strncasecmp(get_sensor_type_desc(j),p,len) ==  0) {
+		   sensor_grps[n++] = (uchar)j;
+		   rv = 0;
+		   break; 
+		}
+	    } /*endfor(j)*/
+	    if (i+1 < sz) p = &pstr[i+1];  /*set p for next word*/
+	    if (j >= NSENSTYPES) { /* sensor type not found */
+		rv = -1; 
+		i = sz;  /*exit loop*/
+	    }
+	    break;
+	 default:
+	    break;
+      } /*end switch*/
+   }  /*end for(i)*/
+   if (rv == 0) rv = n;
+   else rv = -1;
+   return(rv);
+}
+
+static int validate_thresholds(void *pthrs, char flag, uchar *sdr)
+{
+   double *thrf;
+   uchar *thr;
+   int rv = 0;
+   uchar bits;
+
+   if (sdr == NULL) bits = 0xff; /*assume all are used*/
+   else bits = sdr[18];  /*18=indicates which are readable/used */
+
+   if (bits == 0) {
+      printf("No threshold values can be set for this sensor.\n");
+      return(3);
+   }
+   if (flag == 1) { /*float*/
+      thrf = (double *)pthrs;
+      if (fdebug) 
+         printf("validate_thresh: bits=%02x, values: %f>=%f>=%f, %f<=%f<=%f\n",
+		bits, thrf[0],thrf[1],thrf[2], thrf[3],thrf[4],thrf[5]);
+      if ((bits & 0x02) != 0) { /*thrf[1] lo-crit is valid*/
+        if ((thrf[1] > thrf[0]) && ((bits & 0x01) != 0)) rv = 1;
+        if ((thrf[2] > thrf[1]) && ((bits & 0x04) != 0)) rv = 1; /*lo is wrong*/
+      } 
+      if ((bits & 0x10) != 0) { /*thrf[4] hi-crit is valid*/
+        if ((thrf[4] < thrf[3]) && ((bits & 0x08) != 0)) rv = 2;
+        if ((thrf[5] < thrf[4]) && ((bits & 0x20) != 0)) rv = 2; /*hi is wrong*/
+      }
+      if (rv != 0) {
+	  printf("Threshold values: %f>=%f>=%f, %f<=%f<=%f\n",
+		thrf[0], thrf[1], thrf[2], thrf[3], thrf[4], thrf[5]);
+          printf("Invalid threshold order in %s range.\n",
+				((rv == 1)? "lo": "hi"));
+      }
+   } else {
+      thr = (uchar *)pthrs;
+      if ((bits & 0x02) != 0) { /*thr[1] lo-crit is valid*/
+        if ((thr[1] > thr[0]) && ((bits & 0x01) != 0)) rv = 1;
+        if ((thr[2] > thr[1]) && ((bits & 0x04) != 0)) rv = 1; /*lo is wrong*/
+      } 
+      if ((bits & 0x10) != 0) { /*thr[4] hi-crit is valid*/
+        if ((thr[4] < thr[3]) && ((bits & 0x08) != 0)) rv = 2;
+        if ((thr[5] < thr[4]) && ((bits & 0x20) != 0)) rv = 2; /*hi is wrong*/
+      }
+      if (rv != 0) {
+         printf("Threshold values: %02x>=%02x>=%02x %02x<=%02x<=%02x\n",
+		thr[0], thr[1], thr[2], thr[3], thr[4], thr[5]);
+         printf("Invalid threshold order within -u (%s)\n",
+				((rv == 1)? "lo": "hi"));
+      }
+   }
+   return(rv);
+}
+
+int 
+GetSDRRepositoryInfo(int *nret, int *fdev)
+{
+   uchar resp[MAX_BUFFER_SIZE];
+   int sresp = MAX_BUFFER_SIZE;
+   int rc;
+   int nSDR;
+   int freespace;
+   ushort cmd;
+   uchar cc = 0;
+   int   i;
+
+   memset(resp,0,6);  /* init first part of buffer */
+   if (nret != NULL) *nret = 0;
+   if (fdev != NULL) fdevsdrs = *fdev;
+   if (fdevsdrs) cmd = GET_DEVSDR_INFO;
+   else cmd = GET_SDR_REPINFO;
+   rc = ipmi_cmd_mc(cmd, NULL, 0, resp,&sresp, &cc, fdebug);
+   if (fdebug) printf("ipmi_cmd[%04x] repinf(%d) status=%d cc=%x\n",
+			cmd, fdevsdrs,rc,cc);
+   /* some drivers return cc in rc */
+   if ((rc == 0xc1) || (rc == 0xd4)) cc = rc; 
+   else if (rc != 0) return(rc);
+   if (cc != 0) {
+      if ((cc == 0xc1) ||  /*0xC1 (193.) means unsupported command */
+          (cc == 0xd4))    /*0xD4 means insufficient privilege (Sun/HP)*/
+      {
+         /* Must be reporting wrong bit for fdevsdrs, 
+          * so switch & retry */
+         if (fdevsdrs) { 
+            fdevsdrs = 0;
+            cmd = GET_SDR_REPINFO;
+         } else {  
+            fdevsdrs = 1;
+            cmd = GET_DEVSDR_INFO;
+         }
+         sresp = MAX_BUFFER_SIZE;
+         rc = ipmi_cmd_mc(cmd, NULL, 0, resp,&sresp, &cc, fdebug);
+         if (fdebug) 
+             printf("ipmi_cmd[%04x] repinf status=%d cc=%x\n",cmd,rc,cc);
+         if (rc != ACCESS_OK) return(rc);
+         if (cc != 0) return(cc);
+      } else return(cc);
+   }
+
+   if (fdevsdrs) {
+      nSDR = resp[0];
+      freespace = 1;  
+      fReserveOK = 1;
+   } else {
+      nSDR = resp[1] + (resp[2] << 8);
+      freespace = resp[3] + (resp[4] << 8);
+      if ((resp[13] & 0x02) == 0) fReserveOK = 0;
+      else fReserveOK = 1;
+   }
+   if (nret != NULL) *nret = nSDR;
+   if (fdev != NULL) *fdev = fdevsdrs;
+   if (fdebug) {
+      //successful, show data
+      printf("SDR Repository (len=%d): ",sresp);
+      for (i = 0; i < sresp; i++) printf("%02x ",resp[i]);
+      printf("\n");
+      printf("SDR Info: fdevsdrs=%d nSDRs=%d free space = %x ReserveOK=%d\n",
+		fdevsdrs,nSDR,freespace,fReserveOK);
+   }
+
+   return(0);
+}  /*end GetSDRRepositoryInfo()*/
+
+
+int 
+GetSensorThresholds(uchar sens_num, uchar *thr_data)
+{
+	uchar resp[MAX_BUFFER_SIZE];
+	int sresp = MAX_BUFFER_SIZE;
+	uchar inputData[6];
+	int rc;
+	uchar cc = 0;
+
+	inputData[0] = sens_num;
+	rc = ipmi_cmd_mc(GET_SENSOR_THRESHOLD, inputData,1, resp,&sresp, &cc,fdebug);
+	if (fdebug)
+		printf("GetSensorThreshold[%02x] rc = %d, resp(%d) %02x %02x %02x %02x %02x %02x %02x\n",
+			sens_num,rc, sresp,resp[0],resp[1],resp[2],resp[3],
+			resp[4],resp[5],resp[6]);
+	if (rc != ACCESS_OK) return(rc);
+	if (cc != 0) return(cc);
+	if (sresp == 0) return(-2);
+	memcpy(thr_data,resp,sresp);
+	return(0);
+}
+
+int
+RearmSensor(uchar sens_num)
+{
+	uchar resp[MAX_BUFFER_SIZE];
+	int sresp = MAX_BUFFER_SIZE;
+	uchar inputData[8];
+	int rc;
+	uchar cc = 0;
+
+	memset(inputData,0,6);
+	memset(resp,0,6);
+	inputData[0] = sens_num;
+	rc = ipmi_cmd_mc(GET_SEVT_ENABLE, inputData, 1, resp,&sresp, &cc, fdebug);
+	if (rc == 0 && cc != 0) rc = cc;
+	if (rc != 0 || fdebug)
+		printf("GetSensorEventEnable(%02x) rc = %d, cc = %x %02x %02x %02x\n",
+			sens_num,rc,cc,resp[0],resp[1],resp[3]);
+	if (rc == 0 && resp[0] != 0xc0) {
+		printf("EventEnable(%02x) = %02x, is not 0xc0\n",
+			sens_num,resp[0]);
+		memset(inputData,0,6);
+		inputData[0] = sens_num;
+		inputData[1] = resp[0] | 0xc0;
+		inputData[2] = resp[1];
+		inputData[3] = resp[2];
+		inputData[4] = resp[3];
+		inputData[5] = resp[4];
+		rc = ipmi_cmd_mc(SET_SEVT_ENABLE, inputData, 6, resp,&sresp,
+				&cc, fdebug);
+		if (rc == 0 && cc != 0) rc = cc;
+		if (rc != 0 || fdebug)
+		printf("SetSensorEventEnable(%02x) rc = %d, cc = %x\n",
+			sens_num,rc,cc);
+	}
+
+	memset(inputData,0,6);
+	inputData[0] = sens_num;
+	inputData[1] = 0;  /* rearm all events for this sensor */
+	rc = ipmi_cmd_mc(REARM_SENSOR, inputData, 6, resp,&sresp, &cc, fdebug);
+	if (fdebug)
+		printf("RearmSensor(%02x) rc = %d, cc = %x %02x %02x\n",
+			sens_num,rc,cc,resp[0],resp[1]);
+	if (rc == 0 && cc != 0) rc = cc;
+
+	/* Could also do a global rearm via SetEventReceiver. */
+
+	return(rc);
+}  /*end RearmSensor*/
+
+int 
+SetSensorThresholds(uchar sens_num, uchar hi, uchar lo, 
+			uchar *thr_data, uchar *thr_set)
+{
+	uchar resp[MAX_BUFFER_SIZE];
+	int sresp = MAX_BUFFER_SIZE;
+	uchar inputData[8];
+	int rc;
+	uchar cc = 0;
+	uchar sets = 0;
+	int i;
+
+	/* 
+	 * Set the sensor Hysteresis before setting the threshold.
+	 */
+	memset(inputData,0,8);
+	inputData[0] = sens_num;
+	inputData[1] = 0xff;
+	rc = ipmi_cmd_mc(GET_SENSOR_HYSTERESIS,inputData,2, resp,&sresp, &cc,fdebug);
+	if (fdebug)
+		printf("GetSensorHysteresis(%02x) rc = %d, cc = %x %02x %02x\n",
+			sens_num,rc,cc,resp[0],resp[1]);
+	if (rc != ACCESS_OK) return(rc);
+	inputData[0] = sens_num;
+	inputData[1] = 0xff;
+	inputData[2] = resp[0];
+	inputData[3] = resp[1];
+	rc = ipmi_cmd_mc(SET_SENSOR_HYSTERESIS,inputData,4, resp,&sresp, &cc,fdebug);
+	if (fdebug)
+		printf("SetSensorHysteresis(%02x) rc = %d, cc = %x\n",
+			sens_num,rc,cc);
+	if (rc != ACCESS_OK) return(rc);
+	
+	/* 
+	 * The application should validate that values are ordered, 
+	 * e.g.  upper critical should be greater than upper 
+	 * non-critical.
+	 * Due to the limited command line parameter interface, 
+	 * use the hi & lo values to set each of the thresholds.
+	 * For a full implemenation, these thresholds should be set
+	 * individually.
+	 */
+	memset(inputData,0,8);
+	inputData[0] = sens_num;
+	sets = thr_data[0];
+        if (thr_set != NULL) {  /* use specific thr_set values */
+           memcpy(&inputData[2],thr_set,6);
+        } else {  /*default, use hi/lo params */
+	   if (lo == 0xff) sets &= 0x38;  /* don't set lowers */
+	   else {
+	      inputData[2] = lo;      /* lower non-crit  (& 0x01)  */
+	      inputData[3] = lo - 1;  /* lower critical  (& 0x02) */
+	      inputData[4] = lo - 2;  /* lower non-recov (& 0x04) */
+	   }
+	   if (hi == 0xff) sets &= 0x07;  /* don't set uppers */
+	   else {
+	      inputData[5] = hi;      /* upper non-crit  (& 0x08) */
+	      inputData[6] = hi + 1;  /* upper critical  (& 0x10) */
+	      inputData[7] = hi + 2;  /* upper non-recov (& 0x20) */
+	   }
+        } 
+	inputData[1] = sets;  /* which ones to set */
+	{   /* show from/to changes */
+		printf("GetThreshold[%02x]: %02x ",sens_num,sens_num);
+		for (i = 0; i < 7; i++) printf("%02x ",thr_data[i]);
+		printf("\n");
+		printf("SetThreshold[%02x]: ",sens_num);
+		for (i = 0; i < 8; i++) printf("%02x ",inputData[i]);
+		printf("\n");
+	}
+	rc = ipmi_cmd_mc(SET_SENSOR_THRESHOLD, inputData, 8, resp,&sresp, &cc, fdebug);
+	if (fdebug)
+		printf("SetSensorThreshold(%02x) rc = %d, cc = %x\n",
+			sens_num,rc,cc);
+	if (rc == 0 && cc != 0) rc = cc;
+	/* mBMC gets cc = 0xD5 (213.) here, setting thresholds disabled. */
+	return(rc);
+}
+
+int 
+GetSensorReading(uchar sens_num, void *psdr, uchar *sens_data)
+{
+	uchar resp[MAX_BUFFER_SIZE];
+	int sresp = MAX_BUFFER_SIZE;
+	uchar inputData[6];
+        SDR02REC *sdr = NULL;
+        int mc;
+	int rc;
+	uchar cc = 0;
+	uchar lun = 0;
+	uchar chan = 0;
+
+        if (psdr != NULL && fbadsdr == 0) {
+           sdr = (SDR02REC *)psdr;
+           mc = sdr->sens_ownid;
+	   if (mc != BMC_SA) {  /* not BMC, e.g. HSC or ME sensor */
+	      uchar a = ADDR_IPMB;
+	      if (mc == HSC_SA) a = ADDR_SMI;
+	      chan = (sdr->sens_ownlun & 0xf0) >> 4;
+	      lun = (sdr->sens_ownlun & 0x03);
+              ipmi_set_mc(chan,(uchar)mc, lun,a);
+           }
+        } else mc = BMC_SA;
+	inputData[0] = sens_num;
+	rc = ipmi_cmd_mc(GET_SENSOR_READING,inputData,1, resp,&sresp,&cc,fdebug);
+	if (fdebug) 
+	    printf("GetSensorReading mc=%x,%x,%x status=%d cc=%x sz=%d resp: %02x %02x %02x %02x\n",
+		     chan,mc,lun,rc,cc,sresp,resp[0],resp[1],resp[2],resp[3]);
+        if (mc != BMC_SA) ipmi_restore_mc();
+	if ((rc == 0) && (cc != 0)) {
+	    if (fdebug) printf("GetSensorReading error %x %s\n",cc,
+				decode_cc((ushort)0,(uchar)cc));
+            rc = cc;
+        }
+        if (rc != 0) return(rc);
+
+	if (resp[1] & 0x20) { /* init state, reading invalid */
+	   if (fdebug) 
+		printf("sensor[%x] in init state, no reading\n", sens_num);
+           sens_data[1] = resp[1];
+           sens_data[2] = 0x40;  /*set bit num for init state*/
+        } else {     /*valid reading, copy it*/
+	   /* only returns 4 bytes, no matter what type */
+	   memcpy(sens_data,resp,4);
+	}
+	return(0);
+}  /*end GetSensorReading()*/
+
+int 
+GetSensorReadingFactors(uchar snum, uchar raw, int *m, int *b, int *  b_exp, 
+			int *r, int *a)
+{
+	uchar resp[MAX_BUFFER_SIZE];
+	int sresp = MAX_BUFFER_SIZE;
+	uchar inputData[6];
+	int rc;
+	uchar cc = 0;
+	int  toler, a_exp;
+
+	inputData[0] = snum;
+	inputData[1] = raw;
+	rc = ipmi_cmd_mc(GET_SENSOR_READING_FACTORS, inputData, 2, 
+                      resp,&sresp, &cc, fdebug);
+	if (fdebug) printf("GetSensorReadingFactors status = %d\n",rc);
+	if (rc != ACCESS_OK) return(rc);
+	if (cc != 0) return(cc);
+
+        /* successful, copy values */
+	*m = resp[1] + ((resp[2] & 0xc0) << 2);
+	toler = resp[2] & 0x3f;
+	*b = resp[3] + ((resp[4] & 0xc0) << 2);
+	*a = (resp[4] & 0x3f) + ((resp[5] & 0xf0) << 4);
+	a_exp = (resp[5] & 0xc0) >> 2;
+	*r = (resp[6] &0xf0) >> 4;
+	*b_exp = resp[6] & 0x0f;
+	if (fdebug) {
+	      printf("factors: next=%x m=%d b=%d b_exp=%d a=%d a_exp=%d r=%d\n",
+			resp[0],*m,*b,*b_exp,*a,a_exp,*r);
+	}
+	return(0);
+}
+
+int GetSensorType(uchar snum, uchar *stype, uchar *rtype)
+{
+	uchar resp[MAX_BUFFER_SIZE];
+	int sresp = MAX_BUFFER_SIZE;
+	uchar inputData[6];
+	int rc;
+	uchar cc = 0;
+
+	inputData[0] = snum;
+	rc = ipmi_cmd_mc(GET_SENSOR_TYPE, inputData, 1, 
+                      resp,&sresp, &cc, fdebug);
+	if (fdebug) printf("GetSensorType: ipmi_cmd rv = %d, cc = %x\n",rc,cc);
+	if (rc != ACCESS_OK) return(rc);
+	if (cc != 0) return(cc);
+        /* successful, copy values */
+	if (stype != NULL) *stype = resp[0];
+	if (rtype != NULL) *rtype = resp[1] & 0x7f;
+	return(rc);
+}
+
+void set_reserve(int val)
+{
+   fDoReserve = val;
+}
+
+int sdr_get_reservation(uchar *res_id, int fdev)
+{
+   int sresp;
+   uchar resp[MAX_BUFFER_SIZE];
+   uchar cc = 0;
+   ushort cmd;
+   int rc = -1;
+
+   if (fDoReserve == 1) {
+	fDoReserve = 0; /* only reserve SDR the first time */
+        sresp = sizeof(resp);;
+	if (fdev) cmd = RESERVE_DEVSDR_REP;
+	else cmd = RESERVE_SDR_REP;
+	rc = ipmi_cmd_mc(cmd, NULL, 0, resp, &sresp, &cc, fdebug);
+	if (rc == 0 && cc != 0) rc = cc;
+	if (rc == 0) {  /* ok, so set the reservation id */
+	   resid[0] = resp[0]; 
+	   resid[1] = resp[1];
+        }
+	/* A reservation is cancelled by the next reserve request. */
+        if (fdebug)
+	   printf("ipmi_cmd RESERVE status=%d cc=%x id=%02x%02x\n",
+		  rc,cc,resid[0],resid[1]);
+   } else rc = 0;
+   /* if not first time, or if error, return existing resid. */
+   res_id[0] = resid[0];
+   res_id[1] = resid[1];
+   return(rc);
+} /*end sdr_get_reservation*/
+
+int sdr_clear_repo(int fdev)
+{
+   int sresp;
+   uchar resp[MAX_BUFFER_SIZE];
+   uchar inputData[6];
+   uchar cc = 0;
+   int rc = -1;
+   ushort cmd;
+   uchar resv[2] = {0,0};
+
+   if (fReserveOK) 
+	rc = sdr_get_reservation(resv,fdev);
+
+   cmd = 0x27 + (NETFN_STOR << 8);  /*Clear SDR Repository*/
+   inputData[0] = resv[0];     /*res id LSB*/
+   inputData[1] = resv[1];     /*res id MSB*/
+   inputData[2] = 'C';
+   inputData[3] = 'L'; 
+   inputData[4] = 'R';
+   inputData[5] = 0xAA;  
+   sresp = sizeof(resp);;
+   rc = ipmi_cmd_mc(cmd, inputData, 6, resp, &sresp,&cc, fdebug);
+   if (fdebug) printf("sdr_clear_repo: rc = %d, cc = %x, sz=%d\n",rc,cc,sresp);
+   if (rc == 0 && cc != 0) rc = cc;
+
+   if (rc == 0 && (resp[0] & 1) != 1) {
+	if (fdebug) printf("Wait for sdr_clear_repo to complete\n");
+	os_usleep(1,0);
+   }
+   return(rc);
+}
+
+int sdr_add_record(uchar *sdr, int fdev)
+{
+   int sresp;
+   uchar resp[MAX_BUFFER_SIZE];
+   uchar inputData[6+SZCHUNK];
+   uchar cc = 0;
+   int rc = -1;
+   ushort cmd;
+   uchar resv[2] = {0,0};
+   int reclen, len, i;
+   int recid, chunksz;
+   uchar prog;
+
+   reclen = sdr[4] + 5;
+   recid = sdr[0] + (sdr[1] << 8);
+   /* OEM SDRs can be min 8 bytes, less is an error. */
+   if (reclen < 8) return(LAN_ERR_BADLENGTH);  
+   if (fReserveOK) 
+	rc = sdr_get_reservation(resv,fdev);
+   if (fdebug) printf("sdr_add_record[%x]: reclen = %d, reserve rc = %d\n",
+			recid,reclen,rc);
+
+   cmd = 0x25 + (NETFN_STOR << 8); /*PartialAddSdr*/
+   recid = 0;  /*first chunk must be added as 0000*/
+   chunksz = SZCHUNK;
+   for (i = 0; i < reclen; )
+   {
+      prog = 0;
+      len = chunksz;
+      if ((i+chunksz) >= reclen) { /*last record*/
+	  len = reclen - i;
+	  prog = 1;
+      }
+      inputData[0] = resv[0]; /*res id LSB*/
+      inputData[1] = resv[1]; /*res id MSB*/
+      inputData[2] = recid & 0x00ff;         /*record id LSB*/
+      inputData[3] = (recid >> 8) & 0x00ff;  /*record id MSB*/
+      inputData[4] = (uchar)i;       /*offset */
+      inputData[5] = prog;    /*progress: 1=last record*/
+      memcpy(&inputData[6],&sdr[i],len);
+      sresp = sizeof(resp);
+      rc = ipmi_cmd_mc(cmd, inputData, 6+len, resp, &sresp,&cc, fdebug);
+      if (fdebug) 
+         printf("sdr_add_record[%x,%x]: rc = %d, cc = %x, sz=%d\n",
+			recid,i,rc,cc,sresp);
+      if (rc == 0 && cc != 0) rc = cc;
+      if (rc != 0) break;
+      if (recid == 0 && rc == 0)   /*first time, so set recid*/
+         recid = resp[0] + (resp[1] << 8);
+      i += len;
+   }
+   return(rc);
+}
+
+int GetSDR(int r_id, int *r_next, uchar *recdata, int srecdata, int *rlen)
+{
+	int sresp;
+	uchar resp[MAX_BUFFER_SIZE+SZCHUNK];
+	uchar respchunk[SZCHUNK+10];
+	uchar inputData[6];
+	uchar cc = 0;
+	int rc = -1;
+	int  i, chunksz, thislen, off;
+	int reclen;
+	ushort cmd;
+	uchar resv[2] = {0,0};
+  
+	chunksz = SZCHUNK;
+        reclen = srecdata; /*max size of SDR record*/
+	off = 0;
+        *rlen = 0;
+        *r_next = 0xffff;  /*default*/
+	if (fReserveOK) 
+	   rc = sdr_get_reservation(resv,fdevsdrs);
+	if (fdevsdrs) cmd = GET_DEVICE_SDR;
+	else cmd = GET_SDR;
+	if (reclen == 0xFFFF) {  /* get it all in one call */
+	   inputData[0] = resv[0];     /*res id LSB*/
+	   inputData[1] = resv[1];     /*res id MSB*/
+	   inputData[2] = r_id & 0x00ff;        /*record id LSB*/
+	   inputData[3] = (r_id & 0xff00) >> 8;	/*record id MSB*/
+	   inputData[4] = 0;      /*offset */
+	   inputData[5] = 0xFF;  /*bytes to read, ff=all*/
+	   sresp = sizeof(resp);;
+	   if (fdebug) printf("ipmi_cmd SDR id=%d read_all, len=%d\n",
+				r_id,sresp);
+	   rc = ipmi_cmd_mc(cmd, inputData, 6, recdata, &sresp,&cc, fdebug);
+	   /* This will usually return cc = 0xCA (invalid length). */
+	   if (fdebug) printf("ipmi_cmd SDR data status = %d, cc = %x, sz=%d\n",
+				rc,cc,sresp);
+	   reclen = sresp;
+	   *r_next = recdata[0] + (recdata[1] << 8);
+	} else    /* if (reclen > chunksz) do multi-part chunks */
+	for (off = 0; off < reclen; )
+	{
+	   thislen = chunksz;
+	   if ((off+chunksz) > reclen) thislen = reclen - off;
+	   inputData[0] = resv[0];     /*res id LSB*/
+	   inputData[1] = resv[1];     /*res id MSB*/
+	   inputData[2] = r_id & 0x00ff; 	/*record id LSB*/
+	   inputData[3] = (r_id & 0xff00) >> 8;	/*record id MSB*/
+	   inputData[4] = (uchar)off;      /*offset */
+	   inputData[5] = (uchar)thislen;  /*bytes to read, ff=all*/
+	   sresp = sizeof(respchunk);
+	   rc = ipmi_cmd_mc(cmd, inputData, 6, respchunk, &sresp,&cc, fdebug);
+	   if (fdebug) 
+               printf("ipmi_cmd SDR[%x] off=%d ilen=%d status=%d cc=%x sz=%d\n",
+			r_id,off,thislen,rc,cc,sresp);
+	   if (off == 0 && cc == 0xCA && thislen == SZCHUNK) { 
+		/* maybe shorter than SZCHUNK, try again */
+	        chunksz = 0x06;
+	        if (fdebug) printf("sdr[%x] try again with chunksz=%d\n",
+					r_id,chunksz);
+	        continue;
+	   }
+           if (off > chunksz) {
+              /* already have first part of the SDR, ok to truncate */
+              if (rc == -3) { /* if LAN_ERR_RECV_FAIL */
+                  if (fdebug) printf("sdr[%x] error rc=%d len=%d truncated\n",
+                                       r_id,rc,sresp);
+                  sresp = 0;
+                  rc = 0;
+              }
+              if (cc == 0xC8 || cc == 0xCA) { /* length errors */
+                  /* handle certain MCs that report wrong length,
+                   * at least use what we already have (sresp==0) */
+                  if (fdebug) printf("sdr[%x] error cc=%02x len=%d truncated\n",
+                                       r_id,cc,sresp);
+                  cc = 0;
+              }
+           }
+	   if (rc != ACCESS_OK) return(rc);
+	   if (cc != 0) return(cc);
+	   /* if here, successful, chunk was read */
+           if (sresp < (thislen+2)) {
+              /* There are some SDRs that may report the wrong length, and
+               * return less bytes than they reported, so just truncate. */
+              if (fdebug) printf("sdr[%x] off=%d, expected %d, got %d\n",
+                                r_id,off,thislen+2,sresp);
+              if (sresp >= 2) thislen = sresp - 2;
+              else thislen = 0;
+              reclen = off + thislen;  /* truncate, stop reading */
+              fprintf(stderr,"SDR record %x is malformed, length %d is less than minimum %d\n",r_id,sresp,thislen+2);
+			  rc = ERR_SDR_MALFORMED;
+           }
+	   /* successful */
+	   memcpy(&resp[off],&respchunk[2],thislen);
+	   if (off == 0 && sresp >= 5) {
+		*r_next = respchunk[0] + (respchunk[1] << 8);
+                reclen = respchunk[6] + 5;  /*get actual record size*/
+                if (reclen > srecdata) {
+                  if (fdebug) printf("sdr[%x] chunk0, reclen=%d srecdata=%d\n",
+                                        r_id, reclen, srecdata);
+                  reclen = srecdata; /*truncate*/
+                }
+           }
+	   off += thislen;
+           *rlen = off;
+	}
+	if (fdebug) {
+           printf("GetSDR[%04x] next=%x (len=%d): ",r_id,*r_next,reclen); 
+	   for (i = 0; i < reclen; i++) printf("%02x ",resp[i]);
+	   printf("\n");
+	   }
+	memcpy(recdata,&resp[0],reclen);
+        *rlen = reclen;
+	return(rc);
+}  /*end GetSDR*/
+
+static int nsdrs = 0;    /*number of sdrs*/
+static int sz_sdrs = 0;  /*actual size used with sdrs*/
+static uchar *psdrcache = NULL;
+
+void free_sdr_cache(uchar *ptr)
+{
+   if (ptr != NULL) free(ptr);
+   if ((ptr != psdrcache) && (psdrcache != NULL)) 
+		    free(psdrcache);
+   psdrcache = NULL;
+}
+
+int get_sdr_file(char *sdrfile, uchar **sdrlist)
+{
+   int rv = -1;
+   FILE *fp = NULL;
+   int i, n, num, nsdr, isdr, len;
+   uchar *sdrbuf;
+   char buff[255];
+   uchar hbuf[85];
+   char fvalid;
+
+   fp = fopen(sdrfile,"r");
+   if (fp == NULL) {
+         printf("Cannot open file %s\n",sdrfile);
+         return(ERR_FILE_OPEN);
+   }
+   /* determine number of SDRs by number of lines in the file */
+   num = 0;
+   while (fgets(buff, 255, fp)) { num++; }
+   if (fdebug) {
+     printf("Reading %d SDRs from file %s\n",num,sdrfile);
+     if ((psdrcache != NULL) && (nsdrs > 0)) {  /*already have sdrcache*/
+	printf("get_sdr_file: Already have cache\n"); /*fdebug*/
+	free_sdr_cache(psdrcache); /*free previous sdrcache*/
+     }
+   }
+   sdrbuf = malloc(num * SDR_SZ); 
+   if (sdrbuf == NULL) {
+	fclose(fp);
+	return(rv);
+   }
+   fseek(fp, 0L, SEEK_SET); 
+   *sdrlist = sdrbuf; 
+   psdrcache = sdrbuf;
+   nsdrs = num;
+   isdr = 0;
+   nsdr = 0;
+   while (fgets(buff, 255, fp)) {
+   	len = strlen_(buff);
+   	fvalid = 0;
+   	if (buff[0] >= '0' && (buff[0] <= '9')) fvalid = 1;
+   	else if (buff[0] >= 'a' && (buff[0] <= 'f')) fvalid = 1;
+   	else if (buff[0] >= 'A' && (buff[0] <= 'F')) fvalid = 1;
+   	if (fvalid == 0) continue;
+	i = 0;
+   	for (n = 0; n < len; ) {
+           if (buff[n] < 0x20) break; /* '\n', etc. */
+           hbuf[i++] = htoi(&buff[n]);
+	   n += 3;
+   	}
+	memcpy(&sdrbuf[isdr],hbuf,i);
+	isdr += i;
+	nsdr++;
+   } /*end while*/
+   if (fdebug) printf("Read %d SDRs, %d bytes\n",nsdr,isdr);
+   fclose(fp);
+   rv = 0;
+   return(rv);
+}
+
+int get_sdr_cache(uchar **pret)
+{
+   int rv = -1;
+   int i, n, sz, len, asz;
+   int recid, recnext;
+   uchar *pcache;
+   uchar *psdr;
+
+   if (pret == NULL) return(rv);
+   fdevsdrs = use_devsdrs(fpicmg);
+
+   if ((psdrcache != NULL) && (nsdrs > 0)) {  /*already have sdrcache*/
+        *pret = psdrcache;
+	if (fdebug) printf("get_sdr_cache: already have cache (%p)\n",*pret);
+	return(0);
+   }
+   else if (fdebug) printf("get_sdr_cache: Allocating cache\n");
+
+   rv = GetSDRRepositoryInfo(&n,&fdevsdrs);
+   if (rv != 0) return(rv);
+   if (n == 0) {  
+	/* this is an error, probably because fdevsdrs is wrong.*/
+	if (fdebug) printf("get_sdr_cache: nsdrs=0, retrying\n");
+	fdevsdrs = (fdevsdrs ^ 1);
+	n = 150; /*try some default num SDRs*/
+   }
+
+   sz = n * SDR_SZ;  /*estimate max size for n sdrs*/
+   pcache = malloc(sz);
+   if (pcache == NULL) return(rv);
+   psdrcache = pcache;
+   *pret = pcache;
+   memset(pcache,0,sz);
+   recid = 0;
+   asz = 0;
+   for (i = 0; i <= n; i++)
+   {
+	if (recid == 0xffff) break;
+	// psdr = &pcache[i * SDR_SZ];	
+        psdr = &pcache[asz];
+	rv = GetSDR(recid,&recnext,psdr,SDR_SZ,&len);
+	if (fdebug) 
+	   printf("GetSDR[%x] rv = %d len=%d next=%x\n",recid,rv,len,recnext);
+	if (rv != 0) {
+	   if (rv == 0xC5) { set_reserve(1); i--; } /*retry*/
+	   else break;
+	} else {  /*success*/
+	   /* if sdrlen!=len, adjust */
+	   if ((len > 5) && (len != (psdr[4] + 5)) ) {
+		if (fdebug) printf("SDR[%x] adjust len from %d to %d\n",
+					recid,psdr[4]+5,len);
+		psdr[4] = len - 5;
+	   }
+	   asz += len;
+	   if (recnext == recid) recid = 0xffff;
+	   else recid = recnext;
+	}
+   }
+   nsdrs = n;
+   sz_sdrs = asz;  /* save the size for later*/
+   if (fdebug) {
+	printf("get_sdr_cache, n=%d sz=%d asz=%d\n",n,sz,asz);
+	if (i < n) printf("get_sdr_cache error, i=%d < n=%d, rv=%d\n",i,n,rv);
+   }
+   return(rv);
+}
+
+int find_nsdrs(uchar *pcache)
+{
+   int num = 0;
+   ulong asz = 0;
+   int i, len;
+   uchar *sdr;
+   ushort recid = 0;
+
+   if (pcache == NULL) return(num);
+   for (i = 0; (int)asz < sz_sdrs; i++)
+   {
+	sdr = &pcache[asz];
+	if (sdr[2] != 0x51) {  /* Dell SDR length error */
+	    printf("SDR[%x] length error at %ld\n",recid,asz);
+	    sdr = &pcache[++asz]; /*try it if off-by-one*/
+	}
+	len = sdr[4] + 5;
+	recid = sdr[0] + (sdr[1] << 8);
+	if (fdebug) printf("SDR[%x] len=%d i=%d offset=%lx\n",recid,len,i,asz);
+	asz += len;
+   }
+   num = i;
+   return(num);
+}
+
+int find_sdr_by_snum(uchar *psdr, uchar *pcache, uchar snum, uchar sa)
+{
+   int rv = -1;
+   uchar *sdr;
+   int i, k, len;
+   int asz = 0;
+   if (psdr == NULL) return(rv);
+   if (pcache == NULL) return(rv);
+   for (i = 0; i <= nsdrs; i++)
+   {
+	// sdr = &pcache[i * SDR_SZ];	
+	sdr = &pcache[asz];	
+	len = sdr[4] + 5;
+	asz += len;
+	switch(sdr[3]) {
+	case 0x01: k = 7; break; /*full sensor*/
+	case 0x02: k = 7; break; /*compact sensor*/
+	case 0x03: k = 7; break;/*compact sensor*/
+	default:   k = 0; break;
+	}
+	if (k == 0) continue;
+	else {
+	    if ((sdr[5] == sa) && (sdr[k] == snum)) {
+		memcpy(psdr,sdr,len);
+		return(0);
+	    }
+	}
+   }
+   return(rv);
+}
+
+int find_sdr_by_tag(uchar *psdr, uchar *pcache, char *tag, uchar dbg)
+{
+   int rv = -1;
+   uchar *sdr;
+   int i, k, n, len;
+   int asz = 0;
+   if (psdr == NULL) return(rv);
+   if (pcache == NULL) return(rv);
+   if (tag == NULL) return(rv);
+   if (dbg) fdebug = 1;
+   n = strlen_(tag);
+   if (fdebug) printf("find_sdr_by_tag(%s) nsdrs=%d\n",tag,nsdrs);
+   for (i = 0; i <= nsdrs; i++)
+   {
+	// sdr = &pcache[i * SDR_SZ];	
+	sdr = &pcache[asz];
+	len = sdr[4] + 5;
+	asz += len;
+	switch(sdr[3]) { /* set tag offset by SDR type */
+	case 0x01: k = 48; break; /*full SDR*/
+	case 0x02: k = 32; break; /*compact SDR*/
+	case 0x03: k = 17; break; /*event-only SDR*/
+	case 0x10: k = 16; break; /*device locator SDR*/
+	case 0x11: k = 16; break; /*FRU device locator SDR*/
+	case 0x12: k = 16; break; /*IPMB device locator SDR*/
+	default:   k = 0; break;  /*else do not have an ID string/tag*/
+	}
+	if (k == 0) {
+	   if (fdebug) printf("sdr[%d] idx=%02x%02x num=%x type=%x skip\n",
+				i,sdr[1],sdr[0],sdr[7],sdr[3]);
+	   continue;
+	} else {
+	    if (len > SDR_SZ) len = SDR_SZ;
+	    if (fdebug) {
+		char tmp[17];
+		memset(tmp,0,sizeof(tmp));
+		memcpy(tmp,&sdr[k],(len - k));
+		tmp[16] = 0;  /*force stringify*/
+		printf("sdr[%d] idx=%02x%02x num=%x tag: %s\n",i,sdr[1],sdr[0],
+			sdr[7],tmp);
+	    }
+	    if (strncmp(tag,(char *)&sdr[k],n) == 0) {
+		memcpy(psdr,sdr,len);
+		return(0);
+	    }
+	}
+   }
+   return(rv);
+}
+
+
+int find_sdr_next(uchar *psdr, uchar *pcache, ushort id)
+{
+   int rv = -1;
+   uchar *sdr;
+   int i, imatch, len;
+   ushort recid;
+   int asz = 0;
+   if (psdr == NULL) return(rv);
+   if (pcache == NULL) return(rv);
+   imatch = nsdrs;
+   for (i = 0; i < nsdrs; i++)
+   {
+	// sdr = &pcache[i * SDR_SZ];
+	sdr = &pcache[asz];
+	if (sdr[2] != 0x51)   /* Dell SDR off-by-one error */
+	    sdr = &pcache[++asz]; 
+	len = sdr[4] + 5;
+	recid = sdr[0] + (sdr[1] << 8);
+	asz += len;
+	// if (fdebug) printf("SDR[%x] len=%d id=%x i=%d imatch=%d\n",
+	//			recid,len,id,i,imatch);
+	if (recid == id) imatch = i + 1; /*matches prev, return next one*/
+	else if (id == 0) { rv = 0; break; } /* 0000 = first one */
+	if (i == imatch) { rv = 0; break; }
+   }
+   if (rv == 0) memcpy(psdr,sdr,len);
+   return(rv);
+}
+
+int find_sdr_by_id(uchar *psdr, uchar *pcache, ushort id)
+{
+   int rv = -1;
+   uchar *sdr;
+   int i, imatch, len;
+   ushort recid;
+   int asz = 0;
+   if (psdr == NULL) return(rv);
+   if (pcache == NULL) return(rv);
+   imatch = nsdrs;
+   for (i = 0; i < nsdrs; i++)
+   {
+	sdr = &pcache[asz];
+	len = sdr[4] + 5;
+	recid = sdr[0] + (sdr[1] << 8);
+	asz += len;
+	if (recid == id) { rv = 0; break; }
+	else if (id == 0) { rv = 0; break; } /* 0000 = first one */
+   }
+   if (rv == 0) memcpy(psdr,sdr,len);
+   return(rv);
+}
+
+uchar
+bitnum(ushort value)
+{
+   uchar ret = 0;
+   int i;
+   /* returns the highest bit number number set in this word. */
+   /* Bit numbers are 1-based in this routine, 0 means no bits set. */
+   /* scan 15 bits (0x7FFF). */
+   for (i = 0; i < 15; i++) {
+	if (value & 0x01) ret = i+1;  /*was ret++;*/
+	value = (value >> 1);
+   }
+   return(ret);
+}
+
+static double
+expon(int x, int y)
+{
+   double res;
+   int i;
+   /* compute exponent: x to the power y */
+   res = 1;
+   if (y > 0) {
+	for (i = 0; i < y; i++) res = res * x;
+   } else if (y < 0) {
+	for (i = 0; i > y; i--) res = res / x;
+   } /* else if if (y == 0) do nothing, res=1 */
+   return(res);
+}
+
+double
+RawToFloat(uchar raw, uchar *psdr)
+{
+   double floatval;
+   int m, b, a;
+   uchar  ax; 
+   int  rx, b_exp;
+   SDR01REC *sdr;
+   int signval;
+
+   sdr = (SDR01REC *)psdr;
+   floatval = (double)raw;  /*default*/
+
+   // if (raw == 0xff) floatval = 0; else 
+   if (sdr->rectype == 0x01) {  /* SDR rectype == full */
+	if (fdebug)
+	   printf("units=%x base=%d mod=%d (raw=%x, nom_rd=%x)\n",
+		sdr->sens_units,sdr->sens_base,sdr->sens_mod,
+		raw, sdr->nom_reading);
+	m = sdr->m + ((sdr->m_t & 0xc0) << 2);
+	b = sdr->b + ((sdr->b_a & 0xc0) << 2);
+	if (b & 0x0200) b = (b - 0x0400);  /*negative*/
+	if (m & 0x0200) m = (m - 0x0400);  /*negative*/
+	rx = (sdr->rx_bx & 0xf0) >> 4;
+	if (rx & 0x08) rx = (rx - 0x10); /*negative, fix sign w ARM compilers*/
+	a = (sdr->b_a & 0x3f) + ((sdr->a_ax & 0xf0) << 2);
+	ax = (sdr->a_ax & 0x0c) >> 2;
+	b_exp = (sdr->rx_bx & 0x0f);
+	if (b_exp & 0x08) b_exp = (b_exp - 0x10);  /*negative*/
+		//b_exp |= 0xf0;        /* negative 8-bit */
+	if ((sdr->sens_units & 0xc0) == 0) {  /*unsigned*/
+		floatval = (double)raw;
+	} else {  /*signed*/
+		if (raw & 0x80) signval = (raw - 0x100);
+		else signval = raw;
+		floatval = (double)signval;
+	}
+	floatval *= (double) m;
+#ifdef MATH_OK
+	floatval += (b * pow (10,b_exp));
+	floatval *= pow (10,rx);
+#else
+	floatval += (b * expon (10,b_exp));
+	floatval *= expon (10,rx);
+#endif
+	if (fdebug)
+	   printf("decode1: m=%d b=%d b_exp=%x rx=%d, a=%d ax=%d l=%x, floatval=%f\n",
+			m,b,b_exp,rx,a,ax,sdr->linear,floatval);
+	switch(sdr->linear) {
+	case 0:   /*linear*/
+	   break;
+	case 7:   /*invert 1/x*/
+	   /* skip if zero to avoid dividing by zero */
+	   if (raw != 0) floatval = 1 / floatval;
+	   break;
+	case 1:   /*ln*/
+	case 2:   /*log10, log2, e, exp10, exp2, */
+	case 3:   /*log2*/
+	case 4:   /*e*/
+	case 5:   /*exp10*/
+	case 6:   /*exp2*/
+	case 8:   /*sqr(x)*/
+	case 9:   /*cube(x)*/
+	case 10:  /*sqrt(x)*/
+	case 11:  /*cube-1(x)*/
+        default:
+	   if (fdebug) printf("linear mode %x not implemented\n",sdr->linear);
+	   break;
+	}  /*end-switch linear*/
+   }
+
+#ifdef NOT_LINEAR
+   /* else if (sdr->linear != 7) */
+   {
+     double be, re;
+     rc = GetSensorType(sdr->sens_num,&stype,&rtype);
+     if (fdebug)
+	printf("decode: rc=%x, stype=%x, rtype=%x\n",rc,stype,rtype);
+     if (rc != 0) return(floatval);
+
+     /* GetSensorReadingFactors */
+     rc = GetSensorReadingFactors(sdr->sens_num,raw,&m,&b,&b_exp,&r,&a);
+     if (rc == 0) {
+	// floatval = ((m * raw) + (b * be)) * re;
+     } 
+     if (fdebug) printf("decode: rc=%x, floatval=%f\n",rc,floatval);
+   }
+#endif
+
+   return(floatval);
+}
+
+#define IpmiAnalogDataFormatUnsigned 0
+#define IpmiAnalogDataFormat1Compl   1
+#define IpmiAnalogDataFormat2Compl   2
+
+uchar
+FloatToRaw(double val, uchar *psdr, int rounding)
+{
+   double      cval;
+   int         lowraw, highraw, raw, maxraw, minraw, next_raw;
+   int         analog_dfmt;
+
+   analog_dfmt = (psdr[20] >> 6) & 0x03;
+   switch( analog_dfmt )
+   {
+       case IpmiAnalogDataFormat1Compl:
+            lowraw   = -127;
+            highraw  = 127;
+            minraw   = -127;
+            maxraw   = 127;
+            next_raw = 0;
+            break;
+       case IpmiAnalogDataFormat2Compl:
+            lowraw   = -128;
+            highraw  = 127;
+            minraw   = -128;
+            maxraw   = 127;
+            next_raw = 0;
+            break;
+       case IpmiAnalogDataFormatUnsigned:
+       default:
+            lowraw   = 0;
+            highraw  = 255;
+            minraw   = 0;
+            maxraw   = 255;
+            next_raw = 128;
+            break;
+    }
+
+    /* do a binary search for the right nth root value */
+    do {
+        raw = next_raw;
+        cval = RawToFloat( (uchar)raw, psdr );
+        if ( cval < val ) {
+            next_raw = ((highraw - raw) / 2) + raw;
+            lowraw = raw;
+        } else {
+            next_raw = ((raw - lowraw) / 2) + lowraw;
+            highraw = raw;
+        }
+    } while ( raw != next_raw );
+   
+    /* Do rounding to get the final value */
+    switch( rounding ) {
+       case 0:   /* Round Normal = Round to nearest value */
+            if ( val > cval ) {
+                 if ( raw < maxraw ) {
+                      double nval;
+		      nval = RawToFloat((uchar)(raw+1),psdr);
+                      nval = cval + ((nval - cval) / 2.0);
+                      if ( val >= nval ) raw++;
+                 }
+            } else {
+                 if ( raw > minraw ) {
+                      double pval;
+		      pval = RawToFloat((uchar)(raw-1),psdr);
+                      pval = pval + ((cval - pval) / 2.0);
+                      if ( val < pval ) raw--;
+                 }
+            }
+            break;
+       case 1:  /*Round Down*/
+            if ((val < cval) && (raw > minraw )) raw--;
+            break;
+       case 2:  /*Round Up*/
+            if ((val > cval) && (raw < maxraw)) raw++;
+            break;
+     }
+     if ( analog_dfmt == IpmiAnalogDataFormat1Compl )
+        if ( raw < 0 ) raw -= 1;
+    return((uchar)raw);
+}  /*end FloatToRaw()*/
+
+static int fill_thresholds(double *thrf, uchar *sdr)
+{
+   int rv = 0;
+   uchar *vals;
+   uchar bits;
+
+   // snum = sdr[7];
+   bits = sdr[19]; /*which are settable*/
+   vals = &sdr[36];
+   if (fdebug) 
+      printf("fill_thresholds: bits=%02x, values: %f>=%f>=%f, %f<=%f<=%f\n",
+		bits, thrf[0],thrf[1],thrf[2], thrf[3],thrf[4],thrf[5]);
+   if (thrf[0] == THR_EMPTY) {
+	if ((bits & 0x01) != 0) { /*lo-noncrit*/
+	   thrf[0] = RawToFloat(vals[5],sdr);
+           rv++;
+        } else thrf[0] = 0;
+   }
+   if (thrf[1] == THR_EMPTY) { 
+	if ((bits & 0x02) != 0) { /*lo-crit*/
+	   thrf[1] = RawToFloat(vals[4],sdr);
+           rv++;
+	} else thrf[1] = 0;
+   }
+   if (thrf[2] == THR_EMPTY) { 
+	if ((bits & 0x04) != 0) { /*lo-unrec*/
+	   thrf[2] = RawToFloat(vals[3],sdr);
+           rv++;
+	} else thrf[2] = 0;
+   }
+   if (thrf[3] == THR_EMPTY) { 
+	if ((bits & 0x08) != 0) { /*hi-noncrit*/
+	   thrf[3] = RawToFloat(vals[2],sdr);
+           rv++;
+	} else thrf[3] = 0;
+   }
+   if (thrf[4] == THR_EMPTY) { 
+	if ((bits & 0x10) != 0) { /*hi-crit*/
+	   thrf[4] = RawToFloat(vals[1],sdr);
+           rv++;
+	} else thrf[4] = 0;
+   }
+   if (thrf[5] == THR_EMPTY) { 
+	if ((bits & 0x20) != 0) { /*hi-unrec*/
+	   thrf[5] = RawToFloat(vals[0],sdr);
+           rv++;
+	} else thrf[5] = 0;
+   }
+   if (fdebug)
+      printf("fill_thresholds: after rv=%d values: %f>=%f>=%f, %f<=%f<=%f\n",
+	      rv,thrf[0],thrf[1],thrf[2], thrf[3],thrf[4],thrf[5]);
+   return(rv); 
+}
+
+char *
+decode_itype(uchar itype)
+{
+   char *retstr;
+   int i;
+   /* Decode the Interrupt Type from Entity Assoc records */
+
+   retstr = tmpstr;
+   if (itype <= 0x0f) sprintf(retstr,"IRQ_%d",itype);
+   else if (itype <= 0x13) {
+	strcpy(retstr,"PCI-A");
+	for (i=0x10;i<itype;i++) retstr[4]++;
+	}
+   else if (itype == 0x14) strcpy(retstr,"SMI");
+   else if (itype == 0x15) strcpy(retstr,"SCI");
+   else if (itype >= 0x20 && itype <= 0x5f) 
+	sprintf(retstr,"SysInt_%d",itype-0x20);
+   else if (itype == 0x60) strcpy(retstr,"ACPI/PnP");
+   else if (itype == 0xFF) strcpy(retstr,"NoInt");
+   else strcpy(retstr,"Invalid");
+   return(retstr);
+}
+
+int decode_oem_sensor(uchar *sdr,uchar *reading,char *pstring,int slen)
+{
+   int rv = -1;
+#ifdef METACOMMAND
+   switch(vend_id) {
+      case VENDOR_INTEL: 
+          rv = decode_sensor_intel(sdr, reading, pstring, slen);
+          break;
+      case VENDOR_KONTRON: 
+          rv = decode_sensor_kontron(sdr, reading, pstring, slen);
+          break;
+      case VENDOR_FUJITSU:
+          rv = decode_sensor_fujitsu(sdr,reading,pstring,slen);
+          break;
+      case VENDOR_SUN: 
+          rv = decode_sensor_sun(sdr, reading, pstring, slen);
+          break;
+      case VENDOR_MAGNUM: 
+      case VENDOR_SUPERMICRO: 
+      case VENDOR_SUPERMICROX: 
+          rv = decode_sensor_supermicro(sdr,reading,pstring,slen,fsimple,fdebug);
+          break;
+      case VENDOR_QUANTA: 
+          rv = decode_sensor_quanta(sdr, reading, pstring, slen);
+          break;
+      case VENDOR_HP: 
+          rv = decode_sensor_hp(sdr, reading, pstring, slen);
+          break;
+      case VENDOR_DELL: 
+          rv = decode_sensor_dell(sdr, reading, pstring, slen);
+          break;
+      case VENDOR_IBM: 
+      case VENDOR_LENOVO: 
+      case VENDOR_LENOVO2: 
+          rv = decode_sensor_lenovo(sdr, reading, pstring, slen);
+          break;
+      case VENDOR_ASUS: 
+          rv = decode_sensor_asus(sdr, reading, pstring, slen);
+          break;
+      default:
+	  break;
+   } /*end-switch vend_id*/
+   if (fdebug) // && rv == 0)
+      printf("decode_oem_sensor rv=%d vend=%x string=%s\n",rv,vend_id,pstring);
+#endif
+   return (rv);
+}
+
+int show_oemsdr(int vend, uchar *sdr)
+{
+   int rv = -1;
+   int i, len;
+
+#ifdef METACOMMAND
+   if (vend == VENDOR_INTEL) {
+      show_oemsdr_intel(sdr); /*show subtypes for Intel BMC_TAM*/
+      rv = 0;
+   } else if (vend == 4156) {  /*special HP/NewAccess OEM SDR*/
+      show_oemsdr_hp(sdr);    
+      rv = 0;
+   } else if (vend == VENDOR_QUANTA) {
+      printf("Quanta: ");
+      show_oemsdr_nm(sdr); 
+      rv = 0;
+   }
+#endif
+   if (rv != 0) {
+      len = sdr[4] + 5;
+      if (vend == VENDOR_FUJITSU) printf("Fujitsu: ");
+      else if (vend == VENDOR_INTEL) printf("Intel: ");
+      else printf("manuf=%d: ",vend);
+      for (i = 8; i < len; i++) printf("%02x ",sdr[i]);
+      printf("\n");
+   }
+   return(rv);
+}
+
+void
+ShowThresh(int flg, uchar bits, uchar *vals, uchar *sdr)
+{
+   char str[128] = "";
+   char part[24]; /* ~15 bytes used */
+   double ival;
+   char sep[4];
+   char *tag;
+   tag = "";
+   if (fsimple) {
+	sprintf(sep,"%c ",bdelim);
+	tag = "Thresholds";
+   } else sep[0] = 0; /*null string*/
+   if (fshowthr == 2) {
+	double i0, i1, i2, i3, i4, i5;
+        i0 = RawToFloat(vals[0],sdr);
+        i1 = RawToFloat(vals[1],sdr);
+        i2 = RawToFloat(vals[2],sdr);
+        i3 = RawToFloat(vals[3],sdr);
+        i4 = RawToFloat(vals[4],sdr);
+        i5 = RawToFloat(vals[5],sdr);
+	sprintf(str,"%.2f:%.2f:%.2f:%.2f:%.2f:%.2f",i0,i1,i2,i3,i4,i5);
+	printf("\t%s%s%s%c",sep,"Thresh ",str,chEol);
+   } else if (flg != 0) {  /* Compact, did GetThresholds, reverse order */
+	if (bits & 0x20) {
+		ival = RawToFloat(vals[5],sdr);
+		sprintf(part,"%shi-unrec %.2f ",sep,ival);
+		strcat(str,part);
+	}
+	if (bits & 0x10) {
+		ival = RawToFloat(vals[4],sdr);
+		sprintf(part,"%shi-crit %.2f ", sep,ival);
+		strcat(str,part);
+	}
+	if (bits & 0x08) {
+		ival = RawToFloat(vals[3],sdr);
+		sprintf(part,"%shi-noncr %.2f ",sep,ival);
+		strcat(str,part);
+	}
+	if (bits & 0x01) {
+		ival = RawToFloat(vals[0],sdr);
+		sprintf(part,"%slo-noncr %.2f ",sep,ival);
+		strcat(str,part);
+	}
+	if (bits & 0x02) {
+		ival = RawToFloat(vals[1],sdr);
+		sprintf(part,"%slo-crit %.2f ", sep,ival);
+		strcat(str,part);
+	}
+	if (bits & 0x04) {
+		ival = RawToFloat(vals[2],sdr);
+		sprintf(part,"%slo-unrec %.2f ",sep,ival);
+		strcat(str,part);
+	}
+	if (flg == 2) {
+	   if (sens_verbose) tag = "Volatile ";
+	   printf("\t%s%s%s%c",sep,tag,str,chEol);
+	} else 
+	   printf("\t%s%s%s%c",sep,tag,str,chEol);
+   } else {  /*Full SDR*/
+	if (fdebug) printf("ShowThresh[%x]: bits=%02x, sdr18=%02x %02x\n",
+				sdr[7],bits,sdr[18],sdr[19]);
+	if (bits & 0x20) {
+		ival = RawToFloat(vals[0],sdr);
+		sprintf(part,"%shi-unrec %.2f ",sep,ival);
+		strcat(str,part);
+	}
+	if (bits & 0x10) {
+		ival = RawToFloat(vals[1],sdr);
+		sprintf(part,"%shi-crit %.2f ",sep,ival);
+		strcat(str,part);
+	}
+	if (bits & 0x08) {
+		ival = RawToFloat(vals[2],sdr);
+		sprintf(part,"%shi-noncr %.2f ",sep,ival);
+		strcat(str,part);
+	}
+	if (bits & 0x01) {
+		ival = RawToFloat(vals[5],sdr);
+		sprintf(part,"%slo-noncr %.2f ",sep,ival);
+		strcat(str,part);
+	}
+	if (bits & 0x02) {
+		ival = RawToFloat(vals[4],sdr);
+		sprintf(part,"%slo-crit %.2f ",sep,ival);
+		strcat(str,part);
+	}
+	if (bits & 0x04) {
+		ival = RawToFloat(vals[3],sdr);
+		sprintf(part,"%slo-unrec %.2f ",sep,ival);
+		strcat(str,part);
+	}
+	if (sens_verbose) tag = "SdrThres ";
+	printf("\t%s%s%s%c",sep,tag,str,chEol);
+	if (sens_verbose)
+	{ 	/* show max/min  & hysteresis from full sdr */
+		str[0] = 0;
+		ival = RawToFloat(sdr[31],sdr);
+		sprintf(part,"%snom %.2f ",sep,ival);
+		strcat(str,part);
+		ival = RawToFloat(sdr[32],sdr);
+		sprintf(part,"%snmax %.2f ",sep,ival);
+		strcat(str,part);
+		ival = RawToFloat(sdr[33],sdr);
+		sprintf(part,"%snmin %.2f ",sep,ival);
+		strcat(str,part);
+
+		ival = RawToFloat(sdr[34],sdr);
+		sprintf(part,"%ssmax %.2f ",sep,ival);
+		strcat(str,part);
+	 
+		ival = RawToFloat(sdr[35],sdr);
+		sprintf(part,"%ssmin %.2f ",sep,ival);
+		strcat(str,part);
+	   
+#ifdef OLD
+		ival = RawToFloat(sdr[42],sdr);
+		sprintf(part,"%s+hyst %.2f ",sep,ival);
+		strcat(str,part);
+
+		ival = RawToFloat(sdr[43],sdr);
+		sprintf(part,"%s-hyst %.2f ",sep,ival);
+		strcat(str,part);
+#endif
+	
+		printf("\t%s%c",str,chEol);
+	}
+   }  /*endif full sdr*/
+}
+
+int decode_comp_generic(uchar type, uchar evtype, uchar num, ushort reading)
+{
+	int istr = 0;
+    uchar b;
+	   /* decode via evtype */
+       switch(evtype)
+	   {
+	   case 0x02:	
+		  if (reading & 0x01) istr = 85; /* Active */
+		  if (reading & 0x02) istr = 86; /* Busy */
+		  else istr = 84;  /* Idle (OK)) */
+	   case 0x03:
+		  if (reading & 0x01) istr = 13; /* Asserted */
+		  else istr = 0;  /* "OK" Deasserted */
+	      break;
+	   case 0x04:
+		  if (reading & 0x01) istr = 15; /* Predictive Failure */
+		  else istr = 0;  /* "OK" Deasserted */
+	      break;
+	   case 0x05:
+		  if (reading & 0x01) istr = 65; /* Limit Exceeded*/
+		  else istr = 0;  /* "OK" LimitNotExceeded*/
+	      break;
+	   case 0x07:	/* transition */
+		  b = bitnum(reading);
+		  switch(b) {
+		  case 0: istr = 0; break; /*no bits set, OK*/
+		  case 1: istr = 87; break; /*transition up to Non-Critical*/
+		  case 2: istr = 88; break; /*transition up to Critical */
+		  case 3: istr = 89; break; /*transition up to Non-recoverable */
+		  case 4: istr = 87; break; /*transition down to Non-Critical */
+		  case 5: istr = 88; break; /*transition down to Critical */
+		  case 6: istr = 89; break; /*transition to Non-recoverable */
+		  case 7: istr = 90; break; /*Monitor*/
+		  case 8: istr = 91; break; /*Informational*/
+		  default: istr = 8; break; /*'OK*'*/
+		  }
+	      break;
+	   case 0x08:
+		  if (reading & 0x01) istr = 9; /* Present */
+		  else istr = 10;  /*Absent*/
+	      break;
+	   case 0x09:	/*  Availability event states */
+		  if (reading & 0x01) istr = 17; /* Enabled */
+		  else istr = 16;  /*Disabled*/
+	      break;
+	   case 0x0A:	/* */
+		  b = (reading & 0x7f);
+		  switch(b) {
+		  case 0x00: istr = 8;  break; /* 00 'OK*'*/
+		  case 0x01: istr = 92; break; /* transition to Running */
+		  case 0x02: istr = 93; break; /* transition to In Test */
+		  case 0x04: istr = 94; break; /* transition to Power Off */
+		  case 0x08: istr = 95; break; /* transition to On Line */
+		  case 0x10: istr = 96; break; /* transition to Off Line */
+		  case 0x20: istr = 97; break; /* transition to Off Duty */
+		  case 0x40: istr = 98; break; /* transition to Degraded */
+		  case 0x80: istr = 99; break; /* transition to Power Save */
+		  default: istr = 100; break; /* Install Error */
+		  }
+	      break;
+	   case 0x0B:	/* Redundancy */
+		  b = (reading & 0x7f);
+		  switch(b) {
+		  case 0x00: istr = 8;  break; /* 00 'OK*'*/
+		  case 0x01: istr = 18; break; /* 01 Fully Redundant */
+		  case 0x02: istr = 19; break; /* 02 Redundancy Lost */
+		  case 0x04: istr = 20; break; /* 04 Redundancy Degraded */
+		  case 0x08: istr = 82; break; /* 08 Non-Redundant/Sufficient down */
+		  case 0x10: istr = 82; break; /* 10 Non-Redundant/Sufficient up*/
+		  case 0x20: istr = 83; break; /* 20 Non-Redundant/Insufficient */
+		  case 0x40: istr = 20; break; /* 40 Redundancy Degraded down */
+		  default: istr = 20; break; /* Redundancy Degraded up */
+		  }
+	      break;
+	   case 0x0C:	/* ACPI Power States */
+		  if (reading & 0x04)      istr = 21; /* D3, Off */
+		  else if (reading & 0x02) istr = 23; /* D2, Sleeping */
+		  else if (reading & 0x01) istr = 22; /* D1, Working */
+		  else istr = 24;  /*D0, On*/
+	      break;
+	   default:
+	      if (fdebug) 
+		    printf("sensor[%x] et %02x type %02x not decoded, reading = %04x\n",
+				   num,evtype,type,reading);
+	      istr = STR_OTHER;  /* other " - " */
+	      break;
+	   }
+	return(istr);
+}
+
+/* 
+ * decode_comp_reading
+ * 
+ * Decodes the readings from compact SDR sensors.
+ * Use sensor_dstatus array for sensor reading types and meaning strings.
+ * Refer to IPMI Table 36-1 and 36-2 for this.
+ * 
+ * Note that decoding should be based on sensor type and ev_type only,
+ * except for end cases.
+ * 
+ * Note reading1 = sens_reading[2], reading2 = sens_reading[3]
+ */
+int 
+decode_comp_reading(uchar type, uchar evtype, uchar num, 
+		    uchar reading1, uchar reading2)
+{
+   int istr = 0;  /*string index into sensor_dstatus[] */
+   uchar b;
+   ushort reading;
+   static char customstr[35];
+
+   /* usually reading2 has h.o. bit set (0x80). */
+   reading = reading1 | ((reading2 & 0x7f) << 8);
+
+   switch(type)
+   {
+	case 0x01:	/*Discrete Thermal, Temperature*/
+	   if (fdebug) 
+	      printf("Discrete Thermal snum %x evtype=%x reading=%x\n",
+				num,evtype,reading);
+	   if (evtype == 0x07) {
+		   b = bitnum(reading);
+		   if (b == 0) istr = 8; /*no bits set, "OK*"*/
+		   else if (b < 3) istr = 0; /*bits 1,2 "OK"*/
+		   else istr = 65; /*transition to error, Limit Exceeded*/
+	   } else if (evtype == 0x05) {
+		   /* see CPU1 VRD Temp on S5000, snum 0xc0 thru 0xcf */
+		   if (reading & 0x01) istr = 65; /* Limit Exceeded*/
+		   else istr = 0;  /* "OK" LimitNotExceeded*/
+	   } else if (evtype == 0x03) {
+		   if (reading & 0x01) istr = 13; /* Asserted */
+		   else istr = 0;  /* "OK" Deasserted */
+	   } else {   /* evtype == other 0x05 */
+		   if (reading & 0x01) istr = 0; /* 8="OK*", 0="OK" */
+		   else istr = 5; /*state asserted, Crit-hi */
+	   } 
+	   break;
+	case 0x02:	/*Discrete Voltage*/
+	   { /* evtype == 0x05 for VBat, 0x03 for VR Watchdog */
+	      if (reading & 0x01) istr = 65; /*LimitExceeded, was Crit-hi*/
+              else istr = 0; /* "OK" LimitNotExceeded*/ 
+	   }
+	   break;
+	case 0x04:	/*Fan*/
+	   if (evtype == 0x0b) {  /*redundancy*/
+	      b = reading & 0x3f;
+	      if (b == 0x00) istr = 16;   /*sensor disabled*/
+	      else if (b == 0x01) istr = 18;  /*fully redundant*/
+	      else if (b == 0x02) istr = 19; /*redundancy lost*/
+	      else if (b == 0x0b) istr = STR_AC_LOST; /*ac lost*/
+	      else istr = 20; /*redundancy degraded*/
+	   } else if (evtype == 0x08) {  /*presence*/
+              if (reading & 0x02) istr = 9;   /*Present/Inserted*/
+              else if (reading & 0x01) istr = 10; /*Absent/Removed*/
+              else /*reading==00*/ istr = 47; /*Unused*/
+	   } else if (evtype == 0x03) {  /*PS Fan Fail*/
+	      if (reading == 0) istr = 0; /*OK*/
+	      else istr = 12;  /*faulty*/
+	   } else {  /*other evtype*/
+	      b = reading & 0x0f;
+	      if (b == 0) istr = 12;  /*faulty*/
+              else if (b & 0x01) istr = 11;  /*ready*/
+	      else istr = 41;   /*Unknown*/
+	   }
+	   break;
+	case 0x05:	/*Physical Security, Chassis */
+	   if (reading == 0) istr = 0; /*OK*/
+	   else if (reading & 0x01) istr = 38; /*chassis intrusion*/
+	   /* 0x02 Drive bay intrusion */
+	   /* 0x04 IO area intrusion */
+	   /* 0x08 Processor area intrusion */
+	   else if (reading & 0x10) istr = 37; /*lan leash lost*/
+	   /* 0x20 Dock/Undock */
+	   /* 0x40 Fan area intrusion */
+	   else istr = 41; /* Unknown, was bitnum(reading); */
+	   break;
+	case 0x07:	/*Processor Status - 0x80 is OK/Present */
+	   b = bitnum(reading);
+	   if (evtype == 0x03) {
+              if (b <= 1) istr = 0; /*bit1 Deasserted, OK* */
+	      else istr = 13; /*bit2 Asserted*/  
+	   } else {   /*usu 0x6f*/
+              if (b > 10) istr = 41; /*Unknown*/
+              else if (b == 0) istr = 0;  /*OK*/
+              else istr = 47 + b;  /*Proc strings 48 thru 57*/
+	   }
+	   break;
+	case 0x08:	/*Power Supply*/
+	   b = reading & 0x7f;
+	   if (b == 0) istr = 10;  /*absent*/
+	   else if (b & 0x40) istr = STR_PS_CONFIG; /*Config Err*/
+	   else if (b & 0x08) istr = STR_AC_LOST; /*AC Lost*/
+	   else if (b & 0x04) istr = 15; /*Predictive Fail*/
+	   else if (b & 0x02) istr = STR_PS_FAIL; /*PS Fail*/
+	   else if (b & 0x01) istr =  9; /*present*/
+	   break;
+	case 0x09:	/*Power Unit*/
+	   b = reading & 0x3f;
+	   if (evtype == 0x0b) {  /*Power Redundancy*/
+	      if (b == 0x00) istr = 16;   /*sensor disabled*/
+	      else if (b == 0x01) istr = 18;  /*fully redundant*/
+	      else if (b == 0x02) istr = 19; /*redundancy lost*/
+	      else if (b == 0x0b) istr = STR_AC_LOST; /*ac lost*/
+	      else istr = 20; /*redundancy degraded*/
+	   } else {  /* Power Unit (evtype==0x6f or 0xef) */
+	      if (b == 0) istr = 17;   /*enabled*/
+	      else if ((b & 0x01) == 1) istr = 16;  /*disabled*/
+	   }
+	   break;
+	case 0x0C:	/* Memory */
+	   b = reading & 0x3f;
+           if (b == 0) istr = 0;  /*OK*/
+           else if (b & 0x01) istr = 8;  /*Correctable ECC (OK*)*/
+           else if ((b & 0x02) || (b & 0x20)) istr = 39;  /*ECC Error*/
+           else if (b & 0x04) istr = 40;  /*Parity Error*/
+	   else istr = bitnum(b);  /* ECC or other error */
+	   break;
+	case 0x0D:	/* drive slot - usually HSC sens_ownid == 0xc0 */
+	   if (fRomley || fGrantley) {  /* evtype==0x6f, has both status and presence */
+	      if (reading & 0x02) istr = 12; /*Faulty*/
+	      else if (reading & 0x80) istr = STR_REBUILDING; /*Rebuilding*/
+	      else if (reading & 0x01) istr = 9; /*Present (OK)*/
+	      else istr = 10; /*Absent*/
+	   } else {
+	      if (evtype == 8) {  /* HSC slot presence sensors (num > 8)*/
+                 if (reading1 & 0x02) istr = 9;   /*Present/Inserted*/
+                 else if (reading1 & 0x01) istr = 10; /*Absent/Removed*/
+                 else /*reading1==00*/ istr = 47; /*Unused*/
+	      } else { /* HSC slot status sensors (evtype==0x6f)*/
+                 /* usually reading2 == 0x82 or 0x8E if healthy */
+                 if (reading2 & 0x01) istr = 12; /*state8=Rebuild stopped*/
+                 else if (reading2 & 0x02) istr = 11; /*state9=Inserted/Ready */
+                 else if (reading2 & 0x04) istr = 11; /*state10=Safe_to_Remove*/
+                 else if (reading2 & 0x08) istr = 11; /*state11=Ready */
+                 else if (reading2 == 0x80) istr = 47; /*no states, Unused*/
+                 else istr = 12;  /*faulty*/
+                 b = 8;  /*if no bits set, no raid state */
+                 if (reading1 & 0x01) { b = 0; } /*state0=Faulty*/
+                 else if (reading1 & 0x02) b = 1; /*state1=Rebuilding*/
+                 else if (reading1 & 0x04) b = 2; /*state2=InFailedArray*/
+                 else if (reading1 & 0x08) b = 3; /*state3=InCriticalArray*/
+                 else if (reading1 & 0x10) b = 4; /*state4=ParityCheck*/
+                 else if (reading1 & 0x20) b = 5; /*state5=PredictedFault*/
+                 else if (reading1 & 0x40) b = 6; /*state6=Un-configured*/
+                 else if (reading1 & 0x80) b = 7; /*state7=HotSpare*/
+                 if (b < 8) { 
+		     /* also include a raid_state, via custom string */
+		     sprintf(customstr,"%s %s",
+				sensor_dstatus[istr], raid_states[b]);
+		     istr = STR_CUSTOM;
+		     sensor_dstatus[istr] = customstr;
+		     if (fdebug) printf("dstatus=%s\n",sensor_dstatus[istr]);
+                 }
+              } /*end-else-if HSC slot status (0x6f)*/
+           } 
+	   break;
+	case 0x10:	/*Event Logging*/
+	   /* usu evtype==0x6f*/
+	   b = bitnum(reading & 0x3f);
+	   switch (b) {
+	      case 0x00:  istr = 0; break; /*OK*/
+	      case 0x01:  istr = 59; break; /*MemLogDisabled*/
+	      case 0x02:  istr = 60; break; /*TypLogDisabled*/
+	      case 0x03:  istr = 61; break; /*LogCleared*/
+	      case 0x04:  istr = 62; break; /*AllLogDisabled*/
+	      case 0x05:  istr = 63; break; /*SelFull*/
+	      case 0x06:  istr = 64; break; /*SelNearFull*/
+	      default:  istr = 41; break;  /*Unknown*/
+	   }
+	   break;
+	case 0x12:	/*System Event*/
+	   if (reading == 0) istr = 0;
+	   else istr = 13; /*Asserted*/  
+	   break;
+	case 0x13:	/*Critical Interrupt*/
+	   /* valid bits:  0x03FF */
+	   if (reading == 0) istr = 0;  /*OK*/
+	   else {   
+	        b = bitnum(reading);  /* ECC or other error */
+		if (b > 10) b = 10;
+		istr = 24 + b;
+	   }
+	   break;
+	case 0x14:	/*Button*/
+	   if (reading == 0) istr = 0; /*OK*/
+	   else istr = 13; /*Asserted*/  
+	   break;
+	case 0x15:	/*Module/ Board */
+	   if (evtype == 0x08) {  /*presence*/
+              if (reading & 0x02) istr = 9;   /*Present/Inserted*/
+              else if (reading & 0x01) istr = 10; /*Absent/Removed*/
+              else /*reading==00*/ istr = 47; /*Unused*/
+	   }
+	   break;
+	case 0x16:	/* HSBP Status (esp. Romley) */
+	   if (reading & 0x010) istr = STR_HSC_OFF; /*Offline*/
+	   else istr = 0;  /*OK*/
+	   break;
+	case 0x17:	/* ATCA CDM, Air Filter, Filter Tray */
+	   if (reading == 0) istr = 0; /*OK*/
+	   else if (reading & 0x01) istr = 10; /*Absent*/
+	   else istr = bitnum(reading);  /* other error, TODO: fix this */
+	   break;
+	case 0x1C:	/*Terminator (usu SCSI)*/
+	   if (reading & 0x01) istr = 9; /*present*/
+	   else istr = 10;        /*missing,absent*/
+	   break;
+	case 0x21:	/*DIMM memory slot*/
+	   if ((reading & 0x04) != 0) istr = 9; /*present*/
+	   else istr = 10; /*absent*/
+	   sprintf(customstr,"%s", sensor_dstatus[istr]);
+	   if ((reading & 0x01) != 0) strcat(customstr,",Fault");
+	   if ((reading & 0x0100) != 0) strcat(customstr,",Disabled");
+	   istr = 58; /*use a custom string*/
+	   sensor_dstatus[istr] = customstr;
+	   if (fdebug) printf("dstatus=%s\n",sensor_dstatus[istr]);
+	   break;
+	case 0x22:	/*ACPI Power State*/
+           b = bitnum(reading); 
+           switch(b) {
+	     case 0: istr = 0;  break; /*OK*/
+	     case 1: istr = 22;  break; /*Working*/
+	     case 2:
+	     case 3:
+	     case 4:
+	     case 5:
+	     case 9:
+	     case 10: istr = 23;  break; /*Sleeping*/
+	     case 6: 
+	     case 7: 
+	     case 8: 
+	     case 11: 
+	     case 12: istr = 24;  break; /*On*/
+	     case 13: istr = 21;  break; /*Off*/
+	     default: istr = 41;   /*unknown*/
+           }
+	   break;
+	case 0x23:	/*Watchdog*/
+	   if (reading == 0) istr = 0;
+	   else istr = 13; /*Asserted*/  
+	   break;
+	case 0x24:	/*Platform Alert*/
+           b = bitnum(reading); 
+           switch(b) {
+	     case 0: istr = 0;   break; /*OK, no bits set*/
+	     case 1: istr = 66;  break; /*Page, bit 0 set*/
+	     case 2: istr = 67;  break; /*LAN,  bit 1 set*/
+	     case 3: istr = 68;  break; /*PET*/
+	     case 4: istr = 69;  break; /*SNMP OEM*/
+	     default: istr = 70;   /*None*/
+           }
+	   break;
+	case 0x25:	/* Entity Presence */
+	   if (reading & 0x01) istr = 8;  /*Present*/
+	   else if (reading & 0x02) istr = 9;  /*Absent*/
+	   else if (reading & 0x04) istr = 16;  /*Disabled*/
+	   else istr = 42;  /* NotAvailable */
+	   break;
+	case 0x28:	/* BMC FW Health */
+	   if (evtype == 0x6F) {  /*Sensor-specific*/
+	     if (reading == 0) istr = 0;  /*OK*/
+	     else istr = 12; /*Faulty*/
+	   } else {  /*use event/reading type*/
+	     istr = decode_comp_generic(type, evtype, num, reading);
+	   }
+	   break;
+	case 0x29:	/* Battery */
+           switch(reading & 0x7f) {
+	     case 0x00: istr = 0;  break; /*OK*/
+	     case 0x01: istr = 15; break; /*Predict Fail*/
+	     case 0x04: istr = 9;  break; /*Present*/
+	     case 0x02: 
+	     default:   istr = 12; break; /*Failed/Faulty*/
+           }
+	   break;
+	case 0x2A:	/* Session Audit (IPMI 2.0) */
+	   if (reading == 0x00) istr = 45; /*Activated*/
+	   else istr = 46;                 /*Deactivated*/
+	   break;
+	case 0x2B:	/* Version Change */
+	   b = bitnum(reading1); 
+	   switch(b) {
+	     case 0: istr = 0;   break; /*OK, no bits set*/
+	     case 1: istr = 72;  break; /*HW Changed, bit 0 set*/
+	     case 2: istr = 73;  break; /*SW Changed,  bit 1 set*/
+	     case 3: istr = 74;  break; /*HW incompatibility*/
+	     default: istr = 75; break; /*Change error*/
+	   }
+	   break;
+
+        /* sensor types 0xC0 - 0xFF are OEM RESERVED */
+	case 0xF1:	/* ATCA IPMB-0 Sensor */
+	   if ((reading & 0x7fff) == 0x0008) istr = 0; /*OK*/
+	   else istr = bitnum(reading1);  /* other error, TODO: refine this */
+	   break;
+	case 0xC0:	/* SMI State, NMI State */
+	case 0xD8:	/* BIST */
+	case 0xF0:	/* ATCA FRU HotSwap, TODO: refine this */
+	case 0xF2:	/* ATCA Module HotSwap, TODO: refine this */
+	case 0xF3:	/* SMI Timeout, etc. */
+	   if (reading & 0x01) istr = 13; /* Asserted */
+	   else istr = 0;  /* "OK", Deasserted */
+	   break;
+
+	case 0x60:	/* SCSI 1 Term Flt */
+	case 0x61:	/* SCSI 2 Term Flt */
+   	default:
+	   istr = decode_comp_generic(type, evtype, num, reading);
+	   break;
+   }
+   return(istr);
+}  /* end decode_comp_reading */
+
+#define STYPSZ  15
+static char *get_stype_str(uchar stype)
+{   /*return sensor type string, with fixed length */
+    static char stype_str[STYPSZ+1];
+    char *tmpstr;
+    int i, n;
+    tmpstr = get_sensor_type_desc(stype); 
+    n = strlen_(tmpstr);
+    if (n > STYPSZ) n = STYPSZ;
+    strncpy(stype_str,tmpstr,n);
+    for (i = n; i < STYPSZ; i++) stype_str[i] = ' ';
+    stype_str[i] = 0;
+    tmpstr = stype_str;
+    return(tmpstr);
+}
+
+void
+ShowSDR(char *tag, uchar *sdr)
+{
+  SDR01REC *sdr01;
+  SDR02REC *sdr02;
+  SDR08REC *sdr08;
+  SDR11REC *sdr11;
+  SDR12REC *sdr12;
+  SDR14REC *sdr14;
+  SDRc0REC *sdrc0;
+  char idstr[32];
+  char *typestr = NULL;
+  int vend;
+  int len, ilen, i, j;
+  int ioff;
+  uchar sens[4];
+  uchar sens_cap;
+  uchar shar_cnt;
+  int rc; 
+  double val;
+  char brearm;
+  uchar sep[4];
+  char rdgstr[50];
+
+  len = sdr[4] + 5;
+  sens_cap = 0x80;  /*ignore*/
+  if (fdebug) printf("ShowSDR: len=%d, type=%x\n",len,sdr[3]);
+  memset(sens,0,4);
+  if (frawsdr || fdebug) {
+	  /* raw is different than dump_buf */
+	  printf("raw SDR: ");
+	  for (i = 0; i < len; i++)
+		  printf("%02x ",sdr[i]);
+	  printf("\n");
+  }
+  strcpy(idstr,"INIT");  /*always set idstr to some initial string*/
+  switch(sdr[3])
+  {
+    case 0x01:   /* Full sensor record */
+	sdr01 = (SDR01REC *)sdr;
+	ioff = 48;
+	if (ioff > len) {
+		if (fdebug) printf("bad length: type=%x, len=%d, ioff=%d\n",
+					sdr[3],len,ioff);
+		fprintf(stderr,"Bad SDR Length %d, please apply the correct FRU/SDR diskette\n",len);
+		return;
+	}
+	sens_cap = sdr[11];  /*sdr01->sens_capab*/
+	// ilen = (sdr[ioff] & 0x1f);  /*sdr01->id_typelen*/
+	ilen = len - ioff;
+	if (fdebug) printf("SDR[%x] Full ioff=%d idTypLen=0x%02x ilen=%d\n", 
+			sdr01->recid, ioff,sdr[ioff] ,ilen);
+	if (ilen >= sizeof(idstr)) ilen = sizeof(idstr) - 1;
+	if (ilen <= 0) {  /*bug if true*/
+	   fprintf(stderr,"Bad SDR Length %d, omits ID string\n",len);
+	   ilen = 16;  /*less than sizeof(idstr)*/
+	}
+	memcpy(idstr,&sdr[ioff],ilen);
+	for (i=ilen; i<16; i++) { idstr[i] = ' '; ilen++; }
+	idstr[ilen] = 0;  /* stringify */
+	if ((sdr01->sens_capab & 0x40) == 0) brearm = 'm'; /*manual rearm*/
+	else brearm = 'a';  /*automatic rearm*/
+	if (fdebug) printf("entity %d.%d, idlen=%d sizeof=%lu idstr0=%c s0=%x\n",
+					sdr01->entity_id, sdr01->entity_inst, 
+					ilen,sizeof(SDR01REC),idstr[0],sdr[ioff]);
+	rc = GetSensorReading(sdr01->sens_num,sdr01,sens);
+	if (rc != 0) { /* if rc != 0, leave sens values zero */
+	   i = 41;  /* Unknown */
+	   val = 0;
+	   if (rc == 0xCB) {  /*sensor not present*/
+	        i = 10;  /* Absent */
+	        typestr = "na";
+	   } else typestr = decode_rv(rc);
+	} else {
+	   j = (sens[2] & 0x3f);   /*sensor reading state*/
+	   i = bitnum((ushort)j);  /*sensor_dstatus index*/
+	   if (fdebug) 
+		printf("bitnum(%02x)=%d raw=%02x init=%x base/units=%x/%x\n",
+			sens[2],i,sens[0],sens[1],sdr01->sens_base,
+			sdr01->sens_units);
+	   if ((sens[1] & 0x20) != 0) { i = 7; val = 0; } /* Init state */
+       else if (sdr01->sens_units == 0xC0) i = 42; /*reading NotAvailable*/
+	   else if (sens[2] == 0xc7) { i = 10; val = 0;  /* Absent (Intel) */
+			if (fdebug) printf("sensor[%x] is absent (c7), no reading\n", 
+							sdr01->sens_num);
+	   }
+	   else val = RawToFloat(sens[0],sdr);
+	   typestr = get_unit_type(sdr01->sens_units, sdr01->sens_base, 
+					sdr01->sens_mod, fsimple);
+#ifdef WRONG
+	   if (is_romley(vend_id,prod_id) && 
+		(sdr01->sens_type == 0x0C) && (sdr01->sens_units & 0x01))
+	   { /* Intel Memory Thermal Throttling %, raw 0x01 == 0.5 % */
+               val = (val / 2);  /* handle MTT SDR errata */
+	   } /*correct solution is to fix the SDR m-value instead */
+#endif
+	}
+	rc = decode_oem_sensor(sdr,sens,oem_string,sizeof(oem_string));
+	if (rc == 0) {
+	   if (fsimple) strncpy(rdgstr,oem_string,sizeof(rdgstr));
+	   else snprintf(rdgstr,sizeof(rdgstr),"%02x %s",sens[0],oem_string);
+	} else {
+	   if (fsimple) 
+	      snprintf(rdgstr,sizeof(rdgstr),"%s %c %.2f %s",
+			sensor_dstatus[i],bdelim,val,typestr);
+	   else 
+	      snprintf(rdgstr,sizeof(rdgstr),"%02x %s %.2f %s",
+			sens[0], sensor_dstatus[i],val,typestr);
+	}
+	sep[0] = 0; /*null string*/
+    printf("%s", tag);
+	if (fsimple) {
+	   sprintf(sep,"%c ",bdelim);
+	   printf("%04x %c Full    %c %s %c %02x %c %s %c %s%c",
+			sdr01->recid, bdelim, bdelim,
+			get_stype_str(sdr01->sens_type), 
+			bdelim, sdr01->sens_num,bdelim, idstr,
+			bdelim,rdgstr,chEol);
+	} else
+	   printf("%04x SDR Full %02x %02x %02x %c %02x snum %02x %s = %s%c",
+		sdr01->recid, sdr01->rectype, sdr01->ev_type,
+		sdr01->sens_ownid, brearm, sdr01->sens_type, sdr01->sens_num, 
+		idstr, rdgstr, chEol);
+	if (fdebug && fshowthr)
+		   printf("cap=%02x settable=%02x, readable=%02x\n",
+			  sens_cap,sdr[19],sdr[18]);
+	if (sens_verbose)   /* if -v, also show Entity ID */
+	    printf("\t%sEntity ID %d.%d (%s), Capab: %s%c",
+		   sep, sdr01->entity_id, sdr01->entity_inst, 
+		   decode_entity_id(sdr01->entity_id), // sens_cap, 
+		   decode_capab(sens_cap),chEol);
+	if (fshowthr && (sens_cap & 0x0f) != 0x03) {
+		uchar thresh[7];
+		/* Thresholds, so show them */
+		/* Use settable bits to show thresholds, since the 
+		 * readable values will be off for Full SDRs.  
+		 * If cant set any thresholds, only show SDR thresholds */
+		if (sdr[19] == 0) rc = 1; 
+		else {
+		   /* Show volatile thresholds. */
+		   rc = GetSensorThresholds(sdr01->sens_num,&thresh[0]);
+		   if (rc == 0) ShowThresh(2,thresh[0],&thresh[1],sdr);
+		}
+		/* Show SDR non-volatile thresholds. */
+		if (sens_verbose || rc !=0) ShowThresh(0,sdr[18],&sdr[36],sdr); 
+		// ShowThresh(0,0x3f,&sdr[36],sdr); /* to show all %%%% */
+	} 
+	if (fwrap) {  /* (chEol != '\n') include time */
+		time_t ltime;
+		time(&ltime);
+		if (fsimple) 
+	 	   printf("%c %s",bdelim,ctime(&ltime)); /*ctime has '\n' */
+		else
+	 	   printf("at %s",ctime(&ltime)); /*ctime has '\n' */
+	} 
+	break;
+    case 0x02:   /* Compact sensor record */
+	sdr02 = (SDR02REC *)sdr;
+	ioff = 32;
+	if (ioff > len) {
+		if (fdebug) printf("bad length: type=%x, len=%d, ioff=%d\n",
+					sdr[3],len,ioff);
+		fprintf(stderr,"Bad SDR Length, please apply the correct FRU/SDR diskette\n");
+		return;
+	}
+	sens_cap = sdr[11];  /*sdr02->sens_capab*/
+	shar_cnt = sdr02->shar_cnt & 0x0f;  /*sdr[23]*/
+	ilen = len - ioff;
+	if ((ilen+1) >= sizeof(idstr)) ilen = sizeof(idstr) - 2;
+	memcpy(idstr,&sdr[ioff],ilen);
+	if ((shar_cnt > 1) && (sdr02->shar_off & 0x80) != 0) { /*do shared SDR*/
+	   j = (sdr02->shar_off & 0x7f);  /*sdr[24] = modifier offset*/
+	   if (fdebug) printf("share count = %d, mod_offset = %d\n",shar_cnt,j);
+	   if ((sdr02->shar_cnt & 0x10) != 0) { /*alpha*/ 
+	      idstr[ilen++] = 0x40 + j;  /* j=1 -> 'A' */
+	      idstr[ilen] = 0;  /* stringify */
+	   } else { /*numeric*/
+	      sprintf(&idstr[ilen],"%d",j);
+	      ilen = strlen_(idstr);
+	   }
+	} /* else normal idstr */
+	for (i=ilen; i<16; i++) { idstr[i] = ' '; ilen++; }
+	idstr[ilen] = 0;  /* stringify */
+        if ((sdr02->sens_capab & 0x40) == 0) brearm = 'm'; /*manual rearm*/
+        else brearm = 'a';  /*automatic rearm*/
+	if (fdebug) printf("ilen=%d, istr0=%c, sizeof=%zu, s0=%x\n",
+				ilen,idstr[0],sizeof(SDR02REC),sdr[ioff]);
+	memset(sens,0,sizeof(sens));
+	rc = GetSensorReading(sdr02->sens_num,sdr02,sens);
+	if (rc != 0) { /* if rc != 0, leave sens values zero */
+	  i = 41;  /* Unknown */
+	  val = 0;
+	  if (rc == 0xCB) {  /*sensor not present*/
+	        i = 10;  /* Absent */
+		typestr = "na";
+	  } else typestr = decode_rv(rc);
+	} else {
+          if ((sens[1] & 0x20) != 0) i = 42; /*init state, NotAvailable*/
+	  else {
+	    rc = decode_oem_sensor(sdr,sens,oem_string,sizeof(oem_string));
+	    if (rc == 0) i = STR_OEM;
+	    else i = decode_comp_reading(sdr02->sens_type,sdr02->ev_type,
+					sdr02->sens_num,sens[2],sens[3]);
+          }
+        }
+	if (fdebug) 
+            printf("snum %x type %x evt %x reading %02x%02x i=%d rc=%d %s\n",
+			sdr02->sens_num,sdr02->sens_type,sdr02->ev_type,
+			sens[3],sens[2],i,rc, decode_rv(rc));
+	j = sens[2] | ((sens[3] & 0x7f) << 8); /*full reading, less h.o. bit*/
+	sep[0] = 0; /*null string*/
+        printf("%s", tag);
+	if (fsimple) {
+		sprintf(sep,"%c ",bdelim);
+	        printf("%04x %c Compact %c %s %c %02x %c %s %c %s %c%c",
+			sdr02->recid, bdelim, bdelim,
+			get_stype_str(sdr02->sens_type), 
+			bdelim, sdr02->sens_num, bdelim, idstr,
+			bdelim, sensor_dstatus[i],bdelim,chEol);
+	} else if (i == STR_OEM) {
+	   // idstr[ilen] = 0; /*cut out padding in idstr*/
+	   printf("%04x SDR Comp %02x %02x %02x %c %02x snum %02x %s = %04x %s%c",
+		sdr02->recid, sdr02->rectype, sdr02->ev_type,
+		sdr02->sens_ownid, brearm, sdr02->sens_type, sdr02->sens_num, 
+                idstr, j, sensor_dstatus[i],chEol);
+                // sensor_dstatus[i] == oem_string
+	} else {
+	   printf("%04x SDR Comp %02x %02x %02x %c %02x snum %02x %s = %04x %s%c",
+		sdr02->recid, sdr02->rectype, sdr02->ev_type,
+		sdr02->sens_ownid, brearm, sdr02->sens_type, sdr02->sens_num, 
+		idstr, j, sensor_dstatus[i],chEol);
+                /* idstr, sens[0], sens[1], sens[2], sens[3], */ 
+	}
+	if (fdebug && fshowthr)
+		   printf("cap=%02x settable=%02x, readable=%02x\n",
+			  sens_cap,sdr[19],sdr[18]);
+	if (fshowthr)  /*also show Entity ID */
+	    printf("\t%sEntity ID %d.%d (%s), Capab: %s%c",
+		   sep, sdr02->entity_id, sdr02->entity_inst, 
+		   decode_entity_id(sdr02->entity_id), // sens_cap, 
+		   decode_capab(sens_cap),chEol);
+	if (fshowthr &&
+	    ((sens_cap & 0x80) == 0) && (sens_cap & 0x0C) != 0) {
+		uchar thresh[7];
+		/* Thresholds, show them */
+		/* Use readable bits to get & show thresholds */
+		if (sdr[20] != 0) {
+		   rc = GetSensorThresholds(sdr02->sens_num,&thresh[0]);
+		   if (rc == 0) ShowThresh(1,thresh[0],&thresh[1],sdr);
+		}
+	}
+	if (fwrap) {  /*include time and \n */
+		time_t ltime;
+		time(&ltime);
+		if (fsimple) 
+	 	   printf("%c %s",bdelim,ctime(&ltime)); /*ctime has '\n' */
+		else
+	 	   printf("at %s",ctime(&ltime)); /*ctime has '\n' */
+	} 
+	break;
+    case 0x03:   /* Event-only sensor record, treat like Compact SDR */
+	sdr02 = (SDR02REC *)sdr;
+	ioff = 17;
+	if (ioff > len) {
+		fprintf(stderr,"Bad SDR %x Length %d. Please apply the correct FRU/SDR diskette\n",
+			sdr02->recid, len);
+		return;
+	}
+	if (!fsimple)
+	{
+	ilen = len - ioff;
+	if (ilen >= sizeof(idstr)) ilen = sizeof(idstr) - 1;
+	memcpy(idstr,&sdr[ioff],ilen);
+	for (i=ilen; i<16; i++) { idstr[i] = ' '; ilen++; }
+	idstr[ilen] = 0;  /* stringify */
+	sens_cap = sdr[11];
+	memset(sens,0,sizeof(sens));
+        if ((sdr02->sens_capab & 0x40) == 0) brearm = 'm'; /*manual rearm*/
+        else brearm = 'a';  /*automatic rearm*/
+	// rc = GetSensorReading(sdr02->sens_num,sdr02,sens);
+	/* EvtOnly SDRs do not support readings. 
+	 * GetSensorReading would return ccode=0xCB (not present), 
+	 * but this skips error msg */
+	rc = 0xCB;
+	i = bitnum((ushort)sens[2]);
+	j = sens[2] | ((sens[3] & 0x7f) << 8);
+        printf("%s",tag);
+	printf("%04x SDR EvtO %02x %02x %02x %c %02x snum %02x %s = %04x %s\n",
+		sdr02->recid, sdr02->rectype, sdr02->reclen, 
+		sdr02->sens_ownid, 'a', sdr[10], sdr02->sens_num, 
+		idstr, j,  sensor_dstatus[i]);
+		// sens[0], sens[1], sens[2], sens[3], sensor_dstatus[i]
+	}
+	break;
+    case 0x08:   /* Entity Association record */
+	sdr08 = (SDR08REC *)sdr;
+	if (!fsimple)
+	{
+        printf("%s",tag);
+	printf("%04x SDR EntA %02x %02x %02x %02x %02x: ",
+		sdr08->recid, sdr08->rectype, sdr08->reclen, 
+		sdr08->contid, sdr08->continst, sdr08->flags);
+	for (i = 0; i < 8; i++) printf("%02x ",sdr08->edata[i]);
+	printf("\n");
+	}
+	break;
+    case 0x09:   /* Device-relative Entity Association record */
+        sdr08 = (SDR08REC *)sdr;  /*but SDR09 is 26 bytes*/
+        if (!fsimple)
+        {
+        printf("%s",tag);
+        printf("%04x SDR DEnt %02x %02x %02x %02x %02x %02x %02x: ",
+                sdr08->recid, sdr08->rectype, sdr08->reclen,
+                sdr08->contid, sdr08->continst, sdr08->flags,
+                sdr08->edata[0], sdr08->edata[1]);
+        /*display 2 of 4 contained entity devices edata[2-10] */
+        for (i = 2; i < 8; i++) printf("%02x ",sdr08->edata[i]);
+        printf("\n");
+        }
+        break;
+    case 0x10:   /* Generic Device Locator record */
+	sdr11 = (SDR11REC *)sdr;
+	ioff = 16;
+	if (ioff > len) { 
+	    if (fdebug) printf("SDR %x bad length: type=%x, len=%d, ioff=%d\n",
+				sdr11->recid, sdr[3],len,ioff);
+            return;
+        }
+	if (!fsimple)
+	{
+	ilen = len - ioff;
+	if (ilen >= sizeof(idstr)) ilen = sizeof(idstr) - 1;
+	memcpy(idstr,&sdr[ioff],ilen);
+	idstr[ilen] = 0;  /* stringify */
+        printf("%s", tag);
+	if (fsimple)
+		printf("DevLocator record[%x]%c device %02x %c %s\n",
+			sdr11->recid, bdelim,sdr11->dev_slave_adr,bdelim,idstr);
+	else
+	printf("%04x SDR DLoc %02x %02x dev: %02x %02x %02x %02x %02x %02x %s\n",
+		sdr11->recid, sdr11->rectype, sdr11->reclen,
+		sdr11->dev_access_adr, sdr11->dev_slave_adr, 
+		sdr11->access_lun, sdr[8], sdr[10], sdr[11],
+		idstr);
+        }
+	break;
+    case 0x11:   /* FRU record */
+	sdr11 = (SDR11REC *)sdr;
+	ioff = 16;
+	if (ioff > len) {
+	    if (fdebug) printf("SDR %x bad length: type=%x len=%d ioff=%d\n",
+				sdr11->recid, sdr[3],len,ioff);
+		printf("Please apply the correct FRU/SDR diskette\n");
+		return;
+	}
+	if (!fsimple)
+	{
+	ilen = len - ioff;
+	if (ilen >= sizeof(idstr)) ilen = sizeof(idstr) - 1;
+	memcpy(idstr,&sdr[ioff],ilen);
+	idstr[ilen] = 0;  /* stringify */
+	if (fdebug) printf("ilen=%d, istr0=%c, sizeof=%zu, s0=%x\n",
+				ilen,idstr[0],sizeof(SDR11REC),sdr[ioff]);
+        printf("%s", tag);
+	if (fsimple)
+		printf("FRU record[%x]: device %02x : %s\n",
+			sdr11->recid, sdr11->dev_slave_adr,idstr);
+	else
+	printf("%04x SDR FRU  %02x %02x dev: %02x %02x %02x %02x %02x %02x %s\n",
+		sdr11->recid, sdr11->rectype, sdr11->reclen,
+		sdr11->dev_access_adr, sdr11->dev_slave_adr /*fru_id*/, 
+		sdr11->access_lun, sdr11->chan_num,
+		sdr11->entity_id, sdr11->entity_inst, 
+		idstr);
+	}
+	break;
+    case 0x12:   /* IPMB record */
+	sdr12 = (SDR12REC *)sdr;
+	ioff = 16;
+	if (ioff > len) {
+		if (fdebug) printf("bad length: type=%x, len=%d, ioff=%d\n",
+					sdr[3],len,ioff);
+		printf("Please apply the correct FRU/SDR diskette\n");
+		return;
+	}
+	if (!fsimple)
+	{
+	ilen = len - ioff;
+	if (ilen >= sizeof(idstr)) ilen = sizeof(idstr) - 1;
+	memcpy(idstr,&sdr[ioff],ilen);
+	idstr[ilen] = 0;  /* stringify */
+	if (fdebug) printf("ilen=%d, istr0=%c, sizeof=%zu, s0=%x\n",
+				ilen,idstr[0],sizeof(SDR12REC),sdr[ioff]);
+        printf("%s", tag);
+	if (fsimple)
+		printf("IPMB record[%x]%c addr %02x %02x %c %s\n",
+			sdr12->recid, bdelim,sdr12->dev_slave_adr, 
+			sdr12->chan_num,bdelim,idstr);
+	else
+	printf("%04x SDR IPMB %02x %02x dev: %02x %02x %02x %02x %02x %s\n",
+		sdr12->recid, sdr12->rectype, sdr12->reclen,
+		sdr12->dev_slave_adr, sdr12->chan_num, sdr12->dev_capab,
+		sdr12->entity_id, sdr12->entity_inst, 
+		idstr);
+	}
+	break;
+    case 0x14:   /* BMC Message Channel Info record */
+	sdr14 = (SDR14REC *)sdr;
+	if(!fsimple){
+        printf("%s", tag);
+	printf("%04x SDR BMsg %02x %02x: ",
+		sdr14->recid, sdr14->rectype, sdr14->reclen );
+	for (i = 0; i < 8; i++) printf("%02x ",sdr14->mdata[i]);
+	printf("%s %s %02x\n",decode_itype(sdr14->mint),
+		decode_itype(sdr14->eint), sdr14->rsvd);
+	}
+	break;
+    case 0xc0:   /* OEM SDR record (manuf_id 343. = Intel) */
+	sdrc0 = (SDRc0REC *)sdr;
+	if(!fsimple)
+	{
+	  vend = sdrc0->manuf_id[0] + (sdrc0->manuf_id[1] << 8) 
+		+ (sdrc0->manuf_id[2] << 16);
+          printf("%s",tag);
+	  printf("%04x SDR OEM  %02x %02x ", 
+		sdrc0->recid, sdrc0->rectype, sdrc0->reclen);
+	  show_oemsdr(vend,sdr);
+	}
+	break;
+    default:
+	sdrc0 = (SDRc0REC *)sdr;
+	/* also saw type = 0x08 & 0x14 on STL2s */
+	if (!fsimple){
+          printf("%s", tag);
+	  printf("%04x SDR type=%02x ", sdrc0->recid, sdr[3]);
+	  for (i = 0; i < len; i++) printf("%02x ",sdr[i]);
+	  printf("\n");
+	}
+  }
+  return;
+}
+
+static int
+ShowPowerOnHours(void)
+{
+	uchar resp[MAX_BUFFER_SIZE];
+	int sresp = MAX_BUFFER_SIZE;
+	uchar cc;
+	int rc = -1;
+	int i;
+	unsigned int hrs;
+  
+	if (fmBMC) return(0);
+	if (fsimple) return(0);
+	sresp = MAX_BUFFER_SIZE;
+	memset(resp,0,6);  /* default response size is 5 */
+	rc = ipmi_cmd_mc(GET_POWERON_HOURS, NULL, 0, resp, &sresp, &cc, fdebug);
+	if (rc == 0 && cc == 0) {
+	   /* show the hours (32-bits) */
+	   hrs = resp[1] | (resp[2] << 8) | (resp[3] << 16) | (resp[4] << 24);
+	   /*60=normal, more is OOB, so avoid div-by-zero*/ 
+	   if ((resp[0] <= 0) || (resp[0] >= 60)) i = 1; 
+	   else {
+		i = 60 / resp[0];
+		hrs = hrs / i;
+	   }
+	   printf("     SDR IPMI       sensor: Power On Hours \t   = %d hours\n",
+				hrs);
+	}
+	if (fdebug) {
+		   printf("PowerOnHours (rc=%d cc=%x len=%d): ",rc,cc,sresp);
+		   if (rc == 0) 
+		   for (i = 0; i < sresp; i++) printf("%02x ",resp[i]);
+		   printf("\n");
+	}
+	return(rc);
+}
+
+int SaveThreshold(int id, int sensor_num, int sensor_lo, int sensor_hi, 
+		 uchar *thr_set)
+{
+    int rv = 0;
+    char lostr[20];
+    char histr[20];
+    FILE *fd;
+
+    /* persist the thresholds by re-applying with ipmiutil sensor commands.*/
+    if (thr_set != NULL) {
+      sprintf(lostr,"-u 0x%02x%02x%02x%02x%02x%02x",
+		sensor_thr[0], sensor_thr[1], sensor_thr[2], 
+		sensor_thr[3], sensor_thr[4], sensor_thr[5]);
+      histr[0] = 0;  /*empty string*/
+    } else {
+      if (sensor_lo != 0xff) {
+	  sprintf(lostr,"-l 0x%02x",sensor_lo);
+      } else      lostr[0] = 0;
+      if (sensor_hi != 0xff) {
+	  sprintf(histr,"-h 0x%02x",sensor_hi);
+      } else      histr[0] = 0;
+    }
+    fd = fopen(savefile,"a+");
+    if (fd == NULL) return(-1);
+    fprintf(fd, "ipmiutil sensor -i 0x%04x -n 0x%02x %s %s\n", id, sensor_num,
+		lostr,histr);
+    fclose(fd);
+    return(rv);
+}
+
+#ifdef NOT_USED
+#define PICMG_GET_ADDR_INFO  0x01
+static int get_picmg_addrinfo(uchar a1, uchar a2, uchar *addrdata)
+{
+   uchar idata[5];
+   uchar rdata[16];
+   int rlen;
+   ushort icmd;
+   uchar ilen, cc;
+   int rv;
+   
+   idata[0] = 0x00;
+   idata[1] = 0x00;
+   idata[2] = 0x03;
+   idata[3] = a1;  /* 01 thru 0f */
+   idata[4] = a2;  /* 00, 01 thru 09 */
+   ilen = 5;
+   rlen = sizeof(rdata);
+   icmd = PICMG_GET_ADDR_INFO | (NETFN_PICMG << 8);
+   rv = ipmi_cmd_mc(icmd, idata, ilen, rdata, &rlen, &cc, fdebug);
+   if (rv == 0 && cc != 0) rv = cc;
+   if (rv == 0) {
+       if (fdebug) {
+	  printf("picmg_addr(%02x,%02x)",a1,a2);
+	  dump_buf("picmg_addr",rdata,rlen,0);
+       }
+       memcpy(addrdata,rdata,rlen); 
+   }
+   return(rv);
+}
+#endif
+
+#ifdef WIN32
+static int get_filesize(char *fileName, ulong *psize)
+{
+    int rv;
+    WIN32_FILE_ATTRIBUTE_DATA   fileInfo;
+
+    if (fileName == NULL) return -1;
+    if (psize == NULL) return -1;
+    rv = GetFileAttributesEx(fileName, GetFileExInfoStandard, (void*)&fileInfo);
+    if (!rv) return -1;
+    *psize = (long)fileInfo.nFileSizeLow;
+    return 0;
+}
+#endif
+
+int write_sdr_binfile(char *binfile)
+{
+      uchar *pbuf = NULL;
+      FILE *fp;
+      int len, ret;
+      ret = get_sdr_cache(&pbuf); /* sets nsdrs, sz_sdrs */
+      if (ret == 0) {
+		fp = fopen(binfile,"wb");
+    	if (fp == NULL) {
+    	   ret = get_LastError();
+    	   printf("Cannot open file %s for writing, error %d\n",binfile,ret);
+    	} else {
+    	   printf("Writing SDR size %d to %s  ...\n",sz_sdrs,binfile);
+    	   len = (int)fwrite(pbuf, 1, sz_sdrs, fp);
+    	   fclose(fp);
+    	   if (len <= 0) {
+    	      ret = get_LastError();
+    	      printf("Error %d writing file %s\n",ret,binfile);
+    	   } else ret = 0;
+    	}
+    	free_sdr_cache(pbuf);
+      }
+      return(ret);
+}
+
+int read_sdr_binfile(char *binfile, uchar **pbufret, int *buflen)
+{
+      uchar *pbuf = NULL;
+      FILE *fp;
+      int len;
+      int ret;
+#ifdef WIN32
+      {
+	 ulong flen;
+	 ret = get_filesize(binfile, &flen);
+	 if (ret == 0) len = flen;
+	 else {
+	   ret = get_LastError();
+	   printf("Cannot get file size for %s, error %d\n",binfile,ret);
+	   return(ret);
+	 } 
+      }
+#endif
+      fp = fopen(binfile,"rb");
+      if (fp == NULL) {
+    	 ret = get_LastError();
+    	 printf("Cannot open file %s, error %d\n",binfile,ret);
+    	 return(ret);
+      } 
+      fseek(fp, 0L, SEEK_SET);
+#ifndef WIN32
+      { /*not windows but Linux, etc.*/
+	 struct stat st;
+	 /* use fstat to get file size and allocate buffer */
+	 ret = fstat(fileno(fp), &st);
+	 len = st.st_size;  /*file size in bytes*/
+	 if (ret != 0) {
+	    ret = get_LastError();
+	    printf("Cannot stat file %s, error %d\n",binfile,ret);
+	    return(ret);
+	 }
+      }
+#endif
+      /* Could estimate size for nsdrs*SDR_SZ, but we don't yet know nsdrs.
+       * It is better to use the real file size detected above. */
+      sz_sdrs = len;
+      pbuf = malloc(len);
+      if (fdebug) printf("sdr_binfile: malloc(%d) pbuf=%p\n",len,pbuf);
+      if (pbuf == NULL) {
+    	  ret = -2;
+    	  fclose(fp);
+    	  return(ret);
+      }
+      psdrcache = pbuf;
+      /*ok, so proceed with restore*/
+      ret = 0;
+      len = (int)fread(pbuf, 1, sz_sdrs, fp);
+      if (len <= 0) {
+    	 ret = get_LastError();
+    	 printf("Error %d reading file %s\n",ret,binfile);
+    	 sz_sdrs = 0;  /*for safety*/
+      } else if (len < sz_sdrs) {
+    	 /* Show error if this happens in Windows */
+    	 ret = get_LastError();
+    	 printf("truncated fread(%s): attempted %d, got %d, error %d\n",
+    		binfile,sz_sdrs,len,ret);
+         ret = 0; /*try to keep going*/
+      }
+      fclose(fp);
+      if (fdebug) {
+		 printf("SDR buffer from file (len=%d,sz=%d)\n",len,sz_sdrs);
+		 dump_buf("SDR buffer",pbuf,len,1);
+      }
+    *pbufret = pbuf;
+    *buflen = len;
+    return(ret);
+}
+
+#ifdef ALONE
+#ifdef WIN32
+int __cdecl 
+#else
+int 
+#endif
+main(int  argc, char **argv)
+#else
+/* METACOMMAND or libipmiutil */
+int i_sensor(int argc, char **argv)
+#endif
+{
+   int ret, rv;
+   int c;
+   int recid, recnext;
+   uchar sdrdata[MAX_BUFFER_SIZE];
+   uchar devrec[16];
+   int sz, i, j;
+   int fsetfound = 0;
+   int iloop, irec;
+   int ipass, npass;
+   uchar *pset;
+   char *p;
+   char *s1;
+
+   printf("%s version %s\n",progname,progver);
+
+   while ( (c = getopt( argc, argv,"a:bcd:ef:g:h:i:j:k:l:m:n:opqrstu:vwxT:V:J:L:EYF:P:N:R:U:Z:?")) != EOF )
+      switch(c) {
+	  case 'a':   /* reArm sensor number N */
+		if (strncmp(optarg,"0x",2) == 0) frearm = htoi(&optarg[2]);
+		else frearm = htoi(optarg);  /*was atoi()*/
+		break;
+	  case 'c': fsimple = 1;   break;  /* Canonical/simple output*/
+	  case 'd': fdump = 1;      /* Dump SDRs to a file*/
+		    binfile = optarg; break;
+	  case 'b': fchild = 1;    break;  /* Bladed, so get child SDRs */   
+	  case 'e': fchild = 1;    break;  /* Extra bladed child SDRs */   
+	  case 'f': frestore = 1;      /* Restore SDRs from a file*/
+		    binfile = optarg; break;
+	  case 's': fsimple = 1;   break;  /* Simple/canonical output */
+		    /*fcanonical==fsimple*/
+	  case 'g': 
+		rv = get_group_id(optarg);
+		if (rv < 0) { 
+		    printf("Unrecognized sensor type group (%s)\n",optarg);
+		    ret = ERR_BAD_PARAM;
+		    goto do_exit;
+		} else fshowgrp = rv;
+		if (fdebug) printf("num sensor type groups = %d\n",fshowgrp);
+		break;
+	  case 'i': 
+		fshowidx = 1;  
+		get_idx_range(optarg);
+		break;
+	  case 'j': fjumpstart = 1;      /* Load SDR cache from a file*/
+		    binfile = optarg; break;
+	  case 'k': loopsec = atoi(optarg); break;  /*N sec between loops*/
+	  case 'm': /* specific MC, 3-byte address, e.g. "409600" */
+		    g_bus = htoi(&optarg[0]);  /*bus/channel*/
+		    g_sa  = htoi(&optarg[2]);  /*device slave address*/
+		    g_lun = htoi(&optarg[4]);  /*LUN*/
+		    if (optarg[6] == 's') {
+			     g_addrtype = ADDR_SMI;  s1 = "SMI";
+		    } else { g_addrtype = ADDR_IPMB; s1 = "IPMB"; }
+		    fset_mc = 1;
+		    printf("set MC at %s bus=%x sa=%x lun=%x\n",
+			    s1,g_bus,g_sa,g_lun);
+		    break;
+	  case 'o': 
+		fgetmem = 1;
+		break;
+	  case 'n': 
+		if (strncmp(optarg,"0x",2) == 0) i = htoi(&optarg[2]);
+		else i = htoi(optarg);      /*was atoi()*/
+		sensor_num = (uchar)i;
+		printf("sensor_num = 0x%x\n",sensor_num);
+		break;
+	  case 'h':   /* highest threshold */
+		if (strncmp(optarg,"0x",2) == 0) {
+			i = htoi(&optarg[2]);
+			sensor_hi = (uchar)i;
+			fsetthresh = 1;
+		} else {
+			sensor_hi_f = atof(optarg);
+			fsetthresh = 2;  /*indicates float conversion*/
+		}
+		break;
+	  case 'l':    /* lowest threshold */
+		if (strncmp(optarg,"0x",2) == 0) {
+			i = htoi(&optarg[2]);
+			sensor_lo = (uchar)i;
+			fsetthresh = 1;
+		} else {
+			sensor_lo_f = atof(optarg);
+			fsetthresh = 2;  /*indicates float conversion*/
+		}
+		break;
+	  case 'p': fsavethresh = 1;  break;
+	  case 'q': fshowthr = 2; fwrap = 1; break;
+	  case 'r': frawsdr = 1;   break;
+	  case 't': fshowthr = 1;  break;
+	  case 'v': fshowthr = 1; sens_verbose = 1; break;
+	  case 'u':    /* specify unique thresholds in hex or float */
+		/* raw hex format: 0xLNLCLUHNHCHU, all 6 required */
+		if (strncmp(optarg,"0x",2) == 0) {  /*raw hex thresholds*/
+		   sensor_thr[0] = htoi(&optarg[2]);  /*lo noncrit*/
+		   sensor_thr[1] = htoi(&optarg[4]);  /*lo crit*/
+		   sensor_thr[2] = htoi(&optarg[6]);  /*lo unrec*/
+		   sensor_thr[3] = htoi(&optarg[8]);  /*hi noncrit*/
+		   sensor_thr[4] = htoi(&optarg[10]); /*hi crit*/
+		   sensor_thr[5] = htoi(&optarg[12]); /*hi unrec*/
+		   /* validate sensor threshold ordering */
+		   rv = validate_thresholds(&sensor_thr[0],0,NULL);
+		   if (rv == 0) {
+		      sensor_lo = sensor_thr[0];
+		      sensor_hi = sensor_thr[3];
+		      fsetthresh = 3;  /*indicates unique raw thresholds */
+		   } else {
+		      ret = ERR_BAD_PARAM;
+		      goto do_exit;
+		   }
+		} else {  
+		   /* assume float input thresholds, with ':' separator*/
+		   /* format LN:LC:LU:HN:HC:HU */
+		   sz = strlen_(optarg);
+		   p = &optarg[0];
+		   for (i = 0; i < 6; i++) sensor_thrf[i] = THR_EMPTY;
+		   j = 0;
+		   for (i = 0; i <= sz; i++) {
+		      if (j >= 6) break;
+		      switch(optarg[i]) {
+			case ':':
+			case '\n':
+			case '\0':
+			   optarg[i] = 0;
+			   if (p[0] == 0) sensor_thrf[j] = THR_EMPTY;
+			   else sensor_thrf[j] = atof(p);
+			   if (i+1 < sz) p = &optarg[i+1];
+			   j++;
+			   break;
+			default:
+			   break;
+		      }
+		   }
+		   /* validate sensor threshold ordering later */
+		   // rv = validate_thresholds(&sensor_thrf[0],1,NULL);
+		   // if (rv == 0) {
+		      sensor_lo_f = sensor_thrf[0];
+		      sensor_hi_f = sensor_thrf[3];
+		      fsetthresh = 4;  /*indicates unique float thresholds */
+		   // } else {
+		      // ret = ERR_BAD_PARAM;
+		      // goto do_exit;
+		   // }
+		} /*end-else -u float thresholds*/
+		break;
+	  case 'w': fwrap = 1;   break;
+	  case 'x': fdebug = 1;  break;
+	  case 'L':      /* Loop */
+		nloops = atoi(optarg);
+		fdoloop = 1;
+		break;
+	  case 'V':    /* priv level */
+		fprivset = 1;
+	  case 'N':    /* nodename */
+	  case 'U':    /* remote username */
+	  case 'P':    /* remote password */
+	  case 'R':    /* remote password */
+	  case 'E':    /* get password from IPMI_PASSWORD environment var */
+	  case 'F':    /* force driver type */
+	  case 'T':    /* auth type */
+	  case 'J':    /* cipher suite */ 
+	  case 'Y':    /* prompt for remote password */
+	  case 'Z':    /* set local MC address */
+		parse_lan_options(c,optarg,fdebug);
+		break;
+	  default:   /*usage*/
+	     printf("Usage: %s [-abcdefghijlmnprstuvwxL -NUPREFTVYZ]\n",progname);
+	     printf("where -x      shows eXtra debug messages\n");
+	     printf("      -a snum reArms the sensor (snum) for events\n");
+	     printf("      -b      show Bladed child MCs for PICMG (same as -e)\n");
+	     printf("      -c      displays a simpler, Canonical output fmt\n");
+	     printf("      -d file Dump SDRs to a binary file\n");
+	     printf("      -e      show Every bladed child MC for PICMG\n");
+	//   printf("      -f file Restore SDRs from a binary dump file\n");
+	     printf("      -g fan  show only this sensor type group\n");
+	     printf("      -h tval specifies the Highest threshold to set\n");
+	     printf("      -i id   only show these sensor id numbers\n");
+	     printf("      -j file Jump-start SDR cache from a binary file\n");
+	     printf("      -k K    If -L, wait K sec between loops (default=1)\n");
+	     printf("      -l tval specifies the Lowest threshold to set\n");
+	     printf("      -m002000 specific MC (bus 00,sa 20,lun 00)\n");
+	     printf("      -n snum specifies the sensor Number to set hi/lo\n");
+	     printf("      -o      output memory DIMM information\n");
+	     printf("      -p      persist the threshold being set\n");
+	     printf("      -q      shows threshold values in d:d:d format\n");
+	     printf("      -r      show Raw SDR bytes\n");
+	     printf("      -s      displays a Simpler output format\n");
+	     printf("      -t      shows Threshold values in text format\n");
+	     printf("      -u thr  set Unique threshold values (e.g. 3:2:1:48:49:50)\n");
+	     printf("      -v      Verbose: thresholds, max/min, hysteresis\n");
+	     printf("      -w      Wrap thresholds on sensor line\n");
+	     printf("      -L n    Loop n times every k seconds (default k=1)\n");
+	     print_lan_opt_usage(0);
+	     ret = ERR_USAGE;
+	     goto do_exit;
+      }
+    if (fjumpstart && fchild) {
+       printf("Cannot use -j jumpstart cache with -c child SDRs\n");
+       ret = ERR_BAD_PARAM;
+       goto do_exit;
+    }
+    fremote = is_remote();
+#ifndef WIN32
+    if (fremote == 0) {  
+	/* only run this as superuser for accessing IPMI devices. */
+	i = geteuid();
+	if (i > 1) {
+	    printf("Not superuser (%d)\n", i);
+	    /* Show warning, but could be ok if /dev/ipmi0 is accessible */
+	    //ret = ERR_NOT_ALLOWED;
+	    //goto do_exit;
+	}
+    } 
+#endif
+    if (fremote) {
+	if (!fprivset) {
+	  /* on many systems, getting the SDR Reservation ID requires admin */
+	  /* if ((fsetthresh != 0) || (frearm != 0))  also require admin */
+	  parse_lan_options('V',"4",0);
+	}
+    }
+
+   ret = ipmi_getdeviceid(devrec,sizeof(devrec),fdebug);
+   if (ret == 0) {
+	uchar ipmi_maj;
+	uchar ipmi_min;
+	char *pstr;
+	ipmi_maj = devrec[4] & 0x0f;
+	ipmi_min = devrec[4] >> 4;
+	if ((devrec[1] & 0x80) == 0x80) fdevsdrs = 1;
+	vend_id = devrec[6] + (devrec[7] << 8) + (devrec[8] << 16);
+	prod_id = devrec[9] + (devrec[10] << 8);
+	if (vend_id == VENDOR_NSC) { /*NSC mBMC*/ 
+		pstr = "mBMC";
+		fmBMC = 1;
+		fdevsdrs = 0;
+	} else if (vend_id == VENDOR_INTEL) { /*Intel BMC*/ 
+		/*Intel Sahalee BMC */
+		pstr = "BMC";
+		fmBMC = 0;
+		if (is_romley(vend_id,prod_id)) fRomley = 1;
+		if (is_grantley(vend_id,prod_id)) fGrantley = 1;
+		if (prod_id == 0x003E || fRomley || fGrantley)  /*Urbanna,CG2100*/
+		   set_max_kcs_loops(URNLOOPS); /*longer KCS timeout*/
+	} else if ((vend_id == VENDOR_SUPERMICRO)
+                || (vend_id == VENDOR_SUPERMICROX)) {
+                   set_max_kcs_loops(URNLOOPS); /*longer KCS timeout*/
+	} else if (vend_id == 16394) { /*Pigeon Point*/ 
+		fbadsdr = 1;   /* SDR has bad sa/mc value, so ignore it */
+	} else {   /* Other products */
+		pstr = "BMC";
+		fmBMC = 0;
+		if (vend_id == VENDOR_NEC) fdevsdrs = 0;
+	}
+	show_devid( devrec[2],  devrec[3], ipmi_maj, ipmi_min);
+		// "-- %s version %x.%x, IPMI version %d.%d \n", pstr,
+   } else {
+	goto do_exit;
+   }
+
+   ret = ipmi_getpicmg( devrec, sizeof(devrec),fdebug);
+   if (ret == 0) fpicmg = 1;
+   /*if not PICMG, some vendors override to SDR Rep*/
+   fdevsdrs = use_devsdrs(fpicmg);
+   if (fdevsdrs) printf("supports device sdrs\n");
+   npass = 1;
+   if (g_sa != 0) {
+      /* target a specific MC via IPMB (usu a picmg blade) */
+      ipmi_set_mc(g_bus,g_sa,g_lun,g_addrtype);
+      fchild = 0;  /* no children, only the specified address */
+   } else { 
+#ifdef PICMG_CHILD
+      /* fchild set above if -b is specified to get Blade child SDRs */
+      /* npass = 2 will get both SdrRep & DevSdr passes on CMM */
+      if (fpicmg && fdevsdrs) {
+	 npass = 2;
+	 g_addrtype = ADDR_IPMB;
+      }
+#endif
+      g_sa = BMC_SA;
+   }
+
+   if (fgetmem) {
+      if (fremote) printf("Cannot get memory DIMM information remotely.\n");
+      else {
+	 int msz;
+	 char desc[80];
+	 char szstr[25];
+	 ret = -1;
+	 for (j = 0; j < 1; j++) {
+	    for (i = 0; i < 16; i++) {
+	       rv = get_MemDesc(j, i, desc,&msz);
+	       if (rv == 0) {
+		  if (msz == 0) strcpy(szstr,"not present");
+		  else if (msz & 0x8000)
+		       sprintf(szstr,"size=%dKB",(msz & 0x7FFF));
+		  else sprintf(szstr,"size=%dMB",msz);
+		  printf("Memory Device (%d,%d): %s : %s\n",
+			 j,i,desc,szstr);
+		  ret = 0;
+	       }
+	    }
+	 }
+      } /*end-else*/
+      goto do_exit;
+   }
+
+   if (fdump) {
+	  ret = write_sdr_binfile(binfile);
+      goto do_exit;
+   } /*endif fdump*/
+
+   if (frestore) {
+      uchar sdr[MAX_BUFFER_SIZE];
+      ushort id;
+      int slen;
+      uchar *pbuf = NULL;
+
+      ret = read_sdr_binfile(binfile,&pbuf,&slen);
+      if (ret == 0) {   /*successful, so write SDRs */
+		 nsdrs = find_nsdrs(pbuf);
+		 printf("Ready to restore %d SDRs\n",nsdrs);
+		 set_reserve(1);
+		 ret = sdr_clear_repo(fdevsdrs);
+		 if (ret != 0) {
+		    printf("SDR Clear Repository error %d\n",ret);
+		    goto do_exit;
+		 }
+		 id = 0;
+		 while(find_sdr_next(sdr,pbuf,id) == 0) {
+		    id = sdr[0] + (sdr[1] << 8);
+		    if (fdebug) printf("adding SDR[%x]\n",id);
+		    set_reserve(1);
+		    ret = sdr_add_record(sdr,fdevsdrs);
+		    if (ret != 0) {
+			printf("SDR[%x] add error %d\n",id,ret);
+			break;
+		    }
+		 } /*end while sdr*/
+      }
+      if (ret == 0) printf("Restored %d SDRs successfully.\n",nsdrs);
+      free_sdr_cache(pbuf); /* does nothing if (pbuf == NULL) */
+      goto do_exit;
+   } /*endif frestore*/
+
+   if (fjumpstart) {
+      uchar *pbuf = NULL;
+      int slen;
+      ret = read_sdr_binfile(binfile,&pbuf,&slen);
+      if (ret != 0) { 
+    	 /* Try to dump sdrs to this file if not there */
+	     ret = write_sdr_binfile(binfile);
+         if (ret == 0) 
+      		ret = read_sdr_binfile(binfile,&pbuf,&slen);
+         if (ret != 0) {
+    		fjumpstart = 0; /*cannot do jumpstart*/
+		 }
+      } else {  /* set this as the SDR cache */
+    	 psdrcache = pbuf;
+    	 sz_sdrs = slen;
+    	 nsdrs = find_nsdrs(pbuf);
+    	 if (fdebug) printf("jumpstart cache: nsdrs=%d size=%d\n",nsdrs,slen);
+      }
+   } /*endif fjumpstart*/
+
+   for (ipass = 0; ipass < npass; ipass++)
+   {
+     if (fjumpstart) ; /*already got this above*/
+     else {
+	ret = GetSDRRepositoryInfo(&j,&fdevsdrs);
+	if (fdebug) printf("GetSDRRepositoryInfo: ret=%x nSDRs=%d\n",ret,j);
+	if (ret == 0 && j == 0) {
+	   printf("SDR Repository is empty\n");
+	   goto do_exit;
+	}
+	nsdrs = j;
+     }
+
+     /* show header for SDR records */
+     if (fsimple) 
+	printf(" ID  | SDRType | Type            |SNum| Name             |Status| Reading\n");
+     else 
+	printf("_ID_ SDR_Type_xx ET Own Typ S_Num   Sens_Description   Hex & Interp Reading\n");
+
+     if (fwrap) chEol = ' ';
+     if (!fdoloop) nloops = 1;
+     for (iloop = 0; iloop < nloops; iloop++)
+     {
+       if (fshowidx) recid = sensor_idx1;
+       else recid = 0;
+	   irec = 0; /*first sdr record*/
+       while (recid != 0xffff) 
+       {
+	 if (fjumpstart) {
+	   if (irec == 0)  /*need sdr_by_id if fshowid recid>0*/
+	        ret = find_sdr_by_id(sdrdata,psdrcache,recid);
+	   else ret = find_sdr_next(sdrdata,psdrcache,recid);
+	   if (ret != 0) {  /*end of sdrs*/
+		if (fdebug) printf("find_sdr_next(%04x): ret = %d\n", recid,ret);
+		ret = 0; break; 
+	   }
+	   recnext = sdrdata[0] + (sdrdata[1] << 8);  /*same as recid*/
+	   if (fdebug) printf("find_sdr_next(%04x): ret = %d, next=%04x\n",
+				recid,ret,recnext);
+	   if (recid > 0 && recnext == 0) {
+		if (fdebug) printf("Error recid=%04x recnext=%04x\n",recid,recnext);
+		ret = 0; break; 
+	   }
+	   sz = sdrdata[4] + 5;
+	 } else {
+	   int try;
+           for (try = 0; try < 10; try++) {
+             ret = GetSDR(recid,&recnext,sdrdata,sizeof(sdrdata),&sz);
+             if (fdebug)
+               printf("GetSDR[%04x]: ret = %x, next=%x\n",recid,ret,recnext);
+             if (ret != 0) {
+               if (ret == 0xC5) {  /* lost Reservation ID, retry */
+                 /*fprintf(stderr,"%04x lost reservation retrying to get, try: %d,  %d, rlen = %d\n", recid,try,ret,sz);*/
+                 os_usleep((rand() & 3000),0);
+                 fDoReserve = 1;
+               }
+               else {
+                 fprintf(stderr,"%04x GetSDR error %d, rlen = %d\n", recid,ret,sz);
+                 break;
+               }
+             }
+             else {
+               break;
+             }
+             if (try==9){
+               sz=0;
+              fprintf(stderr,"%04x GetSDR error %d, rlen = %d\n", recid,ret,sz);
+             }
+           }
+           if (sz < MIN_SDR_SZ) {  /* don't have recnext, so abort */
+             break;
+           } /* else fall through & continue */
+	 } /*end-else*/
+	 if (ret == 0) {  /* (ret == 0) OK, got full SDR */
+	   if (fdebug) {
+		dump_buf("got SDR",sdrdata,sz,0);
+	   }
+	   if (sz < MIN_SDR_SZ) goto NextSdr;
+	   /* if recid == 0, get real record id */
+	   if (recid == 0) recid = sdrdata[0] + (sdrdata[1] << 8);
+	   if (fshowgrp > 0) {
+	      for (i = 0; i < fshowgrp; i++) {
+		uchar styp;
+		if (sdrdata[3] == 0x03) styp = sdrdata[10]; /*EvtOnly*/
+		else styp = sdrdata[12];
+		if (fdebug) printf("sdrt=%02x styp=%02x sgrp[%d]=%02x\n",
+				sdrdata[3],styp,i,sensor_grps[i]);
+		if (sdrdata[3] == 0xc0) continue; /*skip OEM SDRs*/
+		if (styp == sensor_grps[i]) break;
+	      }
+	      if (i >= fshowgrp) goto NextSdr;
+	   }
+
+	   if ((sensor_num == INIT_SNUM) || (sdrdata[7] == sensor_num) 
+	      || fsetthresh) {
+	       /* if -n not set or if -n matches, parse and show the SDR */
+	       ShowSDR("",sdrdata);
+	   }  /* else filter SDRs if not matching -n sensor_num */
+
+#ifdef PICMG_CHILD
+	   /*
+	    * Special logic for blade child MCs in PICMG ATCA systems 
+	    * if fchild, try all child MCs within the chassis.
+	    * SDR type 12 capabilities bits (sdrdata[8]):
+	    *    80 = Chassis Device
+	    *    40 = Bridge
+	    *    20 = IPMB Event Generator
+	    *    10 = IPMB Event Receiver
+	    *    08 = FRU Device
+	    *    04 = SEL Device
+	    *    02 = SDR Repository Device
+	    *    01 = Sensor Device
+	    *    But all child MCs use Device SDRs anyway.
+	    */
+	   if (fpicmg && fchild && (sdrdata[3] == 0x12)) { /* PICMG MC DLR */
+	      int   _recid, _recnext, _sz;
+	      uchar _sdrdata[MAX_SDR_SIZE];
+	      int   devsdrs_save;
+	      uchar cc;
+
+	      /* save the BMC globals, use IPMB MC */
+	      devsdrs_save  = fdevsdrs;
+	      fdevsdrs = 1;   /* use Device SDRs for the children*/
+	      if (fdebug)
+		printf(" --- IPMB MC (sa=%02x cap=%02x id=%02x devsdrs=%d):\n",
+		       sdrdata[5],sdrdata[8],sdrdata[12],fdevsdrs);
+	      fDoReserve = 1;  /* get a new SDR Reservation ID */
+	      ipmi_set_mc(PICMG_SLAVE_BUS,sdrdata[5],sdrdata[6],g_addrtype);
+
+	      _sz = 16;
+	      ret = ipmi_cmd_mc(GET_DEVICE_ID,NULL,0,_sdrdata,&_sz,&cc,fdebug);
+	      if (ret == 0 && cc == 0) {
+		/* Get the SDRs from the IPMB MC */
+		_recid = 0;
+		while (_recid != 0xffff) 
+		{
+		  ret = GetSDR(_recid,&_recnext,_sdrdata,sizeof(_sdrdata),&_sz);
+		  if (ret != 0) {
+		     fprintf(stderr,"%04x GetSDR error %d, rlen = %d\n",_recid,ret,_sz);
+		     break;
+		  }
+		  else if (_sz >= MIN_SDR_SZ) 
+		     ShowSDR(" ",_sdrdata);
+
+		  if (_recnext == _recid) _recid = 0xffff;
+		  else _recid = _recnext;
+		} /*end while*/
+	      } /*endif ret==0*/
+
+	      /* restore BMC globals */
+	      fdevsdrs = devsdrs_save;
+	      ipmi_restore_mc();
+	      fDoReserve = 1;  /* get a new SDR Reservation ID */
+	   }  /*endif fpicmg && fchild*/
+#endif
+
+	   if (fdebug) printf("fsetthresh=%d snum=%02x(%02x) sa=%02x(%02x)\n",
+			    fsetthresh,sdrdata[7],sensor_num,sdrdata[5],g_sa);
+	   if (fsetthresh && (sdrdata[7] == sensor_num) 
+		&& (sdrdata[5] == g_sa))  /*g_sa usu is BMC_SA*/
+	   {
+	     /* setting threshold, compute threshold raw values */
+	     if (fsetthresh == 2) {   /*set from float*/
+		if (fdebug) 
+		   printf("lof=%.2f hif=%.2f\n", sensor_lo_f,sensor_hi_f);
+		if (sensor_lo_f != 0) 
+		   sensor_lo = FloatToRaw(sensor_lo_f,sdrdata,0);
+		if (sensor_hi_f != 0) 
+		   sensor_hi = FloatToRaw(sensor_hi_f,sdrdata,0);
+	     } else if (fsetthresh == 1) {  /*raw thresholds*/
+		if (sensor_hi != 0xff)
+		   sensor_hi_f = RawToFloat(sensor_hi,sdrdata);
+		if (sensor_lo != 0xff)
+		   sensor_lo_f = RawToFloat(sensor_lo,sdrdata);
+	     } else if (fsetthresh == 3) {  /*unique raw thresholds*/
+		if (sensor_hi != 0xff)
+		   sensor_hi_f = RawToFloat(sensor_hi,sdrdata);
+		if (sensor_lo != 0xff)
+		   sensor_lo_f = RawToFloat(sensor_lo,sdrdata);
+	     } else if (fsetthresh == 4) {   /*set unique from float*/
+		i = fill_thresholds(&sensor_thrf[0], sdrdata); 
+		/* if (i > 0) ;  * filled in some thresholds */
+		{  /* always set lo/hi if any are non-zero */
+		   for (j = 0; j < 3; j++) {
+		      if (sensor_thrf[j] != 0) {
+			  sensor_lo_f = sensor_thrf[j]; break; }
+		   }
+		   for (j = 3; j < 6; j++) {
+		      if (sensor_thrf[j] != 0) {
+			  sensor_hi_f = sensor_thrf[j]; break; }
+		   }
+		}
+		if (fdebug) 
+		   printf("lof=%.2f hif=%.2f\n", sensor_lo_f,sensor_hi_f);
+		/* convert thrf (float) to thr (raw) */
+		if (sensor_lo_f != 0) {
+		   sensor_lo = FloatToRaw(sensor_lo_f,sdrdata,0);
+		   sensor_thr[0] = FloatToRaw(sensor_thrf[0],sdrdata,0);
+		   sensor_thr[1] = FloatToRaw(sensor_thrf[1],sdrdata,0);
+		   sensor_thr[2] = FloatToRaw(sensor_thrf[2],sdrdata,0);
+		}
+		if (sensor_hi_f != 0) {
+		   sensor_hi = FloatToRaw(sensor_hi_f,sdrdata,0);
+		   sensor_thr[3] = FloatToRaw(sensor_thrf[3],sdrdata,0);
+		   sensor_thr[4] = FloatToRaw(sensor_thrf[4],sdrdata,0);
+		   sensor_thr[5] = FloatToRaw(sensor_thrf[5],sdrdata,0);
+		}
+		/* validate threshold ordering */
+		if (validate_thresholds(sensor_thrf,1,sdrdata) != 0) {
+		   ret = ERR_BAD_PARAM;
+		   goto do_exit; 
+		}
+	     }
+	     {
+		printf("\tSetting SDR %04x sensor %02x to lo=%02x hi=%02x\n",
+			    recid,sensor_num,sensor_lo,sensor_hi);
+		if (recid == 0) fsetfound = 1;
+		else fsetfound = recid;
+	     }
+	   } /*endif fsetthresh */
+	 }  /*endif ok, got full SDR */
+
+NextSdr:
+	 if (ret == ERR_SDR_MALFORMED) break;
+	 if (fjumpstart) recid = recnext;
+	 else {
+		if (recnext == recid) recid = 0xffff; /*break;*/
+		else recid = recnext;
+	 }
+	 if (fshowidx) {
+		/* if we have already read the last in the range, done. */
+		if (recid >= sensor_idxN) break; // recnext = 0xffff; // break;
+	 }
+	     irec++;
+       } /*end while recid*/
+       if (fdoloop && (nloops > 1)) {
+     	 printf("\n"); /* output an empty separator line */
+     	 os_usleep(loopsec,0); /*delay 1 sec between loops*/
+       }
+     } /*end for nloops*/
+
+     if (npass > 1) {   /* npass==2 for PICMG */
+	/* Switch fdevsdrs from Device to Repository */
+	if (fdevsdrs == 0) fdevsdrs = 1;
+	else fdevsdrs = 0;
+	fDoReserve = 1;  /* get a new SDR Reservation ID */
+     }
+   } /*end for npass*/
+
+   if ((fshowidx == 0) && (fshowgrp == 0)) {
+      /* use local rv, errors are ignored for POH */
+      rv = ShowPowerOnHours();
+   }
+
+   if (frearm != 0) {
+	ret = RearmSensor((uchar)frearm);
+	printf("RearmSensor(0x%02x) ret = %d\n",frearm,ret);
+   }
+
+   if (fsetthresh != 0) {
+	uchar tdata[7];
+	if (fsetfound == 0) {
+	   printf("Did not find sensor number %02x.\nPlease enter the sensor number parameter in hex, as it is displayed above.\n",sensor_num);
+	}
+	ret = GetSensorThresholds(sensor_num,tdata);
+	if (ret != 0) goto do_exit; 
+#ifdef TEST
+	printf("thresh(%02x): %02x %02x %02x %02x %02x %02x %02x %02x\n",
+		sensor_num, sensor_num, tdata[0], tdata[1], tdata[2],
+		tdata[3], tdata[4], tdata[5], tdata[6]);
+	printf("   set(%02x):          %02x       %02x \n",
+		sensor_num,sensor_lo,sensor_hi);
+#endif
+	if (fsetthresh == 3 || fsetthresh == 4) {  
+	   /* apply unique sensor thresholds */
+	   pset = &sensor_thr[0];
+	} else pset = NULL;  /* use just hi/lo */
+	ret = SetSensorThresholds(sensor_num,sensor_hi,sensor_lo,tdata,pset);
+	printf("SetSensorThreshold[%02x] to lo=%02x(%4.3f) hi=%02x(%4.3f), ret = %d\n",
+		sensor_num,sensor_lo,sensor_lo_f,sensor_hi,sensor_hi_f,ret);
+	if (fsavethresh && ret == 0) {
+	    recid = fsetfound;
+	    rv = SaveThreshold(recid,sensor_num,sensor_lo,sensor_hi,pset);
+	    if (rv == 0) 
+		printf("Saved thresholds for sensor %02x\n",sensor_num);
+	}
+	fsetthresh = 0;  /*only set threshold once*/
+   }
+
+do_exit:
+   // if (fjumpstart) 
+   free_sdr_cache(psdrcache); /* does nothing if ==NULL*/
+   /* show_outcome(progname,ret); *handled in ipmiutil.c*/
+   ipmi_close_();
+   return(ret);  
+}
+
+/* end isensor.c */
diff --git a/util/mem_if.c b/util/mem_if.c
index 3fcc139..c7ca677 100644
--- a/util/mem_if.c
+++ b/util/mem_if.c
@@ -8,6 +8,7 @@
  * 02/26/08 ARCress - decode type 15 log structure
  * 07/21/08 ARCress - fixed for 64-bit memory model
  * 08/12/08 ARCress - trim out extra stuff, consolidated
+ * 01/09/23 ARCress - try UEFI_MEM_RANGE_BASE  0x6d5a7000 if error
  *----------------------------------------------------------------------*/
 /*----------------------------------------------------------------------*
 The BSD License 
@@ -74,6 +75,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #endif
 
 #define	DOS_MEM_RANGE_BASE   0xF0000  //starting memory range for smbios tables
+#define	UEFI_MEM_RANGE_BASE  0x6d5a7000  //starting memory range for UEFI tables
 #define	DOS_NUM_BYTES_TO_MAP 0x0FFFE  //number bytes to map for smbios tables
 
 //smbios defines
@@ -416,8 +418,13 @@ int  getSmBiosTables(UCHAR **ptableAddress)
 	    * unless in debug mode. */
 	   if (fsm_debug) 
 #endif
-		fprintf(stderr, "Cannot map memory.\n");
-	        return ulSmBiosLen; /*==0*/
+		fprintf(stderr, "Cannot map SMBIOS memory.\n");
+	   	/* Try UEFI address */
+		tdStartAddress = UEFI_MEM_RANGE_BASE;
+		if (!MapPhysicalMemory(tdStartAddress,ulSize,&tdVirtualAddress)) {
+		   fprintf(stderr, "Cannot map UEFI SMBIOS memory.\n");
+	           return ulSmBiosLen; /*==0*/
+		}
 	}
 
 	//now find the entry point for smbios
-- 
cgit v1.2.3