xref: /openbmc/ipmitool/lib/ipmi_sel.c (revision 531569ec)
1 /* -*-mode: C; indent-tabs-mode: t; -*-
2  * Copyright (c) 2003 Sun Microsystems, Inc.  All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * Redistribution of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * Redistribution in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * Neither the name of Sun Microsystems, Inc. or the names of
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * This software is provided "AS IS," without a warranty of any kind.
20  * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
21  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
22  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
23  * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
24  * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
25  * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
26  * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
27  * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
28  * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
29  * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
30  * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31  */
32 
33 #include <string.h>
34 #include <math.h>
35 #define __USE_XOPEN /* glibc2 needs this for strptime */
36 #include <time.h>
37 #include <ctype.h>
38 #include <errno.h>
39 
40 #include <ipmitool/helper.h>
41 #include <ipmitool/log.h>
42 #include <ipmitool/ipmi.h>
43 #include <ipmitool/ipmi_mc.h>
44 #include <ipmitool/ipmi_intf.h>
45 #include <ipmitool/ipmi_sel.h>
46 #include <ipmitool/ipmi_sdr.h>
47 #include <ipmitool/ipmi_fru.h>
48 #include <ipmitool/ipmi_sensor.h>
49 
50 extern int verbose;
51 static int sel_extended = 0;
52 static int sel_oem_nrecs = 0;
53 
54 static IPMI_OEM sel_iana = IPMI_OEM_UNKNOWN;
55 
56 struct ipmi_sel_oem_msg_rec {
57 	int	value[14];
58 	char	*string[14];
59 	char	*text;
60 } *sel_oem_msg;
61 
62 #define SEL_BYTE(n) (n-3) /* So we can refer to byte positions in log entries (byte 3 is at index 0, etc) */
63 
64 // Definiation for the Decoding the SEL OEM Bytes for DELL Platfoms
65 #define BIT(x)	 (1 << x)	/* Select the Bit */
66 #define	SIZE_OF_DESC	128	/* Max Size of the description String to be displyed for the Each sel entry */
67 #define	MAX_CARDNO_STR	32	/* Max Size of Card number string */
68 #define	MAX_DIMM_STR	32	/* Max Size of DIMM string */
69 #define	MAX_CARD_STR	32	/* Max Size of Card string */
70 /*
71  * Reads values found in message translation file.  XX is a wildcard, R means reserved.
72  * Returns -1 for XX, -2 for R, -3 for non-hex (string), or positive integer from a hex value.
73  */
74 static int ipmi_sel_oem_readval(char *str)
75 {
76 	int ret;
77 	if (!strcmp(str, "XX")) {
78 		return -1;
79 	}
80 	if (!strcmp(str, "R")) {
81 		return -2;
82 	}
83 	if (sscanf(str, "0x%x", &ret) != 1) {
84 		return -3;
85 	}
86 	return ret;
87 }
88 
89 /*
90  * This is where the magic happens.  SEL_BYTE is a bit ugly, but it allows
91  * reference to byte positions instead of array indexes which (hopefully)
92  * helps make the code easier to read.
93  */
94 static int
95 ipmi_sel_oem_match(uint8_t *evt, const struct ipmi_sel_oem_msg_rec *rec)
96 {
97 	if (evt[2] == rec->value[SEL_BYTE(3)]
98 		&& ((rec->value[SEL_BYTE(4)] < 0)
99 			|| (evt[3] == rec->value[SEL_BYTE(4)]))
100 		&& ((rec->value[SEL_BYTE(5)] < 0)
101 			|| (evt[4] == rec->value[SEL_BYTE(5)]))
102 		&& ((rec->value[SEL_BYTE(6)] < 0)
103 			|| (evt[5] == rec->value[SEL_BYTE(6)]))
104 		&& ((rec->value[SEL_BYTE(7)] < 0)
105 			|| (evt[6] == rec->value[SEL_BYTE(7)]))
106 		&& ((rec->value[SEL_BYTE(11)] < 0)
107 			|| (evt[10] == rec->value[SEL_BYTE(11)]))
108 		&& ((rec->value[SEL_BYTE(12)] < 0)
109 			|| (evt[11] == rec->value[SEL_BYTE(12)]))) {
110 		return 1;
111 	} else {
112 		return 0;
113 	}
114 }
115 
116 int ipmi_sel_oem_init(const char * filename)
117 {
118 	FILE * fp;
119 	int i, j, k, n, byte;
120 	char buf[15][150];
121 
122 	if (filename == NULL) {
123 		lprintf(LOG_ERR, "No SEL OEM filename provided");
124 		return -1;
125 	}
126 
127 	fp = ipmi_open_file_read(filename);
128 	if (fp == NULL) {
129 		lprintf(LOG_ERR, "Could not open %s file", filename);
130 		return -1;
131 	}
132 
133 	/* count number of records (lines) in input file */
134 	sel_oem_nrecs = 0;
135 	while (fscanf(fp, "%*[^\n]\n") == 0) {
136 		sel_oem_nrecs++;
137 	}
138 
139 	printf("nrecs=%d\n", sel_oem_nrecs);
140 
141 	rewind(fp);
142 	sel_oem_msg = (struct ipmi_sel_oem_msg_rec *)calloc(sel_oem_nrecs,
143 				 sizeof(struct ipmi_sel_oem_msg_rec));
144 
145 	for (i=0; i < sel_oem_nrecs; i++) {
146 		n=fscanf(fp, "\"%[^\"]\",\"%[^\"]\",\"%[^\"]\",\"%[^\"]\",\""
147 			       "%[^\"]\",\"%[^\"]\",\"%[^\"]\",\"%[^\"]\",\""
148 			       "%[^\"]\",\"%[^\"]\",\"%[^\"]\",\"%[^\"]\",\""
149 			       "%[^\"]\",\"%[^\"]\",\"%[^\"]\"\n",
150 			 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
151 			 buf[6], buf[7], buf[8], buf[9], buf[10], buf[11],
152 			 buf[12], buf[13], buf[14]);
153 
154 		if (n != 15) {
155 			lprintf (LOG_ERR, "Encountered problems reading line %d of %s",
156 				 i+1, filename);
157 			fclose(fp);
158 			fp = NULL;
159 			sel_oem_nrecs = 0;
160 			/* free all the memory allocated so far */
161 			for (j=0; j<i ; j++) {
162 				for (k=3; k<17; k++) {
163 					if (sel_oem_msg[j].value[SEL_BYTE(k)] == -3) {
164 						free(sel_oem_msg[j].string[SEL_BYTE(k)]);
165 						sel_oem_msg[j].string[SEL_BYTE(k)] = NULL;
166 					}
167 				}
168 			}
169 			free(sel_oem_msg);
170 			sel_oem_msg = NULL;
171 			return -1;
172 		}
173 
174 		for (byte = 3; byte < 17; byte++) {
175 			if ((sel_oem_msg[i].value[SEL_BYTE(byte)] =
176 			     ipmi_sel_oem_readval(buf[SEL_BYTE(byte)])) == -3) {
177 				sel_oem_msg[i].string[SEL_BYTE(byte)] =
178 					(char *)malloc(strlen(buf[SEL_BYTE(byte)]) + 1);
179 				strcpy(sel_oem_msg[i].string[SEL_BYTE(byte)],
180 				       buf[SEL_BYTE(byte)]);
181 			}
182 		}
183 		sel_oem_msg[i].text = (char *)malloc(strlen(buf[SEL_BYTE(17)]) + 1);
184 		strcpy(sel_oem_msg[i].text, buf[SEL_BYTE(17)]);
185 	}
186 
187 	fclose(fp);
188 	fp = NULL;
189 	return 0;
190 }
191 
192 static void ipmi_sel_oem_message(struct sel_event_record * evt, int verbose)
193 {
194 	/*
195 	 * Note: although we have a verbose argument, currently the output
196 	 * isn't affected by it.
197 	 */
198 	int i, j;
199 
200 	for (i=0; i < sel_oem_nrecs; i++) {
201 		if (ipmi_sel_oem_match((uint8_t *)evt, &sel_oem_msg[i])) {
202 			printf (csv_output ? ",\"%s\"" : " | %s", sel_oem_msg[i].text);
203 			for (j=4; j<17; j++) {
204 				if (sel_oem_msg[i].value[SEL_BYTE(j)] == -3) {
205 					printf (csv_output ? ",%s=0x%x" : " %s = 0x%x",
206 						sel_oem_msg[i].string[SEL_BYTE(j)],
207 						((uint8_t *)evt)[SEL_BYTE(j)]);
208 				}
209 			}
210 		}
211 	}
212 }
213 
214 static const struct valstr event_dir_vals[] = {
215 	{ 0, "Assertion Event" },
216 	{ 1, "Deassertion Event" },
217 	{ 0, NULL },
218 };
219 
220 static const char *
221 ipmi_get_event_type(uint8_t code)
222 {
223         if (code == 0)
224                 return "Unspecified";
225         if (code == 1)
226                 return "Threshold";
227         if (code >= 0x02 && code <= 0x0b)
228                 return "Generic Discrete";
229         if (code == 0x6f)
230                 return "Sensor-specific Discrete";
231         if (code >= 0x70 && code <= 0x7f)
232                 return "OEM";
233         return "Reserved";
234 }
235 
236 static char *
237 ipmi_sel_timestamp(uint32_t stamp)
238 {
239 	static char tbuf[40];
240 	time_t s = (time_t)stamp;
241 	memset(tbuf, 0, 40);
242 	strftime(tbuf, sizeof(tbuf), "%m/%d/%Y %H:%M:%S", gmtime(&s));
243 	return tbuf;
244 }
245 
246 static char *
247 ipmi_sel_timestamp_date(uint32_t stamp)
248 {
249 	static char tbuf[11];
250 	time_t s = (time_t)stamp;
251 	strftime(tbuf, sizeof(tbuf), "%m/%d/%Y", gmtime(&s));
252 	return tbuf;
253 }
254 
255 static char *
256 ipmi_sel_timestamp_time(uint32_t stamp)
257 {
258 	static char tbuf[9];
259 	time_t s = (time_t)stamp;
260 	strftime(tbuf, sizeof(tbuf), "%H:%M:%S", gmtime(&s));
261 	return tbuf;
262 }
263 
264 static char *
265 hex2ascii (uint8_t * hexChars, uint8_t numBytes)
266 {
267 	int count;
268 	static char hexString[SEL_OEM_NOTS_DATA_LEN+1];       /*Max Size*/
269 
270 	if(numBytes > SEL_OEM_NOTS_DATA_LEN)
271 		numBytes = SEL_OEM_NOTS_DATA_LEN;
272 
273 	for(count=0;count < numBytes;count++)
274 	{
275 		if((hexChars[count]<0x40)||(hexChars[count]>0x7e))
276 			hexString[count]='.';
277     		else
278 			hexString[count]=hexChars[count];
279 	}
280 	hexString[numBytes]='\0';
281 	return hexString;
282 }
283 
284 IPMI_OEM
285 ipmi_get_oem(struct ipmi_intf * intf)
286 {
287 	/* Execute a Get Device ID command to determine the OEM */
288 	struct ipmi_rs * rsp;
289 	struct ipmi_rq req;
290 	struct ipm_devid_rsp *devid;
291 
292 	if (intf->fd == 0) {
293 		if( sel_iana != IPMI_OEM_UNKNOWN ){
294 			return sel_iana;
295 		}
296 		return IPMI_OEM_UNKNOWN;
297 	}
298 
299 	/*
300 	 * Return the cached manufacturer id if the device is open and
301 	 * we got an identified OEM owner.   Otherwise just attempt to read
302 	 * it.
303 	 */
304 	if (intf->opened && intf->manufacturer_id != IPMI_OEM_UNKNOWN) {
305 		return intf->manufacturer_id;
306 	}
307 
308 	memset(&req, 0, sizeof(req));
309 	req.msg.netfn = IPMI_NETFN_APP;
310 	req.msg.cmd   = BMC_GET_DEVICE_ID;
311 	req.msg.data_len = 0;
312 
313 	rsp = intf->sendrecv(intf, &req);
314 	if (rsp == NULL) {
315 		lprintf(LOG_ERR, "Get Device ID command failed");
316 		return IPMI_OEM_UNKNOWN;
317 	}
318 	if (rsp->ccode > 0) {
319 		lprintf(LOG_ERR, "Get Device ID command failed: %#x %s",
320 			rsp->ccode, val2str(rsp->ccode, completion_code_vals));
321 		return IPMI_OEM_UNKNOWN;
322 	}
323 
324 	devid = (struct ipm_devid_rsp *) rsp->data;
325 
326 	lprintf(LOG_DEBUG,"Iana: %u",
327            IPM_DEV_MANUFACTURER_ID(devid->manufacturer_id));
328 
329 	return  IPM_DEV_MANUFACTURER_ID(devid->manufacturer_id);
330 }
331 
332 static int
333 ipmi_sel_add_entry(struct ipmi_intf * intf, struct sel_event_record * rec)
334 {
335 	struct ipmi_rs * rsp;
336 	struct ipmi_rq req;
337 
338 	memset(&req, 0, sizeof(req));
339 	req.msg.netfn = IPMI_NETFN_STORAGE;
340 	req.msg.cmd = IPMI_CMD_ADD_SEL_ENTRY;
341 	req.msg.data = (unsigned char *)rec;
342 	req.msg.data_len = 16;
343 
344 	ipmi_sel_print_std_entry(intf, rec);
345 
346 	rsp = intf->sendrecv(intf, &req);
347 	if (rsp == NULL) {
348 		lprintf(LOG_ERR, "Add SEL Entry failed");
349 		return -1;
350 	}
351 	else if (rsp->ccode > 0) {
352 		lprintf(LOG_ERR, "Add SEL Entry failed: %s",
353 			val2str(rsp->ccode, completion_code_vals));
354 		return -1;
355 	}
356 
357 	return 0;
358 }
359 
360 
361 static int
362 ipmi_sel_add_entries_fromfile(struct ipmi_intf * intf, const char * filename)
363 {
364 	FILE * fp;
365 	char buf[1024];
366 	char * ptr, * tok;
367 	int i, j;
368 	int rc = 0;
369 	uint8_t rqdata[8];
370 	struct sel_event_record sel_event;
371 
372 	if (filename == NULL)
373 		return -1;
374 
375 	fp = ipmi_open_file_read(filename);
376 	if (fp == NULL)
377 		return -1;
378 
379 	while (feof(fp) == 0) {
380 		if (fgets(buf, 1024, fp) == NULL)
381 			continue;
382 
383 		/* clip off optional comment tail indicated by # */
384 		ptr = strchr(buf, '#');
385 		if (ptr)
386 			*ptr = '\0';
387 		else
388 			ptr = buf + strlen(buf);
389 
390 		/* clip off trailing and leading whitespace */
391 		ptr--;
392 		while (isspace((int)*ptr) && ptr >= buf)
393 			*ptr-- = '\0';
394 		ptr = buf;
395 		while (isspace((int)*ptr))
396 			ptr++;
397 		if (strlen(ptr) == 0)
398 			continue;
399 
400 		/* parse the event, 7 bytes with optional comment */
401 		/* 0x00 0x00 0x00 0x00 0x00 0x00 0x00 # event */
402 		i = 0;
403 		tok = strtok(ptr, " ");
404 		while (tok) {
405 			if (i == 7)
406 				break;
407 			j = i++;
408 			if (str2uchar(tok, &rqdata[j]) != 0) {
409 				break;
410 			}
411 			tok = strtok(NULL, " ");
412 		}
413 		if (i < 7) {
414 			lprintf(LOG_ERR, "Invalid Event: %s",
415 			       buf2str(rqdata, sizeof(rqdata)));
416 			continue;
417 		}
418 
419 		memset(&sel_event, 0, sizeof(struct sel_event_record));
420 		sel_event.record_id = 0x0000;
421 		sel_event.record_type = 0x02;
422 		sel_event.sel_type.standard_type.gen_id = 0x00;
423 		sel_event.sel_type.standard_type.evm_rev = rqdata[0];
424 		sel_event.sel_type.standard_type.sensor_type = rqdata[1];
425 		sel_event.sel_type.standard_type.sensor_num = rqdata[2];
426 		sel_event.sel_type.standard_type.event_type = rqdata[3] & 0x7f;
427 		sel_event.sel_type.standard_type.event_dir = (rqdata[3] & 0x80) >> 7;
428 		sel_event.sel_type.standard_type.event_data[0] = rqdata[4];
429 		sel_event.sel_type.standard_type.event_data[1] = rqdata[5];
430 		sel_event.sel_type.standard_type.event_data[2] = rqdata[6];
431 
432 		rc = ipmi_sel_add_entry(intf, &sel_event);
433 		if (rc < 0)
434 			break;
435 	}
436 
437 	fclose(fp);
438 	return rc;
439 }
440 
441 static struct ipmi_event_sensor_types oem_kontron_event_reading_types[] __attribute__((unused)) = {
442    { 0x70 , 0x00 , 0xff, IPMI_EVENT_CLASS_DISCRETE , "OEM Firmware Info 1", "Code Assert" },
443    { 0x71 , 0x00 , 0xff, IPMI_EVENT_CLASS_DISCRETE , "OEM Firmware Info 2", "Code Assert" },
444 };
445 
446 char *
447 get_kontron_evt_desc(struct ipmi_intf * intf, struct sel_event_record * rec)
448 {
449 	char * description = NULL;
450 	/*
451 	 * Kontron OEM events are described in the product's user manual,  but are limited in favor of
452     * sensor specific
453 	 */
454 
455 	/* Only standard records are defined so far */
456 	if( rec->record_type < 0xC0 ){
457 		struct ipmi_event_sensor_types *st=NULL;
458 		for ( st=oem_kontron_event_reading_types ; st->type != NULL; st++){
459 			if (st->code == rec->sel_type.standard_type.event_type ){
460 				size_t len =strlen(st->desc);
461 				description = (char*)malloc( len + 1 );
462 				memcpy(description, st->desc , len);
463 				description[len] = 0;;
464 				return description;
465 			}
466 		}
467 	}
468 
469 	return NULL;
470 }
471 
472 char *
473 get_newisys_evt_desc(struct ipmi_intf * intf, struct sel_event_record * rec)
474 {
475 	/*
476 	 * Newisys OEM event descriptions can be retrieved through an
477 	 * OEM IPMI command.
478 	 */
479 	struct ipmi_rs * rsp;
480 	struct ipmi_rq req;
481 	uint8_t msg_data[6];
482 	char * description = NULL;
483 
484 	memset(&req, 0, sizeof(req));
485 	req.msg.netfn = 0x2E;
486 	req.msg.cmd   = 0x01;
487 	req.msg.data_len = sizeof(msg_data);
488 
489 	msg_data[0] = 0x15;	/* IANA LSB */
490 	msg_data[1] = 0x24; /* IANA     */
491 	msg_data[2] = 0x00; /* IANA MSB */
492 	msg_data[3] = 0x01; /* Subcommand */
493 	msg_data[4] = rec->record_id & 0x00FF;        /* SEL Record ID LSB */
494 	msg_data[5] = (rec->record_id & 0xFF00) >> 8; /* SEL Record ID MSB */
495 
496 	req.msg.data = msg_data;
497 
498 	rsp = intf->sendrecv(intf, &req);
499 	if (rsp == NULL) {
500 		if (verbose)
501 			lprintf(LOG_ERR, "Error issuing OEM command");
502 		return NULL;
503 	}
504 	if (rsp->ccode > 0) {
505 		if (verbose)
506 			lprintf(LOG_ERR, "OEM command returned error code: %s",
507 					val2str(rsp->ccode, completion_code_vals));
508 		return NULL;
509 	}
510 
511 	/* Verify our response before we use it */
512 	if (rsp->data_len < 5)
513 	{
514 		lprintf(LOG_ERR, "Newisys OEM response too short");
515 		return NULL;
516 	}
517 	else if (rsp->data_len != (4 + rsp->data[3]))
518 	{
519 		lprintf(LOG_ERR, "Newisys OEM response has unexpected length");
520 		return NULL;
521 	}
522 	else if (IPM_DEV_MANUFACTURER_ID(rsp->data) != IPMI_OEM_NEWISYS)
523 	{
524 		lprintf(LOG_ERR, "Newisys OEM response has unexpected length");
525 		return NULL;
526 	}
527 
528 	description = (char*)malloc(rsp->data[3] + 1);
529 	memcpy(description, rsp->data + 4, rsp->data[3]);
530 	description[rsp->data[3]] = 0;;
531 
532 	return description;
533 }
534 
535 char *
536 get_supermicro_evt_desc(struct ipmi_intf *intf, struct sel_event_record *rec)
537 {
538 	struct ipmi_rs *rsp;
539 	struct ipmi_rq req;
540 	char *desc = NULL;
541 	int chipset_type = 1;
542 	int data1;
543 	int data2;
544 	int data3;
545 	int sensor_type;
546 	uint8_t i = 0;
547 	uint16_t oem_id = 0;
548 	/* Get the OEM event Bytes of the SEL Records byte 13, 14, 15 to
549 	 * data1,data2,data3
550 	 */
551 	data1 = rec->sel_type.standard_type.event_data[0];
552 	data2 = rec->sel_type.standard_type.event_data[1];
553 	data3 = rec->sel_type.standard_type.event_data[2];
554 	/* Check for the Standard Event type == 0x6F */
555 	if (rec->sel_type.standard_type.event_type != 0x6F) {
556 		return NULL;
557 	}
558 	/* Allocate mem for te Description string */
559 	desc = malloc(sizeof(char) * SIZE_OF_DESC);
560 	if (desc == NULL) {
561 		lprintf(LOG_ERR, "ipmitool: malloc failure");
562 		return NULL;
563 	}
564 	memset(desc, '\0', SIZE_OF_DESC);
565 	sensor_type = rec->sel_type.standard_type.sensor_type;
566 	switch (sensor_type) {
567 		case SENSOR_TYPE_MEMORY:
568 			memset(&req, 0, sizeof (req));
569 			req.msg.netfn = IPMI_NETFN_APP;
570 			req.msg.lun = 0;
571 			req.msg.cmd = BMC_GET_DEVICE_ID;
572 			req.msg.data = NULL;
573 			req.msg.data_len = 0;
574 
575 			rsp = intf->sendrecv(intf, &req);
576 			if (rsp == NULL) {
577 				lprintf(LOG_ERR, " Error getting system info");
578 				if (desc != NULL) {
579 					free(desc);
580 					desc = NULL;
581 				}
582 				return NULL;
583 			} else if (rsp->ccode > 0) {
584 				lprintf(LOG_ERR, " Error getting system info: %s",
585 						val2str(rsp->ccode, completion_code_vals));
586 				if (desc != NULL) {
587 					free(desc);
588 					desc = NULL;
589 				}
590 				return NULL;
591 			}
592 			/* check the chipset type */
593 			oem_id = ipmi_get_oem_id(intf);
594 			if (oem_id == 0) {
595 				if (desc != NULL) {
596 					free(desc);
597 					desc = NULL;
598 				}
599 				return NULL;
600 			}
601 			for (i = 0; supermicro_X8[i] != 0xFFFF; i++) {
602 				if (oem_id == supermicro_X8[i]) {
603 					chipset_type = 0;
604 					break;
605 				}
606 			}
607 			for (i = 0; supermicro_x9[i] != 0xFFFF; i++) {
608 				if (oem_id == supermicro_x9[i]) {
609 					chipset_type = 2;
610 					break;
611 				}
612 			}
613 			if (chipset_type == 0) {
614 				snprintf(desc, SIZE_OF_DESC, "@DIMM%2X(CPU%x)",
615 						data2,
616 						(data3 & 0x03) + 1);
617 			} else if (chipset_type == 1) {
618 				snprintf(desc, SIZE_OF_DESC, "@DIMM%c%c(CPU%x)",
619 						(data2 >> 4) + 0x40 + (data3 & 0x3) * 4,
620 						(data2 & 0xf) + 0x27, (data3 & 0x03) + 1);
621 			} else if (chipset_type == 2) {
622 				snprintf(desc, SIZE_OF_DESC, "@DIMM%c%c(CPU%x)",
623 						(data2 >> 4) + 0x40 + (data3 & 0x3) * 3,
624 						(data2 & 0xf) + 0x27, (data3 & 0x03) + 1);
625 			} else {
626 				/* No description. */
627 				desc[0] = '\0';
628 			}
629 			break;
630 		case SENSOR_TYPE_SUPERMICRO_OEM:
631 			if (data1 == 0x80 && data3 == 0xFF) {
632 				if (data2 == 0x0) {
633 					snprintf(desc, SIZE_OF_DESC, "BMC unexpected reset");
634 				} else if (data2 == 0x1) {
635 					snprintf(desc, SIZE_OF_DESC, "BMC cold reset");
636 				} else if (data2 == 0x2) {
637 					snprintf(desc, SIZE_OF_DESC, "BMC warm reset");
638 				}
639 			}
640 			break;
641 	}
642 	return desc;
643 }
644 
645 /*
646  * Function 	: Decoding the SEL OEM Bytes for the DELL Platforms.
647  * Description  : The below fucntion will decode the SEL Events OEM Bytes for the Dell specific	Sensors only.
648  * The below function will append the additional information Strings/description to the normal sel desc.
649  * With this the SEL will display additional information sent via OEM Bytes of the SEL Record.
650  * NOTE		: Specific to DELL Platforms only.
651  * Returns	: 	Pointer to the char string.
652  */
653 char * get_dell_evt_desc(struct ipmi_intf * intf, struct sel_event_record * rec)
654 {
655 	int data1, data2, data3;
656 	int sensor_type;
657 	char *desc = NULL;
658 
659 	unsigned char count;
660 	unsigned char node;
661 	unsigned char dimmNum;
662 	unsigned char dimmsPerNode;
663 	char          dimmStr[MAX_DIMM_STR];
664 	char          tmpdesc[SIZE_OF_DESC];
665 	char*         str;
666 	unsigned char incr = 0;
667 	unsigned char i=0,j = 0;
668 	struct ipmi_rs *rsp;
669 	struct ipmi_rq req;
670 	char tmpData;
671 	int version;
672 	/* Get the OEM event Bytes of the SEL Records byte 13, 14, 15 to Data1,data2,data3 */
673 	data1 = rec->sel_type.standard_type.event_data[0];
674 	data2 = rec->sel_type.standard_type.event_data[1];
675 	data3 = rec->sel_type.standard_type.event_data[2];
676 	/* Check for the Standard Event type == 0x6F */
677 	if (0x6F == rec->sel_type.standard_type.event_type)
678 		{
679 		sensor_type = rec->sel_type.standard_type.sensor_type;
680 		/* Allocate mem for te Description string */
681 		desc = (char*)malloc(SIZE_OF_DESC);
682 		if(NULL == desc)
683 			return NULL;
684 		memset(desc,0,SIZE_OF_DESC);
685 		memset(tmpdesc,0,SIZE_OF_DESC);
686 		switch (sensor_type) {
687 			case SENSOR_TYPE_PROCESSOR:	/* Processor/CPU related OEM Sel Byte Decoding for DELL Platforms only */
688 				if((OEM_CODE_IN_BYTE2 == (data1 & DATA_BYTE2_SPECIFIED_MASK)))
689 				{
690 					if(0x00 == (data1 & MASK_LOWER_NIBBLE))
691 						snprintf(desc,SIZE_OF_DESC,"CPU Internal Err | ");
692 					if(0x06 == (data1 & MASK_LOWER_NIBBLE))
693 					{
694 						snprintf(desc,SIZE_OF_DESC,"CPU Protocol Err | ");
695 
696 					}
697 
698 					/* change bit location to a number */
699 					for (count= 0; count < 8; count++)
700 					{
701 					  if (BIT(count)& data2)
702 					  {
703 					    count++;
704 						/* 0x0A - CPU sensor number */
705 						if((0x06 == (data1 & MASK_LOWER_NIBBLE)) && (0x0A == rec->sel_type.standard_type.sensor_num))
706 						    snprintf(desc,SIZE_OF_DESC,"FSB %d ",count);			// Which CPU Has generated the FSB
707 						else
708 						    snprintf(desc,SIZE_OF_DESC,"CPU %d | APIC ID %d ",count,data3);	/* Specific CPU related info */
709 					    break;
710 					  }
711 					}
712 				}
713 			break;
714 			case SENSOR_TYPE_MEMORY:	/* Memory or DIMM related OEM Sel Byte Decoding for DELL Platforms only */
715 			case SENSOR_TYPE_EVT_LOG:	/* Events Logging for Memory or DIMM related OEM Sel Byte Decoding for DELL Platforms only */
716 
717 				/* Get the current version of the IPMI Spec Based on that Decoding of memory info is done.*/
718 				memset(&req, 0, sizeof (req));
719 				req.msg.netfn = IPMI_NETFN_APP;
720 				req.msg.lun = 0;
721 				req.msg.cmd = BMC_GET_DEVICE_ID;
722 				req.msg.data = NULL;
723 				req.msg.data_len = 0;
724 
725 				rsp = intf->sendrecv(intf, &req);
726 				if (NULL == rsp)
727 				{
728 					lprintf(LOG_ERR, " Error getting system info");
729 					if (desc != NULL) {
730 						free(desc);
731 						desc = NULL;
732 					}
733 					return NULL;
734 				}
735 				else if (rsp->ccode > 0)
736 				{
737 					lprintf(LOG_ERR, " Error getting system info: %s",
738 						val2str(rsp->ccode, completion_code_vals));
739 					if (desc != NULL) {
740 						free(desc);
741 						desc = NULL;
742 					}
743 					return NULL;
744 				}
745 				version = rsp->data[4];
746 				/* Memory DIMMS */
747 				if( (data1 &  OEM_CODE_IN_BYTE2) || (data1 & OEM_CODE_IN_BYTE3 ) )
748 				{
749 					/* Memory Redundancy related oem bytes docoding .. */
750 					if( (SENSOR_TYPE_MEMORY == sensor_type) && (0x0B == rec->sel_type.standard_type.event_type) )
751 					{
752 						if(0x00 == (data1 & MASK_LOWER_NIBBLE))
753 						{
754 							snprintf(desc,SIZE_OF_DESC," Redundancy Regained | ");
755 						}
756 						else if(0x01 == (data1 & MASK_LOWER_NIBBLE))
757 						{
758 							snprintf(desc,SIZE_OF_DESC,"Redundancy Lost | ");
759 						}
760 					} /* Correctable and uncorrectable ECC Error Decoding */
761 					else if(SENSOR_TYPE_MEMORY == sensor_type)
762 					{
763 						if(0x00 == (data1 & MASK_LOWER_NIBBLE))
764 						{
765 							/* 0x1C - Memory Sensor Number */
766 							if(0x1C == rec->sel_type.standard_type.sensor_num)
767 							{
768 								/*Add the complete information about the Memory Configs.*/
769 								if((data1 &  OEM_CODE_IN_BYTE2) && (data1 & OEM_CODE_IN_BYTE3 ))
770 								{
771 									count = 0;
772 									snprintf(desc,SIZE_OF_DESC,"CRC Error on:");
773 									for(i=0;i<4;i++)
774 									{
775 										if((BIT(i))&(data2))
776 										{
777 											if(count)
778 											{
779 						                        str = desc+strlen(desc);
780 												*str++ = ',';
781 												str = '\0';
782 						              					count = 0;
783 											}
784 											switch(i) /* Which type of memory config is present.. */
785 											{
786 												case 0: snprintf(tmpdesc,SIZE_OF_DESC,"South Bound Memory");
787 														strcat(desc,tmpdesc);
788 														count++;
789 														break;
790 												case 1:	snprintf(tmpdesc,SIZE_OF_DESC,"South Bound Config");
791 														strcat(desc,tmpdesc);
792 														count++;
793 														break;
794 												case 2: snprintf(tmpdesc,SIZE_OF_DESC,"North Bound memory");
795 														strcat(desc,tmpdesc);
796 														count++;
797 														break;
798 												case 3:	snprintf(tmpdesc,SIZE_OF_DESC,"North Bound memory-corr");
799 														strcat(desc,tmpdesc);
800 														count++;
801 														break;
802 												default:
803 														break;
804 											}
805 										}
806 									}
807 									if(data3>=0x00 && data3<0xFF)
808 									{
809 										snprintf(tmpdesc,SIZE_OF_DESC,"|Failing_Channel:%d",data3);
810 										strcat(desc,tmpdesc);
811 									}
812 								}
813 								break;
814 							}
815 							snprintf(desc,SIZE_OF_DESC,"Correctable ECC | ");
816 						}
817 						else if(0x01 == (data1 & MASK_LOWER_NIBBLE))
818 						{
819 							snprintf(desc,SIZE_OF_DESC,"UnCorrectable ECC | ");
820 						}
821 					} /* Corr Memory log disabled */
822 					else if(SENSOR_TYPE_EVT_LOG == sensor_type)
823 					{
824 						if(0x00 == (data1 & MASK_LOWER_NIBBLE))
825 							snprintf(desc,SIZE_OF_DESC,"Corr Memory Log Disabled | ");
826 					}
827 				}
828 				else
829 				{
830 					if(SENSOR_TYPE_SYS_EVENT == sensor_type)
831 					{
832 						if(0x02 == (data1 & MASK_LOWER_NIBBLE))
833 							snprintf(desc,SIZE_OF_DESC,"Unknown System Hardware Failure ");
834 					}
835 					if(SENSOR_TYPE_EVT_LOG == sensor_type)
836 					{
837 						if(0x03 == (data1 & MASK_LOWER_NIBBLE))
838 							snprintf(desc,SIZE_OF_DESC,"All Even Logging Dissabled");
839 					}
840 				}
841 				/*
842  				 * Based on the above error, we need to find whcih memory slot or
843  				 * Card has got the Errors/Sel Generated.
844  				 */
845 				if(data1 & OEM_CODE_IN_BYTE2 )
846 				{
847 					/* Find the Card Type */
848 					if((0x0F != (data2 >> 4)) && ((data2 >> 4) < 0x08))
849 					{
850 						tmpData = 	('A'+ (data2 >> 4));
851 						if( (SENSOR_TYPE_MEMORY == sensor_type) && (0x0B == rec->sel_type.standard_type.event_type) )
852 						{
853 							snprintf(tmpdesc, SIZE_OF_DESC, "Bad Card %c", tmpData);
854 						}
855 						else
856 						{
857 							snprintf(tmpdesc, SIZE_OF_DESC, "Card %c", tmpData);
858 						}
859 						strcat(desc, tmpdesc);
860 					} /* Find the Bank Number of the DIMM */
861 					if (0x0F != (data2 & MASK_LOWER_NIBBLE))
862 					{
863 						if(0x51  == version)
864 						{
865 							snprintf(tmpdesc, SIZE_OF_DESC, "Bank %d", ((data2 & 0x0F)+1));
866 							strcat(desc, tmpdesc);
867 						}
868 						else
869 						{
870 							incr = (data2 & 0x0f) << 3;
871 						}
872 					}
873 
874 				}
875 				/* Find the DIMM Number of the Memory which has Generated the Fault or Sel */
876 				if(data1 & OEM_CODE_IN_BYTE3 )
877 				{
878 					// Based on the IPMI Spec Need Identify the DIMM Details.
879 					// For the SPEC 1.5 Only the DIMM Number is Valid.
880 					if(0x51  == version)
881 					{
882 						snprintf(tmpdesc, SIZE_OF_DESC, "DIMM %c", ('A'+ data3));
883 						strcat(desc, tmpdesc);
884 					}
885 					/* For the SPEC 2.0 Decode the DIMM Number as it supports more.*/
886 					else if( ((data2 >> 4) > 0x07) && (0x0F != (data2 >> 4) ))
887 					{
888 						strcpy(dimmStr, " DIMM");
889 						str = desc+strlen(desc);
890 						dimmsPerNode = 4;
891 						if(0x09 == (data2 >> 4)) dimmsPerNode = 6;
892 						else if(0x0A == (data2 >> 4)) dimmsPerNode = 8;
893 						else if(0x0B == (data2 >> 4)) dimmsPerNode = 9;
894 						else if(0x0C == (data2 >> 4)) dimmsPerNode = 12;
895 						else if(0x0D == (data2 >> 4)) dimmsPerNode = 24;
896 						else if(0x0E == (data2 >> 4)) dimmsPerNode = 3;
897 						count = 0;
898 				        	for (i = 0; i < 8; i++)
899 				        	{
900 				        		if (BIT(i) & data3)
901 				          		{
902 								if(count)
903 								{
904 									strcat(str,",");
905 									count = 0x00;
906 								}
907 				            		node = (incr + i)/dimmsPerNode;
908 					            	dimmNum = ((incr + i)%dimmsPerNode)+1;
909 					            	dimmStr[5] = node + 'A';
910 					            	sprintf(tmpdesc,"%d",dimmNum);
911 					            	for(j = 0; j < strlen(tmpdesc);j++)
912 								dimmStr[6+j] = tmpdesc[j];
913 							dimmStr[6+j] = '\0';
914 							strcat(str,dimmStr); // final DIMM Details.
915 		 			               	count++;
916 					          	}
917 					        }
918 					}
919 					else
920 					{
921 					        strcpy(dimmStr, " DIMM");
922 						str = desc+strlen(desc);
923 					        count = 0;
924 					        for (i = 0; i < 8; i++)
925 					        {
926 				        		if (BIT(i) & data3)
927 				   			{
928 						            // check if more than one DIMM, if so add a comma to the string.
929 						        	sprintf(tmpdesc,"%d",(i + incr + 1));
930 								if(count)
931 								{
932 									strcat(str,",");
933 									count = 0x00;
934 								}
935 								for(j = 0; j < strlen(tmpdesc);j++)
936 									dimmStr[5+j] = tmpdesc[j];
937 								dimmStr[5+j] = '\0';
938 							        strcat(str, dimmStr);
939 							        count++;
940 				          		}
941 				        	}
942 			        	}
943 				}
944 			break;
945 			/* Sensor In system charectorization Error Decoding.
946 				Sensor type  0x20*/
947 			case SENSOR_TYPE_TXT_CMD_ERROR:
948 				if((0x00 == (data1 & MASK_LOWER_NIBBLE))&&((data1 & OEM_CODE_IN_BYTE2) && (data1 & OEM_CODE_IN_BYTE3)))
949 				{
950 					switch(data3)
951 					{
952 						case 0x01:
953 							snprintf(desc,SIZE_OF_DESC,"BIOS TXT Error");
954 							break;
955 						case 0x02:
956 							snprintf(desc,SIZE_OF_DESC,"Processor/FIT TXT");
957 							break;
958 						case 0x03:
959 							snprintf(desc,SIZE_OF_DESC,"BIOS ACM TXT Error");
960 							break;
961 						case 0x04:
962 							snprintf(desc,SIZE_OF_DESC,"SINIT ACM TXT Error");
963 							break;
964 						case 0xff:
965 							snprintf(desc,SIZE_OF_DESC,"Unrecognized TT Error12");
966 							break;
967 						default:
968 							break;
969 					}
970 				}
971 			break;
972 			/* OS Watch Dog Timer Sel Events */
973 			case SENSOR_TYPE_WTDOG:
974 
975 				if(SENSOR_TYPE_OEM_SEC_EVENT == data1)
976 				{
977 					if(0x04 == data2)
978 					{
979 						snprintf(desc,SIZE_OF_DESC,"Hard Reset|Interrupt type None,SMS/OS Timer used at expiration");
980 					}
981 				}
982 
983 			break;
984 						/* This Event is for BMC to Othe Hardware or CPU . */
985 			case SENSOR_TYPE_VER_CHANGE:
986 				if((0x02 == (data1 & MASK_LOWER_NIBBLE))&&((data1 & OEM_CODE_IN_BYTE2) && (data1 & OEM_CODE_IN_BYTE3)))
987 				{
988 					if(0x02 == data2)
989 					{
990 						if(0x00 == data3)
991 						{
992 							snprintf(desc, SIZE_OF_DESC, "between BMC/iDRAC Firmware and other hardware");
993 						}
994 						else if(0x01 == data3)
995 						{
996 							snprintf(desc, SIZE_OF_DESC, "between BMC/iDRAC Firmware and CPU");
997 						}
998 					}
999 				}
1000 			break;
1001 			/* Flex or Mac tuning OEM Decoding for the DELL. */
1002 			case SENSOR_TYPE_OEM_SEC_EVENT:
1003 				/* 0x25 - Virtual MAC sensory number - Dell OEM */
1004 				if(0x25 == rec->sel_type.standard_type.sensor_num)
1005 				{
1006 					if(0x01 == (data1 & MASK_LOWER_NIBBLE))
1007 					{
1008 						snprintf(desc, SIZE_OF_DESC, "Failed to program Virtual Mac Address");
1009 						if((data1 & OEM_CODE_IN_BYTE2)&&(data1 & OEM_CODE_IN_BYTE3))
1010 						{
1011 							snprintf(tmpdesc, SIZE_OF_DESC, " at bus:%.2x device:%.2x function:%x",
1012 							data3 &0x7F, (data2 >> 3) & 0x1F,
1013 							data2 & 0x07);
1014                             strcat(desc,tmpdesc);
1015 						}
1016 					}
1017 					else if(0x02 == (data1 & MASK_LOWER_NIBBLE))
1018 					{
1019 						snprintf(desc, SIZE_OF_DESC, "Device option ROM failed to support link tuning or flex address");
1020 					}
1021 					else if(0x03 == (data1 & MASK_LOWER_NIBBLE))
1022 					{
1023 						snprintf(desc, SIZE_OF_DESC, "Failed to get link tuning or flex address data from BMC/iDRAC");
1024 					}
1025 				}
1026 			break;
1027 			case SENSOR_TYPE_CRIT_INTR:
1028 			case SENSOR_TYPE_OEM_NFATAL_ERROR:	/* Non - Fatal PCIe Express Error Decoding */
1029 			case SENSOR_TYPE_OEM_FATAL_ERROR:	/* Fatal IO Error Decoding */
1030 				/* 0x29 - QPI Linx Error Sensor Dell OEM */
1031 				if(0x29 == rec->sel_type.standard_type.sensor_num)
1032 				{
1033 					if((0x02 == (data1 & MASK_LOWER_NIBBLE))&&((data1 & OEM_CODE_IN_BYTE2) && (data1 & OEM_CODE_IN_BYTE3)))
1034 					{
1035 						snprintf(tmpdesc, SIZE_OF_DESC, "Partner-(LinkId:%d,AgentId:%d)|",(data2 & 0xC0),(data2 & 0x30));
1036 						strcat(desc,tmpdesc);
1037 						snprintf(tmpdesc, SIZE_OF_DESC, "ReportingAgent(LinkId:%d,AgentId:%d)|",(data2 & 0x0C),(data2 & 0x03));
1038 						strcat(desc,tmpdesc);
1039 						if(0x00 == (data3 & 0xFC))
1040 						{
1041 							snprintf(tmpdesc, SIZE_OF_DESC, "LinkWidthDegraded|");
1042 							strcat(desc,tmpdesc);
1043 						}
1044 						if(BIT(1)& data3)
1045 						{
1046 							snprintf(tmpdesc,SIZE_OF_DESC,"PA_Type:IOH|");
1047 						}
1048 						else
1049 						{
1050 							snprintf(tmpdesc,SIZE_OF_DESC,"PA-Type:CPU|");
1051 						}
1052 						strcat(desc,tmpdesc);
1053 						if(BIT(0)& data3)
1054 						{
1055 							snprintf(tmpdesc,SIZE_OF_DESC,"RA-Type:IOH");
1056 						}
1057 						else
1058 						{
1059 							snprintf(tmpdesc,SIZE_OF_DESC,"RA-Type:CPU");
1060 						}
1061 						strcat(desc,tmpdesc);
1062 					}
1063 				}
1064 				else
1065 				{
1066 
1067 					if(0x02 == (data1 & MASK_LOWER_NIBBLE))
1068 					{
1069 						sprintf(desc,"%s","IO channel Check NMI");
1070                     }
1071 					else
1072 					{
1073 						if(0x00 == (data1 & MASK_LOWER_NIBBLE))
1074 						{
1075 							snprintf(desc, SIZE_OF_DESC, "%s","PCIe Error |");
1076 						}
1077 						else if(0x01 == (data1 & MASK_LOWER_NIBBLE))
1078 						{
1079 							snprintf(desc, SIZE_OF_DESC, "%s","I/O Error |");
1080 						}
1081 						else if(0x04 == (data1 & MASK_LOWER_NIBBLE))
1082 						{
1083 							snprintf(desc, SIZE_OF_DESC, "%s","PCI PERR |");
1084 						}
1085 						else if(0x05 == (data1 & MASK_LOWER_NIBBLE))
1086 						{
1087 							snprintf(desc, SIZE_OF_DESC, "%s","PCI SERR |");
1088 						}
1089 						else
1090 						{
1091 							snprintf(desc, SIZE_OF_DESC, "%s"," ");
1092 						}
1093 						if (data3 & 0x80)
1094 							snprintf(tmpdesc, SIZE_OF_DESC, "Slot %d", data3 & 0x7F);
1095 						else
1096 							snprintf(tmpdesc, SIZE_OF_DESC, "PCI bus:%.2x device:%.2x function:%x",
1097 							data3 &0x7F, (data2 >> 3) & 0x1F,
1098 							data2 & 0x07);
1099 
1100 						strcat(desc,tmpdesc);
1101 					}
1102 				}
1103 			break;
1104 			/* POST Fatal Errors generated from the  Server with much more info*/
1105 			case SENSOR_TYPE_FRM_PROG:
1106 				if((0x0F == (data1 & MASK_LOWER_NIBBLE))&&(data1 & OEM_CODE_IN_BYTE2))
1107 				{
1108 					switch(data2)
1109 					{
1110 						case 0x80:
1111 							snprintf(desc, SIZE_OF_DESC, "No memory is detected.");break;
1112 						case 0x81:
1113 							snprintf(desc,SIZE_OF_DESC, "Memory is detected but is not configurable.");break;
1114 						case 0x82:
1115 							snprintf(desc, SIZE_OF_DESC, "Memory is configured but not usable.");break;
1116 						case 0x83:
1117 							snprintf(desc, SIZE_OF_DESC, "System BIOS shadow failed.");break;
1118 						case 0x84:
1119 							snprintf(desc, SIZE_OF_DESC, "CMOS failed.");break;
1120 						case 0x85:
1121 							snprintf(desc, SIZE_OF_DESC, "DMA controller failed.");break;
1122 						case 0x86:
1123 							snprintf(desc, SIZE_OF_DESC, "Interrupt controller failed.");break;
1124 						case 0x87:
1125 							snprintf(desc, SIZE_OF_DESC, "Timer refresh failed.");break;
1126 						case 0x88:
1127 							snprintf(desc, SIZE_OF_DESC, "Programmable interval timer error.");break;
1128 						case 0x89:
1129 							snprintf(desc, SIZE_OF_DESC, "Parity error.");break;
1130 						case 0x8A:
1131 							snprintf(desc, SIZE_OF_DESC, "SIO failed.");break;
1132 						case 0x8B:
1133 							snprintf(desc, SIZE_OF_DESC, "Keyboard controller failed.");break;
1134 						case 0x8C:
1135 							snprintf(desc, SIZE_OF_DESC, "System management interrupt initialization failed.");break;
1136 						case 0x8D:
1137 							snprintf(desc, SIZE_OF_DESC, "TXT-SX Error.");break;
1138 						case 0xC0:
1139 							snprintf(desc, SIZE_OF_DESC, "Shutdown test failed.");break;
1140 						case 0xC1:
1141 							snprintf(desc, SIZE_OF_DESC, "BIOS POST memory test failed.");break;
1142 						case 0xC2:
1143 							snprintf(desc, SIZE_OF_DESC, "RAC configuration failed.");break;
1144 						case 0xC3:
1145 							snprintf(desc, SIZE_OF_DESC, "CPU configuration failed.");break;
1146 						case 0xC4:
1147 							snprintf(desc, SIZE_OF_DESC, "Incorrect memory configuration.");break;
1148 						case 0xFE:
1149 							snprintf(desc, SIZE_OF_DESC, "General failure after video.");
1150 							break;
1151 					}
1152 				}
1153 			break;
1154 
1155 			default:
1156 			break;
1157 		}
1158 	}
1159 	else
1160 	{
1161 		sensor_type = rec->sel_type.standard_type.event_type;
1162 	}
1163 	return desc;
1164 }
1165 
1166 char *
1167 ipmi_get_oem_desc(struct ipmi_intf * intf, struct sel_event_record * rec)
1168 {
1169 	char * desc = NULL;
1170 
1171 	switch (ipmi_get_oem(intf))
1172 	{
1173 	case IPMI_OEM_NEWISYS:
1174 		desc = get_newisys_evt_desc(intf, rec);
1175 		break;
1176 	case IPMI_OEM_KONTRON:
1177 		desc =  get_kontron_evt_desc(intf, rec);
1178 		break;
1179 	case IPMI_OEM_DELL: // Dell Decoding of the OEM Bytes from SEL Record.
1180 		desc = get_dell_evt_desc(intf, rec);
1181 		break;
1182 	case IPMI_OEM_SUPERMICRO:
1183 	case IPMI_OEM_SUPERMICRO_47488:
1184 		desc = get_supermicro_evt_desc(intf, rec);
1185 		break;
1186 	case IPMI_OEM_UNKNOWN:
1187 	default:
1188 		break;
1189 	}
1190 
1191 	return desc;
1192 }
1193 
1194 
1195 void
1196 ipmi_get_event_desc(struct ipmi_intf * intf, struct sel_event_record * rec, char ** desc)
1197 {
1198 	uint8_t code, offset;
1199 	struct ipmi_event_sensor_types *evt = NULL;
1200 	char *sfx = NULL;	/* This will be assigned if the Platform is DELL,
1201 				 additional info is appended to the current Description */
1202 
1203 	if (desc == NULL)
1204 		return;
1205 	*desc = NULL;
1206 
1207 	if ((rec->sel_type.standard_type.event_type >= 0x70) && (rec->sel_type.standard_type.event_type < 0x7F)) {
1208 		*desc = ipmi_get_oem_desc(intf, rec);
1209 		return;
1210 	} else if (rec->sel_type.standard_type.event_type == 0x6f) {
1211 		if( rec->sel_type.standard_type.sensor_type >= 0xC0 &&  rec->sel_type.standard_type.sensor_type < 0xF0) {
1212 			IPMI_OEM iana = ipmi_get_oem(intf);
1213 
1214 			switch(iana){
1215 				case IPMI_OEM_KONTRON:
1216 					lprintf(LOG_DEBUG, "oem sensor type %x %d using oem type supplied description",
1217 		                       rec->sel_type.standard_type.sensor_type , iana);
1218 
1219 					evt = oem_kontron_event_types;
1220 					code = rec->sel_type.standard_type.sensor_type;
1221 				 break;
1222 				case IPMI_OEM_DELL:		/* OEM Bytes Decoding for DELLi */
1223 					evt = sensor_specific_types;
1224 					code = rec->sel_type.standard_type.sensor_type;
1225 				 	if ( (OEM_CODE_IN_BYTE2 == (rec->sel_type.standard_type.event_data[0] & DATA_BYTE2_SPECIFIED_MASK)) ||
1226 					     (OEM_CODE_IN_BYTE3 == (rec->sel_type.standard_type.event_data[0] & DATA_BYTE3_SPECIFIED_MASK)) )
1227 				 	{
1228 				 		if(rec->sel_type.standard_type.event_data[0] & DATA_BYTE2_SPECIFIED_MASK)
1229 						 	evt->data = rec->sel_type.standard_type.event_data[1];
1230 
1231 						 sfx = ipmi_get_oem_desc(intf, rec);
1232 				 	}
1233 				 break;
1234 				case IPMI_OEM_SUPERMICRO:
1235 				case IPMI_OEM_SUPERMICRO_47488:
1236 					evt = sensor_specific_types;
1237 					code = rec->sel_type.standard_type.sensor_type;
1238 					sfx = ipmi_get_oem_desc(intf, rec);
1239 					break;
1240 				 /* add your oem sensor assignation here */
1241 				default:
1242 					break;
1243 			}
1244 			if( evt == NULL ){
1245 				lprintf(LOG_DEBUG, "oem sensor type %x  using standard type supplied description",
1246 		                          rec->sel_type.standard_type.sensor_type );
1247 			}
1248 		} else {
1249 			switch (ipmi_get_oem(intf)) {
1250 				case IPMI_OEM_SUPERMICRO:
1251 				case IPMI_OEM_SUPERMICRO_47488:
1252 					evt = sensor_specific_types;
1253 					code = rec->sel_type.standard_type.sensor_type;
1254 					sfx = ipmi_get_oem_desc(intf, rec);
1255 				 break;
1256 				default:
1257 				 break;
1258 			}
1259 		}
1260 		if( evt == NULL ){
1261 			evt = sensor_specific_types;
1262 			code = rec->sel_type.standard_type.sensor_type;
1263 		}
1264 		/*
1265  		 * Check for the OEM DELL Interface based on the Dell Specific Vendor Code.
1266  		 * If its Dell Platform, do the OEM Byte decode from the SEL Records.
1267  		 * Additional information should be written by the ipmi_get_oem_desc()
1268  		 */
1269 		if(ipmi_get_oem(intf) == IPMI_OEM_DELL) {
1270 			code = rec->sel_type.standard_type.sensor_type;
1271 			if ( (OEM_CODE_IN_BYTE2 == (rec->sel_type.standard_type.event_data[0] & DATA_BYTE2_SPECIFIED_MASK)) ||
1272 			     (OEM_CODE_IN_BYTE3 == (rec->sel_type.standard_type.event_data[0] & DATA_BYTE3_SPECIFIED_MASK)) )
1273 			{
1274 				if(rec->sel_type.standard_type.event_data[0] & DATA_BYTE2_SPECIFIED_MASK)
1275 					evt->data = rec->sel_type.standard_type.event_data[1];
1276 					 sfx = ipmi_get_oem_desc(intf, rec);
1277 
1278 			}
1279 			else if(SENSOR_TYPE_OEM_SEC_EVENT == rec->sel_type.standard_type.event_data[0])
1280 			{
1281 				/* 0x23 : Sensor Number.*/
1282 				if(0x23 == rec->sel_type.standard_type.sensor_num)
1283 				{
1284 					evt->data = rec->sel_type.standard_type.event_data[1];
1285 					sfx = ipmi_get_oem_desc(intf, rec);
1286 				}
1287 			}
1288 		}
1289 	} else {
1290 		evt = generic_event_types;
1291 		code = rec->sel_type.standard_type.event_type;
1292 	}
1293 
1294 	offset = rec->sel_type.standard_type.event_data[0] & 0xf;
1295 
1296 	while (evt->type) {
1297 		if ((evt->code == code && evt->offset == offset && evt->desc != NULL) &&
1298 			((evt->data == ALL_OFFSETS_SPECIFIED) ||
1299 			 ((rec->sel_type.standard_type.event_data[0] & DATA_BYTE2_SPECIFIED_MASK) &&
1300 			  (evt->data == rec->sel_type.standard_type.event_data[1]))))
1301 		{
1302 			/* Increase the Malloc size to current_size + Dellspecific description size */
1303 			*desc = (char *)malloc(strlen(evt->desc) + 48 + SIZE_OF_DESC);
1304 			if (NULL == *desc) {
1305 				lprintf(LOG_ERR, "ipmitool: malloc failure");
1306 				return;
1307 			}
1308 			memset(*desc, 0, strlen(evt->desc)+ 48 + SIZE_OF_DESC);
1309 			/*
1310  			 * Additional info is present for the DELL Platforms.
1311  			 * Append the same to the evt->desc string.
1312  			 */
1313 			if (sfx) {
1314 				sprintf(*desc, "%s (%s)", evt->desc, sfx);
1315 				free(sfx);
1316 				sfx = NULL;
1317 			} else {
1318 				sprintf(*desc, "%s", evt->desc);
1319 			}
1320 			return;
1321 		}
1322 		evt++;
1323 	}
1324 	/* The Above while Condition was not met beacouse the below sensor type were Newly defined OEM
1325 	   Secondary Events. 0xC1, 0xC2, 0xC3. */
1326     if((sfx) && (0x6F == rec->sel_type.standard_type.event_type))
1327 	{
1328 	    uint8_t flag = 0x00;
1329 	    switch(code)
1330 		{
1331             case SENSOR_TYPE_FRM_PROG:
1332                  if(0x0F == offset)
1333                      flag = 0x01;
1334                  break;
1335 			case SENSOR_TYPE_OEM_SEC_EVENT:
1336 			     if((0x01 == offset) || (0x02 == offset) || (0x03 == offset))
1337                      flag = 0x01;
1338                  break;
1339             case SENSOR_TYPE_OEM_NFATAL_ERROR:
1340                  if((0x00 == offset) || (0x02 == offset))
1341                      flag = 0x01;
1342                  break;
1343             case SENSOR_TYPE_OEM_FATAL_ERROR:
1344                  if(0x01 == offset)
1345                      flag = 0x01;
1346                  break;
1347             case SENSOR_TYPE_SUPERMICRO_OEM:
1348                  flag = 0x02;
1349                  break;
1350             default:
1351                  break;
1352 		}
1353 		if(flag)
1354 		{
1355 		    *desc = (char *)malloc( 48 + SIZE_OF_DESC);
1356 		    if (NULL == *desc)
1357 			{
1358 		        lprintf(LOG_ERR, "ipmitool: malloc failure");
1359 			    return;
1360 		    }
1361 		memset(*desc, 0, 48 + SIZE_OF_DESC);
1362 		if (flag == 0x02) {
1363 			sprintf(*desc, "%s", sfx);
1364 			return;
1365 		}
1366 		sprintf(*desc, "(%s)",sfx);
1367      	}
1368 		free(sfx);
1369 		sfx = NULL;
1370 	}
1371 }
1372 
1373 
1374 const char *
1375 ipmi_sel_get_oem_sensor_type(IPMI_OEM iana, uint8_t code)
1376 {
1377 	struct ipmi_event_sensor_types *st = NULL;
1378 
1379 	switch(iana){
1380 		case IPMI_OEM_KONTRON:
1381 			st = oem_kontron_event_types;
1382 		break;
1383 		/* add you oem sensor type lookup assignement here */
1384 		default:
1385 			lprintf(LOG_DEBUG, "ipmitool: missing OEM sensor type for %ul",iana);
1386 		break;
1387 	}
1388 
1389 	if( st != NULL )
1390 		for (; st->type != NULL; st++)
1391 			if (st->code == code)
1392 				return st->type;
1393 
1394 	return ipmi_sel_get_sensor_type(code);
1395 }
1396 
1397 const char *
1398 ipmi_sel_get_oem_sensor_type_offset(IPMI_OEM iana, uint8_t code, uint8_t offset)
1399 {
1400 	struct ipmi_event_sensor_types *st = NULL;
1401 
1402 	switch(iana){
1403 		case IPMI_OEM_KONTRON:
1404 			st = oem_kontron_event_types;
1405 		break;
1406 		/* add you oem sensor type lookup assignement here */
1407 		default:
1408 			lprintf(LOG_DEBUG,
1409                       "ipmitool: missing OEM sensor type offset for %ul",iana);
1410 		break;
1411 	}
1412 
1413 	if( st != NULL )
1414 		for (; st->type != NULL; st++)
1415 		{
1416 			if (st->code == code && st->offset == (offset&0xf))
1417 				return st->type;
1418 		}
1419 
1420 	return ipmi_sel_get_oem_sensor_type(iana,code);
1421 }
1422 
1423 const char *
1424 ipmi_sel_get_sensor_type(uint8_t code)
1425 {
1426 	struct ipmi_event_sensor_types *st;
1427 	for (st = sensor_specific_types; st->type != NULL; st++)
1428 		if (st->code == code)
1429 			return st->type;
1430 	return "Unknown";
1431 }
1432 
1433 const char *
1434 ipmi_sel_get_sensor_type_offset(uint8_t code, uint8_t offset)
1435 {
1436 	struct ipmi_event_sensor_types *st;
1437 	for (st = sensor_specific_types; st->type != NULL; st++)
1438 		if (st->code == code && st->offset == (offset&0xf))
1439 			return st->type;
1440 
1441 	return ipmi_sel_get_sensor_type(code);
1442 }
1443 
1444 static int
1445 ipmi_sel_get_info(struct ipmi_intf * intf)
1446 {
1447 	struct ipmi_rs * rsp;
1448 	struct ipmi_rq req;
1449 	uint16_t e, version;
1450 	uint32_t f;
1451 	int pctfull = 0;
1452 	uint32_t fs    = 0xffffffff;
1453 	uint32_t zeros = 0;
1454 
1455 
1456 	memset(&req, 0, sizeof(req));
1457 	req.msg.netfn = IPMI_NETFN_STORAGE;
1458 	req.msg.cmd = IPMI_CMD_GET_SEL_INFO;
1459 
1460 	rsp = intf->sendrecv(intf, &req);
1461 	if (rsp == NULL) {
1462 		lprintf(LOG_ERR, "Get SEL Info command failed");
1463 		return -1;
1464 	}
1465 	if (rsp->ccode > 0) {
1466 		lprintf(LOG_ERR, "Get SEL Info command failed: %s",
1467 		       val2str(rsp->ccode, completion_code_vals));
1468 		return -1;
1469 	}
1470 	if (verbose > 2)
1471 		printbuf(rsp->data, rsp->data_len, "sel_info");
1472 
1473 	printf("SEL Information\n");
1474         version = rsp->data[0];
1475 	printf("Version          : %d.%d (%s)\n",
1476 	       version & 0xf, (version>>4) & 0xf,
1477 	       (version == 0x51 || version == 0x02) ? "v1.5, v2 compliant" : "Unknown");
1478 
1479 	/* save the entry count and free space to determine percent full */
1480 	e = buf2short(rsp->data + 1);
1481 	f = buf2short(rsp->data + 3);
1482 	printf("Entries          : %d\n", e);
1483 	printf("Free Space       : %d bytes %s\n", f ,(f==65535 ? "or more" : "" ));
1484 
1485 	if (e) {
1486 		e *= 16; /* each entry takes 16 bytes */
1487 		f += e;	/* this is supposed to give the total size ... */
1488 		pctfull = (int)(100 * ( (double)e / (double)f ));
1489 	}
1490 
1491 	if( f >= 65535 ) {
1492 		printf("Percent Used     : %s\n", "unknown" );
1493 	}
1494 	else {
1495 		printf("Percent Used     : %d%%\n", pctfull);
1496 	}
1497 
1498 
1499 	if ((!memcmp(rsp->data + 5, &fs,    4)) ||
1500 		(!memcmp(rsp->data + 5, &zeros, 4)))
1501 		printf("Last Add Time    : Not Available\n");
1502 	else
1503 		printf("Last Add Time    : %s\n",
1504 			   ipmi_sel_timestamp(buf2long(rsp->data + 5)));
1505 
1506 	if ((!memcmp(rsp->data + 9, &fs,    4)) ||
1507 		(!memcmp(rsp->data + 9, &zeros, 4)))
1508 		printf("Last Del Time    : Not Available\n");
1509 	else
1510 		printf("Last Del Time    : %s\n",
1511 			   ipmi_sel_timestamp(buf2long(rsp->data + 9)));
1512 
1513 
1514 	printf("Overflow         : %s\n",
1515 	       rsp->data[13] & 0x80 ? "true" : "false");
1516 	printf("Supported Cmds   : ");
1517         if (rsp->data[13] & 0x0f)
1518         {
1519 	        if (rsp->data[13] & 0x08)
1520                         printf("'Delete' ");
1521 	        if (rsp->data[13] & 0x04)
1522                         printf("'Partial Add' ");
1523 	        if (rsp->data[13] & 0x02)
1524                         printf("'Reserve' ");
1525 	        if (rsp->data[13] & 0x01)
1526                         printf("'Get Alloc Info' ");
1527         }
1528         else
1529                 printf("None");
1530         printf("\n");
1531 
1532 	/* get sel allocation info if supported */
1533 	if (rsp->data[13] & 1) {
1534 		memset(&req, 0, sizeof(req));
1535 		req.msg.netfn = IPMI_NETFN_STORAGE;
1536 		req.msg.cmd = IPMI_CMD_GET_SEL_ALLOC_INFO;
1537 
1538 		rsp = intf->sendrecv(intf, &req);
1539 		if (rsp == NULL) {
1540 			lprintf(LOG_ERR,
1541 				"Get SEL Allocation Info command failed");
1542 			return -1;
1543 		}
1544 		if (rsp->ccode > 0) {
1545 			lprintf(LOG_ERR,
1546 				"Get SEL Allocation Info command failed: %s",
1547 				val2str(rsp->ccode, completion_code_vals));
1548 			return -1;
1549 		}
1550 
1551 		printf("# of Alloc Units : %d\n", buf2short(rsp->data));
1552 		printf("Alloc Unit Size  : %d\n", buf2short(rsp->data + 2));
1553 		printf("# Free Units     : %d\n", buf2short(rsp->data + 4));
1554 		printf("Largest Free Blk : %d\n", buf2short(rsp->data + 6));
1555 		printf("Max Record Size  : %d\n", rsp->data[8]);
1556 	}
1557 	return 0;
1558 }
1559 
1560 uint16_t
1561 ipmi_sel_get_std_entry(struct ipmi_intf * intf, uint16_t id,
1562 		       struct sel_event_record * evt)
1563 {
1564 	struct ipmi_rq req;
1565 	struct ipmi_rs * rsp;
1566 	uint8_t msg_data[6];
1567 	uint16_t next;
1568 	int data_count;
1569 
1570 	memset(msg_data, 0, 6);
1571 	msg_data[0] = 0x00;	/* no reserve id, not partial get */
1572 	msg_data[1] = 0x00;
1573 	msg_data[2] = id & 0xff;
1574 	msg_data[3] = (id >> 8) & 0xff;
1575 	msg_data[4] = 0x00;	/* offset */
1576 	msg_data[5] = 0xff;	/* length */
1577 
1578 	memset(&req, 0, sizeof(req));
1579 	req.msg.netfn = IPMI_NETFN_STORAGE;
1580 	req.msg.cmd = IPMI_CMD_GET_SEL_ENTRY;
1581 	req.msg.data = msg_data;
1582 	req.msg.data_len = 6;
1583 
1584 	rsp = intf->sendrecv(intf, &req);
1585 	if (rsp == NULL) {
1586 		lprintf(LOG_ERR, "Get SEL Entry %x command failed", id);
1587 		return 0;
1588 	}
1589 	if (rsp->ccode > 0) {
1590 		lprintf(LOG_ERR, "Get SEL Entry %x command failed: %s",
1591 			id, val2str(rsp->ccode, completion_code_vals));
1592 		return 0;
1593 	}
1594 
1595 	/* save next entry id */
1596 	next = (rsp->data[1] << 8) | rsp->data[0];
1597 
1598 	lprintf(LOG_DEBUG, "SEL Entry: %s", buf2str(rsp->data+2, rsp->data_len-2));
1599 	memset(evt, 0, sizeof(*evt));
1600 
1601 	/*Clear SEL Structure*/
1602 	evt->record_id = 0;
1603 	evt->record_type = 0;
1604 	if (evt->record_type < 0xc0)
1605 	{
1606 		evt->sel_type.standard_type.timestamp = 0;
1607 		evt->sel_type.standard_type.gen_id = 0;
1608 		evt->sel_type.standard_type.evm_rev = 0;
1609 		evt->sel_type.standard_type.sensor_type = 0;
1610 		evt->sel_type.standard_type.sensor_num = 0;
1611 		evt->sel_type.standard_type.event_type = 0;
1612 		evt->sel_type.standard_type.event_dir = 0;
1613 		evt->sel_type.standard_type.event_data[0] = 0;
1614 		evt->sel_type.standard_type.event_data[1] = 0;
1615 		evt->sel_type.standard_type.event_data[2] = 0;
1616 	}
1617 	else if (evt->record_type < 0xe0)
1618 	{
1619 		evt->sel_type.oem_ts_type.timestamp = 0;
1620 		evt->sel_type.oem_ts_type.manf_id[0] = 0;
1621 		evt->sel_type.oem_ts_type.manf_id[1] = 0;
1622 		evt->sel_type.oem_ts_type.manf_id[2] = 0;
1623 		for(data_count=0; data_count < SEL_OEM_TS_DATA_LEN ; data_count++)
1624 			evt->sel_type.oem_ts_type.oem_defined[data_count] = 0;
1625 	}
1626 	else
1627 	{
1628 		for(data_count=0; data_count < SEL_OEM_NOTS_DATA_LEN ; data_count++)
1629 			evt->sel_type.oem_nots_type.oem_defined[data_count] = 0;
1630 	}
1631 
1632 	/* save response into SEL event structure */
1633 	evt->record_id = (rsp->data[3] << 8) | rsp->data[2];
1634 	evt->record_type = rsp->data[4];
1635 	if (evt->record_type < 0xc0)
1636 	{
1637     		evt->sel_type.standard_type.timestamp = (rsp->data[8] << 24) |	(rsp->data[7] << 16) |
1638     			(rsp->data[6] << 8) | rsp->data[5];
1639     		evt->sel_type.standard_type.gen_id = (rsp->data[10] << 8) | rsp->data[9];
1640     		evt->sel_type.standard_type.evm_rev = rsp->data[11];
1641     		evt->sel_type.standard_type.sensor_type = rsp->data[12];
1642     		evt->sel_type.standard_type.sensor_num = rsp->data[13];
1643     		evt->sel_type.standard_type.event_type = rsp->data[14] & 0x7f;
1644     		evt->sel_type.standard_type.event_dir = (rsp->data[14] & 0x80) >> 7;
1645     		evt->sel_type.standard_type.event_data[0] = rsp->data[15];
1646     		evt->sel_type.standard_type.event_data[1] = rsp->data[16];
1647     		evt->sel_type.standard_type.event_data[2] = rsp->data[17];
1648   	}
1649   	else if (evt->record_type < 0xe0)
1650   	{
1651     		evt->sel_type.oem_ts_type.timestamp= (rsp->data[8] << 24) |	(rsp->data[7] << 16) |
1652     			(rsp->data[6] << 8) | rsp->data[5];
1653 		evt->sel_type.oem_ts_type.manf_id[0]= rsp->data[11];
1654 		evt->sel_type.oem_ts_type.manf_id[1]= rsp->data[10];
1655 		evt->sel_type.oem_ts_type.manf_id[2]= rsp->data[9];
1656   		for(data_count=0; data_count < SEL_OEM_TS_DATA_LEN ; data_count++)
1657       			evt->sel_type.oem_ts_type.oem_defined[data_count] = rsp->data[(data_count+12)];
1658   	}
1659   	else
1660   	{
1661   		for(data_count=0; data_count < SEL_OEM_NOTS_DATA_LEN ; data_count++)
1662       			evt->sel_type.oem_nots_type.oem_defined[data_count] = rsp->data[(data_count+5)];
1663 	}
1664 	return next;
1665 }
1666 
1667 static void
1668 ipmi_sel_print_event_file(struct ipmi_intf * intf, struct sel_event_record * evt, FILE * fp)
1669 {
1670 	char * description;
1671 
1672 	if (fp == NULL)
1673 		return;
1674 
1675 	ipmi_get_event_desc(intf, evt, &description);
1676 
1677 	fprintf(fp, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x # %s #0x%02x %s\n",
1678 		evt->sel_type.standard_type.evm_rev,
1679 		evt->sel_type.standard_type.sensor_type,
1680 		evt->sel_type.standard_type.sensor_num,
1681 		evt->sel_type.standard_type.event_type | (evt->sel_type.standard_type.event_dir << 7),
1682 		evt->sel_type.standard_type.event_data[0],
1683 		evt->sel_type.standard_type.event_data[1],
1684 		evt->sel_type.standard_type.event_data[2],
1685       (
1686 			(evt->sel_type.standard_type.sensor_type >=0xC0 && evt->sel_type.standard_type.sensor_type < 0xF0)
1687 			?
1688    		ipmi_sel_get_oem_sensor_type_offset(ipmi_get_oem(intf),evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0])
1689 			:
1690 			ipmi_sel_get_sensor_type_offset(evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0])
1691       ),
1692 		evt->sel_type.standard_type.sensor_num,
1693 		(description != NULL) ? description : "Unknown");
1694 
1695 	if (description != NULL) {
1696 		free(description);
1697 		description = NULL;
1698 	}
1699 }
1700 
1701 void
1702 ipmi_sel_print_extended_entry(struct ipmi_intf * intf, struct sel_event_record * evt)
1703 {
1704 	sel_extended++;
1705 	ipmi_sel_print_std_entry(intf, evt);
1706 	sel_extended--;
1707 }
1708 
1709 void
1710 ipmi_sel_print_std_entry(struct ipmi_intf * intf, struct sel_event_record * evt)
1711 {
1712 	char * description;
1713 	struct sdr_record_list * sdr = NULL;
1714 	int data_count;
1715 
1716 	if (sel_extended && (evt->record_type < 0xc0))
1717 		sdr = ipmi_sdr_find_sdr_bynumtype(intf, evt->sel_type.standard_type.gen_id, evt->sel_type.standard_type.sensor_num, evt->sel_type.standard_type.sensor_type);
1718 
1719 
1720 	if (!evt)
1721 		return;
1722 
1723 	if (csv_output)
1724 		printf("%x,", evt->record_id);
1725 	else
1726 		printf("%4x | ", evt->record_id);
1727 
1728 	if (evt->record_type == 0xf0)
1729 	{
1730 		if (csv_output)
1731 			printf(",,");
1732 
1733 		printf ("Linux kernel panic: %.11s\n", (char *) evt + 5);
1734 		return;
1735 	}
1736 
1737 	if (evt->record_type < 0xe0)
1738 	{
1739 		if ((evt->sel_type.standard_type.timestamp < 0x20000000)||(evt->sel_type.oem_ts_type.timestamp <  0x20000000)){
1740 			printf(" Pre-Init ");
1741 
1742 			if (csv_output)
1743 				printf(",");
1744 			else
1745 				printf(" |");
1746 
1747 			printf("%010d", evt->sel_type.standard_type.timestamp );
1748 			if (csv_output)
1749 				printf(",");
1750 			else
1751 				printf("| ");
1752 		}
1753 		else {
1754 			if (evt->record_type < 0xc0)
1755 				printf("%s", ipmi_sel_timestamp_date(evt->sel_type.standard_type.timestamp));
1756 			else
1757         			printf("%s", ipmi_sel_timestamp_date(evt->sel_type.oem_ts_type.timestamp));
1758 			if (csv_output)
1759 				printf(",");
1760 			else
1761 				printf(" | ");
1762 
1763 			if (evt->record_type < 0xc0)
1764 				printf("%s", ipmi_sel_timestamp_time(evt->sel_type.standard_type.timestamp));
1765 			else
1766         			printf("%s", ipmi_sel_timestamp_time(evt->sel_type.oem_ts_type.timestamp));
1767 
1768 			if (csv_output)
1769 				printf(",");
1770 			else
1771 				printf(" | ");
1772 		}
1773 
1774 	}
1775 	else
1776 	{
1777 		if (csv_output)
1778 			printf(",,");
1779 	}
1780 
1781 	if (evt->record_type >= 0xc0)
1782 	{
1783 		printf ("OEM record %02x", evt->record_type);
1784 		if (csv_output)
1785 			printf(",");
1786 		else
1787 			printf(" | ");
1788 
1789 		if(evt->record_type <= 0xdf)
1790 		{
1791 			printf ("%02x%02x%02x", evt->sel_type.oem_ts_type.manf_id[0], evt->sel_type.oem_ts_type.manf_id[1], evt->sel_type.oem_ts_type.manf_id[2]);
1792 			if (csv_output)
1793 				printf(",");
1794 			else
1795 				printf(" | ");
1796 			for(data_count=0;data_count < SEL_OEM_TS_DATA_LEN;data_count++)
1797 				printf("%02x", evt->sel_type.oem_ts_type.oem_defined[data_count]);
1798 		}
1799 		else
1800 		{
1801 			for(data_count=0;data_count < SEL_OEM_NOTS_DATA_LEN;data_count++)
1802 				printf("%02x", evt->sel_type.oem_nots_type.oem_defined[data_count]);
1803 		}
1804 		ipmi_sel_oem_message(evt, 0);
1805 		printf ("\n");
1806 		return;
1807 	}
1808 
1809 	/* lookup SDR entry based on sensor number and type */
1810 	if (sdr != NULL) {
1811 		printf("%s ",
1812 	   (
1813 			(evt->sel_type.standard_type.sensor_type >=0xC0 && evt->sel_type.standard_type.sensor_type < 0xF0)
1814 			?
1815    		ipmi_sel_get_oem_sensor_type_offset(ipmi_get_oem(intf),evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0])
1816 			:
1817 			ipmi_sel_get_sensor_type_offset(evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0])
1818 			)
1819 		);
1820 		switch (sdr->type) {
1821 		case SDR_RECORD_TYPE_FULL_SENSOR:
1822 			printf("%s", sdr->record.full->id_string);
1823 			break;
1824 		case SDR_RECORD_TYPE_COMPACT_SENSOR:
1825 			printf("%s", sdr->record.compact->id_string);
1826 			break;
1827 		case SDR_RECORD_TYPE_EVENTONLY_SENSOR:
1828 			printf("%s", sdr->record.eventonly->id_string);
1829 			break;
1830 		case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR:
1831 			printf("%s", sdr->record.fruloc->id_string);
1832 			break;
1833 		case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR:
1834 			printf("%s", sdr->record.mcloc->id_string);
1835 			break;
1836 		case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR:
1837 			printf("%s", sdr->record.genloc->id_string);
1838 			break;
1839 		default:
1840 			printf("#%02x", evt->sel_type.standard_type.sensor_num);
1841 			break;
1842 		}
1843 	} else {
1844 		printf("%s",(
1845 			(evt->sel_type.standard_type.sensor_type >=0xC0 && evt->sel_type.standard_type.sensor_type < 0xF0)
1846 			?
1847    		ipmi_sel_get_oem_sensor_type_offset(ipmi_get_oem(intf),evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0])
1848 			:
1849 			ipmi_sel_get_sensor_type_offset(evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0])
1850 		));
1851 		if (evt->sel_type.standard_type.sensor_num != 0)
1852 			printf(" #0x%02x", evt->sel_type.standard_type.sensor_num);
1853 	}
1854 
1855 	if (csv_output)
1856 		printf(",");
1857 	else
1858 		printf(" | ");
1859 
1860 	ipmi_get_event_desc(intf, evt, &description);
1861 	if (description) {
1862 		printf("%s", description);
1863 		free(description);
1864 		description = NULL;
1865 	}
1866 
1867 	if (csv_output) {
1868 		printf(",");
1869 	} else {
1870 		printf(" | ");
1871 	}
1872 
1873 	if (evt->sel_type.standard_type.event_dir) {
1874 		printf("Deasserted");
1875 	} else {
1876 		printf("Asserted");
1877 	}
1878 
1879 	if (sdr != NULL && evt->sel_type.standard_type.event_type == 1) {
1880 		/*
1881 		 * Threshold Event
1882 		 */
1883 		float trigger_reading = 0.0;
1884 		float threshold_reading = 0.0;
1885 		uint8_t threshold_reading_provided = 0;
1886 
1887 		/* trigger reading in event data byte 2 */
1888 		if (((evt->sel_type.standard_type.event_data[0] >> 6) & 3) == 1) {
1889 			trigger_reading = sdr_convert_sensor_reading(
1890 				sdr->record.full, evt->sel_type.standard_type.event_data[1]);
1891 		}
1892 
1893 		/* trigger threshold in event data byte 3 */
1894 		if (((evt->sel_type.standard_type.event_data[0] >> 4) & 3) == 1) {
1895 			threshold_reading = sdr_convert_sensor_reading(
1896 				sdr->record.full, evt->sel_type.standard_type.event_data[2]);
1897 			threshold_reading_provided = 1;
1898 		}
1899 
1900 		if (csv_output)
1901 			printf(",");
1902 		else
1903 			printf(" | ");
1904 
1905 		printf("Reading %.*f",
1906 				(trigger_reading==(int)trigger_reading) ? 0 : 2,
1907 				trigger_reading);
1908 		if (threshold_reading_provided) {
1909 			printf(" %s Threshold %.*f %s",
1910 					((evt->sel_type.standard_type.event_data[0] & 0xf) % 2) ? ">" : "<",
1911 					(threshold_reading==(int)threshold_reading) ? 0 : 2,
1912 					threshold_reading,
1913 					ipmi_sdr_get_unit_string(sdr->record.common->unit.pct,
1914 						sdr->record.common->unit.modifier,
1915 						sdr->record.common->unit.type.base,
1916 						sdr->record.common->unit.type.modifier));
1917 		}
1918 	}
1919 	else if (evt->sel_type.standard_type.event_type == 0x6f) {
1920 		int print_sensor = 1;
1921 		switch (ipmi_get_oem(intf)) {
1922 			case IPMI_OEM_SUPERMICRO:
1923 			case IPMI_OEM_SUPERMICRO_47488:
1924 				print_sensor = 0;
1925 			 break;
1926 			default:
1927 			 break;
1928 		}
1929 		/*
1930 		 * Sensor-Specific Discrete
1931 		 */
1932 		if (print_sensor && evt->sel_type.standard_type.sensor_type == 0xC && /*TODO*/
1933 		    evt->sel_type.standard_type.sensor_num == 0 &&
1934 		    (evt->sel_type.standard_type.event_data[0] & 0x30) == 0x20) {
1935 			/* break down memory ECC reporting if we can */
1936 			if (csv_output)
1937 				printf(",");
1938 			else
1939 				printf(" | ");
1940 
1941 			printf("CPU %d DIMM %d",
1942 			       evt->sel_type.standard_type.event_data[2] & 0x0f,
1943 			       (evt->sel_type.standard_type.event_data[2] & 0xf0) >> 4);
1944 		}
1945 	}
1946 
1947 	printf("\n");
1948 }
1949 
1950 void
1951 ipmi_sel_print_std_entry_verbose(struct ipmi_intf * intf, struct sel_event_record * evt)
1952 {
1953   char * description;
1954   int data_count;
1955 
1956 	if (!evt)
1957 		return;
1958 
1959 	printf("SEL Record ID          : %04x\n", evt->record_id);
1960 
1961 	if (evt->record_type == 0xf0)
1962 	{
1963 		printf (" Record Type           : Linux kernel panic (OEM record %02x)\n", evt->record_type);
1964 		printf (" Panic string          : %.11s\n\n", (char *) evt + 5);
1965 		return;
1966 	}
1967 
1968 	printf(" Record Type           : %02x", evt->record_type);
1969 	if (evt->record_type >= 0xc0)
1970 	{
1971 		if (evt->record_type < 0xe0)
1972 			printf("  (OEM timestamped)");
1973 		else
1974 			printf("  (OEM non-timestamped)");
1975 	}
1976 	printf("\n");
1977 
1978 	if (evt->record_type < 0xe0)
1979 	{
1980 		printf(" Timestamp             : ");
1981 		if (evt->record_type < 0xc0)
1982 			printf("%s %s", ipmi_sel_timestamp_date(evt->sel_type.standard_type.timestamp),
1983 				ipmi_sel_timestamp_time(evt->sel_type.standard_type.timestamp));
1984 		else
1985 			printf("%s %s", ipmi_sel_timestamp_date(evt->sel_type.oem_ts_type.timestamp),
1986 				ipmi_sel_timestamp_time(evt->sel_type.oem_ts_type.timestamp));
1987 		printf("\n");
1988 	}
1989 
1990 	if (evt->record_type >= 0xc0)
1991 	{
1992 		if(evt->record_type <= 0xdf)
1993 		{
1994 			printf (" Manufactacturer ID    : %02x%02x%02x\n", evt->sel_type.oem_ts_type.manf_id[0],
1995 			evt->sel_type.oem_ts_type.manf_id[1], evt->sel_type.oem_ts_type.manf_id[2]);
1996 			printf (" OEM Defined           : ");
1997 			for(data_count=0;data_count < SEL_OEM_TS_DATA_LEN;data_count++)
1998 				printf("%02x", evt->sel_type.oem_ts_type.oem_defined[data_count]);
1999 			printf(" [%s]\n\n",hex2ascii (evt->sel_type.oem_ts_type.oem_defined, SEL_OEM_TS_DATA_LEN));
2000 		}
2001 		else
2002 		{
2003 			printf (" OEM Defined           : ");
2004 			for(data_count=0;data_count < SEL_OEM_NOTS_DATA_LEN;data_count++)
2005 				printf("%02x", evt->sel_type.oem_nots_type.oem_defined[data_count]);
2006 			printf(" [%s]\n\n",hex2ascii (evt->sel_type.oem_nots_type.oem_defined, SEL_OEM_NOTS_DATA_LEN));
2007 			ipmi_sel_oem_message(evt, 1);
2008 		}
2009 		return;
2010 	}
2011 
2012 	printf(" Generator ID          : %04x\n",
2013 	       evt->sel_type.standard_type.gen_id);
2014 	printf(" EvM Revision          : %02x\n",
2015 	       evt->sel_type.standard_type.evm_rev);
2016 	printf(" Sensor Type           : %s\n",
2017    (
2018 			(evt->sel_type.standard_type.sensor_type >=0xC0 && evt->sel_type.standard_type.sensor_type < 0xF0)
2019 			?
2020    		ipmi_sel_get_oem_sensor_type_offset(ipmi_get_oem(intf),evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0])
2021 			:
2022 			ipmi_sel_get_sensor_type_offset(evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0])
2023 			)
2024 	);
2025 	printf(" Sensor Number         : %02x\n",
2026 	       evt->sel_type.standard_type.sensor_num);
2027 	printf(" Event Type            : %s\n",
2028 	       ipmi_get_event_type(evt->sel_type.standard_type.event_type));
2029 	printf(" Event Direction       : %s\n",
2030 	       val2str(evt->sel_type.standard_type.event_dir, event_dir_vals));
2031 	printf(" Event Data            : %02x%02x%02x\n",
2032 	       evt->sel_type.standard_type.event_data[0], evt->sel_type.standard_type.event_data[1], evt->sel_type.standard_type.event_data[2]);
2033         ipmi_get_event_desc(intf, evt, &description);
2034 	printf(" Description           : %s\n",
2035                description ? description : "");
2036         free(description);
2037 				description = NULL;
2038 
2039 	printf("\n");
2040 }
2041 
2042 
2043 void
2044 ipmi_sel_print_extended_entry_verbose(struct ipmi_intf * intf, struct sel_event_record * evt)
2045 {
2046 	struct sdr_record_list * sdr;
2047 	char * description;
2048 
2049 	if (!evt)
2050 		return;
2051 
2052 	sdr = ipmi_sdr_find_sdr_bynumtype(intf,
2053 					  evt->sel_type.standard_type.gen_id,
2054 					  evt->sel_type.standard_type.sensor_num,
2055 					  evt->sel_type.standard_type.sensor_type);
2056 	if (sdr == NULL)
2057 	{
2058 	    ipmi_sel_print_std_entry_verbose(intf, evt);
2059 		return;
2060 	}
2061 
2062 	printf("SEL Record ID          : %04x\n", evt->record_id);
2063 
2064 	if (evt->record_type == 0xf0)
2065 	{
2066 		printf (" Record Type           : "
2067 			"Linux kernel panic (OEM record %02x)\n",
2068 			evt->record_type);
2069 		printf (" Panic string          : %.11s\n\n",
2070 			(char *) evt + 5);
2071 		return;
2072 	}
2073 
2074 	printf(" Record Type           : %02x\n", evt->record_type);
2075 	if (evt->record_type < 0xe0)
2076 	{
2077 		printf(" Timestamp             : ");
2078 		printf("%s %s\n", ipmi_sel_timestamp_date(evt->sel_type.standard_type.timestamp),
2079 		ipmi_sel_timestamp_time(evt->sel_type.standard_type.timestamp));
2080 	}
2081 
2082 
2083 	printf(" Generator ID          : %04x\n",
2084 	       evt->sel_type.standard_type.gen_id);
2085 	printf(" EvM Revision          : %02x\n",
2086 	       evt->sel_type.standard_type.evm_rev);
2087 	printf(" Sensor Type           : %s\n",
2088 	       ipmi_sel_get_sensor_type_offset(evt->sel_type.standard_type.sensor_type, evt->sel_type.standard_type.event_data[0]));
2089 	printf(" Sensor Number         : %02x\n",
2090 	       evt->sel_type.standard_type.sensor_num);
2091 	printf(" Event Type            : %s\n",
2092 	       ipmi_get_event_type(evt->sel_type.standard_type.event_type));
2093 	printf(" Event Direction       : %s\n",
2094 	       val2str(evt->sel_type.standard_type.event_dir, event_dir_vals));
2095 	printf(" Event Data (RAW)      : %02x%02x%02x\n",
2096 	       evt->sel_type.standard_type.event_data[0], evt->sel_type.standard_type.event_data[1], evt->sel_type.standard_type.event_data[2]);
2097 
2098 	/* break down event data field
2099 	 * as per IPMI Spec 2.0 Table 29-6 */
2100 	if (evt->sel_type.standard_type.event_type == 1 && sdr->type == SDR_RECORD_TYPE_FULL_SENSOR) {
2101 		/* Threshold */
2102 		switch ((evt->sel_type.standard_type.event_data[0] >> 6) & 3) {  /* EV1[7:6] */
2103 		case 0:
2104 			/* unspecified byte 2 */
2105 			break;
2106 		case 1:
2107 			/* trigger reading in byte 2 */
2108 			printf(" Trigger Reading       : %.3f",
2109 			       sdr_convert_sensor_reading(sdr->record.full,
2110 							  evt->sel_type.standard_type.event_data[1]));
2111 			/* determine units with possible modifiers */
2112 			printf ("%s\n", ipmi_sdr_get_unit_string(sdr->record.common->unit.pct,
2113 								 sdr->record.common->unit.modifier,
2114 								 sdr->record.common->unit.type.base,
2115 								 sdr->record.common->unit.type.modifier));
2116 			break;
2117 		case 2:
2118 			/* oem code in byte 2 */
2119 			printf(" OEM Data              : %02x\n",
2120 			       evt->sel_type.standard_type.event_data[1]);
2121 			break;
2122 		case 3:
2123 			/* sensor-specific extension code in byte 2 */
2124 			printf(" Sensor Extension Code : %02x\n",
2125 			       evt->sel_type.standard_type.event_data[1]);
2126 			break;
2127 		}
2128 		switch ((evt->sel_type.standard_type.event_data[0] >> 4) & 3) {   /* EV1[5:4] */
2129 		case 0:
2130 			/* unspecified byte 3 */
2131 			break;
2132 		case 1:
2133 			/* trigger threshold value in byte 3 */
2134 			printf(" Trigger Threshold     : %.3f",
2135 			       sdr_convert_sensor_reading(sdr->record.full,
2136 							  evt->sel_type.standard_type.event_data[2]));
2137 			/* determine units with possible modifiers */
2138 			printf ("%s\n", ipmi_sdr_get_unit_string(sdr->record.common->unit.pct,
2139 								 sdr->record.common->unit.modifier,
2140 								 sdr->record.common->unit.type.base,
2141 								 sdr->record.common->unit.type.modifier));
2142 			break;
2143 		case 2:
2144 			/* OEM code in byte 3 */
2145 			printf(" OEM Data              : %02x\n",
2146 			       evt->sel_type.standard_type.event_data[2]);
2147 			break;
2148 		case 3:
2149 			/* sensor-specific extension code in byte 3 */
2150 			printf(" Sensor Extension Code : %02x\n",
2151 			       evt->sel_type.standard_type.event_data[2]);
2152 			break;
2153 		}
2154 	} else if (evt->sel_type.standard_type.event_type >= 0x2 && evt->sel_type.standard_type.event_type <= 0xc) {
2155 		/* Generic Discrete */
2156 	} else if (evt->sel_type.standard_type.event_type == 0x6f) {
2157 
2158 		/* Sensor-Specific Discrete */
2159 		if (evt->sel_type.standard_type.sensor_type == 0xC &&
2160 		    evt->sel_type.standard_type.sensor_num  == 0 &&			 /**** THIS LOOK TO BE OEM ****/
2161 		    (evt->sel_type.standard_type.event_data[0] & 0x30) == 0x20)
2162 		{
2163 			/* break down memory ECC reporting if we can */
2164 			printf(" Event Data            : CPU %d DIMM %d\n",
2165 			       evt->sel_type.standard_type.event_data[2] & 0x0f,
2166 			       (evt->sel_type.standard_type.event_data[2] & 0xf0) >> 4);
2167 		}
2168 		else if(
2169 				evt->sel_type.standard_type.sensor_type == 0x2b &&   /* Version change */
2170 				evt->sel_type.standard_type.event_data[0] == 0xC1	 /* Data in Data 2 */
2171 			   )
2172 
2173 		{
2174 			//evt->sel_type.standard_type.event_data[1]
2175 		}
2176 		else
2177 		{
2178 			/* FIXME : Add sensor specific discrete types */
2179 			printf(" Event Interpretation  : Missing\n");
2180 		}
2181 	} else if (evt->sel_type.standard_type.event_type >= 0x70 && evt->sel_type.standard_type.event_type <= 0x7f) {
2182 		/* OEM */
2183 	} else {
2184 		printf(" Event Data            : %02x%02x%02x\n",
2185 		       evt->sel_type.standard_type.event_data[0], evt->sel_type.standard_type.event_data[1], evt->sel_type.standard_type.event_data[2]);
2186 	}
2187 
2188         ipmi_get_event_desc(intf, evt, &description);
2189 	printf(" Description           : %s\n",
2190                description ? description : "");
2191         free(description);
2192 				description = NULL;
2193 
2194 	printf("\n");
2195 }
2196 
2197 static int
2198 __ipmi_sel_savelist_entries(struct ipmi_intf * intf, int count, const char * savefile,
2199 							int binary)
2200 {
2201 	struct ipmi_rs * rsp;
2202 	struct ipmi_rq req;
2203 	uint16_t next_id = 0, curr_id = 0;
2204 	struct sel_event_record evt;
2205 	int n=0;
2206 	FILE * fp = NULL;
2207 
2208 	memset(&req, 0, sizeof(req));
2209 	req.msg.netfn = IPMI_NETFN_STORAGE;
2210 	req.msg.cmd = IPMI_CMD_GET_SEL_INFO;
2211 
2212 	rsp = intf->sendrecv(intf, &req);
2213 	if (rsp == NULL) {
2214 		lprintf(LOG_ERR, "Get SEL Info command failed");
2215 		return -1;
2216 	}
2217 	if (rsp->ccode > 0) {
2218 		lprintf(LOG_ERR, "Get SEL Info command failed: %s",
2219 		       val2str(rsp->ccode, completion_code_vals));
2220 		return -1;
2221 	}
2222 	if (verbose > 2)
2223 		printbuf(rsp->data, rsp->data_len, "sel_info");
2224 
2225 	if (rsp->data[1] == 0 && rsp->data[2] == 0) {
2226 		lprintf(LOG_ERR, "SEL has no entries");
2227 		return 0;
2228 	}
2229 
2230 	memset(&req, 0, sizeof(req));
2231 	req.msg.netfn = IPMI_NETFN_STORAGE;
2232 	req.msg.cmd = IPMI_CMD_RESERVE_SEL;
2233 
2234 	rsp = intf->sendrecv(intf, &req);
2235 	if (rsp == NULL) {
2236 		lprintf(LOG_ERR, "Reserve SEL command failed");
2237 		return -1;
2238 	}
2239 	if (rsp->ccode > 0) {
2240 		lprintf(LOG_ERR, "Reserve SEL command failed: %s",
2241 		       val2str(rsp->ccode, completion_code_vals));
2242 		return -1;
2243 	}
2244 
2245 	if (count < 0) {
2246 		/** Show only the most recent 'count' records. */
2247 		int delta;
2248 		uint16_t entries;
2249 
2250 		req.msg.cmd = IPMI_CMD_GET_SEL_INFO;
2251 		rsp = intf->sendrecv(intf, &req);
2252 		if (rsp == NULL) {
2253 			lprintf(LOG_ERR, "Get SEL Info command failed");
2254 			return -1;
2255 		}
2256 		if (rsp->ccode > 0) {
2257 			lprintf(LOG_ERR, "Get SEL Info command failed: %s",
2258 				val2str(rsp->ccode, completion_code_vals));
2259 			return -1;
2260 		}
2261 		entries = buf2short(rsp->data + 1);
2262 		if (-count > entries)
2263 			count = -entries;
2264 
2265 		/* Get first record. */
2266 		next_id = ipmi_sel_get_std_entry(intf, 0, &evt);
2267 
2268 		delta = next_id - evt.record_id;
2269 
2270 		/* Get last record. */
2271 		next_id = ipmi_sel_get_std_entry(intf, 0xffff, &evt);
2272 
2273 		next_id = evt.record_id + count * delta + delta;
2274 	}
2275 
2276 	if (savefile != NULL) {
2277 		fp = ipmi_open_file_write(savefile);
2278 	}
2279 
2280 	while (next_id != 0xffff) {
2281 		curr_id = next_id;
2282 		lprintf(LOG_DEBUG, "SEL Next ID: %04x", curr_id);
2283 
2284 		next_id = ipmi_sel_get_std_entry(intf, curr_id, &evt);
2285 		if (next_id == 0) {
2286 			/*
2287 			 * usually next_id of zero means end but
2288 			 * retry because some hardware has quirks
2289 			 * and will return 0 randomly.
2290 			 */
2291 			next_id = ipmi_sel_get_std_entry(intf, curr_id, &evt);
2292 			if (next_id == 0)
2293 				break;
2294 		}
2295 
2296 		if (verbose)
2297 			ipmi_sel_print_std_entry_verbose(intf, &evt);
2298 		else
2299 			ipmi_sel_print_std_entry(intf, &evt);
2300 
2301 		if (fp != NULL) {
2302 			if (binary)
2303 				fwrite(&evt, 1, 16, fp);
2304 			else
2305 				ipmi_sel_print_event_file(intf, &evt, fp);
2306 		}
2307 
2308 		if (++n == count) {
2309 			break;
2310 		}
2311 	}
2312 
2313 	if (fp != NULL)
2314 		fclose(fp);
2315 
2316 	return 0;
2317 }
2318 
2319 static int
2320 ipmi_sel_list_entries(struct ipmi_intf * intf, int count)
2321 {
2322 	return __ipmi_sel_savelist_entries(intf, count, NULL, 0);
2323 }
2324 
2325 static int
2326 ipmi_sel_save_entries(struct ipmi_intf * intf, int count, const char * savefile)
2327 {
2328 	return __ipmi_sel_savelist_entries(intf, count, savefile, 0);
2329 }
2330 
2331 /*
2332  * ipmi_sel_interpret
2333  *
2334  * return 0 on success,
2335  *        -1 on error
2336  */
2337 static int
2338 ipmi_sel_interpret(struct ipmi_intf *intf, unsigned long iana,
2339 		const char *readfile, const char *format)
2340 {
2341 	FILE *fp = 0;
2342 	struct sel_event_record evt;
2343 	char *buffer = NULL;
2344 	char *cursor = NULL;
2345 	int status = 0;
2346 	/* since the interface is not used, iana is taken from
2347 	 * the command line
2348 	 */
2349 	sel_iana = iana;
2350 	if (strncmp("pps", format, 3) == 0) {
2351 		/* Parser for the following format */
2352 		/* 0x001F: Event: at Mar 27 06:41:10 2007;from:(0x9a,0,7);
2353 		 * sensor:(0xc3,119); event:0x6f(asserted): 0xA3 0x00 0x88
2354 		 * commonly found in PPS shelf managers
2355 		 * Supports a tweak for hotswap events that are already interpreted.
2356 		 */
2357 		fp = ipmi_open_file(readfile, 0);
2358 		if (fp == NULL) {
2359 			lprintf(LOG_ERR, "Failed to open file '%s' for reading.",
2360 					readfile);
2361 			return (-1);
2362 		}
2363 		buffer = (char *)malloc((size_t)256);
2364 		if (buffer == NULL) {
2365 			lprintf(LOG_ERR, "ipmitool: malloc failure");
2366 			fclose(fp);
2367 			return (-1);
2368 		}
2369 		do {
2370 			/* Only allow complete lines to be parsed,
2371 			 * hardcoded maximum line length
2372 			 */
2373 			if (fgets(buffer, 256, fp) == NULL) {
2374 				status = (-1);
2375 				break;
2376 			}
2377 			if (strlen(buffer) > 255) {
2378 				lprintf(LOG_ERR, "ipmitool: invalid entry found in file.");
2379 				continue;
2380 			}
2381 			cursor = buffer;
2382 			/* assume normal "System" event */
2383 			evt.record_type = 2;
2384 			errno = 0;
2385 			evt.record_id = strtol((const char *)cursor, (char **)NULL, 16);
2386 			if (errno != 0) {
2387 				lprintf(LOG_ERR, "Invalid record ID.");
2388 				status = (-1);
2389 				break;
2390 			}
2391 			evt.sel_type.standard_type.evm_rev = 4;
2392 
2393 			/* FIXME: convert*/
2394 			evt.sel_type.standard_type.timestamp;
2395 
2396 			/* skip timestamp */
2397 			cursor = index((const char *)cursor, ';');
2398 			cursor++;
2399 
2400 			/* FIXME: parse originator */
2401 			evt.sel_type.standard_type.gen_id = 0x0020;
2402 
2403 			/* skip  originator info */
2404 			cursor = index((const char *)cursor, ';');
2405 			cursor++;
2406 
2407 			/* Get sensor type */
2408 			cursor = index((const char *)cursor, '(');
2409 			cursor++;
2410 
2411 			errno = 0;
2412 			evt.sel_type.standard_type.sensor_type =
2413 				strtol((const char *)cursor, (char **)NULL, 16);
2414 			if (errno != 0) {
2415 				lprintf(LOG_ERR, "Invalid Sensor Type.");
2416 				status = (-1);
2417 				break;
2418 			}
2419 			cursor = index((const char *)cursor, ',');
2420 			cursor++;
2421 
2422 			errno = 0;
2423 			evt.sel_type.standard_type.sensor_num =
2424 				strtol((const char *)cursor, (char **)NULL, 10);
2425 			if (errno != 0) {
2426 				lprintf(LOG_ERR, "Invalid Sensor Number.");
2427 				status = (-1);
2428 				break;
2429 			}
2430 
2431 			/* skip  to event type  info */
2432 			cursor = index((const char *)cursor, ':');
2433 			cursor++;
2434 
2435 			errno = 0;
2436 			evt.sel_type.standard_type.event_type=
2437 				strtol((const char *)cursor, (char **)NULL, 16);
2438 			if (errno != 0) {
2439 				lprintf(LOG_ERR, "Invalid Event Type.");
2440 				status = (-1);
2441 				break;
2442 			}
2443 
2444 			/* skip  to event dir  info */
2445 			cursor = index((const char *)cursor, '(');
2446 			cursor++;
2447 			if (*cursor == 'a') {
2448 				evt.sel_type.standard_type.event_dir = 0;
2449 			} else {
2450 				evt.sel_type.standard_type.event_dir = 1;
2451 			}
2452 			/* skip  to data info */
2453 			cursor = index((const char *)cursor, ' ');
2454 			cursor++;
2455 
2456 			if (evt.sel_type.standard_type.sensor_type == 0xF0) {
2457 				/* got to FRU id */
2458 				while (!isdigit(*cursor)) {
2459 					cursor++;
2460 				}
2461 				/* store FRUid */
2462 				errno = 0;
2463 				evt.sel_type.standard_type.event_data[2] =
2464 					strtol(cursor, (char **)NULL, 10);
2465 				if (errno != 0) {
2466 					lprintf(LOG_ERR, "Invalid Event Data#2.");
2467 					status = (-1);
2468 					break;
2469 				}
2470 
2471 				/* Get to previous state */
2472 				cursor = index((const char *)cursor, 'M');
2473 				cursor++;
2474 
2475 				/* Set previous state */
2476 				errno = 0;
2477 				evt.sel_type.standard_type.event_data[1] =
2478 					strtol(cursor, (char **)NULL, 10);
2479 				if (errno != 0) {
2480 					lprintf(LOG_ERR, "Invalid Event Data#1.");
2481 					status = (-1);
2482 					break;
2483 				}
2484 
2485 				/* Get to current state */
2486 				cursor = index((const char *)cursor, 'M');
2487 				cursor++;
2488 
2489 				/* Set current state */
2490 				errno = 0;
2491 				evt.sel_type.standard_type.event_data[0] =
2492 					0xA0 | strtol(cursor, (char **)NULL, 10);
2493 				if (errno != 0) {
2494 					lprintf(LOG_ERR, "Invalid Event Data#0.");
2495 					status = (-1);
2496 					break;
2497 				}
2498 
2499 				/* skip  to cause */
2500 				cursor = index((const char *)cursor, '=');
2501 				cursor++;
2502 				errno = 0;
2503 				evt.sel_type.standard_type.event_data[1] |=
2504 					(strtol(cursor, (char **)NULL, 16)) << 4;
2505 				if (errno != 0) {
2506 					lprintf(LOG_ERR, "Invalid Event Data#1.");
2507 					status = (-1);
2508 					break;
2509 				}
2510 			} else if (*cursor == '0') {
2511 				errno = 0;
2512 				evt.sel_type.standard_type.event_data[0] =
2513 					strtol((const char *)cursor, (char **)NULL, 16);
2514 				if (errno != 0) {
2515 					lprintf(LOG_ERR, "Invalid Event Data#0.");
2516 					status = (-1);
2517 					break;
2518 				}
2519 				cursor = index((const char *)cursor, ' ');
2520 				cursor++;
2521 
2522 				errno = 0;
2523 				evt.sel_type.standard_type.event_data[1] =
2524 					strtol((const char *)cursor, (char **)NULL, 16);
2525 				if (errno != 0) {
2526 					lprintf(LOG_ERR, "Invalid Event Data#1.");
2527 					status = (-1);
2528 					break;
2529 				}
2530 
2531 				cursor = index((const char *)cursor, ' ');
2532 				cursor++;
2533 
2534 				errno = 0;
2535 				evt.sel_type.standard_type.event_data[2] =
2536 					strtol((const char *)cursor, (char **)NULL, 16);
2537 				if (errno != 0) {
2538 					lprintf(LOG_ERR, "Invalid Event Data#2.");
2539 					status = (-1);
2540 					break;
2541 				}
2542 			} else {
2543 				lprintf(LOG_ERR, "ipmitool: can't guess format.");
2544 			}
2545 			/* parse the PPS line into a sel_event_record */
2546 			if (verbose) {
2547 				ipmi_sel_print_std_entry_verbose(intf, &evt);
2548 			} else {
2549 				ipmi_sel_print_std_entry(intf, &evt);
2550 			}
2551 			cursor = NULL;
2552 		} while (status == 0); /* until file is completely read */
2553 		cursor = NULL;
2554 		free(buffer);
2555 		buffer = NULL;
2556 		fclose(fp);
2557 	} else {
2558 		lprintf(LOG_ERR, "Given format '%s' is unknown.", format);
2559 		status = (-1);
2560 	}
2561 	return status;
2562 }
2563 
2564 
2565 static int
2566 ipmi_sel_writeraw(struct ipmi_intf * intf, const char * savefile)
2567 {
2568     return __ipmi_sel_savelist_entries(intf, 0, savefile, 1);
2569 }
2570 
2571 
2572 static int
2573 ipmi_sel_readraw(struct ipmi_intf * intf, const char * inputfile)
2574 {
2575 	struct sel_event_record evt;
2576 	int ret = 0;
2577 	FILE* fp = 0;
2578 
2579 	fp = ipmi_open_file(inputfile, 0);
2580 	if (fp)
2581 	{
2582 		size_t bytesRead;
2583 
2584 		do {
2585 			if ((bytesRead = fread(&evt, 1, 16, fp)) == 16)
2586 			{
2587 				if (verbose)
2588 					ipmi_sel_print_std_entry_verbose(intf, &evt);
2589 				else
2590 					ipmi_sel_print_std_entry(intf, &evt);
2591 			}
2592 			else
2593 			{
2594 				if (bytesRead != 0)
2595 				{
2596 					lprintf(LOG_ERR, "ipmitool: incomplete record found in file.");
2597 					ret = -1;
2598 				}
2599 
2600 				break;
2601 			}
2602 
2603 		} while (1);
2604 		fclose(fp);
2605 	}
2606 	else
2607 	{
2608 		lprintf(LOG_ERR, "ipmitool: could not open input file.");
2609 		ret = -1;
2610 	}
2611 	return ret;
2612 }
2613 
2614 
2615 
2616 static uint16_t
2617 ipmi_sel_reserve(struct ipmi_intf * intf)
2618 {
2619 	struct ipmi_rs * rsp;
2620 	struct ipmi_rq req;
2621 
2622 	memset(&req, 0, sizeof(req));
2623 	req.msg.netfn = IPMI_NETFN_STORAGE;
2624 	req.msg.cmd = IPMI_CMD_RESERVE_SEL;
2625 
2626 	rsp = intf->sendrecv(intf, &req);
2627 	if (rsp == NULL) {
2628 		lprintf(LOG_WARN, "Unable to reserve SEL");
2629 		return 0;
2630 	}
2631 	if (rsp->ccode > 0) {
2632 		printf("Unable to reserve SEL: %s",
2633 		       val2str(rsp->ccode, completion_code_vals));
2634 		return 0;
2635 	}
2636 
2637 	return (rsp->data[0] | (rsp->data[1] << 8));
2638 }
2639 
2640 
2641 
2642 /*
2643  * ipmi_sel_get_time
2644  *
2645  * return 0 on success,
2646  *        -1 on error
2647  */
2648 static int
2649 ipmi_sel_get_time(struct ipmi_intf * intf)
2650 {
2651 	struct ipmi_rs * rsp;
2652 	struct ipmi_rq req;
2653 	static char tbuf[40];
2654 	uint32_t timei;
2655 	time_t time;
2656 
2657 	memset(&req, 0, sizeof(req));
2658 	req.msg.netfn = IPMI_NETFN_STORAGE;
2659 	req.msg.cmd   = IPMI_GET_SEL_TIME;
2660 
2661 	rsp = intf->sendrecv(intf, &req);
2662 
2663 	if (rsp == NULL) {
2664 		lprintf(LOG_ERR, "Get SEL Time command failed");
2665 		return -1;
2666 	}
2667 	if (rsp->ccode > 0) {
2668 		lprintf(LOG_ERR, "Get SEL Time command failed: %s",
2669 			val2str(rsp->ccode, completion_code_vals));
2670 		return -1;
2671 	}
2672 	if (rsp->data_len != 4) {
2673 		lprintf(LOG_ERR, "Get SEL Time command failed: "
2674 			"Invalid data length %d", rsp->data_len);
2675 		return -1;
2676 	}
2677 
2678 	memcpy(&timei, rsp->data, 4);
2679 #if WORDS_BIGENDIAN
2680 	time = (time_t)(BSWAP_32(timei));
2681 #else
2682 	time = (time_t)timei;
2683 #endif
2684 
2685 	strftime(tbuf, sizeof(tbuf), "%m/%d/%Y %H:%M:%S", gmtime(&time));
2686 	printf("%s\n", tbuf);
2687 
2688 	return 0;
2689 }
2690 
2691 
2692 
2693 /*
2694  * ipmi_sel_set_time
2695  *
2696  * return 0 on success,
2697  *        -1 on error
2698  */
2699 static int
2700 ipmi_sel_set_time(struct ipmi_intf * intf, const char * time_string)
2701 {
2702 	struct ipmi_rs     * rsp;
2703 	struct ipmi_rq       req;
2704 	struct tm            tm = {0};
2705 	time_t               t;
2706 	uint32_t	     timei;
2707 	const char *         time_format = "%m/%d/%Y %H:%M:%S";
2708 
2709 	memset(&req, 0, sizeof(req));
2710 	req.msg.netfn    = IPMI_NETFN_STORAGE;
2711 	req.msg.cmd      = IPMI_SET_SEL_TIME;
2712 
2713 	/* See if user requested set to current client system time */
2714 	if (strncasecmp(time_string, "now", 3) == 0) {
2715 		t = time(NULL);
2716 	}
2717 	else {
2718 		/* Now how do we get our time_t from our ascii version? */
2719 		if (strptime(time_string, time_format, &tm) == 0) {
2720 			lprintf(LOG_ERR, "Specified time could not be parsed");
2721 			return -1;
2722 		}
2723 		tm.tm_isdst = (-1); /* look up DST information */
2724 		t = mktime(&tm);
2725 		if (t < 0) {
2726 			lprintf(LOG_ERR, "Specified time could not be parsed");
2727 			return -1;
2728 		}
2729 	}
2730 
2731 	{
2732 		//modify UTC time to local time expressed in number of seconds from 1/1/70 0:0:0 1970 GMT
2733 		struct tm * tm_tmp = {0};
2734 		int gt_year,gt_yday,gt_hour,lt_year,lt_yday,lt_hour;
2735 		int delta_hour;
2736 		tm_tmp=gmtime(&t);
2737 		gt_year=tm_tmp->tm_year;
2738 		gt_yday=tm_tmp->tm_yday;
2739 		gt_hour=tm_tmp->tm_hour;
2740 		memset(&*tm_tmp, 0, sizeof(struct tm));
2741 		tm_tmp=localtime(&t);
2742 		lt_year=tm_tmp->tm_year;
2743 		lt_yday=tm_tmp->tm_yday;
2744 		lt_hour=tm_tmp->tm_hour;
2745 		delta_hour=lt_hour - gt_hour;
2746 		if ( (lt_year > gt_year) || ((lt_year == gt_year) && (lt_yday > gt_yday)) )
2747 			delta_hour += 24;
2748 		if ( (lt_year < gt_year) || ((lt_year == gt_year) && (lt_yday < gt_yday)) )
2749 			delta_hour -= 24;
2750 
2751 		t += (delta_hour * 60 * 60);
2752 	}
2753 
2754 	timei = (uint32_t)t;
2755 	req.msg.data = (uint8_t *)&timei;
2756 	req.msg.data_len = 4;
2757 
2758 #if WORDS_BIGENDIAN
2759 	timei = BSWAP_32(timei);
2760 #endif
2761 
2762 	rsp = intf->sendrecv(intf, &req);
2763 	if (rsp == NULL) {
2764 		lprintf(LOG_ERR, "Set SEL Time command failed");
2765 		return -1;
2766 	}
2767 	if (rsp->ccode > 0) {
2768 		lprintf(LOG_ERR, "Set SEL Time command failed: %s",
2769 			val2str(rsp->ccode, completion_code_vals));
2770 		return -1;
2771 	}
2772 
2773 	ipmi_sel_get_time(intf);
2774 
2775 	return 0;
2776 }
2777 
2778 
2779 
2780 static int
2781 ipmi_sel_clear(struct ipmi_intf * intf)
2782 {
2783 	struct ipmi_rs * rsp;
2784 	struct ipmi_rq req;
2785 	uint16_t reserve_id;
2786 	uint8_t msg_data[6];
2787 
2788 	reserve_id = ipmi_sel_reserve(intf);
2789 	if (reserve_id == 0)
2790 		return -1;
2791 
2792 	memset(msg_data, 0, 6);
2793 	msg_data[0] = reserve_id & 0xff;
2794 	msg_data[1] = reserve_id >> 8;
2795 	msg_data[2] = 'C';
2796 	msg_data[3] = 'L';
2797 	msg_data[4] = 'R';
2798 	msg_data[5] = 0xaa;
2799 
2800 	memset(&req, 0, sizeof(req));
2801 	req.msg.netfn = IPMI_NETFN_STORAGE;
2802 	req.msg.cmd = IPMI_CMD_CLEAR_SEL;
2803 	req.msg.data = msg_data;
2804 	req.msg.data_len = 6;
2805 
2806 	rsp = intf->sendrecv(intf, &req);
2807 	if (rsp == NULL) {
2808 		lprintf(LOG_ERR, "Unable to clear SEL");
2809 		return -1;
2810 	}
2811 	if (rsp->ccode > 0) {
2812 		lprintf(LOG_ERR, "Unable to clear SEL: %s",
2813 			val2str(rsp->ccode, completion_code_vals));
2814 		return -1;
2815 	}
2816 
2817 	printf("Clearing SEL.  Please allow a few seconds to erase.\n");
2818 	return 0;
2819 }
2820 
2821 static int
2822 ipmi_sel_delete(struct ipmi_intf * intf, int argc, char ** argv)
2823 {
2824 	struct ipmi_rs * rsp;
2825 	struct ipmi_rq req;
2826 	uint16_t id;
2827 	uint8_t msg_data[4];
2828 	int rc = 0;
2829 
2830 	if (argc == 0 || strncmp(argv[0], "help", 4) == 0) {
2831 		lprintf(LOG_ERR, "usage: delete <id>...<id>\n");
2832 		return -1;
2833 	}
2834 
2835 	id = ipmi_sel_reserve(intf);
2836 	if (id == 0)
2837 		return -1;
2838 
2839 	memset(msg_data, 0, 4);
2840 	msg_data[0] = id & 0xff;
2841 	msg_data[1] = id >> 8;
2842 
2843 	for (; argc != 0; argc--)
2844 	{
2845 		if (str2ushort(argv[argc-1], &id) != 0) {
2846 			lprintf(LOG_ERR, "Given SEL ID '%s' is invalid.",
2847 					argv[argc-1]);
2848 			rc = (-1);
2849 			continue;
2850 		}
2851 		msg_data[2] = id & 0xff;
2852 		msg_data[3] = id >> 8;
2853 
2854 		memset(&req, 0, sizeof(req));
2855 		req.msg.netfn = IPMI_NETFN_STORAGE;
2856 		req.msg.cmd = IPMI_CMD_DELETE_SEL_ENTRY;
2857 		req.msg.data = msg_data;
2858 		req.msg.data_len = 4;
2859 
2860 		rsp = intf->sendrecv(intf, &req);
2861 		if (rsp == NULL) {
2862 			lprintf(LOG_ERR, "Unable to delete entry %d", id);
2863 			rc = -1;
2864 		}
2865 		else if (rsp->ccode > 0) {
2866 			lprintf(LOG_ERR, "Unable to delete entry %d: %s", id,
2867 				val2str(rsp->ccode, completion_code_vals));
2868 			rc = -1;
2869 		}
2870 		else {
2871 			printf("Deleted entry %d\n", id);
2872 		}
2873 	}
2874 
2875 	return rc;
2876 }
2877 
2878 static int
2879 ipmi_sel_show_entry(struct ipmi_intf * intf, int argc, char ** argv)
2880 {
2881 	struct entity_id entity;
2882 	struct sdr_record_list *entry;
2883 	struct sdr_record_list *list;
2884 	struct sdr_record_list *sdr;
2885 	struct sel_event_record evt;
2886 	int i;
2887 	int oldv;
2888 	int rc = 0;
2889 	uint16_t id;
2890 
2891 	if (argc == 0 || strncmp(argv[0], "help", 4) == 0) {
2892 		lprintf(LOG_ERR, "usage: sel get <id>...<id>");
2893 		return (-1);
2894 	}
2895 
2896 	if (ipmi_sel_reserve(intf) == 0) {
2897 		lprintf(LOG_ERR, "Unable to reserve SEL");
2898 		return (-1);
2899 	}
2900 
2901 	for (i = 0; i < argc; i++) {
2902 		if (str2ushort(argv[i], &id) != 0) {
2903 			lprintf(LOG_ERR, "Given SEL ID '%s' is invalid.",
2904 					argv[i]);
2905 			rc = (-1);
2906 			continue;
2907 		}
2908 
2909 		lprintf(LOG_DEBUG, "Looking up SEL entry 0x%x", id);
2910 
2911 		/* lookup SEL entry based on ID */
2912 		if (!ipmi_sel_get_std_entry(intf, id, &evt)) {
2913 			lprintf(LOG_DEBUG, "SEL Entry 0x%x not found.", id);
2914 			rc = (-1);
2915 			continue;
2916 		}
2917 		if (evt.sel_type.standard_type.sensor_num == 0
2918 				&& evt.sel_type.standard_type.sensor_type == 0
2919 				&& evt.record_type == 0) {
2920 			lprintf(LOG_WARN, "SEL Entry 0x%x not found", id);
2921 			rc = (-1);
2922 			continue;
2923 		}
2924 
2925 		/* lookup SDR entry based on sensor number and type */
2926 		ipmi_sel_print_extended_entry_verbose(intf, &evt);
2927 
2928 		sdr = ipmi_sdr_find_sdr_bynumtype(intf,
2929 				evt.sel_type.standard_type.gen_id,
2930 				evt.sel_type.standard_type.sensor_num,
2931 				evt.sel_type.standard_type.sensor_type);
2932 		if (sdr == NULL) {
2933 			continue;
2934 		}
2935 
2936 		/* print SDR entry */
2937 		oldv = verbose;
2938 		verbose = verbose ? verbose : 1;
2939 		switch (sdr->type) {
2940 		case SDR_RECORD_TYPE_FULL_SENSOR:
2941 		case SDR_RECORD_TYPE_COMPACT_SENSOR:
2942 			ipmi_sensor_print_fc(intf, sdr->record.common,
2943 					     sdr->type);
2944 			entity.id = sdr->record.common->entity.id;
2945 			entity.instance = sdr->record.common->entity.instance;
2946 			break;
2947 		case SDR_RECORD_TYPE_EVENTONLY_SENSOR:
2948 			ipmi_sdr_print_sensor_eventonly(intf, sdr->record.eventonly);
2949 			entity.id = sdr->record.eventonly->entity.id;
2950 			entity.instance = sdr->record.eventonly->entity.instance;
2951 			break;
2952 		default:
2953 			verbose = oldv;
2954 			continue;
2955 		}
2956 		verbose = oldv;
2957 
2958 		/* lookup SDR entry based on entity id */
2959 		list = ipmi_sdr_find_sdr_byentity(intf, &entity);
2960 		for (entry=list; entry; entry=entry->next) {
2961 			/* print FRU devices we find for this entity */
2962 			if (entry->type == SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR)
2963 				ipmi_fru_print(intf, entry->record.fruloc);
2964 		}
2965 
2966 		if ((argc > 1) && (i < (argc - 1))) {
2967 			printf("----------------------\n\n");
2968 		}
2969 	}
2970 
2971 	return rc;
2972 }
2973 
2974 int ipmi_sel_main(struct ipmi_intf * intf, int argc, char ** argv)
2975 {
2976 	int rc = 0;
2977 
2978 	if (argc == 0)
2979 		rc = ipmi_sel_get_info(intf);
2980 	else if (strncmp(argv[0], "help", 4) == 0)
2981 		lprintf(LOG_ERR, "SEL Commands:  "
2982 				"info clear delete list elist get add time save readraw writeraw interpret");
2983 	else if (strncmp(argv[0], "interpret", 9) == 0) {
2984 		uint32_t iana = 0;
2985 		if (argc < 4) {
2986 			lprintf(LOG_NOTICE, "usage: sel interpret iana filename format(pps)");
2987 			return 0;
2988 		}
2989 		if (str2uint(argv[1], &iana) != 0) {
2990 			lprintf(LOG_ERR, "Given IANA '%s' is invalid.",
2991 					argv[1]);
2992 			return (-1);
2993 		}
2994 		rc = ipmi_sel_interpret(intf, iana, argv[2], argv[3]);
2995 	}
2996 	else if (strncmp(argv[0], "info", 4) == 0)
2997 		rc = ipmi_sel_get_info(intf);
2998 	else if (strncmp(argv[0], "save", 4) == 0) {
2999 		if (argc < 2) {
3000 			lprintf(LOG_NOTICE, "usage: sel save <filename>");
3001 			return 0;
3002 		}
3003 		rc = ipmi_sel_save_entries(intf, 0, argv[1]);
3004 	}
3005 	else if (strncmp(argv[0], "add", 3) == 0) {
3006 		if (argc < 2) {
3007 			lprintf(LOG_NOTICE, "usage: sel add <filename>");
3008 			return 0;
3009 		}
3010 		rc = ipmi_sel_add_entries_fromfile(intf, argv[1]);
3011 	}
3012 	else if (strncmp(argv[0], "writeraw", 8) == 0) {
3013 		if (argc < 2) {
3014 			lprintf(LOG_NOTICE, "usage: sel writeraw <filename>");
3015 			return 0;
3016 		}
3017 		rc = ipmi_sel_writeraw(intf, argv[1]);
3018 	}
3019 	else if (strncmp(argv[0], "readraw", 7) == 0) {
3020 		if (argc < 2) {
3021 			lprintf(LOG_NOTICE, "usage: sel readraw <filename>");
3022 			return 0;
3023 		}
3024 		rc = ipmi_sel_readraw(intf, argv[1]);
3025 	}
3026 	else if (strncmp(argv[0], "ereadraw", 8) == 0) {
3027 		if (argc < 2) {
3028 			lprintf(LOG_NOTICE, "usage: sel ereadraw <filename>");
3029 			return 0;
3030 		}
3031 		sel_extended = 1;
3032 		rc = ipmi_sel_readraw(intf, argv[1]);
3033 	}
3034 	else if (strncmp(argv[0], "list", 4) == 0 ||
3035 		 strncmp(argv[0], "elist", 5) == 0) {
3036 		/*
3037 		 * Usage:
3038 		 *	list           - show all SEL entries
3039 		 *  list first <n> - show the first (oldest) <n> SEL entries
3040 		 *  list last <n>  - show the last (newsest) <n> SEL entries
3041 		 */
3042 		int count = 0;
3043 		int sign = 1;
3044 		char *countstr = NULL;
3045 
3046 		if (strncmp(argv[0], "elist", 5) == 0)
3047 			sel_extended = 1;
3048 		else
3049 			sel_extended = 0;
3050 
3051 		if (argc == 2) {
3052 			countstr = argv[1];
3053 		}
3054 		else if (argc == 3) {
3055 			countstr = argv[2];
3056 
3057 			if (strncmp(argv[1], "last", 4) == 0) {
3058 				sign = -1;
3059 			}
3060 			else if (strncmp(argv[1], "first", 5) != 0) {
3061 				lprintf(LOG_ERR, "Unknown sel list option");
3062 				return -1;
3063 			}
3064 		}
3065 
3066 		if (countstr) {
3067 			if (str2int(countstr, &count) != 0) {
3068 				lprintf(LOG_ERR, "Numeric argument required; got '%s'",
3069 					countstr);
3070 				return -1;
3071 			}
3072 		}
3073 		count *= sign;
3074 
3075 		rc = ipmi_sel_list_entries(intf,count);
3076 	}
3077 	else if (strncmp(argv[0], "clear", 5) == 0)
3078 		rc = ipmi_sel_clear(intf);
3079 	else if (strncmp(argv[0], "delete", 6) == 0) {
3080 		if (argc < 2)
3081 			lprintf(LOG_ERR, "usage: sel delete <id>...<id>");
3082 		else
3083 			rc = ipmi_sel_delete(intf, argc-1, &argv[1]);
3084 	}
3085 	else if (strncmp(argv[0], "get", 3) == 0) {
3086 		if (argc < 2)
3087 			lprintf(LOG_ERR, "usage: sel get <entry>");
3088 		else
3089 			rc = ipmi_sel_show_entry(intf, argc-1, &argv[1]);
3090 	}
3091 	else if (strncmp(argv[0], "time", 4) == 0) {
3092 		if (argc < 2)
3093 			lprintf(LOG_ERR, "sel time commands: get set");
3094 		else if (strncmp(argv[1], "get", 3) == 0)
3095 			ipmi_sel_get_time(intf);
3096 		else if (strncmp(argv[1], "set", 3) == 0) {
3097 			if (argc < 3)
3098 				lprintf(LOG_ERR, "usage: sel time set \"mm/dd/yyyy hh:mm:ss\"");
3099 			else
3100 				rc = ipmi_sel_set_time(intf, argv[2]);
3101 		} else {
3102 			lprintf(LOG_ERR, "sel time commands: get set");
3103 		}
3104 	}
3105 	else {
3106 		lprintf(LOG_ERR, "Invalid SEL command: %s", argv[0]);
3107 		rc = -1;
3108 	}
3109 
3110 	return rc;
3111 }
3112