/*
 * sensor.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.
 */
/*M*
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 Kontron 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>
#include <getopt.h>
#endif
#include "ipmicmd.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

extern int get_LastError( void );  /* ipmilan.c */
extern int use_devsdrs(void);  /* ipmicmd.c */
#ifdef METACOMMAND
extern void show_oemsdr_intel(uchar *sdr);
extern int decode_sensor_intel(uchar *sdr,uchar *reading,char *pstring,
				int slen);  /*see oem_fujitsu.c*/
extern int decode_sensor_kontron(uchar *sdr,uchar *reading,char *pstring,
				int slen);  /*see oem_fujitsu.c*/
extern int decode_sensor_fujitsu(uchar *sdr,uchar *reading,char *pstring,
				int slen);  /*see oem_fujitsu.c*/
#endif
/************************
 *  Global Data
 ************************/
static char *progname  = "isensor";
static char *progver   = "2.72";
#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 */
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 fremote  = 0;  
static int fdump    = 0;  
static int frestore = 0;  
static int nloops   = 1;   /* num times to show repeated sensor readings */
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 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 77
#define CUSTOM_STRING  58
#define OEM_STRING     71
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    ",
/*22 ACPI*/ "Working",
/*23 ACPI*/ "Sleeping", /*D2/S2*/
/*24 ACPI*/ "On",
/* 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 ",
/*33 CrI */ "Bus_Error",
/*34 CrI */ "Fatal_NMI",
/* Power Supply event states */
/*35 PS  */ "AC_Lost  ",
/*36 PS  */ "PS_Failed",
/* 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  */   "Config_Err"
};

static char *raid_states[9] = {  /*for sensor type 0x0d drive status */
  "Faulty",
  "Rebuilding",
  "InFailedArray",  
  "InCriticalArray",  
  "ParityCheck",  
  "PredictedFault",  
  "Un-configured",  
  "HotSpare",
  "NoRaid" };

#define NSENSTYPES   0x2a
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",
};

#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 */ "UNK",
/* 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"
};

typedef struct {
  ushort recid;
  uchar  sdrver;  /*usu. 0x51 = v1.5*/
  uchar  rectype; /* 01, 02, 11, 12, c0 */
  uchar  reclen;
  uchar  sens_ownid;
  uchar  sens_ownlun;
  uchar  sens_num;
  uchar  entity_id;
  uchar  entity_inst;
  uchar  sens_init; 
  uchar  sens_capab; 
  uchar  sens_type; 
  uchar  ev_type;
  uchar  data1[6];
  uchar  sens_units;
  uchar  sens_base;
  uchar  sens_mod;
  uchar  linear;
  uchar  m;
  uchar  m_t;
  uchar  b;
  uchar  b_a;
  uchar  a_ax;
  uchar  rx_bx;
  uchar  flags;
  uchar  nom_reading;
  uchar  norm_max;
  uchar  norm_min;
  uchar  sens_max_reading;
  uchar  sens_min_reading;
  uchar  data3[11];
  uchar  id_strlen;
  uchar  id_string[16];
  } SDR01REC;

typedef struct {
  ushort recid;
  uchar  sdrver;  /*usu. 0x51 = v1.5*/
  uchar  rectype; /* 01, 02, 11, 12, c0 */
  uchar  reclen;
  uchar  sens_ownid;
  uchar  sens_ownlun;
  uchar  sens_num;
  uchar  entity_id;
  uchar  entity_inst;
  uchar  sens_init; 
  uchar  sens_capab; 
  uchar  sens_type; 
  uchar  ev_type;
  uchar  data1[6];
  uchar  sens_units;
  uchar  sens_base;
  uchar  sens_mod;
  uchar  shar_cnt;
  uchar  shar_off;
  uchar  data2[5];
  uchar  id_strlen;
  uchar  id_string[16];
  } SDR02REC;

typedef struct {  /* 0x08 = Entity Association record */
  ushort recid;
  uchar  sdrver;  /*usu. 0x51 = v1.5*/
  uchar  rectype; /* 01, 02, 11, 12, c0 */
  uchar  reclen;
  uchar  contid;
  uchar  continst;
  uchar  flags;
  uchar  edata[8];
  } SDR08REC;

typedef struct {  /*0x14 = BMC Message Channel Info record */
  ushort recid;
  uchar  sdrver;  /*usu. 0x51 = v1.5*/
  uchar  rectype; /* 01, 02, 11, 12, c0 */
  uchar  reclen;
  uchar  mdata[8];
  uchar  mint;
  uchar  eint;
  uchar  rsvd;
  } SDR14REC;

typedef struct {  /*0x11 = FRU Locator*/
  ushort recid;
  uchar  sdrver;  /*usu. 0x51 = v1.5*/
  uchar  rectype; /* 01, 02, 11, 12, c0 */
  uchar  reclen;
  uchar  dev_access_adr; /*usu sa*/
  uchar  dev_slave_adr;  /*usu fru_id*/
  uchar  access_lun;
  uchar  chan_num;
  uchar  reserved;
  uchar  dev_type; 
  uchar  dev_typemod; 
  uchar  entity_id; 
  uchar  entity_inst; 
  uchar  oem; 
  uchar  id_strlen;
  uchar  id_string[16];
  } SDR11REC;

typedef struct {  /*0x12 = IPMB Locator, for MCs*/
  ushort recid;
  uchar  sdrver;  /*usu. 0x51 = v1.5*/
  uchar  rectype; /* 01, 02, 11, 12, c0 */
  uchar  reclen;
  uchar  dev_slave_adr;
  uchar  chan_num;
  uchar  power_state;
  uchar  dev_capab; 
  uchar  reserved[3]; 
  uchar  entity_id; 
  uchar  entity_inst; 
  uchar  oem; 
  uchar  id_strlen;
  uchar  id_string[16];
  } SDR12REC;

typedef struct {  /*0xc0 = OEM Record*/
  ushort recid;
  uchar  sdrver;  /*usu. 0x51 = v1.5*/
  uchar  rectype; /* 01, 02, 11, 12, c0 */
  uchar  reclen;
  uchar  manuf_id[3]; /*Intel = 0x57,0x01,0x00 = 343.*/
  uchar  oem_data[60]; /* (reclen-3 bytes)*/
  } SDRc0REC;


int parse_idx(char *str)
{
    int i;
    if (strncmp(str,"0x",2) == 0) str += 2;
    if (strlen(str) > 2) {
        i = (htoi(str) << 8) + htoi(&str[2]);
    } else i = htoi(str);      /*was atoi()*/
    printf("idx = 0x%x\n",i);
    return(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;
    punittypes = unit_types;
    if (fshort) punittypes = unit_types_short;
    if ( ibase < NUNITS) { 
           pstr = punittypes[ibase];
    } else if ((iunits > 0) && (iunits < NUNITS)) {
           pstr = punittypes[iunits];
           /* For Tyan fans:  base=42, units=24.(0x18) -> cycles/hour */
           if (ibase == 42 && iunits == 24) pstr = "cycles/hour";
    } else {
           if (fdebug) printf("units base %02x > %d\n",ibase,NUNITS);
           pstr = punittypes[0];
   }
   return(pstr);
}

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((char *)sensor_types[j],p,len) ==  0) {
		   sensor_grps[n++] = 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);
}

int 
GetSDRRepositoryInfo(int *nret)
{
   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 (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 status=%d cc=%x\n",cmd,rc,cc);
   if (rc == 0xc1) cc = 0xc1;  /* some drivers return the cc in rc */
   else if (rc != ACCESS_OK) return(rc);
   if (cc != 0) {
      if (cc == 0xc1) {   /*0xC1 (193.) means unsupported command */
         /* 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 (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: nSDRs = %d, free space = %x ReserveOK = %d\n",
		nSDR,freespace,fReserveOK);
   }

   return(0);
}  /*end GetSDRRepositoryInfo()*/


int 
GetSensorThresholds(int 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(int sens_num, void *psdr, uchar *sens_data)
{
	uchar resp[MAX_BUFFER_SIZE];
	int sresp = MAX_BUFFER_SIZE;
	uchar inputData[6];
        SDR02REC *sdr;
        int mc;
	int rc;
	uchar cc = 0;

        sdr = (SDR02REC *)psdr;
        if (psdr != NULL) {
           mc = sdr->sens_ownid;
	   if (mc != BMC_SA) {  /* not BMC, e.g. HSC sensor */
              // if (fipmi_lan) return(LAN_ERR_NOTSUPPORT);
              ipmi_set_mc(PUBLIC_BUS,(uchar)mc, sdr->sens_ownlun,ADDR_SMI);
           }
        } else mc = BMC_SA;
	inputData[0] = sens_num;
	rc = ipmi_cmd_mc(GET_SENSOR_READING,inputData,1, resp,&sresp,&cc,fdebug);
	if (fdebug) 
	    printf("ipmi_cmd %x mc=%x status=%d cc=%x resp: %02x %02x %02x %02x\n",
		   GET_SENSOR_READING,mc,rc,cc,resp[0],resp[1],resp[2],resp[3]);
        if (mc != BMC_SA) ipmi_restore_mc();
	if ((rc == 0) && (cc != 0)) {
	    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(int snum, int 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("ipmi_cmd 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(int 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("ipmi_cmd status = %d\n",rc);
	if (rc != ACCESS_OK) return(rc);
	if (cc != 0) return(cc);
        /* successful, copy values */
	*stype = resp[0];
	*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] = 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 == 0xFF) {  /* 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] = off;      /*offset */
	   inputData[5] = 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 */
           }
	   /* 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*/

#define SDR_SZ    80     /*max SDR size*/
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);
}

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();

   rv = GetSDRRepositoryInfo(&n);
   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 = 60; /*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;
   memset(pcache,0,sz);
   *pret = pcache;
   nsdrs = n;
   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*/
	   asz += len;
	   if (recnext == recid) recid = 0xffff;
	   else recid = recnext;
	}
   }
   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;
   int asz = 0;
   int i, len;
   uchar *sdr;
   ushort recid;

   if (pcache == NULL) return(num);
   for (i = 0; asz < sz_sdrs; i++)
   {
	sdr = &pcache[asz];
	len = sdr[4] + 5;
	recid = sdr[0] + (sdr[1] << 8);
	asz += len;
	if (fdebug) printf("SDR[%x] len=%d i=%d\n", recid,len,i);
   }
   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_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];
	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*/
	if (id == 0) { rv = 0; break; }
	if (i == imatch) { rv = 0; break; }
   }
   if (rv == 0) memcpy(psdr,sdr,len);
   return(rv);
}

int
bitnum(ushort value)
{
   int 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, char 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; 
   char   rx, b_exp;
   SDR01REC *sdr;
   char 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 |= 0xFFFFFC00;  /* negative 32-bit*/
	rx = (sdr->rx_bx & 0xf0) >> 4;
	if (rx & 0x08) rx |= 0xf0;        /* negative 8-bit */
	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 |= 0xf0;  /* negative 8-bit */

	if ((sdr->sens_units & 0xc0) == 0) {  /*unsigned*/
		floatval = (double)raw;
	} else {  /*signed*/
		signval = (char)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);
	if (raw != 0) {   /* skip if zero to avoid dividing by zero */
	  if (sdr->linear == 7) floatval = 1 / floatval;
	  /* else method not implemented, pass through */
	}
   }

#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()*/

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_FUJITSU:
          rv = decode_sensor_fujitsu(sdr,reading,pstring,slen);
          break;
      case VENDOR_INTEL: 
          rv = decode_sensor_intel(sdr, reading, pstring, slen);
          break;
      case VENDOR_KONTRON: 
          rv = decode_sensor_kontron(sdr, reading, pstring, slen);
          break;
      default:
	  break;
   } /*end-switch vend_id*/
#endif
   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);
   else sep[0] = 0; /*null string*/
   if (flg != 0) {  /* Compact, did GetThresholds, reverse order */
	if (bits & 0x20) {
		ival = RawToFloat(vals[5],sdr);
		sprintf(part,"%shi-norec %.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-norec %.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\n",sdr[7],bits);
	if (bits & 0x20) {
		ival = RawToFloat(vals[0],sdr);
		sprintf(part,"%shi-norec %.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-norec %.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*/
}

/* 
 * 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.
 *
 * TODO, some additional types to consider:
type=01 reading=00 discrete temp/thermal sensors
type=02 reading=01 (v10) SCSI LVD vrefA
type=06 reading=00 Scrty Violation
type=0c reading=00 Memory, MEM Error
type=0e reading=00 (v10) POST MEM Resize
type=0f reading=00 POST Error, BIOS POST Code
type=10 reading=00 Log Disable
type=12 reading=00 System Event
type=14 reading=00 Button
type=15 reading=00 Proc Missing
type=15 reading=01 (v10) No Proc or Term
type=1d reading=00 (v10) Boot Init
type=1e reading=00 (v10) Boot Erro
type=1f reading=00 (v10) OS Boot
type=20 reading=00 (v10) OS Stop
type=23 reading=00 BMC Watchdog
type=29 reading=04 (v20) BB Vbat
type=c0 reading=00 (v15) SMI State or NMI State
type=f3 reading=00 (v15) SMI Timeout, (v10) SMI State 
type=f6 reading=00 Sensor Failure
type=f7 reading=00 FSB Mismatch
 */
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];
   char *pstr = NULL;
   int rv = -1;

   /* 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 = 5; /*transition to error state, Crit-Hi*/
	   } 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 == 0x03 or other 0x05 */
		   if (reading & 0x01) istr = 0; /* 8="OK*", 0="OK" */
		   else istr = 5; /*state asserted, Crit-hi */
	   } 
	   break;
	case 0x02:	/*Discrete Voltage*/
	   /* usu evtype == 0x05 for VBat */
		   if (reading & 0x01) istr = 5;  /*Crit-hi, LimitExceeded*/
                   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 = 35; /*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 {
	      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 & 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 = 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 = 76; /*Config Err*/
	   else if (b & 0x08) istr = 35; /*AC Lost*/
	   else if (b & 0x04) istr = 15; /*Predictive Fail*/
	   else if (b & 0x02) istr = 36; /*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 = 35; /*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:	/* HSC drive slot - usually sens_ownid == 0xc0 */
	   {
	      if (num > 8) {  /*HSC slot presence sensors (evtype==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 */
                 /* 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 = CUSTOM_STRING;
		     sensor_dstatus[istr] = customstr;
		     if (fdebug) printf("dstatus=%s\n",sensor_dstatus[istr]);
		 }
              }
           } 
	   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 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 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");
	   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 13: istr = 21;  break; /*Off*/
	     case 12: istr = 24;  break; /*On*/
	     default: istr = 41;   /*unknown*/
           }
	   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 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;
	case 0x60:	/* SCSI 1 Term Flt */
	case 0x61:	/* SCSI 2 Term Flt */
	   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 = 43; /*enabled*/
           else istr = 44;                /*disabled*/
	   break;
   	default:
	   if (fdebug) 
		printf("sensor[%x] type %02x not decoded, reading = %04x\n",
				num,type,reading);
	   istr = bitnum(reading);
   }
   return(istr);
}  /* end decode_comp_reading */


void
ShowSDR(char *tag, uchar *sdr)
{
  SDR01REC *sdr01;
  SDR02REC *sdr02;
  SDR08REC *sdr08;
  SDR11REC *sdr11;
  SDR12REC *sdr12;
  SDR14REC *sdr14;
  SDRc0REC *sdrc0;
  char idstr[20];
  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;

  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) {
	  printf("raw SDR: ");
	  for (i = 0; i < len; i++)
		  printf("%02x ",sdr[i]);
	  printf("\n");
  }
  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);
		printf("Bad SDR Length, please apply the correct FRU/SDR diskette\n");
		return;
	}
	sens_cap = sdr[11];
	ilen = len - ioff;
	memcpy(idstr,&sdr[ioff],ilen);
	for (i=ilen; i<16; i++) idstr[i] = ' ';
	idstr[16] = 0;  /* stringify */
        if ((sdr01->sens_capab & 0x40) == 0) brearm = 'm'; /*manual rearm*/
        else brearm = 'a';  /*automatic rearm*/
	if (fdebug) printf("ilen=%d, istr0=%c, sizeof=%d, s0=%x\n",
				ilen,idstr[0],sizeof(SDR01REC),sdr[ioff]);
	rc = GetSensorReading(sdr01->sens_num,sdr01,sens);
	/* if rc != 0, leave sens values zero */
	/* 
	 * There should not be any OEM Full sensors.
	 * if (sdr01->sens_type >= 0xC0) 
	 *     rc = decode_oem_sensor(sdr,sens,oem_string,sizeof(oem_string));
	 */
	i = bitnum((ushort)sens[2]);  /*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)  val = 0;  /* init state */
	else val = RawToFloat(sens[0],sdr);

        typestr = get_unit_type(sdr01->sens_units, sdr01->sens_base, 
				sdr01->sens_mod, 0);
        printf("%s", tag);
	if (fsimple) {
	   printf("Full sensor   [%04x]%c snum %02x %c %s %c %s %c %.2f %s%c",
			sdr01->recid, bdelim, sdr01->sens_num,bdelim, idstr,
			bdelim,sensor_dstatus[i],bdelim,val,typestr,chEol);
	} else
	printf("%04x SDR Full %02x %02x %02x %c %02x snum %02x %s = %02x %s %.2f %s%c",
		sdr01->recid, sdr01->rectype, sdr01->ev_type,
		sdr01->sens_ownid, brearm, sdr01->sens_type, sdr01->sens_num, 
		idstr, sens[0], sensor_dstatus[i], 
		val, typestr,chEol);
	if (fdebug && fshowthr)
		   printf("cap=%02x settable=%02x, readable=%02x\n",
			  sens_cap,sdr[19],sdr[20]);
	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[19],&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);
		printf("Bad SDR Length, please apply the correct FRU/SDR diskette\n");
		return;
	}
	sens_cap = sdr[11];
	shar_cnt = sdr02->shar_cnt & 0x0f;  /*sdr[23]*/
	ilen = len - ioff;
	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] = ' ';
	idstr[16] = 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=%d, 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) i = 42;  /* error in reading, NotAvailable */
        else if ((sens[1] & 0x20) != 0) i = 42; /*init state, NotAvailable*/
	else {
	    // if (sdr02->sens_type >= 0xC0) {
	    rc = decode_oem_sensor(sdr,sens,oem_string,sizeof(oem_string));
	    // } else rc = 1;
	    if (rc == 0) i = OEM_STRING;
	    else i = decode_comp_reading(sdr02->sens_type,sdr02->ev_type,
					sdr02->sens_num,sens[2],sens[3]);
        }
	if (fdebug) 
            printf("sens_num %x type %x evt %x reading %02x%02x i=%d rc=%d\n",
			sdr02->sens_num,sdr02->sens_type,sdr02->ev_type,
			sens[3],sens[2],i,rc);
	j = sens[2] | ((sens[3] & 0x7f) << 8); /*full reading, less h.o. bit*/
        printf("%s", tag);
	if (fsimple) {
		printf("Compact sensor[%04x]%c snum %02x %c %s %c %s%c",
			sdr02->recid, bdelim, sdr02->sens_num,bdelim, idstr,
			bdelim, sensor_dstatus[i],chEol);
	} else if (i == OEM_STRING) {
	   // idstr[ilen] = 0; /*cut out padding in idstr*/
	   printf("%04x SDR Comp %02x %02x %02x %c %02x snum %02x %s = %02x %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 = %02x%02x %s%c",
		sdr02->recid, sdr02->rectype, sdr02->ev_type,
		sdr02->sens_ownid, brearm, sdr02->sens_type, sdr02->sens_num, 
                /* idstr, sens[0], sens[1], sens[2], sens[3], */ 
		idstr, sens[3],sens[2], sensor_dstatus[i],chEol);
	}
	if (fdebug && fshowthr)
		   printf("cap=%02x settable=%02x, readable=%02x\n",
			  sens_cap,sdr[19],sdr[20]);
	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:   /* mBMC event-only sensor record, treat like Compact SDR */
	sdr02 = (SDR02REC *)sdr;
	ioff = 17;
	if (ioff > len) {
		printf("Bad SDR %x Length %d. Please apply the correct FRU/SDR diskette\n",
			sdr02->recid, len);
		return;
	}
	if (!fsimple)
	{
	ilen = len - ioff;
	memcpy(idstr,&sdr[ioff],ilen);
	for (i=ilen; i<16; i++) idstr[i] = ' ';  /*fill in with blanks*/
	idstr[16] = 0;
	sens_cap = sdr[11];
	memset(sens,0,sizeof(sens));
	// 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]);
        printf("%s",tag);
	printf("%04x SDR EvtO %02x %02x %02x %02x snum %02x %s = %02x %02x %02x %02x %s\n",
		sdr02->recid, sdr02->rectype, sdr02->reclen, 
		sdr02->sens_ownid, sdr02->sens_type, sdr02->sens_num, idstr,
		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*/
        for (i = 2; i < 10; 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;
	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;
	memcpy(idstr,&sdr[ioff],ilen);
	idstr[ilen] = 0;  /* stringify */
	if (fdebug) printf("ilen=%d, istr0=%c, sizeof=%d, 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;
	memcpy(idstr,&sdr[ioff],ilen);
	idstr[ilen] = 0;  /* stringify */
	if (fdebug) printf("ilen=%d, istr0=%c, sizeof=%d, 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);
	  if (vend == VENDOR_INTEL) {
            show_oemsdr_intel(sdr); /*show subtypes for Intel BMC_TAM*/
	  } else { 
	    if (vend == VENDOR_FUJITSU) printf("Fujitsu: ");
	    else printf("manuf=%d: ",vend);
	    for (i = 8; i < len; i++) 
		printf("%02x ",sdr[i]);
	    printf("\n");
	  }
	}
	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);
	   if (resp[0] == 60) /*normal*/ 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, "sensor -i 0x%04x -n 0x%02x %s %s\n", id, sensor_num,
                lostr,histr);
    fclose(fd);
    return(rv);
}

#define PICMG_GET_ADDR_INFO  0x01
static int get_picmg_addrinfo(uchar a1, uchar a2, uchar *addrdata)
{
   uchar idata[5];
   uchar rdata[16];
   int ilen, rlen;
   ushort icmd;
   uchar 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);
}


#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

#ifdef METACOMMAND
int i_sensor(int argc, char **argv)
#else
#ifdef WIN32
int __cdecl 
#else
int 
#endif
main(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;
   uchar fsetfound = 0;
   int iloop;
   int ipass, npass;
   uchar *pset;
   char *p;
   char *s1;

   printf("%s: version %s\n",progname,progver);

   while ( (c = getopt( argc, argv,"a:bcd:f:g:h:i:l:m:n:prstu: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 'b': fchild = 1;    break;  /* Bladed, so get child SDRs */   
          case 'c': fsimple = 1;   break;  /* Canonical/simple output*/
          case 'd': fdump = 1;      /* Dump SDRs to a file*/
		    binfile = optarg; break;
          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 't': fshowthr = 1;  break;
	  case 'v': fshowthr = 1; sens_verbose = 1; break;
	  case 'p': fsavethresh = 1;  break;
	  case 'r': frawsdr = 1;   break;
          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 '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 'u':    /* specify unique thresholds in hex or float */
		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 nonrec*/
                   sensor_thr[3] = htoi(&optarg[8]);  /*hi noncrit*/
                   sensor_thr[4] = htoi(&optarg[10]); /*hi crit*/
                   sensor_thr[5] = htoi(&optarg[12]); /*hi nonrec*/
		   /* validate sensor threshold ordering */
		   rv = 0;
                   if ((sensor_thr[1] > sensor_thr[0]) ||
                       (sensor_thr[2] > sensor_thr[1])) { rv = 1; }
                   if ((sensor_thr[4] < sensor_thr[3]) ||
                       (sensor_thr[5] < sensor_thr[4])) { rv = 2; }
                   if (rv == 0) {
                      sensor_lo = sensor_thr[0];
                      sensor_hi = sensor_thr[3];
		      fsetthresh = 3;  /*indicates unique raw thresholds */
                   } else {
		      printf("Threshold values: %02x>=%02x>=%02x %02x<=%02x<=%02x\n",
			sensor_thr[0], sensor_thr[1], sensor_thr[2],
			sensor_thr[3], sensor_thr[4], sensor_thr[5]);
		      printf("Invalid threshold order within -u (%s)\n",
				((rv == 1)? "lo": "hi"));
		      ret = ERR_BAD_PARAM;
		      goto do_exit;
                   }
		} else {  
		   /*assume float input thresholds, with ':' separator*/
		   sz = strlen(optarg);
		   p = &optarg[0];
		   j = 0;
		   for (i = 0; i <= sz; i++) {
		      if (j >= 6) break;
		      switch(optarg[i]) {
			case ':':
			case '\n':
			case '\0':
			   optarg[i] = 0;
			   sensor_thrf[j] = atof(p);
			   if (i+1 < sz) p = &optarg[i+1];
			   j++;
			   break;
			default:
			   break;
		      }
		   }
		   /* validate sensor threshold ordering */
		   rv = 0;
                   if ((sensor_thrf[1] > sensor_thrf[0]) ||
                       (sensor_thrf[2] > sensor_thrf[1])) { rv = 1; }
                   if ((sensor_thrf[4] < sensor_thrf[3]) ||
                       (sensor_thrf[5] < sensor_thrf[4])) { rv = 2; }
                   if (rv == 0) {
                      sensor_lo_f = sensor_thrf[0];
                      sensor_hi_f = sensor_thrf[3];
		      fsetthresh = 4;  /*indicates unique float thresholds */
                   } else {
		      printf("Threshold values: %f>=%f>=%f %f<=%f<=%f\n",
			sensor_thrf[0], sensor_thrf[1], sensor_thrf[2],
			sensor_thrf[3], sensor_thrf[4], sensor_thrf[5]);
		      printf("Invalid threshold order within -u (%s)\n",
				((rv == 1)? "lo": "hi"));
		      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 '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 'V':    /* priv level */
          case 'Y':    /* prompt for remote password */
          case 'Z':    /* set local MC address */
                parse_lan_options(c,optarg,fdebug);
                break;
          default:
             printf("Usage: %s [-abcdghilmnprstuvwxL -NUPREFTVY]\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\n");
             printf("      -c      displays a simpler, Canonical output fmt\n");
             printf("      -d file Dump SDRs to a binary file\n");
             printf("      -m002000 specific MC (bus 00,sa 20,lun 00)\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 ids\n");
             printf("      -l tval specifies the Lowest threshold to set\n");
             printf("      -n snum specifies the sensor Number to set hi/lo\n");
             printf("      -p      persist the threshold being set\n");
             printf("      -r      show Raw SDR bytes\n");
             printf("      -s      displays a Simpler output format\n");
             printf("      -t      shows Threshold values also\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\n");
	     print_lan_opt_usage();
             ret = ERR_USAGE;
	     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);
            ret = ERR_NOT_ALLOWED;
	    goto do_exit;
        }
    } 
#endif
    if (fremote) {
	/* on some systems, getting the SDR Reservation ID requires admin */
        // if ((fsetthresh != 0) || (frearm != 0)) 
	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;
#ifdef LINUX
		if (prod_id == 0x003E)   /*Urbanna NSN2U or CG2100*/
	           set_max_kcs_loops(1000); /*longer KCS timeout*/
#endif
	} 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 (vend_id == VENDOR_INTEL && !fpicmg) /* if not Intel ATCA */
		fdevsdrs = 0;   /* override & use SDR repository*/
   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 (fdump) {
      uchar *pbuf = NULL;
      FILE *fp;
      int len;
      ret = get_sdr_cache(&pbuf);
      fp = fopen(binfile,"w");
      if (fp == NULL) {
         ret = get_LastError();
         printf("Cannot open file %s, error %d\n",binfile,ret);
      } else {
         printf("Writing SDR size %d to %s  ...\n",sz_sdrs,binfile);
         len = 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);
      goto do_exit;
   } /*endif fdump*/

   if (frestore) {
      uchar sdr[MAX_BUFFER_SIZE];
      uchar *pbuf = NULL;
      FILE *fp;
      int len;
      ushort id;

#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);
	   goto do_exit;
         } 
      }
#endif
      fp = fopen(binfile,"r");
      if (fp == NULL) {
         ret = get_LastError();
         printf("Cannot open file %s, error %d\n",binfile,ret);
	 goto do_exit;
      } 
#ifndef WIN32
      {
         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);
	    goto do_exit;
         }
      }
#endif
      // len = nsdrs * SDR_SZ;  /*estimate max size for n sdrs*/
      sz_sdrs = len;
      pbuf = malloc(len);
      if (fdebug) printf("restore: malloc(%d) pbuf=%p\n",len,pbuf);
      if (pbuf == NULL) {
	  ret = -1;
	  goto do_exit;
      }
      /*ok, so proceed with restore*/
      ret = 0;
      len = 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*/
      }
      fclose(fp);
      if (fdebug) {
         printf("SDR buffer from file (len=%d,sz=%d)\n",len,sz_sdrs);
         dump_buf("SDR buffer",pbuf,len,1);
      }
      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(pbuf);
      goto do_exit;
   } /*endif frestore*/

   for (ipass = 0; ipass < npass; ipass++)
   {
     ret = GetSDRRepositoryInfo(&j);
     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_ 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;
       while (recid != 0xffff) 
       {
         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 > 0) {  /* ret is a completion code error */
                printf("%04x GetSDR error 0x%02x %s, rlen=%d\n",recid,ret,
			decode_cc((ushort)0,(uchar)ret),sz);
		if (ret == 0xC5) {  /* lost Reservation ID, retry */
                   /* This means that some other IPMI software has 
                    * requested a Reservation before we finished, so
                    * we need to refresh the Reservation ID * retry. */
                   fDoReserve = 1;  /* get a new SDR Reservation ID */
                   ret = GetSDR(recid,&recnext,sdrdata,sizeof(sdrdata),&sz);
                   if (fdebug)
                      printf("GetSDR[%04x]: ret = %x, next=%x\n",recid,ret,
				recnext);
                }
           } else printf("%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 */
         }
         else {  /* (ret == 0) OK, got full SDR */
           if (fdebug) {
                dump_buf("got SDR",sdrdata,sz,0);
           }
           if (sz < MIN_SDR_SZ) goto NextSdr;
           if (fshowgrp > 0) {
	      for (i = 0; i < fshowgrp; i++) {
		if (fdebug) printf("styp=%02x sgrp[%d]=%02x\n",
					sdrdata[12],i,sensor_grps[i]);
		if (sdrdata[12] == sensor_grps[i]) break;
	      }
	      if (i >= fshowgrp) goto NextSdr;
	   }

	   if ((sensor_num == INIT_SNUM) || (sdrdata[7] == sensor_num) 
	      || fsetthresh) {
               /* Parse and show the SDR data */
               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) {
  	             printf("%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 (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=%4.3f hif=%4.3f\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*/
	        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);
		}
	     }
	     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 (fshowidx) {
		/* if we have already read the last in the range, done. */
		if (recid >= sensor_idxN) break; // recnext = 0xffff; // break;
	 } 
         if (recnext == recid) recid = 0xffff; /*break;*/
         else recid = recnext;
       } /*end while recid*/
       if (fdoloop && (nloops > 1)) {
	 printf("\n"); /* output an empty separator line */
         os_usleep(1,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) {
      int rv;  
      /* 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 0x%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);
        }
   }

do_exit:
   // show_outcome(progname,ret); 
   // myexit(ret);
   ipmi_close_();
   return(ret);  
}

/* end isensor.c */
