/*
 * oem_intel.c
 *
 * This module handles code specific to Intel platforms, 
 * including the Intel/Kontron Telco Alarms panel.  
 * 
 * Note that the Intel BMC TAM will set these alarms
 * based on firmware-detected thresholds and events.
 * 
 * Author:  Andy Cress  arcress at users.sourceforge.net
 * Copyright (c) 2010 Kontron America, Inc.
 *
 * Compile flags for oem_intel.c:
 * NO_CMD would be defined if linking with ievents.c only (no ipmicmd code)
 * NO_EVENTS would be defined if linking with ialarms.c (no ievents code)
 *
 * 09/02/10 Andy Cress - separated from ialarms.c
 */
/*M*
Copyright (c) 2010 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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#if defined(DOS)
#include <dos.h>
#endif
#include "ipmicmd.h"
#include "oem_intel.h"
 
#ifdef METACOMMAND
#include "ievents.h"
extern int   sens_verbose;  /*isensor.c*/
extern char fsm_debug;       /*mem_if.c*/
extern int get_sensdesc(uchar sa, int snum, char *sdesc, int *pstyp, int *pidx);
extern int  get_MemDesc(int array, int dimm, char *desc, int *psz); /*mem_if.c*/
#else
static int   sens_verbose = 0; 
static char fsm_debug = 0;       /*mem_if.c*/
static int  get_MemDesc(int array, int dimm, char *desc, int *psz) { return -1;}
#if !defined(NO_CMD)
int get_sensdesc(uchar sa, int snum, char *sensdesc, int *pstyp, int *pidx)
{ return(-1); }
#endif
static char *get_sensor_type_desc(uchar stype) 
{ 
    static char tstr[12];
    sprintf(tstr,"%02x",stype);
    return(tstr); 
}
#endif
extern char  fdebug;   /*ipmicmd.c*/ 

/*
 * Global variables 
 */
static char  fRelayBits = 0;
static uchar g_bus = PUBLIC_BUS;
static uchar g_sa  = BMC_SA;
static uchar g_lun = BMC_LUN;
static uchar g_addrtype = ADDR_SMI;

#ifdef OLD
#ifdef WIN32
/* Windows tamutil is installed by the ipmirastools package from Kontron. */
static char *tam1cmd = "\"\"%ProgramFiles%\"\\Intel\\ipmirastools\\tamutil\" >NUL: 2>NUL:";
static char *tam2cmd = "\"\"%ProgramFiles%\"\\Intel\\ipmirastools\\tamutil\" |findstr TAM.Status >NUL:";
//static char * tam3cmd = "ipmiutil sensor |findstr BMC_TAM >NUL:"; 
//static char * tambcmd = "bmcTamActive"; /*old*/
#define RET_NOT_FOUND  1  /*command not found (%ERRORLEVE%=9009)*/
#else
/* Linux tamutil is installed by the ipmimisc package from Kontron. */
static char *tam1cmd = "/usr/share/ipmimisc/tamutil >/dev/null 2>&1";
static char *tam2cmd = "/usr/share/ipmimisc/tamutil 2>/dev/null |grep TAM.Status >/dev/null";
//static char * tam3cmd = "ipmiutil sensor |grep BMC_TAM  >/dev/null"; 
//static char * tambcmd = "/usr/local/tam/bin/bmcTamActive 2>/dev/null"; /*old*/
//#define RET_TAMB_ACTIVE 256   /*from bmcTamActive, if BMC TAM is enabled*/
#define RET_NOT_FOUND 32512   /*command not found ($?=127 if shell)*/ 
#endif
#endif

#ifdef NOT
#define PRIVATE_BUS_ID      0x03 // w Sahalee,  the 8574 is on Private Bus 1
#define PRIVATE_BUS_ID5     0x05 // for Intel TIGI2U
#define PRIVATE_BUS_ID7     0x07 // for Intel S5000
#define PERIPHERAL_BUS_ID   0x24 // w mBMC, the 8574 is on the Peripheral Bus
#define ALARMS_PANEL_WRITE  0x40 
#define ALARMS_PANEL_READ   0x41
#define DISK_LED_WRITE      0x44 // only used for Chesnee mBMC
#define DISK_LED_READ       0x45 // only used for Chesnee mBMC
#endif

#if defined(NO_CMD)
const char * val2str(ushort val, const struct valstr *vs)
{
        static char un_str[32];
        int i;
        for (i = 0; vs[i].str != NULL; i++) 
                if (vs[i].val == val) return vs[i].str;
        memset(un_str, 0, 32);
        snprintf(un_str, 32, "Unknown (0x%x)", val);
        return un_str;
}
#else
uchar get_nsc_diskleds(uchar busid)
{
	uchar inputData[4];
	uchar rdata[16];
	int responseLength = 4;
	uchar completionCode;
	int ret;

	inputData[0] = busid;
	inputData[1] = DISK_LED_READ;
	inputData[2] = 0x1;   // return one byte of LED data
	inputData[3] = 0x00;  // init data to zero
        ret = ipmi_cmd(MASTER_WRITE_READ, inputData, 3, rdata,
                        &responseLength, &completionCode, fdebug);
	if (ret != 0) {
           printf("get_nsc_diskleds: ret = %d, ccode %02x, leds = %02x\n",
                    ret, completionCode, rdata[0]);
	   return(0);
	   }
	return(rdata[0]);
}  /*end get_nsc_diskleds()*/

int set_nsc_diskleds(uchar val, uchar busid)
{
	uchar inputData[4];
	uchar rdata[16];
	int responseLength = 4;
	uchar completionCode;
	int ret;

	inputData[0] = busid;
	inputData[1] = DISK_LED_WRITE;
	inputData[2] = 0x01;   // len = one byte of LED data
	inputData[3] = val;    
        ret = ipmi_cmd(MASTER_WRITE_READ, inputData, 4, rdata,
                        &responseLength, &completionCode, fdebug);
	if (ret != 0) {
           printf("set_nsc_diskleds: ret = %d, ccode %02x, leds = %02x\n",
                    ret, completionCode, val);
	   return(0);
	   }
	return(ret);
}  /*end set_nsc_diskleds()*/

void show_nsc_diskleds(uchar val)
{
	if (fdebug) printf("diskled = %02x\n",val);
	printf("disk A: ");
	if ((val & 0x20) == 0) printf("present");
	else printf("not present");
	if ((val & 0x02) == 0) printf("/faulted ");
	printf("\ndisk B: ");
	if ((val & 0x10) == 0) printf("present");
	else printf("not present");
	if ((val & 0x01) == 0) printf("/faulted ");
	printf("\n");
}

uchar get_alarms_intel(uchar busid)
{
	uchar inputData[4];
	uchar rdata[16];
	int responseLength = 4;
	uchar completionCode;
	int ret;

	inputData[0] = busid;
	inputData[1] = ALARMS_PANEL_READ;
	inputData[2] = 0x1;   // return one byte of alarms data
	inputData[3] = 0x00;  // init data to zero
        ret = ipmi_cmd(MASTER_WRITE_READ, inputData, 3, rdata,
                        &responseLength, &completionCode, fdebug);
	if (ret != 0 || completionCode != 0) {
           printf("get_alarms: ret = %d, ccode %02x, alarms = %02x\n",
                    ret, completionCode, rdata[0]);
	   return(0);
	   }
	return(rdata[0]);
}  /*end get_alarms()*/

int set_alarms_intel(uchar val, uchar busid)
{
	uchar inputData[4];
	uchar rdata[16];
	int responseLength = 4;
	uchar completionCode;
	int ret;

	inputData[0] = busid; 
	inputData[1] = ALARMS_PANEL_WRITE;
	inputData[2] = 0x1;   // one byte of alarms data
	inputData[3] = val;  
        ret = ipmi_cmd(MASTER_WRITE_READ, inputData, 4, rdata,
                        &responseLength, &completionCode, fdebug);
	if (ret != 0) {
           printf("set_alarms: ret = %d, ccode %02x, value = %02x\n",
                    ret, completionCode, val);
	   return(ret);
	   }
	if (completionCode != 0) ret = completionCode;
	return(ret);
}  /*end set_alarms()*/

/* 
 * show_alarms
 *
 * The alarm control/status byte is decoded as follows:
 * bit
 * 7 = reserved, always write 1
 * 6 = LED colors, 1 = amber (default), 0 = red
 *     Colors were added in some later firmware versions, but
 *     not for all platforms.
 * 5 = Minor Relay bit, 0 = on, 1 = off, always write 1
 * 4 = Major Relay bit, 0 = on, 1 = off, always write 1
 * 3 = Minor LED bit, 0 = on, 1 = off
 * 2 = Major LED bit, 0 = on, 1 = off
 * 1 = Critical LED bit, 0 = on, 1 = off
 * 0 = Power LED bit, 0 = on, 1 = off
 *
 * Note that the Power LED is also wired to the System Fault LED 
 * in the back of the system, so this state may be off for Power,
 * but the LED could be lit for a System Fault reason instead.
 */
void show_alarms_intel(uchar val)
{
   char *scrit = "ON ";
   char *smaj = "ON ";
   char *smin = "ON ";
   char *spow = "ON ";
   char *rmaj = "ON";
   char *rmin = "ON";
   if (fdebug) printf("alarms = %02x\n",val);
    
   if (val & 0x01) spow = "off";
   if (val & 0x02) scrit = "off";
   if (val & 0x04) smaj = "off";
   if (val & 0x08) smin = "off";
   printf("Alarm LEDs:   critical = %s major = %s minor = %s power = %s\n",
           scrit,smaj,smin,spow);
   if (fRelayBits == 1) {  /*CG2100 platforms have Relay bits reversed*/
      if (val & 0x10) rmin = "off ";
      if (val & 0x20) rmaj = "off ";
   } else {
      if (val & 0x10) rmaj = "off ";
      if (val & 0x20) rmin = "off ";
   }
   printf("Alarm Relays: major = %s minor = %s\n", rmaj, rmin);
}

/*
 * get_led_status_intel
 * uses Intel OEM command to get the status of the ID LED.
 * if success, rv=0, pstate:  0=off, 1=on, 2=blinking
 */
int get_led_status_intel(uchar *pstate)
{
   uchar rdata[64];
   int rv, rlen;
   uchar cc, b_leds, bstate;

   /* This command is only supported on Intel S5000 motherboards */
   rlen = sizeof(rdata);
   rv = ipmi_cmdraw(0x40,0x32, g_sa,g_bus,g_lun,
                        NULL, 0, rdata, &rlen, &cc, fdebug);
   if (fdebug) printf("get_led_status_intel: rv = %d, cc=%02x\n", rv,cc);
   if (rv == 0 && cc != 0) rv = cc;
   if (rv == 0) {
	b_leds = rdata[0];
	bstate = 0;   /*off*/
	if (b_leds & 0x80) { bstate = 1;  /*on*/ }
	else if (b_leds & 0x40) { bstate = 2;  /*blink*/ }
	if (pstate != NULL) *pstate = bstate;
   }
   return(rv);
}


int detect_capab_intel(int vend_id,int prod_id, int *cap, int *ndisk,char fdbg)
{
     int busid = PRIVATE_BUS_ID;
     int f = 0;
     char fbmctam = 0;
     char fHasAlarms = 0;
     char fHasEnc = 0; 
     char fpicmg = 0;
     char fChesnee = 0;
     int styp, idx, rv;
     char desc[20];

     fdebug = fdbg;
     if (vend_id == VENDOR_NSC)  { /*NSC mBMC, Chesnee*/
	  busid = PERIPHERAL_BUS_ID;
          fHasAlarms = 1;
          fChesnee = 1;
     } else if (vend_id == VENDOR_INTEL) { /*Intel BMC*/
          switch (prod_id) {
             case 0x0022:   
		busid = PRIVATE_BUS_ID5;  /* Intel TIGI2U */
		fbmctam = 1;  /* Intel TIGI2U may have bmc tam */
                fHasAlarms = 1;
                fHasEnc = 2;
                break;
             case 0x001B:   
	        busid = PRIVATE_BUS_ID;
		fbmctam = 1;  /* Intel TIGPR2U may have bmc tam */
                fHasAlarms = 1;
                break;
             case 0x0808:   
             case 0x0841:   
	        fpicmg = 1;   /* Intel ATCA platform, supports PICMG */
                fHasAlarms = 1;
                break;
             case 0x4311:  
	        busid = PERIPHERAL_BUS_ID; /* SJR2 (NSI2U) mBMC */
                break;
             case 0x0026:     /*BridgePort*/
             case 0x0028:     /*S5000PAL*/
	     case 0x0029:     /*S5000PSL*/
             case 0x0811:     /*S5000PHB*/   
		busid = PRIVATE_BUS_ID7;  /* Intel Harbision (TIGW1U/NSW1U) */
		/* Check for SAS Drv Pres sensor on HSC, if TIGW1U */
		rv = get_sensdesc(0xC0,0x09,desc,&styp, &idx);
		if (fdebug) printf("get_sensdesc rv = %d\n",rv);
		if (rv == ERR_NOT_FOUND) { /* NSW1U does not have alarm panel*/
		   fHasAlarms = 0;
                   fHasEnc = 0;
		} else {   /* has HSC, like TIGW1U */
		   fbmctam = 1;  /* TIGW1U may have bmc tam */
                   fHasAlarms = 1;
		   if (prod_id == 0x0811) fHasEnc = 3;
                   else fHasEnc = 6;
		} 
                break;
             case 0x003E:     /*S5520UR*/
	        busid = PRIVATE_BUS_ID;
		fbmctam = 1;  /* CG2100 has bmc tam */
                fHasAlarms = 1;
                fHasEnc = 8; /* CG2100 has 8 disks */
		fRelayBits = 1;
	        set_max_kcs_loops(URNLOOPS); /*longer for cmds (default 300)*/
                break;
	     case 0x005D:      /* Copper Pass, CG2200*/
	        busid = PRIVATE_BUS_ID;
		fbmctam = 1;  /* CG2200 has bmc tam */
                fHasAlarms = 1;
		fRelayBits = 1;
                fHasEnc = 6; /* 6 disks */
	        set_max_kcs_loops(URNLOOPS); /*longer for cmds (default 300)*/
                break;
             case 0x0048:  /* "(S1200BT)"  *BearTooth Pass*/
             case 0x004A:  /* "(S2600CP)"  *Canoe Pass*/
	     case 0x0051:  /*  Eagle Pass */
	     case 0x0055:  /*  Iron Pass */
	     case 0x005C:  /*  Lizard Head Pass */
                fHasEnc = 8;
	        set_max_kcs_loops(URNLOOPS); /*longer for cmds (default 300)*/
                break;
             default:
	        busid = PRIVATE_BUS_ID;
                fHasEnc = 8;
                break;
          }
   } 
   if (fHasAlarms) f |= HAS_ALARMS_MASK;
   if (fbmctam)    f |= HAS_BMCTAM_MASK;
   if (fHasEnc > 0) {
		   f |= HAS_ENCL_MASK;
		   if (ndisk != NULL) *ndisk = fHasEnc;
   }
   if (fpicmg)     f |= HAS_PICMG_MASK;
   if (fChesnee)   f |= HAS_NSC_MASK;
   if (is_romley(vend_id,prod_id)) {
	if (prod_id == 0x005D) fHasEnc = 6;   /*CG2200*/
	fHasEnc = 8;
        set_max_kcs_loops(URNLOOPS); /*longer for cmds (default 300)*/
	f |= HAS_ROMLEY_MASK;
   }
   *cap = f;
   return(busid); 
}

int check_bmctam_intel(void)
{
   int ret, rlen;
   uchar rdata[16];
   uchar cc;

   /* Check if BMC TAM is enabled */
   rlen = sizeof(rdata);
   ret = ipmi_cmdraw(0x00, 0x36, g_sa,g_bus,g_lun,
			NULL, 0, rdata, &rlen, &cc, fdebug);
   if ((ret == 0) && (cc == 0)) {
	printf("Warning: BMC TAM is active and managing the LEDs.\n"
		"Use tamutil (from ipmimisc rpm) to set alarms instead.\n");
	return(LAN_ERR_ABORT);
   } else return(0);
#ifdef OLD
   ret = system(tam1cmd);
   if (fdebug) printf("%s ret = %d\n",tam1cmd,ret);
   if (ret == RET_NOT_FOUND) { /*command not found, no such file*/
	      /* Could also do "ipmiutil sensor |grep BMC_TAM" (tam3cmd),
	       * but this would take a while to complete. */
	      printf("Warning: BMC TAM may be active and managing the LEDs.\n"
		  "If so, use tamutil to set the alarm LEDs instead.\n");
   } else if (ret == 0) {  
	      /*the command was found, check if BMC TAM enabled*/
	      ret = system(tam2cmd);
	      if (fdebug) printf("%s ret = %d\n",tam2cmd,ret);
	      if (ret == 0) {
	         /*If so, print warning, use Intel tamutil instead.*/
	         printf("Warning: BMC TAM is active and managing the LEDs.\n"
		   "Use tamutil or the Intel TAM API to set alarms instead.\n"
		   "Aborting.\n");
	         return(LAN_ERR_ABORT);
	      }
   } 
   /* else tamutil was there but did not show BMC TAM active, so
    *	assume BMC TAM is not active and do nothing. */
    return(ret);
#endif
}

int soft_reset_intel(uchar func)
{
   int ret, rlen;
   uchar idata[16];
   uchar rdata[16];
   uchar cc;

   /* Do an Intel S5000 soft reset, via an OS bridge agent (ipmiutil_asy) */
   rlen = sizeof(rdata);
   idata[0] = func;  /* 0=read, 1=shutdown, 2=reset */
   ret = ipmi_cmdraw(0x70, 0x30, g_sa,g_bus,g_lun,
			idata, 1, rdata, &rlen, &cc, fdebug);
   if ((ret == 0) && (cc != 0)) ret = cc;
   return(ret);
}

int lan_failover_intel(uchar func, uchar *mode)
{
   int ret, rlen;
   uchar idata[16];
   uchar rdata[16];
   uchar cc;

   /* Do an Intel S2600 LAN Failover command, where func is: 
    *  0x00=disable,
    *  0x01=enable with leash monitor, 
    *  0x02=enable with ARP monitor if blade,
    *  0xFF=no set, just get current mode
    */
   rlen = sizeof(rdata);
   idata[0] = func;  /* 0=read, 1=shutdown, 2=reset */
   ret = ipmi_cmdraw(0x40, 0x3E, g_sa,g_bus,g_lun,
			idata, 1, rdata, &rlen, &cc, fdebug);
   if ((ret == 0) && (cc != 0)) ret = cc;
   if (ret == 0 && mode != NULL) *mode = rdata[0];
   return(ret);
}

int get_power_restore_delay_intel(int *delay)
{
   int ret, rlen;
   uchar idata[16];
   uchar rdata[16];
   uchar cc;

   rlen = sizeof(rdata);
   ret = ipmi_cmdraw(0x55, 0x30, g_sa,g_bus,g_lun,
			idata, 0, rdata, &rlen, &cc, fdebug);
   if ((ret == 0) && (cc != 0)) ret = cc;
   if (ret == 0 && delay != NULL) 
	 *delay = ( rdata[1] + ((rdata[0] & 0x07) << 8) );
   return(ret);
}

/* end-else NO_CMD not defined */
#endif

#define NTAMSEV  8
static char *tam_sev[] = {
/*0*/ "OFF",
/*1*/ "MNR",
/*2*/ "MNR+P",
/*3*/ "MJR",
/*4*/ "MJR+P",
/*5*/ "CRT",
/*6*/ "CRT+P",
/*7*/ "UNK"
};

int decode_sensor_intel_nm(uchar *sdr,uchar *reading,char *pstring, int slen)
{
   int rv = -1;
   uchar stype;
   char *pstr = NULL;
   char mystr[60];
   uchar nm_sa, chan, lun;

   if (sdr == NULL) return(rv);
   if (pstring == NULL || slen == 0) return(rv);
   switch(sdr[3]) {  /*SDR type*/
	case 0xC0:  /*OEM sensor*/
	   if (sdr[8] == 0x0D) {   /* OEM NM Reference SDR, no reading */
		nm_sa = sdr[10];
		chan = (sdr[11] & 0xf0) >> 4;
		lun = (sdr[11] & 0x0f);
		/* show NM location and sensor numbers for NM sensors */
		sprintf(mystr,"NM(%x,%x,%x) health=%x excep=%x capab=%x thresh=%x",
			chan,nm_sa,lun, sdr[12],sdr[13],sdr[14],sdr[15]);
		strncpy(pstring, mystr, slen);
		if ((int)strlen(mystr) > slen) pstring[slen-1] = 0; /*string*/
		rv = 0;
	   }
	   break;
	case 0x02:	/*compact sensor*/
	   if (reading == NULL) return(rv);
	   stype = sdr[12];  /*sensor type*/
	   if (stype == 0xDC) { /* NM Capabilities sensor, usu snum 0x1a (26.)*/
		mystr[0] = 0;
		if (reading[2] == 0x00) strcat(mystr,"None"); 
		else {  
		   if (reading[2] & 0x01) strcat(mystr,"Policy "); 
		   if (reading[2] & 0x02) strcat(mystr,"Monitor "); 
		   if (reading[2] & 0x04) strcat(mystr,"Power "); 
		}
		strncpy(pstring, mystr, slen);
		if ((int)strlen(mystr) > slen) pstring[slen-1] = 0; /*string*/
		rv = 0;
	   }
	   break;
	default:
	   break;
   }
   return(rv);
}

static void show_oem_hex(uchar *sdr, int slen)
{
   int i;
   for (i = 8; i < slen; i++)
	printf("%02x ",sdr[i]);
   printf("\n");
}

void show_oemsdr_nm(uchar *sdr)
{
    int rv, len;
    char mystr[60];

    /* vendor id has already been shown */
    len = sdr[4] + 5;
    rv = decode_sensor_intel_nm(sdr,NULL,mystr,sizeof(mystr));
    if (rv == 0) printf("%s\n",mystr);
    else show_oem_hex(sdr, len);
    return;
}

/* 
 * show_oemsdr_intel
 */
void show_oemsdr_intel(uchar *sdr)
{
    uchar idx, len, c, i, n, j, k, t;
    int vend;

    len = sdr[4] + 5;
    /*double-check that this is an Intel OEM SDR*/
    vend = sdr[5] | (sdr[6] << 8) | (sdr[7] << 16);
    if (vend != VENDOR_INTEL) {
	if (fdebug) printf("show_oemsdr_intel: vendor %x != %x (Intel)\n",
				vend,VENDOR_INTEL);
	return;
    }
    printf("Intel: "); 
    switch(sdr[8]) {  /*OEM subtype*/
    case 0x53:   /* SDR version subtype (has ASCII) */
       for (i = 8; i < len; i++) {
           c = sdr[i];
           if (c < 0x20 || c > 0x7f) printf("[%02x]",c);
           else printf("%c",c);
       }
       printf("\n");
       break;
    case 0x60:  /* BMC TAM subtype */
       idx = (sdr[10] & 0xf0) >> 4;
       n = (sdr[10] & 0x0f) + 1;  /*number of TAM records*/
       printf("BMC_TAM%d ",idx);
       for (i = 8; i < len; i++) 
   	printf("%02x ",sdr[i]);
       if (idx == 0) {
           printf(" nrec=%d cfg=%02x",n,sdr[11]);
       }
       printf("\n");
       if (fdebug || sens_verbose) {
         /* show decoded BMC_TAM rules */
         if (idx > 0) {
           uchar map, off, sev, sa;
           const char *tstr;
           sa = sdr[12];
           for (i = 13; i < len; ) {
              k = (sdr[i] & 0xf0) >> 4;
              t = sdr[i+1];
	      tstr = get_sensor_type_desc(t); 
              printf("\tBMC_TAM%d sa=%02x %s (",idx,sa,tstr);
              for (j = 0; j < k; j++) {
                 map = sdr[i+3+j];
                 off = (map & 0xf0) >> 4;
                 sev = map & 0x0f;
                 if (sev >= NTAMSEV) sev = NTAMSEV - 1;
                 printf("%d=%s ",off,tam_sev[sev]);
              }
              printf(")\n");
              i += 3 + k;
           }
         }
       }
       break;
    case 0x0C: /* Fan Speed Control */
       printf("FanCtl ");
       show_oem_hex(sdr, len);
       break;
    case 0x0D: /* ME NM Reference SDR */
       show_oemsdr_nm(sdr);
       break;
    case 0x02: /*S5500 Power Unit Redundancy subtype*/
    case 0x05: /*S5500 Fan Redundancy subtype*/
    case 0x06: /*S5000 System Information/Capab */
    case 0x09: /*S5500 Voltage sensor scaling*/
    case 0x0A: /*S5500 Fan sensor scaling*/
    case 0x0B: /*S5500 Thermal Profile data*/
    default:   /* other subtypes 07,0e,15 etc. */
       show_oem_hex(sdr, len);
       break;
    } /*end switch*/
} /*end show_oemsdr_intel*/

#ifdef NO_EVENTS
/* if not linking with ievents.c, need to skip decode_sel_intel because it 
 * would have unresolved externals for fmt_time, get_sev_str, get_sensor_tag */
#else
/* 
 * decode_sel_intel
 * inputs:
 *    evt    = the 16-byte IPMI SEL event
 *    outbuf = points to the output string buffer
 *    outsz  = size of the output buffer
 * outputs: 
 *    rv     = 0 if this event was successfully interpreted here,
 *             non-zero otherwise, to use default interpretations.
 *    outbuf = will contain the interpreted event text string (if rv==0)
 */
int decode_sel_intel(uchar *evt, char *outbuf, int outsz, char fdesc,
			char fdebug)
{
   int rv = -1;
   ushort id;
   uchar rectype;
   ulong timestamp;
   uchar sdr[MAX_BUFFER_SIZE];
   char sensdesc[36];
   char timestr[40]; 
   char mybuf[64]; 
   char oembuf[64]; 
   char *type_str = NULL;
   char *gstr = NULL;
   char *pstr = NULL;
   int sevid;
   ushort genid;
   uchar snum;
   int isdr = 0;
   char *p1;
   int d, f;
         
   sevid = SEV_INFO;
   id = evt[0] + (evt[1] << 8);
   rectype = evt[2];
   snum = evt[11];
   timestamp = evt[3] + (evt[4] << 8) + (evt[5] << 16) + (evt[6] << 24);
   genid = evt[7] | (evt[8] << 8);
   gstr = "BMC ";
   if (genid == 0x0033) gstr = "Bios";
   if (rectype == 0x00) {
      if (snum == 0x0A && evt[12] == 0x03) rectype = 0x02; /*internal wdog*/
   } 
   if (rectype == 0x02) {
      type_str = "";
      sprintf(mybuf,"%02x [%02x %02x %02x]", evt[12],evt[13],evt[14],evt[15]);
      pstr = ""; /*default*/
      switch(evt[10]) {  /*sensor type*/
	case 0x00:	/* type undefined */
	   // type_str = get_sensor_type_desc(0x28);
	   type_str = "Management Subsystem Health";
           if (snum == 0x0A && evt[12] == 0x03) {  /*internal wd event*/
		pstr = "BMC wd restart";
		sevid = SEV_CRIT; 
		rv = 0;
	   }
	   break;
	case 0x13:	/* Critical Interrupt */
	   type_str = "Critical Interrupt";
	   if (evt[12] == 0x70) { /*event trigger/type = OEM AER events */
	      pstr = &oembuf[0];
	      switch(evt[13]) {  /*data1/offset*/
              case 0xA0: p1 = "PCIe Fatal Data Link Protocol Error"; break;
              case 0xA1: p1 = "PCIe Fatal Surprise Link Down"; break;
              case 0xA2: p1 = "PCIe Fatal Unexpected Completion"; break;
              case 0xA3: p1 = "PCIe Fatal Unsupported Request"; break;
              case 0xA4: p1 = "PCIe Fatal Poisoned TLP"; break;
              case 0xA5: p1 = "PCIe Fatal Flow Control Protocol"; break;
              case 0xA6: p1 = "PCIe Fatal Completion Timeout"; break;
              case 0xA7: p1 = "PCIe Fatal Completer Abort"; break;
              case 0xA8: p1 = "PCIe Fatal Recv Buffer Overflow"; break;
              case 0xA9: p1 = "PCIe Fatal ACS Violation"; break;
              case 0xAA: p1 = "PCIe Fatal Malformed TLP"; break;
              case 0xAB: p1 = "PCIe Fatal Recvd Fatal Message"; break;
              case 0xAC: p1 = "PCIe Fatal Unexpected Completion Error"; break;
              case 0xAD: p1 = "PCIe Fatal Recvd Warning Message"; break;
              default:   p1 = "PCIe Fatal Other AER"; break;
              }
	      rv = 0;  
	      sevid = SEV_CRIT;
	      /* also include the bus dev/func bytes (as shown by lspci) */
	      d = (evt[15] & 0xF8) >> 3;
	      f = (evt[15] & 0x07);
              snprintf(oembuf,sizeof(oembuf),"%s (%02x:%02x.%d)",
			p1,evt[14],d,f);
	   }
	   if (evt[12] == 0x71) { /*event trigger/type = OEM AER warnings */
	      pstr = &oembuf[0];
	      switch(evt[13]) {  /*data1/offset*/
              case 0xA0: p1 = "PCIe Warn Receiver Error"; break;
              case 0xA1: p1 = "PCIe Warn Bad DLLP"; break;
              case 0xA2: p1 = "PCIe Warn Bad TLLP"; break;
              case 0xA3: p1 = "PCIe Warn Replay Num Rollover"; break;
              case 0xA4: p1 = "PCIe Warn Replay Timeout"; break;
              case 0xA5: p1 = "PCIe Warn Advisory Non-Fatal"; break;
              case 0xA6: p1 = "PCIe Warn Link BW Changed"; break;
              default:   p1 = "PCIe Warn Other AER"; break;
	      }
	      rv = 0;  
	      sevid = SEV_MAJ;
	      /* also include the bus dev/func bytes (as shown by lspci) */
	      d = (evt[15] & 0xF8) >> 3;
	      f = (evt[15] & 0x07);
              snprintf(oembuf,sizeof(oembuf),"%s (%02x:%02x.%d)",
			p1,evt[14],d,f);
	   }
           break;
	case 0x2B:	/* Version Change */
	   type_str = "Version Change";
	   if (evt[12] == 0x70) { /*event trigger/type */
	      switch(evt[13]) {  /*data1/offset*/
              case 0x00: pstr = "Update started"; break;
              case 0x01: pstr = "Update completed"; break;
              case 0x02: pstr = "Update failed"; sevid = SEV_MIN; break;
	      default:   pstr = "-"; break;
	      }
	      rv = 0;
	   }
           break;
	default:  break;
      }
      if (rv == 0) {
	 sensdesc[0] = 0;
	 if (fdesc) {
           rv = get_sensor_tag(isdr,genid,snum,sensdesc,sdr,sizeof(sdr));
	   if (fdebug) printf("get_sensor_tag(%x) rv = %d\n",snum,rv);
	   if (rv != 0) rv = 0;
	 }
         fmt_time(timestamp, timestr, sizeof(timestr));
         snprintf(outbuf,outsz, "%04x %s %s %s %s #%02x %s%s %s\n",
                        id,timestr,get_sev_str(sevid), gstr, type_str, 
                        snum, sensdesc, /*sensor number, desc */
                        pstr,mybuf);    /*evt description*/
      }
   } 
   return rv;
}  /*end decode_sel_intel*/
#endif

const struct valstr intel_mem_s2600[] = {  
 {  0, "DIMM_A1" },
 {  1, "DIMM_A2" },
 {  2, "DIMM_A3" },
 {  3, "DIMM_B1" },
 {  4, "DIMM_B2" },
 {  5, "DIMM_B3" },
 {  6, "DIMM_C1" },
 {  7, "DIMM_C2" },
 {  8, "DIMM_C3" },
 {  9, "DIMM_D1" },
 { 10, "DIMM_D2" },
 { 11, "DIMM_D3" },
 { 12, "DIMM_E1" },
 { 13, "DIMM_E2" },
 { 14, "DIMM_E3" },
 { 15, "DIMM_F1" },
 { 16, "DIMM_F2" },
 { 17, "DIMM_F3" },
 { 18, "DIMM_G1" },
 { 19, "DIMM_G2" },
 { 20, "DIMM_G3" },
 { 21, "DIMM_H1" },
 { 22, "DIMM_H2" },
 { 23, "DIMM_H3" },
 { 24, "DIMM_I1" },
 { 25, "DIMM_I2" },
 { 26, "DIMM_I3" },
 { 27, "DIMM_J1" },
 { 28, "DIMM_J2" },
 { 29, "DIMM_J3" },
 { 30, "DIMM_K1" },
 { 31, "DIMM_K2" },
 { 32, "DIMM_K3" },
 { 33 , NULL }  /*end of list*/
};

const struct valstr intel_mem_s5520ur[] = {  
 {  0, "DIMM_A1" },
 {  1, "DIMM_A2" },
 {  2, "DIMM_B1" },
 {  3, "DIMM_B2" },
 {  4, "DIMM_C1" },
 {  5, "DIMM_C2" },
 {  6, "DIMM_D1" },
 {  7, "DIMM_D2" },
 {  8, "DIMM_E1" },
 {  9, "DIMM_E2" },
 { 10, "DIMM_F1" },
 { 11, "DIMM_F2" },
 { 12 , NULL }  /*end of list*/
};
const struct valstr intel_mem_s5000phb[] = {  
 { 0, "DIMM_A1" },
 { 1, "DIMM_A2" },
 { 2, "DIMM_A3" },
 { 3, "DIMM_B1" },
 { 4, "DIMM_B2" },
 { 5, "DIMM_B3" },
 { 6 , NULL }  /*end of list*/
};
const struct valstr intel_mem_s5000pal[] = {  
 { 0, "DIMM_A1" },
 { 1, "DIMM_A2" },
 { 2, "DIMM_B1" },
 { 3, "DIMM_B2" },
 { 4, "DIMM_C1" },
 { 5, "DIMM_C2" },
 { 6, "DIMM_D1" },
 { 7, "DIMM_D2" },
 { 8 , NULL }  /*end of list*/
};
const struct valstr intel_mem_tigi2u[] = {  
 { 0, "DIMM_1B" },
 { 1, "DIMM_1A" },
 { 2, "DIMM_2B" },
 { 3, "DIMM_2A" },
 { 4, "DIMM_3B" },
 { 5, "DIMM_3A" },
 { 6 , NULL }  /*end of list*/
};

#define LIDS   8
ushort lan2i_ids[LIDS] = {  /*Intel prod_ids that use lan2i, rest use lan2*/
                  0x0000, /*unititialized */
                  0x0022, /*TIGI2U */
                  0x0026, /*Bridgeport */
                  0x0028, /*S5000PAL, Alcolu*/
                  0x0029, /*S5000PSL, StarLake*/
                  0x002B, /*S5000VSA */
                  0x002D, /*ClearBay*/
                  0x0811  /*S5000PHB, TIGW1U*/ };

#define RIDS   21 /* Intel Romley product ids: */
struct { ushort id; char *desc; } romleys[RIDS] = {
 { 0x0048, "S1200BT" },  /* S1200BT, BearTooth Pass  */
 { 0x0049, "S2600GL" },  /* S2600GL, S2600GZ   */
 { 0x004A, "S2600CP" },  /* S2600CP, Canoe Pass  */
 { 0x004D, "S2600JF" },  /* S2600JF, Jefferson Pass, Appro 512X */
 { 0x004E, "S2600WP" },  /* S2600WP  */
 { 0x004F, "S2400SC" },  /* S2400SC  */
 { 0x0050, "S2400LP" },  /* S2400LP   */
 { 0x0051, "S2400EP" },  /* S2400EP, Eagle Pass */
 { 0x0052, "S1400FP" },  /* S1400FP   */
 { 0x0053, "S1400SP" },  /* S1400SP   */
 { 0x0054, "S2600KI" },  /* S2600KI   */
 { 0x0055, "S2600IP" },  /* S2600IP, Iron Pass   */
 { 0x0056, "W2600CR" },  /* W2600CR   */
 { 0x0057, "S2400GP" },  /* S2400GP   */
 { 0x0058, "Badger Pass" },  /* Badger Pass  */
 { 0x0059, "S2400BB" },  /* S2400BB   */
 { 0x005A, "Taylor Pass" },  /* Taylor Pass  */
 { 0x005B, "S1600JP" },  /* S1600JP   */
 { 0x005C, "S4600LH" },  /* S4600LH, Lizard Head Pass  */
 { 0x005D, "CG2200" },   /* S2600CO, Copper Pass, Kontron CG2200  */
 { 0x005E, "Big Ridge"} /* Big Ridge */
};

#define TIDS   5
ushort thurley_ids[TIDS] = { /* Intel Thurley product ids: */
  0x003A,   /* Snow Hill */
  0x003B,   /* Shoffner */
  0x003D,   /* Melstone */
  0x003E,   /* S5520UR, S5500WB, Kontron CG2100, Penguin Computing Relion 700 */
  0x0040 }; /* Stoutland, Quanta QSSC-S4R/Appro GB812X-CN (Nehalem-EX) */

int is_romley(int vend, int prod) 
{
  int ret = 0;
  int i;
  if (vend != VENDOR_INTEL) return(ret);
  for (i = 0; i < RIDS; i++) 
     if ((ushort)prod == romleys[i].id) { ret = 1; break; }
  return(ret);
}

int intel_romley_desc(int vend, int prod, char **pdesc) 
{
  int ret = -1;
  int i;
  if (vend != VENDOR_INTEL) return(ret);
  if (pdesc == NULL) return(ret);
  for (i = 0; i < RIDS; i++) {
     if ((ushort)prod == romleys[i].id) { 
	*pdesc = romleys[i].desc;
	ret = 0; 
	break; 
     }
  }
  return(ret);
}

int is_thurley(int vend, int prod) 
{
  int ret = 0;
  int i;
  if (vend != VENDOR_INTEL) return(ret);
  for (i = 0; i < TIDS; i++) 
     if ((ushort)prod == thurley_ids[i]) { ret = 1; break; }
  return(ret);
}

int is_lan2intel(int vend, int prod) 
{
  int ret = 0;
  int i; 
  if (vend != VENDOR_INTEL) return(ret);
  if (is_thurley(vend,prod) || is_romley(vend,prod)) 
    ret = 0; /*iBMC does not use lan2i*/
  else {
    for (i = 0; i < LIDS; i++) 
       if ((ushort)prod == lan2i_ids[i]) { ret = 1; break; }
  }
  return(ret);
}

int decode_mem_intel(int prod, uchar b2, uchar b3, int array, int dimm, 
		     char *desc, int *psz)
{
   const char *pstr = NULL;
   int node, chan, sock, n;
   int rv = -1;
   uchar bdata;
   int vend;
   int iBMC = 0;
			      
   if ((desc == NULL) || (psz == NULL)) return -1;

   vend = VENDOR_INTEL;
   if (is_thurley(vend,prod)) iBMC = 1;
   if (is_romley(vend,prod))  iBMC = 2;
   if (iBMC != 0) {
      /* custom DIMM decoding for iBMC on Intel S5500 and S2600 */
      // rank = (b2 & 0x03); /* psel->event_data2 & 0x03; */
      node = (b3 & 0xE0) >> 5; /*socket*/
      chan = (b3 & 0x18) >> 3;
      sock = (b3 & 0x07);
      array = 0; /* is 0 for Thurley & Romley currently, else see b2. */
      if (iBMC == 1) dimm = (node * 6) + (chan * 2) + sock;
      else           dimm = (node * 12) + (chan * 3) + sock;
      if (fdebug) printf("iBMC DIMM (%d,%d,%d) = idx %d\n",
			  node,chan,sock,dimm);
   } else {  /* use straight DIMM index */
      if (prod == 0x4311) bdata = b2;  /*mini-BMC*/
      else if (b3 == 0xff) bdata = b2;  /*ff is reserved*/
      else  bdata = b3; /* normal case */
      /* (data3 & 0xc0) = SMBIOS type 16 mem array */
      array = (bdata & 0xc0) >> 6;
      /* (data3 & 0x3f) = SMBIOS type 17 dimm index */
      dimm = bdata & 0x3f;
   }
			
   if (! is_remote()) {
      fsm_debug = fdebug;
      rv = get_MemDesc(array,dimm,desc,psz);
      /* if (rv != 0) desc has "DIMM[%d}" */ 
   } 
   if (rv != 0) {  
      /* either remote, or get_MemDesc failed, use common product defaults*/
      switch(prod) {
      case 0x0811:  /*S5000PHB*/
	pstr = val2str(dimm,intel_mem_s5000phb);
	break;
      case 0x0028:  /*S5000PAL*/
	pstr = val2str(dimm,intel_mem_s5000pal);
	break;
      case 0x0022:  /*TIGI2U*/
	pstr = val2str(dimm,intel_mem_tigi2u);
	break;
      default:
        if (iBMC == 1)      pstr = val2str(dimm,intel_mem_s5520ur); 
        else if (iBMC == 2) pstr = val2str(dimm,intel_mem_s2600); 
	else  rv = -2;  /*do not guess, use raw index below*/
	break;
      }
      if (pstr != NULL) rv = 0;
      if (rv == 0) {
        /* These strings are usually 7 chars, desc is 80 chars */
        n = strlen(pstr);
        strncpy(desc, pstr, n+1);
      } else {
	sprintf(desc,"DIMM[%d]",dimm);
        n = strlen(desc);
      } 
      *psz = n;
   }
   return(rv);
} /*end decode_mem_intel*/

/*
 * decode_sensor_intel
 * inputs:
 *    sdr     = the SDR buffer
 *    reading = the 3 or 4 bytes of data from GetSensorReading
 *    pstring = points to the output string buffer
 *    slen    = size of the output buffer
 * outputs:
 *    rv     = 0 if this sensor was successfully interpreted here,
 *             non-zero otherwise, to use default interpretations.
 *    pstring = contains the sensor reading interpretation string (if rv==0)
 */
int decode_sensor_intel(uchar *sdr,uchar *reading,char *pstring, int slen)
{
   int rv = -1;
   uchar stype;
   char *pstr = NULL;

   if (sdr == NULL || reading == NULL) return(rv);
   if (pstring == NULL || slen == 0) return(rv);
   if (sdr[3] == 0x02) {  /*Compact SDR*/
     stype = sdr[12];
     switch(stype) {
	case 0xC0:	/* SMI State, NMI State */
	case 0xC7:	/* FanBoost */
	case 0xCC:	/* Debug Info */
	case 0xD8:	/* BIST */
	case 0xF0:	/* ATCA HotSwap, TODO: refine this */
	case 0xF3:	/* SMI Timeout, etc. */
	case 0xF6:	/* Sensor Failure */
	case 0xF7:	/* FSB Mismatch */
	   if (reading[2] & 0x01) pstr = "Asserted"; /*Asserted, error*/
           else pstr = "OK";               /*deasserted*/
	   strncpy(pstring, pstr, slen);
	   rv = 0;
	   break;
	case 0xDC:	/* NM Capabilities sensor */
	   rv = decode_sensor_intel_nm(sdr,reading,pstring,slen);
	   break;
	default:
	   break;
     }
   } else if (sdr[3] == 0xC0) {  /*OEM SDR*/
	   rv = decode_sensor_intel_nm(sdr,reading,pstring,slen);
   }
   return(rv);
}

const struct valstr intel_s5000_post[] = {  /*from S5000 TPS*/
 { 0x004C, "Keyboard/interface error" },
 { 0x0012, "CMOS date/time not set" },
 { 0x0048, "Password check failed" },
 { 0x0141, "PCI resource conflict" },
 { 0x0146, "Insufficient memory to shadow PCI ROM" },
 { 0x0192, "L3 cache size mismatch" },
 { 0x0194, "CPUID, processor family are different" },
 { 0x0195, "Front side bus mismatch" },
 { 0x0197, "Processor speeds mismatched" },
 { 0x5220, "Configuration cleared by jumper" },
 { 0x5221, "Passwords cleared by jumper" },
 { 0x5223, "Configuration default loaded" },
 { 0x8110, "Proc1 internal error (IERR) on last boot" },
 { 0x8111, "Proc2 internal error (IERR) on last boot" },
 { 0x8120, "Proc1 thermal trip error on last boot" },
 { 0x8121, "Proc2 thermal trip error on last boot" },
 { 0x8130, "Proc1 disabled" },
 { 0x8131, "Proc2 disabled" },
 { 0x8160, "Proc1 unable to apply BIOS update" },
 { 0x8161, "Proc2 unable to apply BIOS update" },
 { 0x8170, "Proc1 failed Self Test (BIST)" },
 { 0x8171, "Proc2 failed Self Test (BIST)" },
 { 0x8180, "Proc1 BIOS does not support current CPU stepping" },
 { 0x8181, "Proc2 BIOS does not support current CPU stepping" },
 { 0x8190, "Watchdog timer failed on last boot" },
 { 0x8198, "OS boot watchdog timer expired on last boot" },
 { 0x8300, "Baseboard management controller failed self-test" },
 { 0x8306, "Front panel controller locked" },
 { 0x8305, "Hot swap controller failed" },
 { 0x84F2, "Baseboard management controller failed to respond" },
 { 0x84F3, "Baseboard management controller in update mode" },
 { 0x84F4, "Sensor data record empty" },
 { 0x84FF, "System event log full" },
 { 0x8500, "Memory could not be configured in the selected RAS mode" },
 { 0x8510, "Memory above 16GB maximum" }, /*S5000V only*/
 { 0x8520, "DIMM_A1 failed Self Test (BIST)" },
 { 0x8521, "DIMM_A2 failed Self Test (BIST)" },
 { 0x8522, "DIMM_A3 failed Self Test (BIST)" },
 { 0x8523, "DIMM_A4 failed Self Test (BIST)" },
 { 0x8524, "DIMM_B1 failed Self Test (BIST)" },
 { 0x8525, "DIMM_B2 failed Self Test (BIST)" },
 { 0x8526, "DIMM_B3 failed Self Test (BIST)" },
 { 0x8527, "DIMM_B4 failed Self Test (BIST)" },
 { 0x8528, "DIMM_C1 failed Self Test (BIST)" },
 { 0x8529, "DIMM_C2 failed Self Test (BIST)" },
 { 0x852A, "DIMM_C3 failed Self Test (BIST)" },
 { 0x852B, "DIMM_C4 failed Self Test (BIST)" },
 { 0x852C, "DIMM_D1 failed Self Test (BIST)" },
 { 0x852D, "DIMM_D2 failed Self Test (BIST)" },
 { 0x852E, "DIMM_D3 failed Self Test (BIST)" },
 { 0x852F, "DIMM_D4 failed Self Test (BIST)" },
 { 0x8540, "Memory lost redundancy during last boot" },
 { 0x8580, "DIMM_A1 Correctable ECC error" },
 { 0x8581, "DIMM_A2 Correctable ECC error" },
 { 0x8582, "DIMM_A3 Correctable ECC error" },
 { 0x8583, "DIMM_A4 Correctable ECC error" },
 { 0x8584, "DIMM_B1 Correctable ECC error" },
 { 0x8585, "DIMM_B2 Correctable ECC error" },
 { 0x8586, "DIMM_B3 Correctable ECC error" },
 { 0x8587, "DIMM_B4 Correctable ECC error" },
 { 0x8588, "DIMM_C1 Correctable ECC error" },
 { 0x8589, "DIMM_C2 Correctable ECC error" },
 { 0x858A, "DIMM_C3 Correctable ECC error" },
 { 0x858B, "DIMM_C4 Correctable ECC error" },
 { 0x858C, "DIMM_D1 Correctable ECC error" },
 { 0x858D, "DIMM_D2 Correctable ECC error" },
 { 0x858E, "DIMM_D3 Correctable ECC error" },
 { 0x858F, "DIMM_D4 Correctable ECC error" },
 { 0x8600, "Primary and secondary BIOS IDs do not match" },
 { 0x8601, "BIOS Bank Override jumper set to lower bank" },
 { 0x8602, "WatchDog timer expired (check secondary BIOS bank)" },
 { 0x8603, "Secondary BIOS checksum fail" },
 { 0xffff , NULL }  /*end of list*/
};
const struct valstr intel_s5500_post[] = {  /*from S5520UR TPS*/
 { 0x0012, "CMOS date/time not set" },
 { 0x0048, "Password check failed" },
 { 0x0108, "Keyboard locked error" },
 { 0x0109, "Keyboard stuck key error" },
 { 0x0113, "The SAS RAID firmware cannot run properly" },
 { 0x0140, "PCI PERR detected" },
 { 0x0141, "PCI resource conflict" },
 { 0x0146, "PCI out of resources error" },
 { 0x0192, "Processor cache size mismatch" },
 { 0x0194, "Processor family mismatch" },
 { 0x0195, "Processor QPI speed mismatch" },
 { 0x0196, "Processor Model mismatch" },
 { 0x0197, "Processor speeds mismatched" },
 { 0x0198, "Processor family is unsupported" },
 { 0x019F, "Processor/chipset stepping configuration is unsupported" },
 { 0x5220, "CMOS/NVRAM Configuration Cleared" },
 { 0x5221, "Passwords cleared by jumper" },
 { 0x5224, "Password clear jumper is Set" },
 { 0x8110, "Proc1 internal error (IERR) on last boot" }, /*not used*/
 { 0x8111, "Proc2 internal error (IERR) on last boot" }, /*not used*/
 { 0x8120, "Proc1 thermal trip error on last boot" }, /*not used*/
 { 0x8121, "Proc2 thermal trip error on last boot" }, /*not used*/
 { 0x8130, "Proc1 disabled" }, /*not used*/
 { 0x8131, "Proc2 disabled" }, /*not used*/
 { 0x8140, "Proc1 Failed FRB-3 Timer" }, /*not used*/
 { 0x8141, "Proc2 Failed FRB-3 Timer" }, /*not used*/
 { 0x8160, "Proc1 unable to apply microcode update" },
 { 0x8161, "Proc2 unable to apply microcode update" },
 { 0x8170, "Proc1 failed Self Test (BIST)" },  /*not used*/
 { 0x8171, "Proc2 failed Self Test (BIST)" },  /*not used*/
 { 0x8180, "Processor microcode update not found" },
 { 0x8190, "Watchdog timer failed on last boot" },
 { 0x8198, "OS boot watchdog timer expired on last boot" },
 { 0x8300, "iBMC failed self-test" },
 { 0x8305, "Hotswap controller failure" },
 { 0x84F2, "iBMC failed to respond" },
 { 0x84F3, "iBMC in update mode" },
 { 0x84F4, "Sensor data record empty" },
 { 0x84FF, "System event log full" },
 { 0x8500, "Memory could not be configured in the selected RAS mode" },
 { 0x8520, "DIMM_A1 failed Self Test (BIST)" },
 { 0x8521, "DIMM_A2 failed Self Test (BIST)" },
 { 0x8522, "DIMM_B1 failed Self Test (BIST)" },
 { 0x8523, "DIMM_B2 failed Self Test (BIST)" },
 { 0x8524, "DIMM_C1 failed Self Test (BIST)" },
 { 0x8525, "DIMM_C2 failed Self Test (BIST)" },
 { 0x8526, "DIMM_D1 failed Self Test (BIST)" },
 { 0x8527, "DIMM_D2 failed Self Test (BIST)" },
 { 0x8528, "DIMM_E1 failed Self Test (BIST)" },
 { 0x8529, "DIMM_E2 failed Self Test (BIST)" },
 { 0x852A, "DIMM_F1 failed Self Test (BIST)" },
 { 0x852B, "DIMM_F2 failed Self Test (BIST)" },
 { 0x8540, "DIMM_A1 Disabled" },
 { 0x8541, "DIMM_A2 Disabled" },
 { 0x8542, "DIMM_B1 Disabled" },
 { 0x8543, "DIMM_B2 Disabled" },
 { 0x8544, "DIMM_C1 Disabled" },
 { 0x8545, "DIMM_C2 Disabled" },
 { 0x8546, "DIMM_D1 Disabled" },
 { 0x8547, "DIMM_D2 Disabled" },
 { 0x8548, "DIMM_E1 Disabled" },
 { 0x8549, "DIMM_E2 Disabled" },
 { 0x854A, "DIMM_F1 Disabled" },
 { 0x854B, "DIMM_F2 Disabled" },
 { 0x8560, "DIMM_A1 SPD fail error." },
 { 0x8561, "DIMM_A2 SPD fail error" },
 { 0x8562, "DIMM_B1 SPD fail error" },
 { 0x8563, "DIMM_B2 SPD fail error" },
 { 0x8564, "DIMM_C1 SPD fail error" },
 { 0x8565, "DIMM_C2 SPD fail error" },
 { 0x8566, "DIMM_D1 SPD fail error" },
 { 0x8567, "DIMM_D2 SPD fail error" },
 { 0x8568, "DIMM_E1 SPD fail error" },
 { 0x8569, "DIMM_E2 SPD fail error" },
 { 0x856A, "DIMM_F1 SPD fail error" },
 { 0x856B, "DIMM_F2 SPD fail error" },
 { 0x8580, "DIMM_A1 Correctable ECC error" },
 { 0x8581, "DIMM_A2 Correctable ECC error" },
 { 0x8582, "DIMM_B1 Correctable ECC error" },
 { 0x8583, "DIMM_B2 Correctable ECC error" },
 { 0x8584, "DIMM_C1 Correctable ECC error" },
 { 0x8585, "DIMM_C2 Correctable ECC error" },
 { 0x8586, "DIMM_D1 Correctable ECC error" },
 { 0x8587, "DIMM_D2 Correctable ECC error" },
 { 0x8588, "DIMM_E1 Correctable ECC error" },
 { 0x8589, "DIMM_E2 Correctable ECC error" },
 { 0x858A, "DIMM_F1 Correctable ECC error" },
 { 0x858B, "DIMM_F2 Correctable ECC error" },
 { 0x85A0, "DIMM_A1 Uncorrectable ECC error" },
 { 0x85A1, "DIMM_A2 Uncorrectable ECC error" },
 { 0x85A2, "DIMM_B1 Uncorrectable ECC error" },
 { 0x85A3, "DIMM_B2 Uncorrectable ECC error" },
 { 0x85A4, "DIMM_C1 Uncorrectable ECC error" },
 { 0x85A5, "DIMM_C2 Uncorrectable ECC error" },
 { 0x85A6, "DIMM_D1 Uncorrectable ECC error" },
 { 0x85A7, "DIMM_D2 Uncorrectable ECC error" },
 { 0x85A8, "DIMM_E1 Uncorrectable ECC error" },
 { 0x85A9, "DIMM_E2 Uncorrectable ECC error" },
 { 0x85AA, "DIMM_F1 Uncorrectable ECC error" },
 { 0x85AB, "DIMM_F2 Uncorrectable ECC error" },
 { 0x8601, "BIOS Bank Override jumper set to lower bank" }, /*not used*/
 { 0x8602, "WatchDog timer expired (check secondary BIOS bank)" }, /*not used*/
 { 0x8603, "Secondary BIOS checksum fail" }, /*not used*/
 { 0x8604, "Chipset Reclaim of non critical variables complete" },
 { 0x9000, "Unspecified processor component error" },
 { 0x9223, "Keyboard was not detected" }, /*not used*/
 { 0x9226, "Keyboard controller error" },
 { 0x9243, "Mouse was not detected" },
 { 0x9246, "Mouse controller error" },
 { 0x9266, "Local Console controller error" },
 { 0x9268, "Local Console output error" },
 { 0x9269, "Local Console resource conflict error" },
 { 0x9286, "Remote Console controller error" },
 { 0x9287, "Remote Console input error" },
 { 0x9288, "Remote Console output error" },
 { 0x92A3, "Serial port was not detected" },
 { 0x92A9, "Serial port resource conflict error" },
 { 0x92C6, "Serial Port controller error" },
 { 0x92C7, "Serial Port input error" },
 { 0x92C8, "Serial Port output error" },
 { 0x94C6, "LPC controller error" },
 { 0x94C9, "LPC resource conflict error" },
 { 0x9506, "ATA/ATPI controller error" },
 { 0x95A6, "PCI controller error" },
 { 0x95A7, "PCI read error" },
 { 0x95A8, "PCI write error" },
 { 0x9609, "Unspecified software start error" },
 { 0x9641, "PEI Core load error" },
 { 0x9667, "PEI module Illegal software state error" },
 { 0x9687, "DXE core Illegal software state error" },
 { 0x96A7, "DXE driver Illegal software state error" },
 { 0x96AB, "DXE driver Invalid configuration" },
 { 0x96E7, "SMM driver Illegal software state error" },
 { 0xA000, "TPM device not detected" },
 { 0xA001, "TPM device missing" },
 { 0xA002, "TPM device failure" },
 { 0xA003, "TPM device failed self-test" },
 { 0xA022, "Processor mismatch error" },
 { 0xA027, "Processor low voltage error" },
 { 0xA028, "Processor high voltage error" },
 { 0xA421, "PCI SERR detected" },
 { 0xA500, "ATA/ATPI ATA bus SMART not supported" },
 { 0xA501, "ATA/ATPI ATA SMART is disabled" },
 { 0xA5A0, "PCI Express PERR" },
 { 0xA5A1, "PCI Express SERR" },
 { 0xA5A4, "PCI Express IBIST error" },
 { 0xA6A0, "DXE driver Not enough memory to shadow legacy OpROM" },
 { 0xB6A3, "DXE driver unrecognized" },
 { 0xffff , NULL }  /*end of list*/
};
const struct valstr intel_s2600_post[] = {  /*from S2600CP TPS*/
 { 0x0012, "CMOS date/time not set" },
 { 0x0048, "Password check failed" },
 { 0x0108, "Keyboard locked error" },
 { 0x0109, "Keyboard stuck key error" },
 { 0x0113, "The SAS RAID firmware cannot run properly" },
 { 0x0140, "PCI PERR detected" },
 { 0x0141, "PCI resource conflict" },
 { 0x0146, "PCI out of resources error" },
 { 0x0191, "Processor core/thread count mismatch" },
 { 0x0192, "Processor cache size mismatch" },
 { 0x0194, "Processor family mismatch" },
 { 0x0195, "Processor QPI speed mismatch" },
 { 0x0196, "Processor Model mismatch" },
 { 0x0197, "Processor speeds mismatched" },
 { 0x0198, "Processor family is unsupported" },
 { 0x019F, "Processor/chipset stepping configuration is unsupported" },
 { 0x5220, "CMOS/NVRAM Configuration Cleared" },
 { 0x5221, "Passwords cleared by jumper" },
 { 0x5224, "Password clear jumper is Set" },
 { 0x8110, "Proc1 internal error (IERR) on last boot" }, /*not used*/
 { 0x8111, "Proc2 internal error (IERR) on last boot" }, /*not used*/
 { 0x8120, "Proc1 thermal trip error on last boot" }, /*not used*/
 { 0x8121, "Proc2 thermal trip error on last boot" }, /*not used*/
 { 0x8130, "Proc1 disabled" }, /*not used*/
 { 0x8131, "Proc2 disabled" }, /*not used*/
 { 0x8140, "Proc1 Failed FRB-3 Timer" }, /*not used*/
 { 0x8141, "Proc2 Failed FRB-3 Timer" }, /*not used*/
 { 0x8160, "Proc1 unable to apply microcode update" },
 { 0x8161, "Proc2 unable to apply microcode update" },
 { 0x8170, "Proc1 failed Self Test (BIST)" },  /*not used*/
 { 0x8171, "Proc2 failed Self Test (BIST)" },  /*not used*/
 { 0x8180, "Processor microcode update not found" },
 { 0x8181, "Proc2 microcode update not found" },
 { 0x8190, "Watchdog timer failed on last boot" },
 { 0x8198, "OS boot watchdog timer expired on last boot" },
 { 0x8300, "iBMC failed self-test" },
 { 0x8305, "Hotswap controller failure" },
 { 0x84F2, "iBMC failed to respond" },
 { 0x84F3, "iBMC in update mode" },
 { 0x84F4, "Sensor data record empty" },
 { 0x84FF, "System event log full" },
 { 0x8500, "Memory could not be configured in the selected RAS mode" },
 { 0x8501, "DIMM Population Error" },
 { 0x8520, "DIMM_A1 failed test/initialization" },
 { 0x8521, "DIMM_A2 failed test/initialization" },
 { 0x8522, "DIMM_A3 failed test/initialization" },
 { 0x8523, "DIMM_B1 failed test/initialization" },
 { 0x8524, "DIMM_B2 failed test/initialization" },
 { 0x8525, "DIMM_B3 failed test/initialization" },
 { 0x8526, "DIMM_C1 failed test/initialization" },
 { 0x8527, "DIMM_C2 failed test/initialization" },
 { 0x8528, "DIMM_C3 failed test/initialization" },
 { 0x8529, "DIMM_D1 failed test/initialization" },
 { 0x852A, "DIMM_D2 failed test/initialization" },
 { 0x852B, "DIMM_D3 failed test/initialization" },
 { 0x852C, "DIMM_E1 failed test/initialization" },
 { 0x852D, "DIMM_E2 failed test/initialization" },
 { 0x852E, "DIMM_E3 failed test/initialization" },
 { 0x852F, "DIMM_F1 failed test/initialization" },
 { 0x8530, "DIMM_F2 failed test/initialization" },
 { 0x8531, "DIMM_F3 failed test/initialization" },
 { 0x8532, "DIMM_G1 failed test/initialization" },
 { 0x8533, "DIMM_G2 failed test/initialization" },
 { 0x8534, "DIMM_G3 failed test/initialization" },
 { 0x8535, "DIMM_H1 failed test/initialization" },
 { 0x8536, "DIMM_H2 failed test/initialization" },
 { 0x8537, "DIMM_H3 failed test/initialization" },
 { 0x8538, "DIMM_I1 failed test/initialization" },
 { 0x8539, "DIMM_I2 failed test/initialization" },
 { 0x853A, "DIMM_I3 failed test/initialization" },
 { 0x853B, "DIMM_J1 failed test/initialization" },
 { 0x853C, "DIMM_J2 failed test/initialization" },
 { 0x853D, "DIMM_J3 failed test/initialization" },
 { 0x853E, "DIMM_K1 failed test/initialization" },
 { 0x853F, "DIMM_K2 failed test/initialization" },
 { 0x8540, "DIMM_A1 Disabled" },
 { 0x8541, "DIMM_A2 Disabled" },
 { 0x8542, "DIMM_A3 Disabled" },
 { 0x8543, "DIMM_B1 Disabled" },
 { 0x8544, "DIMM_B2 Disabled" },
 { 0x8545, "DIMM_B3 Disabled" },
 { 0x8546, "DIMM_C1 Disabled" },
 { 0x8547, "DIMM_C2 Disabled" },
 { 0x8548, "DIMM_C3 Disabled" },
 { 0x8549, "DIMM_D1 Disabled" },
 { 0x854A, "DIMM_D2 Disabled" },
 { 0x854B, "DIMM_D3 Disabled" },
 { 0x854C, "DIMM_E1 Disabled" },
 { 0x854D, "DIMM_E2 Disabled" },
 { 0x854E, "DIMM_E3 Disabled" },
 { 0x854F, "DIMM_F1 Disabled" },
 { 0x8550, "DIMM_F2 Disabled" },
 { 0x8551, "DIMM_F3 Disabled" },
 { 0x8552, "DIMM_G1 Disabled" },
 { 0x8553, "DIMM_G2 Disabled" },
 { 0x8554, "DIMM_G3 Disabled" },
 { 0x8555, "DIMM_H1 Disabled" },
 { 0x8556, "DIMM_H1 Disabled" },
 { 0x8557, "DIMM_H1 Disabled" },
 { 0x8558, "DIMM_I1 Disabled" },
 { 0x8559, "DIMM_I2 Disabled" },
 { 0x855A, "DIMM_I3 Disabled" },
 { 0x855B, "DIMM_J1 Disabled" },
 { 0x855C, "DIMM_J2 Disabled" },
 { 0x855D, "DIMM_J3 Disabled" },
 { 0x855E, "DIMM_K1 Disabled" },
 { 0x855F, "DIMM_K2 Disabled" },
 { 0x8560, "DIMM_A1 SPD fail error" },
 { 0x8561, "DIMM_A2 SPD fail error" },
 { 0x8562, "DIMM_A3 SPD fail error" },
 { 0x8563, "DIMM_B1 SPD fail error" },
 { 0x8564, "DIMM_B2 SPD fail error" },
 { 0x8565, "DIMM_B3 SPD fail error" },
 { 0x8566, "DIMM_C1 SPD fail error" },
 { 0x8567, "DIMM_C2 SPD fail error" },
 { 0x8568, "DIMM_C3 SPD fail error" },
 { 0x8569, "DIMM_D1 SPD fail error" },
 { 0x856A, "DIMM_D2 SPD fail error" },
 { 0x856B, "DIMM_D3 SPD fail error" },
 { 0x856C, "DIMM_E1 SPD fail error" },
 { 0x856D, "DIMM_E2 SPD fail error" },
 { 0x856E, "DIMM_E3 SPD fail error" },
 { 0x856F, "DIMM_F1 SPD fail error" },
 { 0x8570, "DIMM_F2 SPD fail error" },
 { 0x8571, "DIMM_F3 SPD fail error" },
 { 0x8572, "DIMM_G1 SPD fail error" },
 { 0x8573, "DIMM_G2 SPD fail error" },
 { 0x8574, "DIMM_G3 SPD fail error" },
 { 0x8575, "DIMM_H1 SPD fail error" },
 { 0x8576, "DIMM_H1 SPD fail error" },
 { 0x8577, "DIMM_H1 SPD fail error" },
 { 0x8578, "DIMM_I1 SPD fail error" },
 { 0x8579, "DIMM_I2 SPD fail error" },
 { 0x857A, "DIMM_I3 SPD fail error" },
 { 0x857B, "DIMM_J1 SPD fail error" },
 { 0x857C, "DIMM_J2 SPD fail error" },
 { 0x857D, "DIMM_J3 SPD fail error" },
 { 0x857E, "DIMM_K1 SPD fail error" },
 { 0x857F, "DIMM_K2 SPD fail error" },
 /* some missing here for DIMM_K3 thru DIMM_P3 */
 { 0x8604, "Chipset Reclaim of non critical variables complete" },
 { 0x8605, "BIOS settings are corrupt" },
 { 0x92A3, "Serial port was not detected" },
 { 0x92A9, "Serial port resource conflict error" },
 { 0xA000, "TPM device not detected" },
 { 0xA001, "TPM device missing" },
 { 0xA002, "TPM device failure" },
 { 0xA003, "TPM device failed self-test" },
 { 0xA100, "BIOS ACM errord" },
 { 0xA421, "PCI SERR detected" },
 { 0xA5A0, "PCI Express PERR" },
 { 0xA5A1, "PCI Express SERR" },
 { 0xffff , NULL }  /*end of list*/
};

/* decode Intel POST codes for some platforms*/
int decode_post_intel(int prod, ushort code, char *outbuf,int szbuf)
{
   int rv = -1;
   const char *poststr = NULL;
   if (is_thurley(VENDOR_INTEL,prod))  /* S5520UR/T5520UR (CG2100 or NSN2U)*/
      poststr = val2str(code,intel_s5500_post);
   else if (is_romley(VENDOR_INTEL,prod))  /* S2600CO, CG2200 */
      poststr = val2str(code,intel_s2600_post);
   else switch(prod) {
      case 0x0028:   /*S5000PAL*/
      case 0x0029:   /*S5000PSL*/
      case 0x0811:   /*S5000PHB*/
	  poststr = val2str(code,intel_s5000_post);
	  break;
      default:   break;
   }
   if (poststr != NULL) {
      strncpy(outbuf, poststr, szbuf);
      rv = 0;
   }
   return(rv);
}

#define ENCLED_SLEEP  50000
static uchar rdisk_led_override = 0x00;  /*override is off by default*/

int get_enc_leds_intel(uchar *val)
{
   uchar idata[8];
   uchar rdata[4];
   int rv, rv2, rlen, i;
   uchar cc;

   *val = 0;  /*make sure to initialize the reading*/
   for (i = 0; i < 3; i++) { 
     /* For Romley/Patsburg get disk fault LED status */
     idata[0] = 0x0A;   // 0x0A
     idata[1] = 0xD0;
     idata[2] = 0x01;   // return one byte of LED data
     idata[3] = 0x0E;   // LED_Status
     rlen = sizeof(rdata);
     rv = ipmi_cmd(MASTER_WRITE_READ, idata, 4, rdata, &rlen, &cc, fdebug);
     if (rv == 0 && cc != 0) rv = cc;
     os_usleep(0,ENCLED_SLEEP);  /* wait before re-reading values */
     if (rv == 0) {
	*val = rdata[0];
	break;
     }
     /*else retry reading the LED Status*/
   }

   /* get & save the override status for use in show() */
   idata[0] = 0x0A;   // 0x0A
   idata[1] = 0xD0;
   idata[2] = 0x01;   // return one byte of LED data
   idata[3] = 0x0D;   // LED_Override
   rlen = sizeof(rdata);
   rv2 = ipmi_cmd(MASTER_WRITE_READ, idata, 4, rdata, &rlen, &cc, fdebug);
   if (rv2 == 0 && cc != 0) rv2 = cc;
   if (rv2 == 0) rdisk_led_override = rdata[0];

   return (rv);
}

int set_enc_override_intel(uchar val)
{
   uchar idata[8];
   uchar rdata[4];
   int rv, rlen, i;
   uchar cc;

   if (val != 0) val = 1;
   for (i = 0; i < 3; i++) { 
      /* Set LED Override to 0=off or 1=on */
      idata[0] = 0x0A;   // 0x0A bus
      idata[1] = 0xD0;
      idata[2] = 0x00;   // return no data
      idata[3] = 0x0D;   // LED_Override
      idata[4] = val;   /* turn on override */
      rlen = sizeof(rdata);
      rv = ipmi_cmd(MASTER_WRITE_READ, idata, 5, rdata, &rlen, &cc, fdebug);
      if (rv == 0 && cc != 0) rv = cc;
      os_usleep(0,ENCLED_SLEEP);  /* wait before next LED cmd */
      if (rv == 0) break;
   }
   return(rv);
}

int set_enc_leds_intel(uchar val)
{
   uchar idata[8];
   uchar rdata[4];
   int rv, rv2, rlen, i;
   uchar cc;

   if ((val != 0) && (rdisk_led_override == 0)) {  
      /* setting some LED, so set disk led override*/
      rv2 = set_enc_override_intel(1);
   } 

   for (i = 0; i < 3; i++) { 
      /* For Romley/Patsburg set disk fault LED status */
      idata[0] = 0x0A;   // 0x0A bus
      idata[1] = 0xD0;
      idata[2] = 0x00;   // return one byte of LED data
      idata[3] = 0x0E;   // LED_Status
      idata[4] = val;
      rlen = sizeof(rdata);
      rv = ipmi_cmd(MASTER_WRITE_READ, idata, 5, rdata, &rlen, &cc, fdebug);
      if ((rv == 0) && (cc != 0)) rv = cc;
      os_usleep(0,ENCLED_SLEEP);  /* wait before next LED cmd */
      if (rv == 0) break;
   }

   if (val == 0x00) {  /* clear the disk led override */
      rv2 = set_enc_override_intel(0);
   }
   return (rv);  
}

void show_enc_leds_intel(uchar val, int numd)
{
    char *enc_pattn = "disk slot %d LED:   %s %s\n";
    char *pstat;
    char *pover;
    uchar mask;
    int i;
    if (fdebug) printf("leds = %02x override = %02x\n",val,rdisk_led_override); 
    /* Some backplanes support 6 slots, but max is 8. */
    if (numd > 8) numd = 8;
    mask = 0x01;
    for (i = 0; i < numd; i++) {
       if (val & mask) pstat = "ON";
       else pstat = "off";
       if (rdisk_led_override) pover = "(override)";
       else pover = "";
       printf(enc_pattn,i,pstat,pover);
       mask = (mask << 1);
    }
}

int get_hsbp_version_intel(uchar *maj, uchar *min)
{
   uchar idata[8];
   uchar rdata[4];
   int rv, rlen, i;
   uchar cc;

   *maj = 0;
   *min = 0;
   /* For Romley/CG2200 get HSBP FW Version */
   for (i = 0; i < 3; i++) { 
     idata[0] = 0x0A;   // 0x0A
     idata[1] = 0xD0;
     idata[2] = 0x01;   // return one byte of LED data
     idata[3] = 0x0A;   // HSBP Major version
     rlen = sizeof(rdata);
     rv = ipmi_cmd(MASTER_WRITE_READ, idata, 4, rdata, &rlen, &cc, fdebug);
     if (rv == 0 && cc != 0) rv = cc;
     os_usleep(0,ENCLED_SLEEP);  /* wait before re-reading values */
     if (rv == 0) {
	*maj = rdata[0];
	break;
     }
     /*else retry reading it*/
   }
   for (i = 0; i < 3; i++) { 
     idata[0] = 0x0A;   // 0x0A
     idata[1] = 0xD0;
     idata[2] = 0x01;   // return one byte of LED data
     idata[3] = 0x0B;   // HSBP Minor version
     rlen = sizeof(rdata);
     rv = ipmi_cmd(MASTER_WRITE_READ, idata, 4, rdata, &rlen, &cc, fdebug);
     if (rv == 0 && cc != 0) rv = cc;
     os_usleep(0,ENCLED_SLEEP);  /* wait before re-reading values */
     if (rv == 0) {
	*min = rdata[0];
	break;
     }
     /*else retry reading it*/
   }
   return (rv);
}

/* end oem_intel.c */
