xref: /openbmc/ipmitool/lib/ipmi_fru.c (revision 7bd7c4fd)
1 /*
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 <ipmitool/ipmi.h>
34 #include <ipmitool/log.h>
35 #include <ipmitool/helper.h>
36 #include <ipmitool/ipmi_intf.h>
37 #include <ipmitool/ipmi_fru.h>
38 #include <ipmitool/ipmi_mc.h>
39 #include <ipmitool/ipmi_sdr.h>
40 #include <ipmitool/ipmi_strings.h>  /* IANA id strings */
41 
42 #include <stdlib.h>
43 #include <string.h>
44 #include <time.h>
45 #include <errno.h>
46 
47 #if HAVE_CONFIG_H
48 # include <config.h>
49 #endif
50 
51 #define FRU_MULTIREC_CHUNK_SIZE     (255 + sizeof(struct fru_multirec_header))
52 
53 extern int verbose;
54 
55 static void ipmi_fru_read_to_bin(struct ipmi_intf * intf, char * pFileName, uint8_t fruId);
56 static void ipmi_fru_write_from_bin(struct ipmi_intf * intf, char * pFileName, uint8_t fruId);
57 static int ipmi_fru_upg_ekeying(struct ipmi_intf * intf, char * pFileName, uint8_t fruId);
58 static int ipmi_fru_get_multirec_location_from_fru(struct ipmi_intf * intf, uint8_t fruId,
59 							struct fru_info *pFruInfo, uint32_t * pRetLocation,
60 							uint32_t * pRetSize);
61 static int ipmi_fru_get_multirec_from_file(char * pFileName, uint8_t * pBufArea,
62 						uint32_t size, uint32_t offset);
63 static int ipmi_fru_get_multirec_size_from_file(char * pFileName, uint32_t * pSize, uint32_t * pOffset);
64 int ipmi_fru_get_adjust_size_from_buffer(uint8_t *pBufArea, uint32_t *pSize);
65 static void ipmi_fru_picmg_ext_print(uint8_t * fru_data, int off, int length);
66 
67 static int ipmi_fru_set_field_string(struct ipmi_intf * intf, unsigned
68 						char fruId, uint8_t f_type, uint8_t f_index, char *f_string);
69 static int
70 ipmi_fru_set_field_string_rebuild(struct ipmi_intf * intf, uint8_t fruId,
71 											struct fru_info fru, struct fru_header header,
72 											uint8_t f_type, uint8_t f_index, char *f_string);
73 
74 static void
75 fru_area_print_multirec_bloc(struct ipmi_intf * intf, struct fru_info * fru,
76 			uint8_t id, uint32_t offset);
77 int
78 read_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
79 			uint32_t offset, uint32_t length, uint8_t *frubuf);
80 void free_fru_bloc(t_ipmi_fru_bloc *bloc);
81 
82 /* get_fru_area_str  -  Parse FRU area string from raw data
83 *
84 * @data:   raw FRU data
85 * @offset: offset into data for area
86 *
87 * returns pointer to FRU area string
88 */
89 char * get_fru_area_str(uint8_t * data, uint32_t * offset)
90 {
91 	static const char bcd_plus[] = "0123456789 -.:,_";
92 	char * str;
93 	int len, off, size, i, j, k, typecode;
94 	union {
95 		uint32_t bits;
96 		char chars[4];
97 	} u;
98 
99 	size = 0;
100 	off = *offset;
101 
102 	/* bits 6:7 contain format */
103 	typecode = ((data[off] & 0xC0) >> 6);
104 
105 	// printf("Typecode:%i\n", typecode);
106 	/* bits 0:5 contain length */
107 	len = data[off++];
108 	len &= 0x3f;
109 
110 	switch (typecode) {
111 	case 0:           /* 00b: binary/unspecified */
112 		/* hex dump -> 2x length */
113 		size = (len*2);
114 		break;
115 	case 2:           /* 10b: 6-bit ASCII */
116 		/* 4 chars per group of 1-3 bytes */
117 		size = ((((len+2)*4)/3) & ~3);
118 		break;
119 	case 3:           /* 11b: 8-bit ASCII */
120 	case 1:           /* 01b: BCD plus */
121 		/* no length adjustment */
122 		size = len;
123 		break;
124 	}
125 
126 	if (size < 1) {
127 		*offset = off;
128 		return NULL;
129 	}
130 	str = malloc(size+1);
131 	if (str == NULL)
132 		return NULL;
133 	memset(str, 0, size+1);
134 
135 	if (len == 0) {
136 		str[0] = '\0';
137 		*offset = off;
138 		return str;
139 	}
140 
141 	switch (typecode) {
142 	case 0:        /* Binary */
143 		strncpy(str, buf2str(&data[off], len), len*2);
144 		break;
145 
146 	case 1:        /* BCD plus */
147 		for (k=0; k<len; k++)
148 			str[k] = bcd_plus[(data[off+k] & 0x0f)];
149 		str[k] = '\0';
150 		break;
151 
152 	case 2:        /* 6-bit ASCII */
153 		for (i=j=0; i<len; i+=3) {
154 			u.bits = 0;
155 			k = ((len-i) < 3 ? (len-i) : 3);
156 #if WORDS_BIGENDIAN
157 			u.chars[3] = data[off+i];
158 			u.chars[2] = (k > 1 ? data[off+i+1] : 0);
159 			u.chars[1] = (k > 2 ? data[off+i+2] : 0);
160 #define CHAR_IDX 3
161 #else
162 			memcpy((void *)&u.bits, &data[off+i], k);
163 #define CHAR_IDX 0
164 #endif
165 			for (k=0; k<4; k++) {
166 				str[j++] = ((u.chars[CHAR_IDX] & 0x3f) + 0x20);
167 				u.bits >>= 6;
168 			}
169 		}
170 		str[j] = '\0';
171 		break;
172 
173 	case 3:
174 		memcpy(str, &data[off], len);
175 		str[len] = '\0';
176 		break;
177 	}
178 
179 	off += len;
180 	*offset = off;
181 
182 	return str;
183 }
184 
185 /* is_valid_filename - checks file/path supplied by user
186  *
187  * input_filename - user input string
188  *
189  * returns   0  if path is ok
190  * returns (-1) if path is NULL
191  * returns (-2) if path is too short
192  * returns (-3) if path is too long
193  */
194 int
195 is_valid_filename(const char *input_filename)
196 {
197 	if (input_filename == NULL) {
198 		lprintf(LOG_ERR, "ERROR: NULL pointer passed.");
199 		return (-1);
200 	}
201 
202 	if (strlen(input_filename) < 1) {
203 		lprintf(LOG_ERR, "File/path is invalid.");
204 		return (-2);
205 	}
206 
207 	if (strlen(input_filename) >= 512) {
208 		lprintf(LOG_ERR, "File/path must be shorter than 512 bytes.");
209 		return (-3);
210 	}
211 
212 	return 0;
213 } /* is_valid_filename() */
214 
215 /* build_fru_bloc  -  build fru bloc for write protection
216 *
217 * @intf:     ipmi interface
218 * @fru_info: information about FRU device
219 * @id      : Fru id
220 * @soffset : Source offset      (from buffer)
221 * @doffset : Destination offset (in device)
222 * @length  : Size of data to write (in bytes)
223 * @pFrubuf : Pointer on data to write
224 *
225 * returns 0 on success
226 * returns -1 on error
227 */
228 #define FRU_NUM_BLOC_COMMON_HEADER  6
229 t_ipmi_fru_bloc *
230 build_fru_bloc(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id)
231 {
232 	t_ipmi_fru_bloc * p_first, * p_bloc, * p_new;
233 	struct ipmi_rs * rsp;
234 	struct ipmi_rq req;
235 	struct fru_header header;
236 	struct fru_multirec_header rec_hdr;
237 	uint8_t msg_data[4];
238 	uint32_t off;
239 	uint16_t i;
240 
241 	/*
242 	* get COMMON Header format
243 	*/
244 	msg_data[0] = id;
245 	msg_data[1] = 0;
246 	msg_data[2] = 0;
247 	msg_data[3] = 8;
248 
249 	memset(&req, 0, sizeof(req));
250 	req.msg.netfn = IPMI_NETFN_STORAGE;
251 	req.msg.cmd = GET_FRU_DATA;
252 	req.msg.data = msg_data;
253 	req.msg.data_len = 4;
254 
255 	rsp = intf->sendrecv(intf, &req);
256 
257 	if (rsp == NULL) {
258 		lprintf(LOG_ERR, " Device not present (No Response)");
259 		return NULL;
260 	}
261 
262 	if (rsp->ccode > 0) {
263 		lprintf(LOG_ERR," Device not present (%s)",
264 				val2str(rsp->ccode, completion_code_vals));
265 		return NULL;
266 	}
267 
268 	if (verbose > 1) {
269 		printbuf(rsp->data, rsp->data_len, "FRU DATA");
270 	}
271 
272 	memcpy(&header, rsp->data + 1, 8);
273 
274 	/* verify header checksum */
275 	if (ipmi_csum((uint8_t *)&header, 8)) {
276 		lprintf(LOG_ERR, " Bad header checksum");
277 		return NULL;
278 	}
279 
280 	if (header.version != 1) {
281 		lprintf(LOG_ERR, " Unknown FRU header version 0x%02x", header.version);
282 		return NULL;
283 	}
284 
285 	/******************************************
286 		Malloc and fill up the bloc contents
287 	*******************************************/
288 
289 	// Common header
290 	p_first = malloc(sizeof(struct ipmi_fru_bloc));
291 	if (!p_first) {
292 		lprintf(LOG_ERR, "ipmitool: malloc failure");
293 		return NULL;
294 	}
295 
296 	p_bloc = p_first;
297 	p_bloc->next = NULL;
298 	p_bloc->start= 0;
299 	p_bloc->size = fru->size;
300 	strcpy((char *)p_bloc->blocId, "Common Header Section");
301 
302 	for (i = 0; i < 4; i++) {
303 		if (header.offsets[i]) {
304 			p_new = malloc(sizeof(struct ipmi_fru_bloc));
305 			if (!p_new) {
306 				lprintf(LOG_ERR, "ipmitool: malloc failure");
307 				free_fru_bloc(p_first);
308 				return NULL;
309 			}
310 
311 			p_new->next = NULL;
312 			p_new->start = header.offsets[i] * 8;
313 			p_new->size = fru->size - p_new->start;
314 
315 			strncpy((char *)p_new->blocId, section_id[i], sizeof(p_new->blocId));
316 			/* Make sure string is null terminated */
317 			p_new->blocId[sizeof(p_new->blocId)-1] = 0;
318 
319 			p_bloc->next = p_new;
320 			p_bloc->size = p_new->start - p_bloc->start;
321 			p_bloc = p_new;
322 		}
323 	}
324 
325 	// Multi
326 	if (header.offset.multi) {
327 		off = header.offset.multi * 8;
328 
329 		do {
330 			/*
331 			 * check for odd offset for the case of fru devices
332 			 * accessed by words
333 			 */
334 			if (fru->access && (off & 1)) {
335 				lprintf(LOG_ERR, " Unaligned offset for a block: %d", off);
336 				/* increment offset */
337 				off++;
338 				break;
339 			}
340 
341 			if (read_fru_area(intf, fru, id, off, 5,
342 					(uint8_t *) &rec_hdr) < 0) {
343 				break;
344 			}
345 
346 			p_new = malloc(sizeof(struct ipmi_fru_bloc));
347 			if (!p_new) {
348 				lprintf(LOG_ERR, "ipmitool: malloc failure");
349 				free_fru_bloc(p_first);
350 				return NULL;
351 			}
352 
353 			p_new->next = NULL;
354 			p_new->start = off;
355 			p_new->size = fru->size - p_new->start;
356 			sprintf((char *)p_new->blocId, "Multi-Rec Area: Type %i",
357 					rec_hdr.type);
358 
359 			p_bloc->next = p_new;
360 			p_bloc->size = p_new->start - p_bloc->start;
361 			p_bloc = p_new;
362 
363 			off += rec_hdr.len + sizeof(struct fru_multirec_header);
364 
365 			/* verify record header */
366 			if (ipmi_csum((uint8_t *)&rec_hdr,
367 					sizeof(struct fru_multirec_header))) {
368 				/* can't reliably judge for the rest space */
369 				break;
370 			}
371 		} while (!(rec_hdr.format & 0x80) && (off < fru->size));
372 
373 		lprintf(LOG_DEBUG,"Multi-Record area ends at: %i (%xh)", off, off);
374 
375 		if (fru->size > off) {
376 			// Bloc for remaining space
377 			p_new = malloc(sizeof(struct ipmi_fru_bloc));
378 			if (!p_new) {
379 				lprintf(LOG_ERR, "ipmitool: malloc failure");
380 				free_fru_bloc(p_first);
381 				return NULL;
382 			}
383 
384 			p_new->next = NULL;
385 			p_new->start = off;
386 			p_new->size = fru->size - p_new->start;
387 			strcpy((char *)p_new->blocId, "Unused space");
388 
389 			p_bloc->next = p_new;
390 			p_bloc->size = p_new->start - p_bloc->start;
391 		}
392 	}
393 
394 	/* Dump blocs */
395 	for(p_bloc = p_first, i = 0; p_bloc; p_bloc = p_bloc->next) {
396 		lprintf(LOG_DEBUG ,"Bloc Numb : %i", i++);
397 		lprintf(LOG_DEBUG ,"Bloc Id   : %s", p_bloc->blocId);
398 		lprintf(LOG_DEBUG ,"Bloc Start: %i", p_bloc->start);
399 		lprintf(LOG_DEBUG ,"Bloc Size : %i", p_bloc->size);
400 		lprintf(LOG_DEBUG ,"");
401 	}
402 
403 	return p_first;
404 }
405 
406 void
407 free_fru_bloc(t_ipmi_fru_bloc *bloc)
408 {
409 	t_ipmi_fru_bloc * del;
410 
411 	while (bloc) {
412 		del = bloc;
413 		bloc = bloc->next;
414 		free(del);
415 		del = NULL;
416 	}
417 }
418 
419 /*
420  * write FRU[doffset:length] from the pFrubuf[soffset:length]
421  * rc=1 on success
422 **/
423 int
424 write_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
425 					uint16_t soffset,  uint16_t doffset,
426 					uint16_t length, uint8_t *pFrubuf)
427 {
428 	uint16_t tmp, finish;
429 	struct ipmi_rs * rsp;
430 	struct ipmi_rq req;
431 	uint8_t msg_data[255+3];
432 	uint16_t writeLength;
433 	uint16_t found_bloc = 0;
434 
435 	finish = doffset + length;        /* destination offset */
436 	if (finish > fru->size)
437 	{
438 		lprintf(LOG_ERROR, "Return error");
439 		return -1;
440 	}
441 
442 	if (fru->access && ((doffset & 1) || (length & 1))) {
443 		lprintf(LOG_ERROR, "Odd offset or length specified");
444 		return (-1);
445 	}
446 
447 	t_ipmi_fru_bloc * fru_bloc = build_fru_bloc(intf, fru, id);
448 	t_ipmi_fru_bloc * saved_fru_bloc = fru_bloc;
449 
450 	memset(&req, 0, sizeof(req));
451 	req.msg.netfn = IPMI_NETFN_STORAGE;
452 	req.msg.cmd = SET_FRU_DATA;
453 	req.msg.data = msg_data;
454 
455 	/* initialize request size only once */
456 	if (fru->max_write_size == 0) {
457 		uint16_t max_rq_size = ipmi_intf_get_max_request_data_size(intf);
458 
459 		/* validate lower bound of the maximum request data size */
460 		if (max_rq_size <= 3) {
461 			lprintf(LOG_ERROR, "Maximum request size is too small to send "
462 					"a write request");
463 			return -1;
464 		}
465 
466 		/*
467 		 * Write FRU Info command returns the number of written bytes in
468 		 * a single byte field.
469 		 */
470 		if (max_rq_size - 3 > 255) {
471 			/*  Limit the max write size with 255 bytes. */
472 			fru->max_write_size = 255;
473 		} else {
474 			/* subtract 1 byte for FRU ID an 2 bytes for offset */
475 			fru->max_write_size = max_rq_size - 3;
476 		}
477 
478 		/* check word access */
479 		if (fru->access) {
480 			fru->max_write_size &= ~1;
481 		}
482 	}
483 
484 	do {
485 		uint16_t end_bloc;
486 		uint8_t protected_bloc = 0;
487 
488 		/* Write per bloc, try to find the end of a bloc*/
489 		while (fru_bloc && fru_bloc->start + fru_bloc->size <= doffset) {
490 			fru_bloc = fru_bloc->next;
491 			found_bloc++;
492 		}
493 
494 		if (fru_bloc && fru_bloc->start + fru_bloc->size < finish) {
495 			end_bloc = fru_bloc->start + fru_bloc->size;
496 		} else {
497 			end_bloc = finish;
498 		}
499 
500 		/* calculate write length */
501 		tmp = end_bloc - doffset;
502 
503 		/* check that write length is more than maximum request size */
504 		if (tmp > fru->max_write_size) {
505 			writeLength = fru->max_write_size;
506 		} else {
507 			writeLength = tmp;
508 		}
509 
510 		/* copy fru data */
511 		memcpy(&msg_data[3], pFrubuf + soffset, writeLength);
512 
513 		/* check word access */
514 		if (fru->access) {
515 			writeLength &= ~1;
516 		}
517 
518 		tmp = doffset;
519 		if (fru->access) {
520 			tmp >>= 1;
521 		}
522 
523 		msg_data[0] = id;
524 		msg_data[1] = (uint8_t)tmp;
525 		msg_data[2] = (uint8_t)(tmp >> 8);
526 		req.msg.data_len = writeLength + 3;
527 
528 		if(fru_bloc) {
529 			lprintf(LOG_INFO,"Writing %d bytes (Bloc #%i: %s)",
530 					writeLength, found_bloc, fru_bloc->blocId);
531 		} else {
532 			lprintf(LOG_INFO,"Writing %d bytes", writeLength);
533 		}
534 
535 		rsp = intf->sendrecv(intf, &req);
536 		if (!rsp) {
537 			break;
538 		}
539 
540 		if (rsp->ccode == 0xc7 || rsp->ccode == 0xc8 || rsp->ccode == 0xca) {
541 			if (fru->max_write_size > 8) {
542 				fru->max_write_size -= 8;
543 				lprintf(LOG_INFO, "Retrying FRU write with request size %d",
544 						fru->max_write_size);
545 				continue;
546 			}
547 		} else if(rsp->ccode == 0x80) {
548 			rsp->ccode = 0;
549 			// Write protected section
550 			protected_bloc = 1;
551 		}
552 
553 		if (rsp->ccode > 0)
554 			break;
555 
556 		if (protected_bloc == 0) {
557 			// Write OK, bloc not protected, continue
558 			lprintf(LOG_INFO,"Wrote %d bytes", writeLength);
559 			doffset += writeLength;
560 			soffset += writeLength;
561 		} else {
562 			if(fru_bloc) {
563 				// Bloc protected, advise user and jump over protected bloc
564 				lprintf(LOG_INFO,
565 						"Bloc [%s] protected at offset: %i (size %i bytes)",
566 						fru_bloc->blocId, fru_bloc->start, fru_bloc->size);
567 				lprintf(LOG_INFO,"Jumping over this bloc");
568 			} else {
569 				lprintf(LOG_INFO,
570 						"Remaining FRU is protected following offset: %i",
571 						doffset);
572 			}
573 			soffset += end_bloc - doffset;
574 			doffset = end_bloc;
575 		}
576 	} while (doffset < finish);
577 
578 	if (saved_fru_bloc) {
579 		free_fru_bloc(saved_fru_bloc);
580 	}
581 
582 	return doffset >= finish;
583 }
584 
585 /* read_fru_area  -  fill in frubuf[offset:length] from the FRU[offset:length]
586 *
587 * @intf:   ipmi interface
588 * @fru: fru info
589 * @id:     fru id
590 * @offset: offset into buffer
591 * @length: how much to read
592 * @frubuf: buffer read into
593 *
594 * returns -1 on error
595 * returns 0 if successful
596 */
597 int
598 read_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
599 			uint32_t offset, uint32_t length, uint8_t *frubuf)
600 {
601 	uint32_t off = offset, tmp, finish;
602 	struct ipmi_rs * rsp;
603 	struct ipmi_rq req;
604 	uint8_t msg_data[4];
605 
606 	if (offset > fru->size) {
607 		lprintf(LOG_ERR, "Read FRU Area offset incorrect: %d > %d",
608 			offset, fru->size);
609 		return -1;
610 	}
611 
612 	finish = offset + length;
613 	if (finish > fru->size) {
614 		finish = fru->size;
615 		lprintf(LOG_NOTICE, "Read FRU Area length %d too large, "
616 			"Adjusting to %d",
617 			offset + length, finish - offset);
618 	}
619 
620 	memset(&req, 0, sizeof(req));
621 	req.msg.netfn = IPMI_NETFN_STORAGE;
622 	req.msg.cmd = GET_FRU_DATA;
623 	req.msg.data = msg_data;
624 	req.msg.data_len = 4;
625 
626 	if (fru->max_read_size == 0) {
627 		uint16_t max_rs_size = ipmi_intf_get_max_response_data_size(intf) - 1;
628 
629 		/* validate lower bound of the maximum response data size */
630 		if (max_rs_size <= 1) {
631 			lprintf(LOG_ERROR, "Maximum response size is too small to send "
632 					"a read request");
633 			return -1;
634 		}
635 
636 		/*
637 		 * Read FRU Info command may read up to 255 bytes of data.
638 		 */
639 		if (max_rs_size - 1 > 255) {
640 			/*  Limit the max read size with 255 bytes. */
641 			fru->max_read_size = 255;
642 		} else {
643 			/* subtract 1 byte for bytes count */
644 			fru->max_read_size = max_rs_size - 1;
645 		}
646 
647 		/* check word access */
648 		if (fru->access) {
649 			fru->max_read_size &= ~1;
650 		}
651 	}
652 
653 	do {
654 		tmp = fru->access ? off >> 1 : off;
655 		msg_data[0] = id;
656 		msg_data[1] = (uint8_t)(tmp & 0xff);
657 		msg_data[2] = (uint8_t)(tmp >> 8);
658 		tmp = finish - off;
659 		if (tmp > fru->max_read_size)
660 			msg_data[3] = (uint8_t)fru->max_read_size;
661 		else
662 			msg_data[3] = (uint8_t)tmp;
663 
664 		rsp = intf->sendrecv(intf, &req);
665 		if (rsp == NULL) {
666 			lprintf(LOG_NOTICE, "FRU Read failed");
667 			break;
668 		}
669 		if (rsp->ccode > 0) {
670 			/* if we get C8h or CAh completion code then we requested too
671 			* many bytes at once so try again with smaller size */
672 			if ((rsp->ccode == 0xc8 || rsp->ccode == 0xca)
673 					&& fru->max_read_size > 8) {
674 				if (fru->max_read_size > 32) {
675 					/* subtract read length more aggressively */
676 					fru->max_read_size -= 8;
677 				} else {
678 					/* subtract length less aggressively */
679 					fru->max_read_size--;
680 				}
681 
682 				lprintf(LOG_INFO, "Retrying FRU read with request size %d",
683 						fru->max_read_size);
684 				continue;
685 			}
686 
687 			lprintf(LOG_NOTICE, "FRU Read failed: %s",
688 				val2str(rsp->ccode, completion_code_vals));
689 			break;
690 		}
691 
692 		tmp = fru->access ? rsp->data[0] << 1 : rsp->data[0];
693 		memcpy(frubuf, rsp->data + 1, tmp);
694 		off += tmp;
695 		frubuf += tmp;
696 		/* sometimes the size returned in the Info command
697 		* is too large.  return 0 so higher level function
698 		* still attempts to parse what was returned */
699 		if (tmp == 0 && off < finish) {
700 			return 0;
701 		}
702 	} while (off < finish);
703 
704 	if (off < finish) {
705 		return -1;
706 	}
707 
708 	return 0;
709 }
710 
711 /* read_fru_area  -  fill in frubuf[offset:length] from the FRU[offset:length]
712 *
713 * @intf:   ipmi interface
714 * @fru: fru info
715 * @id:     fru id
716 * @offset: offset into buffer
717 * @length: how much to read
718 * @frubuf: buffer read into
719 *
720 * returns -1 on error
721 * returns 0 if successful
722 */
723 int
724 read_fru_area_section(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
725 			uint32_t offset, uint32_t length, uint8_t *frubuf)
726 {
727 	static uint32_t fru_data_rqst_size = 20;
728 	uint32_t off = offset, tmp, finish;
729 	struct ipmi_rs * rsp;
730 	struct ipmi_rq req;
731 	uint8_t msg_data[4];
732 
733 	if (offset > fru->size) {
734 		lprintf(LOG_ERR, "Read FRU Area offset incorrect: %d > %d",
735 			offset, fru->size);
736 		return -1;
737 	}
738 
739 	finish = offset + length;
740 	if (finish > fru->size) {
741 		finish = fru->size;
742 		lprintf(LOG_NOTICE, "Read FRU Area length %d too large, "
743 			"Adjusting to %d",
744 			offset + length, finish - offset);
745 	}
746 
747 	memset(&req, 0, sizeof(req));
748 	req.msg.netfn = IPMI_NETFN_STORAGE;
749 	req.msg.cmd = GET_FRU_DATA;
750 	req.msg.data = msg_data;
751 	req.msg.data_len = 4;
752 
753 #ifdef LIMIT_ALL_REQUEST_SIZE
754 	if (fru_data_rqst_size > 16)
755 #else
756 	if (fru->access && fru_data_rqst_size > 16)
757 #endif
758 		fru_data_rqst_size = 16;
759 	do {
760 		tmp = fru->access ? off >> 1 : off;
761 		msg_data[0] = id;
762 		msg_data[1] = (uint8_t)(tmp & 0xff);
763 		msg_data[2] = (uint8_t)(tmp >> 8);
764 		tmp = finish - off;
765 		if (tmp > fru_data_rqst_size)
766 			msg_data[3] = (uint8_t)fru_data_rqst_size;
767 		else
768 			msg_data[3] = (uint8_t)tmp;
769 
770 		rsp = intf->sendrecv(intf, &req);
771 		if (rsp == NULL) {
772 			lprintf(LOG_NOTICE, "FRU Read failed");
773 			break;
774 		}
775 		if (rsp->ccode > 0) {
776 			/* if we get C7 or C8  or CA return code then we requested too
777 			* many bytes at once so try again with smaller size */
778 			if ((rsp->ccode == 0xc7 || rsp->ccode == 0xc8 || rsp->ccode == 0xca) &&
779 				(--fru_data_rqst_size > 8)) {
780 				lprintf(LOG_INFO, "Retrying FRU read with request size %d",
781 					fru_data_rqst_size);
782 				continue;
783 			}
784 			lprintf(LOG_NOTICE, "FRU Read failed: %s",
785 				val2str(rsp->ccode, completion_code_vals));
786 			break;
787 		}
788 
789 		tmp = fru->access ? rsp->data[0] << 1 : rsp->data[0];
790 		memcpy((frubuf + off)-offset, rsp->data + 1, tmp);
791 		off += tmp;
792 
793 		/* sometimes the size returned in the Info command
794 		* is too large.  return 0 so higher level function
795 		* still attempts to parse what was returned */
796 		if (tmp == 0 && off < finish)
797 			return 0;
798 
799 	} while (off < finish);
800 
801 	if (off < finish)
802 		return -1;
803 
804 	return 0;
805 }
806 
807 
808 static void
809 fru_area_print_multirec_bloc(struct ipmi_intf * intf, struct fru_info * fru,
810 			uint8_t id, uint32_t offset)
811 {
812 	uint8_t * fru_data = NULL;
813 	uint32_t fru_len, i;
814 	struct fru_multirec_header * h;
815 	uint32_t last_off, len;
816 
817 	i = last_off = offset;
818 	fru_len = 0;
819 
820 	fru_data = malloc(fru->size + 1);
821 	if (fru_data == NULL) {
822 		lprintf(LOG_ERR, " Out of memory!");
823 		return;
824 	}
825 
826 	memset(fru_data, 0, fru->size + 1);
827 
828 	do {
829 		h = (struct fru_multirec_header *) (fru_data + i);
830 
831 		// read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time
832 		if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len)))
833 		{
834 			len = fru->size - last_off;
835 			if (len > FRU_MULTIREC_CHUNK_SIZE)
836 				len = FRU_MULTIREC_CHUNK_SIZE;
837 
838 			if (read_fru_area(intf, fru, id, last_off, len, fru_data) < 0)
839 				break;
840 
841 			last_off += len;
842 		}
843 
844 		//printf("Bloc Numb : %i\n", counter);
845 		printf("Bloc Start: %i\n", i);
846 		printf("Bloc Size : %i\n", h->len);
847 		printf("\n");
848 
849 		i += h->len + sizeof (struct fru_multirec_header);
850 	} while (!(h->format & 0x80));
851 
852 	i = offset;
853 	do {
854 		h = (struct fru_multirec_header *) (fru_data + i);
855 
856 		printf("Bloc Start: %i\n", i);
857 		printf("Bloc Size : %i\n", h->len);
858 		printf("\n");
859 
860 		i += h->len + sizeof (struct fru_multirec_header);
861 	} while (!(h->format & 0x80));
862 
863 	lprintf(LOG_DEBUG ,"Multi-Record area ends at: %i (%xh)",i,i);
864 
865 	free(fru_data);
866 	fru_data = NULL;
867 }
868 
869 
870 /* fru_area_print_chassis  -  Print FRU Chassis Area
871 *
872 * @intf:   ipmi interface
873 * @fru: fru info
874 * @id:  fru id
875 * @offset: offset pointer
876 */
877 static void
878 fru_area_print_chassis(struct ipmi_intf * intf, struct fru_info * fru,
879 			uint8_t id, uint32_t offset)
880 {
881 	char * fru_area;
882 	uint8_t * fru_data;
883 	uint32_t fru_len, i;
884 	uint8_t tmp[2];
885 
886 	fru_len = 0;
887 
888 	/* read enough to check length field */
889 	if (read_fru_area(intf, fru, id, offset, 2, tmp) == 0) {
890 		fru_len = 8 * tmp[1];
891 	}
892 
893 	if (fru_len == 0) {
894 		return;
895 	}
896 
897 	fru_data = malloc(fru_len);
898 	if (fru_data == NULL) {
899 		lprintf(LOG_ERR, "ipmitool: malloc failure");
900 		return;
901 	}
902 
903 	memset(fru_data, 0, fru_len);
904 
905 	/* read in the full fru */
906 	if (read_fru_area(intf, fru, id, offset, fru_len, fru_data) < 0) {
907 		free(fru_data);
908 		fru_data = NULL;
909 		return;
910 	}
911 
912 	/*
913 	 * skip first two bytes which specify
914 	 * fru area version and fru area length
915 	 */
916 	i = 2;
917 
918 	printf(" Chassis Type          : %s\n",
919  		chassis_type_desc[fru_data[i] >
920  		(sizeof(chassis_type_desc)/sizeof(chassis_type_desc[0])) - 1 ?
921  		2 : fru_data[i]]);
922 
923  	i++;
924 
925 	fru_area = get_fru_area_str(fru_data, &i);
926 	if (fru_area != NULL) {
927 		if (strlen(fru_area) > 0) {
928 			printf(" Chassis Part Number   : %s\n", fru_area);
929 		}
930 		free(fru_area);
931 		fru_area = NULL;
932 	}
933 
934 	fru_area = get_fru_area_str(fru_data, &i);
935 	if (fru_area != NULL) {
936 		if (strlen(fru_area) > 0) {
937 			printf(" Chassis Serial        : %s\n", fru_area);
938 		}
939 		free(fru_area);
940 		fru_area = NULL;
941 	}
942 
943 	/* read any extra fields */
944 	while ((fru_data[i] != 0xc1) && (i < fru_len))
945 	{
946 		int j = i;
947 		fru_area = get_fru_area_str(fru_data, &i);
948 		if (fru_area != NULL) {
949 			if (strlen(fru_area) > 0) {
950 				printf(" Chassis Extra         : %s\n", fru_area);
951 			}
952 			free(fru_area);
953 			fru_area = NULL;
954 		}
955 
956 		if (i == j) {
957 			break;
958 		}
959 	}
960 
961 	if (fru_data != NULL) {
962 		free(fru_data);
963 		fru_data = NULL;
964 	}
965 }
966 
967 /* fru_area_print_board  -  Print FRU Board Area
968 *
969 * @intf:   ipmi interface
970 * @fru: fru info
971 * @id:  fru id
972 * @offset: offset pointer
973 */
974 static void
975 fru_area_print_board(struct ipmi_intf * intf, struct fru_info * fru,
976 			uint8_t id, uint32_t offset)
977 {
978 	char * fru_area;
979 	uint8_t * fru_data;
980 	uint32_t fru_len;
981 	uint32_t i;
982 	time_t tval;
983 	uint8_t tmp[2];
984 
985 	fru_len = 0;
986 
987 	/* read enough to check length field */
988 	if (read_fru_area(intf, fru, id, offset, 2, tmp) == 0) {
989 		fru_len = 8 * tmp[1];
990 	}
991 
992 	if (fru_len <= 0) {
993 		return;
994 	}
995 
996 	fru_data = malloc(fru_len);
997 	if (fru_data == NULL) {
998 		lprintf(LOG_ERR, "ipmitool: malloc failure");
999 		return;
1000 	}
1001 
1002 	memset(fru_data, 0, fru_len);
1003 
1004 	/* read in the full fru */
1005 	if (read_fru_area(intf, fru, id, offset, fru_len, fru_data) < 0) {
1006 		free(fru_data);
1007 		fru_data = NULL;
1008 		return;
1009 	}
1010 
1011 	/*
1012 	 * skip first three bytes which specify
1013 	 * fru area version, fru area length
1014 	 * and fru board language
1015 	 */
1016 	i = 3;
1017 
1018 	tval=((fru_data[i+2] << 16) + (fru_data[i+1] << 8) + (fru_data[i]));
1019 	tval=tval * 60;
1020 	tval=tval + secs_from_1970_1996;
1021 	printf(" Board Mfg Date        : %s", asctime(localtime(&tval)));
1022 	i += 3;  /* skip mfg. date time */
1023 
1024 	fru_area = get_fru_area_str(fru_data, &i);
1025 	if (fru_area != NULL) {
1026 		if (strlen(fru_area) > 0) {
1027 			printf(" Board Mfg             : %s\n", fru_area);
1028 		}
1029 		free(fru_area);
1030 		fru_area = NULL;
1031 	}
1032 
1033 	fru_area = get_fru_area_str(fru_data, &i);
1034 	if (fru_area != NULL) {
1035 		if (strlen(fru_area) > 0) {
1036 			printf(" Board Product         : %s\n", fru_area);
1037 		}
1038 		free(fru_area);
1039 		fru_area = NULL;
1040 	}
1041 
1042 	fru_area = get_fru_area_str(fru_data, &i);
1043 	if (fru_area != NULL) {
1044 		if (strlen(fru_area) > 0) {
1045 			printf(" Board Serial          : %s\n", fru_area);
1046 		}
1047 		free(fru_area);
1048 		fru_area = NULL;
1049 	}
1050 
1051 	fru_area = get_fru_area_str(fru_data, &i);
1052 	if (fru_area != NULL) {
1053 		if (strlen(fru_area) > 0) {
1054 			printf(" Board Part Number     : %s\n", fru_area);
1055 		}
1056 		free(fru_area);
1057 		fru_area = NULL;
1058 	}
1059 
1060 	fru_area = get_fru_area_str(fru_data, &i);
1061 	if (fru_area != NULL) {
1062 		if (strlen(fru_area) > 0 && verbose > 0) {
1063 			printf(" Board FRU ID          : %s\n", fru_area);
1064 		}
1065 		free(fru_area);
1066 		fru_area = NULL;
1067 	}
1068 
1069 	/* read any extra fields */
1070 	while ((fru_data[i] != 0xc1) && (i < fru_len))
1071 	{
1072 		int j = i;
1073 		fru_area = get_fru_area_str(fru_data, &i);
1074 		if (fru_area != NULL) {
1075 			if (strlen(fru_area) > 0) {
1076 				printf(" Board Extra           : %s\n", fru_area);
1077 			}
1078 			free(fru_area);
1079 			fru_area = NULL;
1080 		}
1081 		if (i == j)
1082 			break;
1083 	}
1084 
1085 	if (fru_data != NULL) {
1086 		free(fru_data);
1087 		fru_data = NULL;
1088 	}
1089 }
1090 
1091 /* fru_area_print_product  -  Print FRU Product Area
1092 *
1093 * @intf:   ipmi interface
1094 * @fru: fru info
1095 * @id:  fru id
1096 * @offset: offset pointer
1097 */
1098 static void
1099 fru_area_print_product(struct ipmi_intf * intf, struct fru_info * fru,
1100 				uint8_t id, uint32_t offset)
1101 {
1102 	char * fru_area;
1103 	uint8_t * fru_data;
1104 	uint32_t fru_len, i;
1105 	uint8_t tmp[2];
1106 
1107 	fru_len = 0;
1108 
1109 	/* read enough to check length field */
1110 	if (read_fru_area(intf, fru, id, offset, 2, tmp) == 0) {
1111 		fru_len = 8 * tmp[1];
1112 	}
1113 
1114 	if (fru_len == 0) {
1115 		return;
1116 	}
1117 
1118 	fru_data = malloc(fru_len);
1119 	if (fru_data == NULL) {
1120 		lprintf(LOG_ERR, "ipmitool: malloc failure");
1121 		return;
1122 	}
1123 
1124 	memset(fru_data, 0, fru_len);
1125 
1126 
1127 	/* read in the full fru */
1128 	if (read_fru_area(intf, fru, id, offset, fru_len, fru_data) < 0) {
1129 		free(fru_data);
1130 		fru_data = NULL;
1131 		return;
1132 	}
1133 
1134 	/*
1135 	 * skip first three bytes which specify
1136 	 * fru area version, fru area length
1137 	 * and fru board language
1138 	 */
1139 	i = 3;
1140 
1141 	fru_area = get_fru_area_str(fru_data, &i);
1142 	if (fru_area != NULL) {
1143 		if (strlen(fru_area) > 0) {
1144 			printf(" Product Manufacturer  : %s\n", fru_area);
1145 		}
1146 		free(fru_area);
1147 		fru_area = NULL;
1148 	}
1149 
1150 	fru_area = get_fru_area_str(fru_data, &i);
1151 	if (fru_area != NULL) {
1152 		if (strlen(fru_area) > 0) {
1153 			printf(" Product Name          : %s\n", fru_area);
1154 		}
1155 		free(fru_area);
1156 		fru_area = NULL;
1157 	}
1158 
1159 	fru_area = get_fru_area_str(fru_data, &i);
1160 	if (fru_area != NULL) {
1161 		if (strlen(fru_area) > 0) {
1162 			printf(" Product Part Number   : %s\n", fru_area);
1163 		}
1164 		free(fru_area);
1165 		fru_area = NULL;
1166 	}
1167 
1168 	fru_area = get_fru_area_str(fru_data, &i);
1169 	if (fru_area != NULL) {
1170 		if (strlen(fru_area) > 0) {
1171 			printf(" Product Version       : %s\n", fru_area);
1172 		}
1173 		free(fru_area);
1174 		fru_area = NULL;
1175 	}
1176 
1177 	fru_area = get_fru_area_str(fru_data, &i);
1178 	if (fru_area != NULL) {
1179 		if (strlen(fru_area) > 0) {
1180 			printf(" Product Serial        : %s\n", fru_area);
1181 		}
1182 		free(fru_area);
1183 		fru_area = NULL;
1184 	}
1185 
1186 	fru_area = get_fru_area_str(fru_data, &i);
1187 	if (fru_area != NULL) {
1188 		if (strlen(fru_area) > 0) {
1189 			printf(" Product Asset Tag     : %s\n", fru_area);
1190 		}
1191 		free(fru_area);
1192 		fru_area = NULL;
1193 	}
1194 
1195 	fru_area = get_fru_area_str(fru_data, &i);
1196 	if (fru_area != NULL) {
1197 		if (strlen(fru_area) > 0 && verbose > 0) {
1198 			printf(" Product FRU ID        : %s\n", fru_area);
1199 		}
1200 		free(fru_area);
1201 		fru_area = NULL;
1202 	}
1203 
1204 	/* read any extra fields */
1205 	while ((fru_data[i] != 0xc1) && (i < fru_len))
1206 	{
1207 		int j = i;
1208 		fru_area = get_fru_area_str(fru_data, &i);
1209 		if (fru_area != NULL) {
1210 			if (strlen(fru_area) > 0) {
1211 				printf(" Product Extra         : %s\n", fru_area);
1212 			}
1213 			free(fru_area);
1214 			fru_area = NULL;
1215 		}
1216 		if (i == j)
1217 			break;
1218 	}
1219 
1220 	if (fru_data != NULL) {
1221 		free(fru_data);
1222 		fru_data = NULL;
1223 	}
1224 	if (fru_area != NULL) {
1225 		free(fru_area);
1226 		fru_area = NULL;
1227 	}
1228 }
1229 
1230 /* fru_area_print_multirec  -  Print FRU Multi Record Area
1231 *
1232 * @intf:   ipmi interface
1233 * @fru: fru info
1234 * @id:  fru id
1235 * @offset: offset pointer
1236 */
1237 static void
1238 fru_area_print_multirec(struct ipmi_intf * intf, struct fru_info * fru,
1239 			uint8_t id, uint32_t offset)
1240 {
1241 	uint8_t * fru_data;
1242 	struct fru_multirec_header * h;
1243 	struct fru_multirec_powersupply * ps;
1244 	struct fru_multirec_dcoutput * dc;
1245 	struct fru_multirec_dcload * dl;
1246 	uint16_t peak_capacity;
1247 	uint8_t peak_hold_up_time;
1248 	uint32_t last_off;
1249 
1250 	last_off = offset;
1251 
1252 	fru_data = malloc(FRU_MULTIREC_CHUNK_SIZE);
1253 	if (fru_data == NULL) {
1254 		lprintf(LOG_ERR, "ipmitool: malloc failure");
1255 		return;
1256 	}
1257 
1258 	memset(fru_data, 0, FRU_MULTIREC_CHUNK_SIZE);
1259 
1260 	h = (struct fru_multirec_header *) (fru_data);
1261 
1262 	do {
1263 		if (read_fru_area(intf, fru, id, last_off, sizeof(*h), fru_data) < 0) {
1264 			break;
1265 		}
1266 
1267 		if (h->len && read_fru_area(intf, fru, id,
1268 				last_off + sizeof(*h), h->len, fru_data + sizeof(*h)) < 0) {
1269 			break;
1270 		}
1271 
1272 		last_off += h->len + sizeof(*h);
1273 
1274 		switch (h->type) {
1275 		case FRU_RECORD_TYPE_POWER_SUPPLY_INFORMATION:
1276 			ps = (struct fru_multirec_powersupply *)
1277 				(fru_data + sizeof(struct fru_multirec_header));
1278 
1279 #if WORDS_BIGENDIAN
1280 			ps->capacity      = BSWAP_16(ps->capacity);
1281 			ps->peak_va    = BSWAP_16(ps->peak_va);
1282 			ps->lowend_input1 = BSWAP_16(ps->lowend_input1);
1283 			ps->highend_input1   = BSWAP_16(ps->highend_input1);
1284 			ps->lowend_input2 = BSWAP_16(ps->lowend_input2);
1285 			ps->highend_input2   = BSWAP_16(ps->highend_input2);
1286 			ps->combined_capacity   = BSWAP_16(ps->combined_capacity);
1287 			ps->peak_cap_ht      = BSWAP_16(ps->peak_cap_ht);
1288 #endif
1289 			peak_hold_up_time = (ps->peak_cap_ht & 0xf000) >> 12;
1290 			peak_capacity     = ps->peak_cap_ht & 0x0fff;
1291 
1292 			printf (" Power Supply Record\n");
1293 			printf ("  Capacity                   : %d W\n",
1294 				ps->capacity);
1295 			printf ("  Peak VA                    : %d VA\n",
1296 				ps->peak_va);
1297 			printf ("  Inrush Current             : %d A\n",
1298 				ps->inrush_current);
1299 			printf ("  Inrush Interval            : %d ms\n",
1300 				ps->inrush_interval);
1301 			printf ("  Input Voltage Range 1      : %d-%d V\n",
1302 				ps->lowend_input1 / 100, ps->highend_input1 / 100);
1303 			printf ("  Input Voltage Range 2      : %d-%d V\n",
1304 				ps->lowend_input2 / 100, ps->highend_input2 / 100);
1305 			printf ("  Input Frequency Range      : %d-%d Hz\n",
1306 				ps->lowend_freq, ps->highend_freq);
1307 			printf ("  A/C Dropout Tolerance      : %d ms\n",
1308 				ps->dropout_tolerance);
1309 			printf ("  Flags                      : %s%s%s%s%s\n",
1310 				ps->predictive_fail ? "'Predictive fail' " : "",
1311 				ps->pfc ? "'Power factor correction' " : "",
1312 				ps->autoswitch ? "'Autoswitch voltage' " : "",
1313 				ps->hotswap ? "'Hot swap' " : "",
1314 				ps->predictive_fail ? ps->rps_threshold ?
1315 				ps->tach ? "'Two pulses per rotation'" : "'One pulse per rotation'" :
1316 				ps->tach ? "'Failure on pin de-assertion'" : "'Failure on pin assertion'" : "");
1317 			printf ("  Peak capacity              : %d W\n",
1318 				peak_capacity);
1319 			printf ("  Peak capacity holdup       : %d s\n",
1320 				peak_hold_up_time);
1321 			if (ps->combined_capacity == 0)
1322 				printf ("  Combined capacity          : not specified\n");
1323 			else
1324 				printf ("  Combined capacity          : %d W (%s and %s)\n",
1325 					ps->combined_capacity,
1326 					combined_voltage_desc [ps->combined_voltage1],
1327 					combined_voltage_desc [ps->combined_voltage2]);
1328 			if (ps->predictive_fail)
1329 				printf ("  Fan lower threshold        : %d RPS\n",
1330 					ps->rps_threshold);
1331 			break;
1332 
1333 		case FRU_RECORD_TYPE_DC_OUTPUT:
1334 			dc = (struct fru_multirec_dcoutput *)
1335 				(fru_data + sizeof(struct fru_multirec_header));
1336 
1337 #if WORDS_BIGENDIAN
1338 			dc->nominal_voltage  = BSWAP_16(dc->nominal_voltage);
1339 			dc->max_neg_dev      = BSWAP_16(dc->max_neg_dev);
1340 			dc->max_pos_dev      = BSWAP_16(dc->max_pos_dev);
1341 			dc->ripple_and_noise = BSWAP_16(dc->ripple_and_noise);
1342 			dc->min_current      = BSWAP_16(dc->min_current);
1343 			dc->max_current      = BSWAP_16(dc->max_current);
1344 #endif
1345 
1346 			printf (" DC Output Record\n");
1347 			printf ("  Output Number              : %d\n",
1348 				dc->output_number);
1349 			printf ("  Standby power              : %s\n",
1350 				dc->standby ? "Yes" : "No");
1351 			printf ("  Nominal voltage            : %.2f V\n",
1352 				(double) dc->nominal_voltage / 100);
1353 			printf ("  Max negative deviation     : %.2f V\n",
1354 				(double) dc->max_neg_dev / 100);
1355 			printf ("  Max positive deviation     : %.2f V\n",
1356 				(double) dc->max_pos_dev / 100);
1357 			printf ("  Ripple and noise pk-pk     : %d mV\n",
1358 				dc->ripple_and_noise);
1359 			printf ("  Minimum current draw       : %.3f A\n",
1360 				(double) dc->min_current / 1000);
1361 			printf ("  Maximum current draw       : %.3f A\n",
1362 				(double) dc->max_current / 1000);
1363 			break;
1364 
1365 		case FRU_RECORD_TYPE_DC_LOAD:
1366 			dl = (struct fru_multirec_dcload *)
1367 				(fru_data + sizeof(struct fru_multirec_header));
1368 
1369 #if WORDS_BIGENDIAN
1370 			dl->nominal_voltage  = BSWAP_16(dl->nominal_voltage);
1371 			dl->min_voltage      = BSWAP_16(dl->min_voltage);
1372 			dl->max_voltage      = BSWAP_16(dl->max_voltage);
1373 			dl->ripple_and_noise = BSWAP_16(dl->ripple_and_noise);
1374 			dl->min_current      = BSWAP_16(dl->min_current);
1375 			dl->max_current      = BSWAP_16(dl->max_current);
1376 #endif
1377 
1378 			printf (" DC Load Record\n");
1379 			printf ("  Output Number              : %d\n",
1380 				dl->output_number);
1381 			printf ("  Nominal voltage            : %.2f V\n",
1382 				(double) dl->nominal_voltage / 100);
1383 			printf ("  Min voltage allowed        : %.2f V\n",
1384 				(double) dl->min_voltage / 100);
1385 			printf ("  Max voltage allowed        : %.2f V\n",
1386 				(double) dl->max_voltage / 100);
1387 			printf ("  Ripple and noise pk-pk     : %d mV\n",
1388 				dl->ripple_and_noise);
1389 			printf ("  Minimum current load       : %.3f A\n",
1390 				(double) dl->min_current / 1000);
1391 			printf ("  Maximum current load       : %.3f A\n",
1392 				(double) dl->max_current / 1000);
1393 			break;
1394 		case FRU_RECORD_TYPE_OEM_EXTENSION:
1395 			{
1396 				struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *)
1397 										&fru_data[sizeof(struct fru_multirec_header)];
1398 				uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16;
1399 
1400 				/* Now makes sure this is really PICMG record */
1401 
1402 				if( iana == IPMI_OEM_PICMG ){
1403 					printf("  PICMG Extension Record\n");
1404 					ipmi_fru_picmg_ext_print(fru_data,
1405 													sizeof(struct fru_multirec_header),
1406 													h->len);
1407 				}
1408 				/* FIXME: Add OEM record support here */
1409 				else{
1410 					printf("  OEM (%s) Record\n", val2str( iana, ipmi_oem_info));
1411 				}
1412 			}
1413 			break;
1414 		}
1415 	} while (!(h->format & 0x80));
1416 
1417 	lprintf(LOG_DEBUG ,"Multi-Record area ends at: %i (%xh)", last_off, last_off);
1418 
1419 	free(fru_data);
1420 }
1421 
1422 /* ipmi_fru_query_new_value  -  Query new values to replace original FRU content
1423 *
1424 * @data:   FRU data
1425 * @offset: offset of the bytes to be modified in data
1426 * @len:    size of the modified data
1427 *
1428 * returns : TRUE if data changed
1429 * returns : FALSE if data not changed
1430 */
1431 int ipmi_fru_query_new_value(uint8_t *data,int offset, size_t len)
1432 {
1433 	int status=FALSE;
1434 	int ret;
1435 	char answer;
1436 
1437 	printf("Would you like to change this value <y/n> ? ");
1438 	ret = scanf("%c", &answer);
1439 	if (ret != 1) {
1440 		return FALSE;
1441 	}
1442 
1443 	if( answer == 'y' || answer == 'Y' ){
1444 		int i;
1445 		unsigned int *holder;
1446 
1447 		holder = malloc(len);
1448 		printf(
1449 		 "Enter hex values for each of the %d entries (lsb first), "
1450 		 "hit <enter> between entries\n", (int)len);
1451 
1452 		/* I can't assign scanf' %x into a single char */
1453 		for( i=0;i<len;i++ ){
1454 			ret = scanf("%x", holder+i);
1455 			if (ret != 1) {
1456 				free(holder);
1457 				return FALSE;
1458 			}
1459 		}
1460 		for( i=0;i<len;i++ ){
1461 			data[offset++] = (unsigned char) *(holder+i);
1462 		}
1463 		/* &data[offset++] */
1464 		free(holder);
1465 		holder = NULL;
1466 		status = TRUE;
1467 	}
1468 	else{
1469 		printf("Entered %c\n",answer);
1470 	}
1471 
1472 	return status;
1473 }
1474 
1475 /* ipmi_fru_oemkontron_edit  -
1476 *  Query new values to replace original FRU content
1477 *  This is a generic enough to support any type of 'OEM' record
1478 *  because the user supplies 'IANA number' , 'record Id' and 'record' version'
1479 *
1480 * However, the parser must have 'apriori' knowledge of the record format
1481 * The currently supported record is :
1482 *
1483 *    IANA          : 15000  (Kontron)
1484 *    RECORD ID     : 3
1485 *    RECORD VERSION: 0 (or 1)
1486 *
1487 * I would have like to put that stuff in an OEM specific file, but apart for
1488 * the record format information, all commands are really standard 'FRU' command
1489 *
1490 *
1491 * @data:   FRU data
1492 * @offset: start of the current multi record (start of header)
1493 * @len:    len of the current record (excluding header)
1494 * @h:      pointer to record header
1495 * @oh:     pointer to OEM /PICMG header
1496 *
1497 * returns: TRUE if data changed
1498 * returns: FALSE if data not changed
1499 */
1500 #define OEM_KONTRON_INFORMATION_RECORD 3
1501 
1502 #define EDIT_OEM_KONTRON_COMPLETE_ARG_COUNT    12
1503 #define GET_OEM_KONTRON_COMPLETE_ARG_COUNT     5
1504 /*
1505 ./src/ipmitool  fru edit 0
1506 oem 15000 3 0 name instance FIELD1 FIELD2 FIELD3 crc32
1507 */
1508 
1509 #define OEM_KONTRON_SUBCOMMAND_ARG_POS   2
1510 #define OEM_KONTRON_IANA_ARG_POS         3
1511 #define OEM_KONTRON_RECORDID_ARG_POS     4
1512 #define OEM_KONTRON_FORMAT_ARG_POS       5
1513 #define OEM_KONTRON_NAME_ARG_POS         6
1514 #define OEM_KONTRON_INSTANCE_ARG_POS     7
1515 #define OEM_KONTRON_VERSION_ARG_POS      8
1516 #define OEM_KONTRON_BUILDDATE_ARG_POS    9
1517 #define OEM_KONTRON_UPDATEDATE_ARG_POS   10
1518 #define OEM_KONTRON_CRC32_ARG_POS        11
1519 
1520 #define OEM_KONTRON_FIELD_SIZE          8
1521 #define OEM_KONTRON_VERSION_FIELD_SIZE 10
1522 
1523 #ifdef HAVE_PRAGMA_PACK
1524 #pragma pack(1)
1525 #endif
1526 typedef struct OemKontronInformationRecordV0{
1527 	uint8_t field1TypeLength;
1528 	uint8_t field1[OEM_KONTRON_FIELD_SIZE];
1529 	uint8_t field2TypeLength;
1530 	uint8_t field2[OEM_KONTRON_FIELD_SIZE];
1531 	uint8_t field3TypeLength;
1532 	uint8_t field3[OEM_KONTRON_FIELD_SIZE];
1533 	uint8_t crcTypeLength;
1534 	uint8_t crc32[OEM_KONTRON_FIELD_SIZE];
1535 }tOemKontronInformationRecordV0;
1536 #ifdef HAVE_PRAGMA_PACK
1537 #pragma pack(0)
1538 #endif
1539 
1540 
1541 #ifdef HAVE_PRAGMA_PACK
1542 #pragma pack(1)
1543 #endif
1544 typedef struct OemKontronInformationRecordV1{
1545 	uint8_t field1TypeLength;
1546 	uint8_t field1[OEM_KONTRON_VERSION_FIELD_SIZE];
1547 	uint8_t field2TypeLength;
1548 	uint8_t field2[OEM_KONTRON_FIELD_SIZE];
1549 	uint8_t field3TypeLength;
1550 	uint8_t field3[OEM_KONTRON_FIELD_SIZE];
1551 	uint8_t crcTypeLength;
1552 	uint8_t crc32[OEM_KONTRON_FIELD_SIZE];
1553 }tOemKontronInformationRecordV1;
1554 #ifdef HAVE_PRAGMA_PACK
1555 #pragma pack(0)
1556 #endif
1557 
1558 /*
1559 ./src/ipmitool  fru get 0 oem iana 3
1560 
1561 */
1562 
1563 static void ipmi_fru_oemkontron_get( int argc, char ** argv,uint8_t * fru_data,
1564 												int off,int len,
1565 												struct fru_multirec_header *h,
1566 												struct fru_multirec_oem_header *oh)
1567 {
1568 	static int badParams=FALSE;
1569 	int start = off;
1570 	int offset = start;
1571 	offset += sizeof(struct fru_multirec_oem_header);
1572 
1573 	if(!badParams){
1574 		/* the 'OEM' field is already checked in caller */
1575 		if( argc > OEM_KONTRON_SUBCOMMAND_ARG_POS ){
1576 			if(strncmp("oem", argv[OEM_KONTRON_SUBCOMMAND_ARG_POS],3)){
1577 				printf("usage: fru get <id> <oem>\n");
1578 				badParams = TRUE;
1579 				return;
1580 			}
1581 		}
1582 		if( argc<GET_OEM_KONTRON_COMPLETE_ARG_COUNT ){
1583 			printf("usage: oem <iana> <recordid>\n");
1584 			printf("usage: oem 15000 3\n");
1585 			badParams = TRUE;
1586 			return;
1587 		}
1588 	}
1589 
1590 	if(!badParams){
1591 
1592 		if(oh->record_id == OEM_KONTRON_INFORMATION_RECORD ) {
1593 
1594 			uint8_t version;
1595 
1596 			printf("Kontron OEM Information Record\n");
1597 			version = oh->record_version;
1598 
1599 			int blockstart;
1600 			uint8_t blockCount;
1601 			uint8_t blockIndex=0;
1602 
1603 			unsigned int matchInstance = 0;
1604 			uint8_t instance = 0;
1605 
1606 			if (str2uchar(argv[OEM_KONTRON_INSTANCE_ARG_POS], &instance) != 0) {
1607 				lprintf(LOG_ERR,
1608 						"Instance argument '%s' is either invalid or out of range.",
1609 						argv[OEM_KONTRON_INSTANCE_ARG_POS]);
1610 				badParams = TRUE;
1611 				return;
1612 			}
1613 
1614 			blockCount = fru_data[offset++];
1615 
1616 			for(blockIndex=0;blockIndex<blockCount;blockIndex++){
1617 				void * pRecordData;
1618 				uint8_t nameLen;
1619 
1620 				blockstart = offset;
1621 				nameLen = ( fru_data[offset++] &= 0x3F );
1622 				printf("  Name: %*.*s\n",nameLen, nameLen, (const char *)(fru_data+offset));
1623 
1624 				offset+=nameLen;
1625 
1626 				pRecordData = &fru_data[offset];
1627 
1628 				printf("  Record Version: %d\n", version);
1629 				if( version == 0 )
1630 				{
1631 					printf("  Version: %*.*s\n",
1632 						OEM_KONTRON_FIELD_SIZE,
1633 						OEM_KONTRON_FIELD_SIZE,
1634 						((tOemKontronInformationRecordV0 *) pRecordData)->field1);
1635 					printf("  Build Date: %*.*s\n",
1636 						OEM_KONTRON_FIELD_SIZE,
1637 						OEM_KONTRON_FIELD_SIZE,
1638 						((tOemKontronInformationRecordV0 *) pRecordData)->field2);
1639 					printf("  Update Date: %*.*s\n",
1640 						OEM_KONTRON_FIELD_SIZE,
1641 						OEM_KONTRON_FIELD_SIZE,
1642 						((tOemKontronInformationRecordV0 *) pRecordData)->field3);
1643 					printf("  Checksum: %*.*s\n\n",
1644 						OEM_KONTRON_FIELD_SIZE,
1645 						OEM_KONTRON_FIELD_SIZE,
1646 						((tOemKontronInformationRecordV0 *) pRecordData)->crc32);
1647 					matchInstance++;
1648 					offset+= sizeof(tOemKontronInformationRecordV0);
1649 					offset++;
1650 				}
1651 				else if ( version == 1 )
1652 				{
1653 					printf("  Version: %*.*s\n",
1654 						OEM_KONTRON_VERSION_FIELD_SIZE,
1655 						OEM_KONTRON_VERSION_FIELD_SIZE,
1656 						((tOemKontronInformationRecordV1 *) pRecordData)->field1);
1657 					printf("  Build Date: %*.*s\n",
1658 						OEM_KONTRON_FIELD_SIZE,
1659 						OEM_KONTRON_FIELD_SIZE,
1660 						((tOemKontronInformationRecordV1 *) pRecordData)->field2);
1661 					printf("  Update Date: %*.*s\n",
1662 						OEM_KONTRON_FIELD_SIZE,
1663 						OEM_KONTRON_FIELD_SIZE,
1664 						((tOemKontronInformationRecordV1 *) pRecordData)->field3);
1665 					printf("  Checksum: %*.*s\n\n",
1666 						OEM_KONTRON_FIELD_SIZE,
1667 						OEM_KONTRON_FIELD_SIZE,
1668 						((tOemKontronInformationRecordV1 *) pRecordData)->crc32);
1669 					matchInstance++;
1670 					offset+= sizeof(tOemKontronInformationRecordV1);
1671 					offset++;
1672 				}
1673 				else
1674 				{
1675 					printf ("  Unsupported version %d\n",version);
1676 				}
1677 			}
1678 		}
1679 	}
1680 }
1681 
1682 static int ipmi_fru_oemkontron_edit( int argc, char ** argv,uint8_t * fru_data,
1683 												int off,int len,
1684 												struct fru_multirec_header *h,
1685 												struct fru_multirec_oem_header *oh)
1686 {
1687 	static int badParams=FALSE;
1688 	int hasChanged = FALSE;
1689 	int start = off;
1690 	int offset = start;
1691 	int length = len;
1692 	int i;
1693 	uint8_t record_id = 0;
1694 	offset += sizeof(struct fru_multirec_oem_header);
1695 
1696 	if(!badParams){
1697 		/* the 'OEM' field is already checked in caller */
1698 		if( argc > OEM_KONTRON_SUBCOMMAND_ARG_POS ){
1699 			if(strncmp("oem", argv[OEM_KONTRON_SUBCOMMAND_ARG_POS],3)){
1700 				printf("usage: fru edit <id> <oem> <args...>\n");
1701 				badParams = TRUE;
1702 				return hasChanged;
1703 			}
1704 		}
1705 		if( argc<EDIT_OEM_KONTRON_COMPLETE_ARG_COUNT ){
1706 			printf("usage: oem <iana> <recordid> <format> <args...>\n");
1707 			printf("usage: oem 15000 3 0 <name> <instance> <field1>"\
1708 					" <field2> <field3> <crc32>\n");
1709 			badParams = TRUE;
1710 			return hasChanged;
1711 		}
1712 		if (str2uchar(argv[OEM_KONTRON_RECORDID_ARG_POS], &record_id) != 0) {
1713 			lprintf(LOG_ERR,
1714 					"Record ID argument '%s' is either invalid or out of range.",
1715 					argv[OEM_KONTRON_RECORDID_ARG_POS]);
1716 			badParams = TRUE;
1717 			return hasChanged;
1718 		}
1719 		if (record_id == OEM_KONTRON_INFORMATION_RECORD) {
1720 			for(i=OEM_KONTRON_VERSION_ARG_POS;i<=OEM_KONTRON_CRC32_ARG_POS;i++){
1721 				if( (strlen(argv[i]) != OEM_KONTRON_FIELD_SIZE) &&
1722 					(strlen(argv[i]) != OEM_KONTRON_VERSION_FIELD_SIZE)) {
1723 					printf("error: version fields must have %d characters\n",
1724 										OEM_KONTRON_FIELD_SIZE);
1725 					badParams = TRUE;
1726 					return hasChanged;
1727 				}
1728 			}
1729 		}
1730 	}
1731 
1732 	if(!badParams){
1733 
1734 		if(oh->record_id == OEM_KONTRON_INFORMATION_RECORD ) {
1735 			uint8_t formatVersion = 0;
1736 			uint8_t version;
1737 
1738 			if (str2uchar(argv[OEM_KONTRON_FORMAT_ARG_POS], &formatVersion) != 0) {
1739 				lprintf(LOG_ERR,
1740 						"Format argument '%s' is either invalid or out of range.",
1741 						argv[OEM_KONTRON_FORMAT_ARG_POS]);
1742 				badParams = TRUE;
1743 				return hasChanged;
1744 			}
1745 
1746 			printf("   Kontron OEM Information Record\n");
1747 			version = oh->record_version;
1748 
1749 			if( version == formatVersion  ){
1750 				int blockstart;
1751 				uint8_t blockCount;
1752 				uint8_t blockIndex=0;
1753 
1754 				uint8_t matchInstance = 0;
1755 				uint8_t instance = 0;
1756 
1757 				if (str2uchar(argv[OEM_KONTRON_INSTANCE_ARG_POS], &instance) != 0) {
1758 					lprintf(LOG_ERR,
1759 							"Instance argument '%s' is either invalid or out of range.",
1760 							argv[OEM_KONTRON_INSTANCE_ARG_POS]);
1761 					badParams = TRUE;
1762 					return hasChanged;
1763 				}
1764 
1765 				blockCount = fru_data[offset++];
1766 				printf("   blockCount: %d\n",blockCount);
1767 
1768 				for(blockIndex=0;blockIndex<blockCount;blockIndex++){
1769 					void * pRecordData;
1770 					uint8_t nameLen;
1771 
1772 					blockstart = offset;
1773 
1774 					nameLen = ( fru_data[offset++] & 0x3F );
1775 
1776 					if( version == 0 || version == 1 )
1777 					{
1778 						if(!strncmp((char *)argv[OEM_KONTRON_NAME_ARG_POS],
1779 						(const char *)(fru_data+offset),nameLen)&& (matchInstance == instance)){
1780 
1781 							printf ("Found : %s\n",argv[OEM_KONTRON_NAME_ARG_POS]);
1782 							offset+=nameLen;
1783 
1784 							pRecordData =  &fru_data[offset];
1785 
1786 							if( version == 0 )
1787 							{
1788 								memcpy( ((tOemKontronInformationRecordV0 *)
1789 															pRecordData)->field1 ,
1790 								argv[OEM_KONTRON_VERSION_ARG_POS] ,
1791 								OEM_KONTRON_FIELD_SIZE);
1792 								memcpy( ((tOemKontronInformationRecordV0 *)
1793 															pRecordData)->field2 ,
1794 								argv[OEM_KONTRON_BUILDDATE_ARG_POS],
1795 								OEM_KONTRON_FIELD_SIZE);
1796 								memcpy( ((tOemKontronInformationRecordV0 *)
1797 															pRecordData)->field3 ,
1798 								argv[OEM_KONTRON_UPDATEDATE_ARG_POS],
1799 								OEM_KONTRON_FIELD_SIZE);
1800 								memcpy( ((tOemKontronInformationRecordV0 *)
1801 															pRecordData)->crc32 ,
1802 							argv[OEM_KONTRON_CRC32_ARG_POS] ,
1803 							OEM_KONTRON_FIELD_SIZE);
1804 							}
1805 							else
1806 							{
1807 								memcpy( ((tOemKontronInformationRecordV1 *)
1808 															pRecordData)->field1 ,
1809 								argv[OEM_KONTRON_VERSION_ARG_POS] ,
1810 								OEM_KONTRON_VERSION_FIELD_SIZE);
1811 								memcpy( ((tOemKontronInformationRecordV1 *)
1812 															pRecordData)->field2 ,
1813 								argv[OEM_KONTRON_BUILDDATE_ARG_POS],
1814 								OEM_KONTRON_FIELD_SIZE);
1815 								memcpy( ((tOemKontronInformationRecordV1 *)
1816 															pRecordData)->field3 ,
1817 								argv[OEM_KONTRON_UPDATEDATE_ARG_POS],
1818 								OEM_KONTRON_FIELD_SIZE);
1819 								memcpy( ((tOemKontronInformationRecordV1 *)
1820 															pRecordData)->crc32 ,
1821 							argv[OEM_KONTRON_CRC32_ARG_POS] ,
1822 							OEM_KONTRON_FIELD_SIZE);
1823 							}
1824 
1825 							matchInstance++;
1826 							hasChanged = TRUE;
1827 						}
1828 						else if(!strncmp((char *)argv[OEM_KONTRON_NAME_ARG_POS],
1829 							(const char *)(fru_data+offset), nameLen)){
1830 							printf ("Skipped : %s  [instance %d]\n",argv[OEM_KONTRON_NAME_ARG_POS],
1831 									(unsigned int)matchInstance);
1832 							matchInstance++;
1833 							offset+=nameLen;
1834 						}
1835 						else {
1836 							offset+=nameLen;
1837 						}
1838 
1839 						if( version == 0 )
1840 						{
1841 							offset+= sizeof(tOemKontronInformationRecordV0);
1842 						}
1843 						else
1844 						{
1845 							offset+= sizeof(tOemKontronInformationRecordV1);
1846 						}
1847 						offset++;
1848 					}
1849 					else
1850 					{
1851 						printf ("  Unsupported version %d\n",version);
1852 					}
1853 				}
1854 			}
1855 			else{
1856 				printf("   Version: %d\n",version);
1857 			}
1858 		}
1859 		if( hasChanged ){
1860 
1861 			uint8_t record_checksum =0;
1862 			uint8_t header_checksum =0;
1863 			int index;
1864 
1865 			lprintf(LOG_DEBUG,"Initial record checksum : %x",h->record_checksum);
1866 			lprintf(LOG_DEBUG,"Initial header checksum : %x",h->header_checksum);
1867 			for(index=0;index<length;index++){
1868 				record_checksum+=  fru_data[start+index];
1869 			}
1870 			/* Update Record checksum */
1871 			h->record_checksum =  ~record_checksum + 1;
1872 
1873 
1874 			for(index=0;index<(sizeof(struct fru_multirec_header) -1);index++){
1875 				uint8_t data= *( (uint8_t *)h+ index);
1876 				header_checksum+=data;
1877 			}
1878 			/* Update header checksum */
1879 			h->header_checksum =  ~header_checksum + 1;
1880 
1881 			lprintf(LOG_DEBUG,"Final record checksum : %x",h->record_checksum);
1882 			lprintf(LOG_DEBUG,"Final header checksum : %x",h->header_checksum);
1883 
1884 			/* write back data */
1885 		}
1886 	}
1887 
1888 	return hasChanged;
1889 }
1890 
1891 /* ipmi_fru_picmg_ext_edit  -  Query new values to replace original FRU content
1892 *
1893 * @data:   FRU data
1894 * @offset: start of the current multi record (start of header)
1895 * @len:    len of the current record (excluding header)
1896 * @h:      pointer to record header
1897 * @oh:     pointer to OEM /PICMG header
1898 *
1899 * returns: TRUE if data changed
1900 * returns: FALSE if data not changed
1901 */
1902 static int ipmi_fru_picmg_ext_edit(uint8_t * fru_data,
1903 												int off,int len,
1904 												struct fru_multirec_header *h,
1905 												struct fru_multirec_oem_header *oh)
1906 {
1907 	int hasChanged = FALSE;
1908 	int start = off;
1909 	int offset = start;
1910 	int length = len;
1911 	offset += sizeof(struct fru_multirec_oem_header);
1912 
1913 	switch (oh->record_id)
1914 	{
1915 		case FRU_AMC_ACTIVATION:
1916 			printf("    FRU_AMC_ACTIVATION\n");
1917 			{
1918 				int index=offset;
1919 				uint16_t max_current;
1920 
1921 				max_current = fru_data[offset];
1922 				max_current |= fru_data[++offset]<<8;
1923 
1924 				printf("      Maximum Internal Current(@12V): %.2f A (0x%02x)\n",
1925 								(float)max_current / 10.0f, max_current);
1926 
1927 				if( ipmi_fru_query_new_value(fru_data,index,2) ){
1928 					max_current = fru_data[index];
1929 					max_current |= fru_data[++index]<<8;
1930 					printf("      New Maximum Internal Current(@12V): %.2f A (0x%02x)\n",
1931 								(float)max_current / 10.0f, max_current);
1932 					hasChanged = TRUE;
1933 
1934 				}
1935 
1936 				printf("      Module Activation Readiness:       %i sec.\n", fru_data[++offset]);
1937 				printf("      Descriptor Count: %i\n", fru_data[++offset]);
1938 				printf("\n");
1939 
1940 				for (++offset;
1941 					offset < (off + length);
1942 					offset += sizeof(struct fru_picmgext_activation_record)) {
1943 					struct fru_picmgext_activation_record * a =
1944 						(struct fru_picmgext_activation_record *) &fru_data[offset];
1945 
1946 					printf("        IPMB-Address:         0x%x\n", a->ibmb_addr);
1947 					printf("        Max. Module Current:  %.2f A\n", (float)a->max_module_curr / 10.0f);
1948 
1949 					printf("\n");
1950 				}
1951 			}
1952 			break;
1953 
1954 		case FRU_AMC_CURRENT:
1955 			printf("    FRU_AMC_CURRENT\n");
1956 			{
1957 				int index=offset;
1958 				unsigned char current;
1959 
1960 				current = fru_data[index];
1961 
1962 				printf("      Current draw(@12V): %.2f A (0x%02x)\n",
1963 								(float)current / 10.0f, current);
1964 
1965 				if( ipmi_fru_query_new_value(fru_data, index, 1) ){
1966 					current = fru_data[index];
1967 
1968 					printf("      New Current draw(@12V): %.2f A (0x%02x)\n",
1969 								(float)current / 10.0f, current);
1970 					hasChanged = TRUE;
1971 				}
1972 			}
1973 			break;
1974 	}
1975 
1976 	if( hasChanged ){
1977 
1978 		uint8_t record_checksum =0;
1979 		uint8_t header_checksum =0;
1980 		int index;
1981 
1982 		lprintf(LOG_DEBUG,"Initial record checksum : %x",h->record_checksum);
1983 		lprintf(LOG_DEBUG,"Initial header checksum : %x",h->header_checksum);
1984 		for(index=0;index<length;index++){
1985 			record_checksum+=  fru_data[start+index];
1986 		}
1987 		/* Update Record checksum */
1988 		h->record_checksum =  ~record_checksum + 1;
1989 
1990 
1991 		for(index=0;index<(sizeof(struct fru_multirec_header) -1);index++){
1992 			uint8_t data= *( (uint8_t *)h+ index);
1993 			header_checksum+=data;
1994 		}
1995 		/* Update header checksum */
1996 		h->header_checksum =  ~header_checksum + 1;
1997 
1998 		lprintf(LOG_DEBUG,"Final record checksum : %x",h->record_checksum);
1999 		lprintf(LOG_DEBUG,"Final header checksum : %x",h->header_checksum);
2000 
2001 		/* write back data */
2002 	}
2003 
2004 	return hasChanged;
2005 }
2006 
2007 /* ipmi_fru_picmg_ext_print  - prints OEM fru record (PICMG)
2008 *
2009 * @fru_data:  FRU data
2010 * @offset:    offset of the bytes to be modified in data
2011 * @length:    size of the record
2012 *
2013 * returns : n/a
2014 */
2015 static void ipmi_fru_picmg_ext_print(uint8_t * fru_data, int off, int length)
2016 {
2017 	struct fru_multirec_oem_header *h;
2018 	int guid_count;
2019 	int offset = off;
2020 	int start_offset = off;
2021 	int i;
2022 
2023 	h = (struct fru_multirec_oem_header *) &fru_data[offset];
2024 	offset += sizeof(struct fru_multirec_oem_header);
2025 
2026 	switch (h->record_id)
2027 	{
2028 		case FRU_PICMG_BACKPLANE_P2P:
2029 		{
2030 			uint8_t index;
2031 			unsigned int data;
2032 			struct fru_picmgext_slot_desc *slot_d;
2033 
2034 			slot_d =
2035 				(struct fru_picmgext_slot_desc*)&fru_data[offset];
2036 			offset += sizeof(struct fru_picmgext_slot_desc);
2037 			printf("    FRU_PICMG_BACKPLANE_P2P\n");
2038 
2039 			while (offset <= (start_offset+length)) {
2040 				printf("\n");
2041 				printf("    Channel Type:  ");
2042 				switch (slot_d->chan_type)
2043 				{
2044 					case 0x00:
2045 					case 0x07:
2046 						printf("PICMG 2.9\n");
2047 						break;
2048 					case 0x08:
2049 						printf("Single Port Fabric IF\n");
2050 						break;
2051 					case 0x09:
2052 						printf("Double Port Fabric IF\n");
2053 						break;
2054 					case 0x0a:
2055 						printf("Full Channel Fabric IF\n");
2056 						break;
2057 					case 0x0b:
2058 						printf("Base IF\n");
2059 						break;
2060 					case 0x0c:
2061 						printf("Update Channel IF\n");
2062 						break;
2063 					case 0x0d:
2064 						printf("ShMC Cross Connect\n");
2065 						break;
2066 					default:
2067 						printf("Unknown IF (0x%x)\n",
2068 								slot_d->chan_type);
2069 						break;
2070 				}
2071 				printf("    Slot Addr.   : %02x\n",
2072 						slot_d->slot_addr );
2073 				printf("    Channel Count: %i\n",
2074 						slot_d->chn_count);
2075 
2076 				for (index = 0;
2077 						index < (slot_d->chn_count);
2078 						index++) {
2079 					struct fru_picmgext_chn_desc *d;
2080 					data = (fru_data[offset+0]) |
2081 						(fru_data[offset+1] << 8) |
2082 						(fru_data[offset+2] << 16);
2083 					d = (struct fru_picmgext_chn_desc *)&data;
2084 					if (verbose) {
2085 						printf( "       "
2086 								"Chn: %02x  ->  "
2087 								"Chn: %02x in "
2088 								"Slot: %02x\n",
2089 								d->local_chn,
2090 								d->remote_chn,
2091 								d->remote_slot);
2092 					}
2093 					offset += FRU_PICMGEXT_CHN_DESC_RECORD_SIZE;
2094 				}
2095 				slot_d = (struct fru_picmgext_slot_desc*)&fru_data[offset];
2096 				offset += sizeof(struct fru_picmgext_slot_desc);
2097 			}
2098 		}
2099 		break;
2100 
2101 		case FRU_PICMG_ADDRESS_TABLE:
2102 		{
2103 			unsigned int hwaddr;
2104 			unsigned int sitetype;
2105 			unsigned int sitenum;
2106 			unsigned int entries;
2107 			unsigned int i;
2108 			char *picmg_site_type_strings[] = {
2109 					"AdvancedTCA Board",
2110 					"Power Entry",
2111 					"Shelf FRU Information",
2112 					"Dedicated ShMC",
2113 					"Fan Tray",
2114 					"Fan Filter Tray",
2115 					"Alarm",
2116 					"AdvancedMC Module",
2117 					"PMC",
2118 					"Rear Transition Module"};
2119 
2120 
2121 			printf("    FRU_PICMG_ADDRESS_TABLE\n");
2122 			printf("      Type/Len:  0x%02x\n", fru_data[offset++]);
2123 			printf("      Shelf Addr: ");
2124 			for (i=0;i<20;i++) {
2125 				printf("0x%02x ", fru_data[offset++]);
2126 			}
2127 			printf("\n");
2128 
2129 			entries = fru_data[offset++];
2130 			printf("      Addr Table Entries: 0x%02x\n", entries);
2131 
2132 			for (i=0; i<entries; i++) {
2133 				hwaddr = fru_data[offset];
2134 				sitenum = fru_data[offset + 1];
2135 				sitetype = fru_data[offset + 2];
2136 				printf(
2137 						"        HWAddr: 0x%02x (0x%02x) SiteNum: %d SiteType: 0x%02x %s\n",
2138 						hwaddr, hwaddr * 2,
2139 						sitenum, sitetype,
2140 						(sitetype < 0xa) ?
2141 						picmg_site_type_strings[sitetype] :
2142 						"Reserved");
2143 				offset += 3;
2144 			}
2145 		}
2146 		break;
2147 
2148 		case FRU_PICMG_SHELF_POWER_DIST:
2149 		{
2150 			unsigned int entries;
2151 			unsigned int feeds;
2152 			unsigned int hwaddr;
2153 			unsigned int i;
2154 			unsigned int id;
2155 			unsigned int j;
2156 			unsigned int maxext;
2157 			unsigned int maxint;
2158 			unsigned int minexp;
2159 
2160 			printf("    FRU_PICMG_SHELF_POWER_DIST\n");
2161 
2162 			feeds = fru_data[offset++];
2163 			printf("      Number of Power Feeds:   0x%02x\n",
2164 					feeds);
2165 
2166 			for (i=0; i<feeds; i++) {
2167 				printf("    Feed %d:\n", i);
2168 				maxext = fru_data[offset] |
2169 					(fru_data[offset+1] << 8);
2170 				offset += 2;
2171 				maxint = fru_data[offset] |
2172 					(fru_data[offset+1] << 8);
2173 				offset += 2;
2174 				minexp = fru_data[offset];
2175 				offset += 1;
2176 				entries = fru_data[offset];
2177 				offset += 1;
2178 
2179 				printf(
2180 						"      Max External Current:   %d.%d Amps (0x%04x)\n",
2181 						maxext / 10, maxext % 10, maxext);
2182 				if (maxint < 0xffff) {
2183 					printf(
2184 							"      Max Internal Current:   %d.%d Amps (0x%04x)\n",
2185 							maxint / 10, maxint % 10,
2186 							maxint);
2187 				} else {
2188 					printf(
2189 							"      Max Internal Current:   Not Specified\n");
2190 				}
2191 
2192 				if (minexp >= 0x48 && minexp <= 0x90) {
2193 					printf(
2194 							"      Min Expected Voltage:   -%02d.%dV\n",
2195 							minexp / 2, (minexp % 2) * 5);
2196 				} else {
2197 					printf(
2198 							"      Min Expected Voltage:   -%dV (actual invalid value 0x%x)\n",
2199 							36, minexp);
2200 				}
2201 				for (j=0; j < entries; j++) {
2202 					hwaddr = fru_data[offset++];
2203 					id = fru_data[offset++];
2204 					printf(
2205 							"        FRU HW Addr: 0x%02x (0x%02x)",
2206 							hwaddr, hwaddr * 2);
2207 					printf(
2208 							"   FRU ID: 0x%02x\n",
2209 							id);
2210 				}
2211 			}
2212 		}
2213 		break;
2214 
2215 		case FRU_PICMG_SHELF_ACTIVATION:
2216 		{
2217 			unsigned int i;
2218 			unsigned int count = 0;
2219 
2220 			printf("    FRU_PICMG_SHELF_ACTIVATION\n");
2221 			printf(
2222 					"      Allowance for FRU Act Readiness:   0x%02x\n",
2223 					fru_data[offset++]);
2224 
2225 			count = fru_data[offset++];
2226 			printf(
2227 					"      FRU activation and Power Desc Cnt: 0x%02x\n",
2228 					count);
2229 
2230 			for (i=0; i<count; i++) {
2231 				printf("         HW Addr: 0x%02x ",
2232 						fru_data[offset++]);
2233 				printf("         FRU ID: 0x%02x ",
2234 						fru_data[offset++]);
2235 				printf("         Max FRU Power: 0x%04x ",
2236 						fru_data[offset+0] |
2237 						(fru_data[offset+1]<<8));
2238 				offset += 2;
2239 				printf("         Config: 0x%02x \n",
2240 						fru_data[offset++]);
2241 			}
2242 		}
2243 		break;
2244 
2245 		case FRU_PICMG_SHMC_IP_CONN:
2246 			printf("    FRU_PICMG_SHMC_IP_CONN\n");
2247 			break;
2248 
2249 		case FRU_PICMG_BOARD_P2P:
2250 			printf("    FRU_PICMG_BOARD_P2P\n");
2251 
2252 			guid_count = fru_data[offset++];
2253 			printf("      GUID count: %2d\n", guid_count);
2254 			for (i = 0 ; i < guid_count; i++ ) {
2255 				int j;
2256 				printf("        GUID [%2d]: 0x", i);
2257 
2258 				for (j=0; j < sizeof(struct fru_picmgext_guid);
2259 						j++) {
2260 					printf("%02x", fru_data[offset+j]);
2261 				}
2262 
2263 				printf("\n");
2264 				offset += sizeof(struct fru_picmgext_guid);
2265 			}
2266 			printf("\n");
2267 
2268 			for (; offset < off + length;
2269 					offset += sizeof(struct fru_picmgext_link_desc)) {
2270 
2271 				/* to solve little endian /big endian problem */
2272 				struct fru_picmgext_link_desc *d;
2273 				unsigned int data = (fru_data[offset+0]) |
2274 					(fru_data[offset+1] << 8) |
2275 					(fru_data[offset+2] << 16) |
2276 					(fru_data[offset+3] << 24);
2277 				d = (struct fru_picmgext_link_desc *) &data;
2278 
2279 				printf("      Link Grouping ID:     0x%02x\n",
2280 						d->grouping);
2281 				printf("      Link Type Extension:  0x%02x - ",
2282 						d->ext);
2283 				if (d->type == FRU_PICMGEXT_LINK_TYPE_BASE) {
2284 					switch (d->ext)
2285 					{
2286 						case 0:
2287 							printf("10/100/1000BASE-T Link (four-pair)\n");
2288 							break;
2289 						case 1:
2290 							printf("ShMC Cross-connect (two-pair)\n");
2291 							break;
2292 						default:
2293 							printf("Unknwon\n");
2294 							break;
2295 					}
2296 				} else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_ETHERNET) {
2297 					switch (d->ext)
2298 					{
2299 						case 0:
2300 							printf("Fixed 1000Base-BX\n");
2301 							break;
2302 						case 1:
2303 							printf("Fixed 10GBASE-BX4 [XAUI]\n");
2304 							break;
2305 						case 2:
2306 							printf("FC-PI\n");
2307 							break;
2308 						default:
2309 							printf("Unknwon\n");
2310 							break;
2311 					}
2312 				} else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_INFINIBAND) {
2313 					printf("Unknwon\n");
2314 				} else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_STAR) {
2315 					printf("Unknwon\n");
2316 				} else if (d->type == FRU_PICMGEXT_LINK_TYPE_PCIE) {
2317 					printf("Unknwon\n");
2318 				} else {
2319 					printf("Unknwon\n");
2320 				}
2321 
2322 				printf("      Link Type:            0x%02x - ",
2323 						d->type);
2324 				if (d->type == 0 || d->type == 0xff) {
2325 					printf("Reserved\n");
2326 				}
2327 				else if (d->type >= 0x06 && d->type <= 0xef) {
2328 					printf("Reserved\n");
2329 				}
2330 				else if (d->type >= 0xf0 && d->type <= 0xfe) {
2331 					printf("OEM GUID Definition\n");
2332 				}
2333 				else {
2334 					switch (d->type)
2335 					{
2336 						case FRU_PICMGEXT_LINK_TYPE_BASE:
2337 							printf("PICMG 3.0 Base Interface 10/100/1000\n");
2338 							break;
2339 						case FRU_PICMGEXT_LINK_TYPE_FABRIC_ETHERNET:
2340 							printf("PICMG 3.1 Ethernet Fabric Interface\n");
2341 							break;
2342 						case FRU_PICMGEXT_LINK_TYPE_FABRIC_INFINIBAND:
2343 							printf("PICMG 3.2 Infiniband Fabric Interface\n");
2344 							break;
2345 						case FRU_PICMGEXT_LINK_TYPE_FABRIC_STAR:
2346 							printf("PICMG 3.3 Star Fabric Interface\n");
2347 							break;
2348 						case  FRU_PICMGEXT_LINK_TYPE_PCIE:
2349 							printf("PICMG 3.4 PCI Express Fabric Interface\n");
2350 							break;
2351 						default:
2352 							printf("Invalid\n");
2353 							break;
2354 					}
2355 				}
2356 				printf("      Link Designator: \n");
2357 				printf("        Port Flag:            0x%02x\n",
2358 						d->desig_port);
2359 				printf("        Interface:            0x%02x - ",
2360 						d->desig_if);
2361 				switch (d->desig_if)
2362 				{
2363 					case FRU_PICMGEXT_DESIGN_IF_BASE:
2364 						printf("Base Interface\n");
2365 						break;
2366 					case FRU_PICMGEXT_DESIGN_IF_FABRIC:
2367 						printf("Fabric Interface\n");
2368 						break;
2369 					case FRU_PICMGEXT_DESIGN_IF_UPDATE_CHANNEL:
2370 						printf("Update Channel\n");
2371 						break;
2372 					case FRU_PICMGEXT_DESIGN_IF_RESERVED:
2373 						printf("Reserved\n");
2374 						break;
2375 					default:
2376 						printf("Invalid");
2377 						break;
2378 				}
2379 				printf("        Channel Number:       0x%02x\n",
2380 						d->desig_channel);
2381 				printf("\n");
2382 			}
2383 
2384 			break;
2385 
2386 		case FRU_AMC_CURRENT:
2387 		{
2388 			unsigned char current;
2389 			printf("    FRU_AMC_CURRENT\n");
2390 
2391 			current = fru_data[offset];
2392 			printf("      Current draw(@12V): %.2f A [ %.2f Watt ]\n",
2393 					(float)current / 10.0f,
2394 					(float)current / 10.0f * 12.0f);
2395 			printf("\n");
2396 		}
2397 		break;
2398 
2399 		case FRU_AMC_ACTIVATION:
2400 			printf("    FRU_AMC_ACTIVATION\n");
2401 			{
2402 				uint16_t max_current;
2403 
2404 				max_current = fru_data[offset];
2405 				max_current |= fru_data[++offset]<<8;
2406 				printf("      Maximum Internal Current(@12V): %.2f A [ %.2f Watt ]\n",
2407 						(float)max_current / 10.0f,
2408 						(float)max_current / 10.0f * 12.0f);
2409 
2410 				printf("      Module Activation Readiness:    %i sec.\n", fru_data[++offset]);
2411 				printf("      Descriptor Count: %i\n", fru_data[++offset]);
2412 				printf("\n");
2413 
2414 				for(++offset; offset < off + length;
2415 						offset += sizeof(struct fru_picmgext_activation_record))
2416 				{
2417 					struct fru_picmgext_activation_record *a;
2418 					a = (struct fru_picmgext_activation_record *)&fru_data[offset];
2419 					printf("        IPMB-Address:         0x%x\n",
2420 							a->ibmb_addr);
2421 					printf("        Max. Module Current:  %.2f A\n",
2422 							(float)a->max_module_curr / 10.0f);
2423 					printf("\n");
2424 				}
2425 			}
2426 			break;
2427 
2428 		case FRU_AMC_CARRIER_P2P:
2429 			{
2430 				uint16_t index;
2431 				printf("    FRU_CARRIER_P2P\n");
2432 				for(; offset < off + length; ) {
2433 					struct fru_picmgext_carrier_p2p_record * h =
2434 						(struct fru_picmgext_carrier_p2p_record *)&fru_data[offset];
2435 					printf("\n");
2436 					printf("      Resource ID:      %i",
2437 							(h->resource_id & 0x07));
2438 						printf("  Type: ");
2439 					if ((h->resource_id>>7) == 1) {
2440 						printf("AMC\n");
2441 					} else {
2442 						printf("Local\n");
2443 					}
2444 					printf("      Descriptor Count: %i\n",
2445 							h->p2p_count);
2446 					offset += sizeof(struct fru_picmgext_carrier_p2p_record);
2447 					for (index = 0; index < h->p2p_count; index++) {
2448 						/* to solve little endian /big endian problem */
2449 						unsigned char data[3];
2450 						struct fru_picmgext_carrier_p2p_descriptor * desc;
2451 # ifndef WORDS_BIGENDIAN
2452 						data[0] = fru_data[offset+0];
2453 						data[1] = fru_data[offset+1];
2454 						data[2] = fru_data[offset+2];
2455 # else
2456 						data[0] = fru_data[offset+2];
2457 						data[1] = fru_data[offset+1];
2458 						data[2] = fru_data[offset+0];
2459 # endif
2460 						desc = (struct fru_picmgext_carrier_p2p_descriptor*)&data;
2461 						printf("        Port: %02d\t->  Remote Port: %02d\t",
2462 								desc->local_port, desc->remote_port);
2463 						if ((desc->remote_resource_id >> 7) == 1) {
2464 							printf("[ AMC   ID: %02d ]\n",
2465 									desc->remote_resource_id & 0x0F);
2466 						} else {
2467 							printf("[ local ID: %02d ]\n",
2468 									desc->remote_resource_id & 0x0F);
2469 						}
2470 						offset += sizeof(struct fru_picmgext_carrier_p2p_descriptor);
2471 					}
2472 				}
2473 			}
2474 			break;
2475 
2476 		case FRU_AMC_P2P:
2477 			{
2478 				unsigned int index;
2479 				unsigned char channel_count;
2480 				struct fru_picmgext_amc_p2p_record * h;
2481 				printf("    FRU_AMC_P2P\n");
2482 				guid_count = fru_data[offset];
2483 				printf("      GUID count: %2d\n", guid_count);
2484 				for (i = 0 ; i < guid_count; i++) {
2485 					int j;
2486 					printf("        GUID %2d: ", i);
2487 					for (j=0; j < sizeof(struct fru_picmgext_guid);
2488 							j++) {
2489 						printf("%02x", fru_data[offset+j]);
2490 						offset += sizeof(struct fru_picmgext_guid);
2491 						printf("\n");
2492 					}
2493 					h = (struct fru_picmgext_amc_p2p_record *)&fru_data[++offset];
2494 					printf("      %s",
2495 							(h->record_type ?
2496 							 "AMC Module:" : "On-Carrier Device"));
2497 					printf("   Resource ID: %i\n", h->resource_id);
2498 					offset += sizeof(struct fru_picmgext_amc_p2p_record);
2499 					channel_count = fru_data[offset++];
2500 					printf("       Descriptor Count: %i\n",
2501 							channel_count);
2502 					for (index = 0; index < channel_count; index++) {
2503 						unsigned int data;
2504 						struct fru_picmgext_amc_channel_desc_record *d;
2505 						/* pack the data in little endian format.
2506 						 * Stupid intel...
2507 						 */
2508 						data = fru_data[offset] |
2509 							(fru_data[offset + 1] << 8) |
2510 							(fru_data[offset + 2] << 16);
2511 						d = (struct fru_picmgext_amc_channel_desc_record *)&data;
2512 						printf("        Lane 0 Port: %i\n",
2513 								d->lane0port);
2514 						printf("        Lane 1 Port: %i\n",
2515 								d->lane1port);
2516 						printf("        Lane 2 Port: %i\n",
2517 								d->lane2port);
2518 						printf("        Lane 3 Port: %i\n\n",
2519 								d->lane3port);
2520 						offset += FRU_PICMGEXT_AMC_CHANNEL_DESC_RECORD_SIZE;
2521 					}
2522 					for (; offset < off + length;) {
2523 						unsigned int data[2];
2524 						struct fru_picmgext_amc_link_desc_record *l;
2525 						l = (struct fru_picmgext_amc_link_desc_record *)&data[0];
2526 						data[0] = fru_data[offset] |
2527 							(fru_data[offset + 1] << 8) |
2528 							(fru_data[offset + 2] << 16) |
2529 							(fru_data[offset + 3] << 24);
2530 						data[1] = fru_data[offset + 4];
2531 						printf( "      Link Designator:  Channel ID: %i\n"
2532 								"            Port Flag 0: %s%s%s%s\n",
2533 								l->channel_id,
2534 								(l->port_flag_0)?"o":"-",
2535 								(l->port_flag_1)?"o":"-",
2536 								(l->port_flag_2)?"o":"-",
2537 								(l->port_flag_3)?"o":"-"  );
2538 						switch (l->type) {
2539 							case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE:
2540 								/* AMC.1 */
2541 								printf( "        Link Type:       %02x - "
2542 										"AMC.1 PCI Express\n", l->type);
2543 								switch (l->type_ext) {
2544 									case AMC_LINK_TYPE_EXT_PCIE_G1_NSSC:
2545 										printf( "        Link Type Ext:   %i - "
2546 												" Gen 1 capable - non SSC\n",
2547 												l->type_ext);
2548 									break;
2549 									case AMC_LINK_TYPE_EXT_PCIE_G1_SSC:
2550 										printf( "        Link Type Ext:   %i - "
2551 												" Gen 1 capable - SSC\n",
2552 												l->type_ext);
2553 										break;
2554 									case AMC_LINK_TYPE_EXT_PCIE_G2_NSSC:
2555 										printf( "        Link Type Ext:   %i - "
2556 												" Gen 2 capable - non SSC\n",
2557 												l->type_ext);
2558 										break;
2559 									case AMC_LINK_TYPE_EXT_PCIE_G2_SSC:
2560 										printf( "        Link Type Ext:   %i - "
2561 												" Gen 2 capable - SSC\n",
2562 												l->type_ext);
2563 										break;
2564 									default:
2565 										printf( "        Link Type Ext:   %i - "
2566 												" Invalid\n",
2567 												l->type_ext);
2568 										break;
2569 								}
2570 								break;
2571 
2572 							case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS1:
2573 							case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS2:
2574 								/* AMC.1 */
2575 								printf( "        Link Type:       %02x - "
2576 										"AMC.1 PCI Express Advanced Switching\n",
2577 										l->type);
2578 								printf("        Link Type Ext:   %i\n",
2579 										l->type_ext);
2580 								break;
2581 
2582 							case FRU_PICMGEXT_AMC_LINK_TYPE_ETHERNET:
2583 								/* AMC.2 */
2584 								printf( "        Link Type:       %02x - "
2585 										"AMC.2 Ethernet\n",
2586 										l->type);
2587 								switch (l->type_ext) {
2588 									case AMC_LINK_TYPE_EXT_ETH_1000_BX:
2589 										printf( "        Link Type Ext:   %i - "
2590 												" 1000Base-Bx (SerDES Gigabit) Ethernet Link\n",
2591 												l->type_ext);
2592 										break;
2593 
2594 									case AMC_LINK_TYPE_EXT_ETH_10G_XAUI:
2595 										printf( "        Link Type Ext:   %i - "
2596 												" 10Gbit XAUI Ethernet Link\n",
2597 										l->type_ext);
2598 										break;
2599 
2600 									default:
2601 										printf( "        Link Type Ext:   %i - "
2602 												" Invalid\n",
2603 												l->type_ext);
2604 										break;
2605 								}
2606 								break;
2607 
2608 							case FRU_PICMGEXT_AMC_LINK_TYPE_STORAGE:
2609 								/* AMC.3 */
2610 								printf( "        Link Type:       %02x - "
2611 										"AMC.3 Storage\n",
2612 										l->type);
2613 								switch (l->type_ext) {
2614 									case AMC_LINK_TYPE_EXT_STORAGE_FC:
2615 										printf( "        Link Type Ext:   %i - "
2616 												" Fibre Channel\n",
2617 												l->type_ext);
2618 										break;
2619 
2620 									case AMC_LINK_TYPE_EXT_STORAGE_SATA:
2621 										printf( "        Link Type Ext:   %i - "
2622 												" Serial ATA\n",
2623 												l->type_ext);
2624 										break;
2625 
2626 									case AMC_LINK_TYPE_EXT_STORAGE_SAS:
2627 										printf( "        Link Type Ext:   %i - "
2628 												" Serial Attached SCSI\n",
2629 												l->type_ext);
2630 										break;
2631 
2632 									default:
2633 										printf( "        Link Type Ext:   %i - "
2634 												" Invalid\n",
2635 												l->type_ext);
2636 										break;
2637 								}
2638 								break;
2639 
2640 							case FRU_PICMGEXT_AMC_LINK_TYPE_RAPIDIO:
2641 								/* AMC.4 */
2642 								printf( "        Link Type:       %02x - "
2643 										"AMC.4 Serial Rapid IO\n",
2644 										l->type);
2645 								printf("        Link Type Ext:   %i\n",
2646 										l->type_ext);
2647 								break;
2648 
2649 							default:
2650 								printf( "        Link Type:       %02x - "
2651 										"reserved or OEM GUID",
2652 										l->type);
2653 								printf("        Link Type Ext:   %i\n",
2654 										l->type_ext);
2655 								break;
2656 						}
2657 
2658 						printf("        Link group Id:   %i\n",
2659 								l->group_id);
2660 						printf("        Link Asym Match: %i\n\n",
2661 								l->asym_match);
2662 						offset += FRU_PICMGEXT_AMC_LINK_DESC_RECORD_SIZE;
2663 					}
2664 				}
2665 			}
2666 			break;
2667 
2668 		case FRU_AMC_CARRIER_INFO:
2669 		{
2670 			unsigned char extVersion;
2671 			unsigned char siteCount;
2672 
2673 			printf("    FRU_CARRIER_INFO\n");
2674 
2675 			extVersion = fru_data[offset++];
2676 			siteCount  = fru_data[offset++];
2677 
2678 			printf("      AMC.0 extension version: R%d.%d\n",
2679 					(extVersion >> 0)& 0x0F,
2680 					(extVersion >> 4)& 0x0F );
2681 			printf("      Carrier Sie Number Cnt: %d\n", siteCount);
2682 
2683 			for (i = 0 ; i < siteCount; i++ ){
2684 				printf("       Site ID: %i \n", fru_data[offset++]);
2685 			}
2686 			printf("\n");
2687 		}
2688 		break;
2689 		case FRU_PICMG_CLK_CARRIER_P2P:
2690 		{
2691 			unsigned char desc_count;
2692 			int i,j;
2693 
2694 			printf("    FRU_PICMG_CLK_CARRIER_P2P\n");
2695 
2696 			desc_count = fru_data[offset++];
2697 
2698 			for(i=0; i<desc_count; i++){
2699 				unsigned char resource_id;
2700 				unsigned char channel_count;
2701 
2702 				resource_id   = fru_data[offset++];
2703 				channel_count = fru_data[offset++];
2704 
2705 				printf("\n");
2706 				printf("      Clock Resource ID: 0x%02x  Type: ", resource_id);
2707 				if((resource_id & 0xC0)>>6 == 0) {printf("On-Carrier-Device\n");}
2708 				else if((resource_id & 0xC0)>>6 == 1) {printf("AMC slot\n");}
2709 				else if((resource_id & 0xC0)>>6 == 2) {printf("Backplane\n");}
2710 				else{ printf("reserved\n");}
2711 				printf("      Channel Count: 0x%02x\n", channel_count);
2712 
2713 				for(j=0; j<channel_count; j++){
2714 					unsigned char loc_channel, rem_channel, rem_resource;
2715 
2716 					loc_channel  = fru_data[offset++];
2717 					rem_channel  = fru_data[offset++];
2718 					rem_resource = fru_data[offset++];
2719 
2720 					printf("        CLK-ID: 0x%02x    ->", loc_channel);
2721 					printf(" remote CLKID: 0x%02x   ", rem_channel);
2722 					if((rem_resource & 0xC0)>>6 == 0) {printf("[ Carrier-Dev");}
2723 					else if((rem_resource & 0xC0)>>6 == 1) {printf("[ AMC slot   ");}
2724 					else if((rem_resource & 0xC0)>>6 == 2) {printf("[ Backplane  ");}
2725 					else{ printf("reserved         ");}
2726 					printf(" 0x%02x ]\n", rem_resource&0xF);
2727 				}
2728 			}
2729 			printf("\n");
2730 		}
2731 		break;
2732 		case FRU_PICMG_CLK_CONFIG:
2733 		{
2734 			unsigned char resource_id, descr_count;
2735 			int i,j;
2736 
2737 			printf("    FRU_PICMG_CLK_CONFIG\n");
2738 
2739 			resource_id = fru_data[offset++];
2740 			descr_count = fru_data[offset++];
2741 
2742 			printf("\n");
2743 			printf("      Clock Resource ID: 0x%02x\n", resource_id);
2744 			printf("      Descr. Count:      0x%02x\n", descr_count);
2745 
2746 			for(i=0; i<descr_count; i++){
2747 				unsigned char channel_id, control;
2748 				unsigned char indirect_cnt, direct_cnt;
2749 
2750 				channel_id = fru_data[offset++];
2751 				control    = fru_data[offset++];
2752 				printf("        CLK-ID: 0x%02x  -  ", channel_id);
2753 				printf("CTRL 0x%02x [ %12s ]\n",
2754 									control,
2755 									((control&0x1)==0)?"Carrier IPMC":"Application");
2756 
2757 				indirect_cnt = fru_data[offset++];
2758 				direct_cnt   = fru_data[offset++];
2759 				printf("         Cnt: Indirect 0x%02x  /  Direct 0x%02x\n",
2760 						indirect_cnt,
2761 						direct_cnt);
2762 
2763 				/* indirect desc */
2764 				for(j=0; j<indirect_cnt; j++){
2765 					unsigned char feature;
2766 					unsigned char dep_chn_id;
2767 
2768 					feature    = fru_data[offset++];
2769 					dep_chn_id = fru_data[offset++];
2770 
2771 					printf("          Feature: 0x%02x [%8s] - ", feature, (feature&0x1)==1?"Source":"Receiver");
2772 					printf("          Dep. CLK-ID: 0x%02x\n", dep_chn_id);
2773 				}
2774 
2775 				/* direct desc */
2776 				for(j=0; j<direct_cnt; j++){
2777 					unsigned char feature, family, accuracy;
2778 					unsigned int freq, min_freq, max_freq;
2779 
2780 					feature  = fru_data[offset++];
2781 					family   = fru_data[offset++];
2782 					accuracy = fru_data[offset++];
2783 					freq     = (fru_data[offset+0] << 0 ) | (fru_data[offset+1] << 8 )
2784 								| (fru_data[offset+2] << 16) | (fru_data[offset+3] << 24);
2785 					offset += 4;
2786 					min_freq = (fru_data[offset+0] << 0 ) | (fru_data[offset+1] << 8 )
2787 								| (fru_data[offset+2] << 16) | (fru_data[offset+3] << 24);
2788 					offset += 4;
2789 					max_freq = (fru_data[offset+0] << 0 ) | (fru_data[offset+1] << 8 )
2790 								| (fru_data[offset+2] << 16) | (fru_data[offset+3] << 24);
2791 					offset += 4;
2792 
2793 					printf("          - Feature: 0x%02x  - PLL: %x / Asym: %s\n",
2794 							feature,
2795 							(feature > 1) & 1,
2796 							(feature&1)?"Source":"Receiver");
2797 					printf("            Family:  0x%02x  - AccLVL: 0x%02x\n", family, accuracy);
2798 					printf("            FRQ: %-9ld - min: %-9ld - max: %-9ld\n",
2799 							freq, min_freq, max_freq);
2800 				}
2801 				printf("\n");
2802 			}
2803 			printf("\n");
2804 		}
2805 		break;
2806 
2807 		case FRU_UTCA_FRU_INFO_TABLE:
2808 		case FRU_UTCA_CARRIER_MNG_IP:
2809 		case FRU_UTCA_CARRIER_INFO:
2810 		case FRU_UTCA_CARRIER_LOCATION:
2811 		case FRU_UTCA_SHMC_IP_LINK:
2812 		case FRU_UTCA_POWER_POLICY:
2813 		case FRU_UTCA_ACTIVATION:
2814 		case FRU_UTCA_PM_CAPABILTY:
2815 		case FRU_UTCA_FAN_GEOGRAPHY:
2816 		case FRU_UTCA_CLOCK_MAPPING:
2817 		case FRU_UTCA_MSG_BRIDGE_POLICY:
2818 		case FRU_UTCA_OEM_MODULE_DESC:
2819 			printf("    Not implemented yet. uTCA specific record found!!\n");
2820 			printf("     - Record ID: 0x%02x\n", h->record_id);
2821 		break;
2822 
2823 		default:
2824 			printf("    Unknown OEM Extension Record ID: %x\n", h->record_id);
2825 		break;
2826 
2827 	}
2828 }
2829 
2830 
2831 /* __ipmi_fru_print  -  Do actual work to print a FRU by its ID
2832 *
2833 * @intf:   ipmi interface
2834 * @id:     fru id
2835 *
2836 * returns -1 on error
2837 * returns 0 if successful
2838 * returns 1 if device not present
2839 */
2840 static int
2841 __ipmi_fru_print(struct ipmi_intf * intf, uint8_t id)
2842 {
2843 	struct ipmi_rs * rsp;
2844 	struct ipmi_rq req;
2845 	struct fru_info fru;
2846 	struct fru_header header;
2847 	uint8_t msg_data[4];
2848 
2849 	memset(&fru, 0, sizeof(struct fru_info));
2850 	memset(&header, 0, sizeof(struct fru_header));
2851 
2852 	/*
2853 	* get info about this FRU
2854 	*/
2855 	memset(msg_data, 0, 4);
2856 	msg_data[0] = id;
2857 
2858 	memset(&req, 0, sizeof(req));
2859 	req.msg.netfn = IPMI_NETFN_STORAGE;
2860 	req.msg.cmd = GET_FRU_INFO;
2861 	req.msg.data = msg_data;
2862 	req.msg.data_len = 1;
2863 
2864 	rsp = intf->sendrecv(intf, &req);
2865 	if (rsp == NULL) {
2866 		printf(" Device not present (No Response)\n");
2867 		return -1;
2868 	}
2869 	if (rsp->ccode > 0) {
2870 		printf(" Device not present (%s)\n",
2871 			val2str(rsp->ccode, completion_code_vals));
2872 		return -1;
2873 	}
2874 
2875 	memset(&fru, 0, sizeof(fru));
2876 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
2877 	fru.access = rsp->data[2] & 0x1;
2878 
2879 	lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)",
2880 		fru.size, fru.access ? "words" : "bytes");
2881 
2882 	if (fru.size < 1) {
2883 		lprintf(LOG_ERR, " Invalid FRU size %d", fru.size);
2884 		return -1;
2885 	}
2886 
2887 	/*
2888 	* retrieve the FRU header
2889 	*/
2890 	msg_data[0] = id;
2891 	msg_data[1] = 0;
2892 	msg_data[2] = 0;
2893 	msg_data[3] = 8;
2894 
2895 	memset(&req, 0, sizeof(req));
2896 	req.msg.netfn = IPMI_NETFN_STORAGE;
2897 	req.msg.cmd = GET_FRU_DATA;
2898 	req.msg.data = msg_data;
2899 	req.msg.data_len = 4;
2900 
2901 	rsp = intf->sendrecv(intf, &req);
2902 	if (rsp == NULL) {
2903 		printf(" Device not present (No Response)\n");
2904 		return 1;
2905 	}
2906 	if (rsp->ccode > 0) {
2907 		printf(" Device not present (%s)\n",
2908 				val2str(rsp->ccode, completion_code_vals));
2909 		return 1;
2910 	}
2911 
2912 	if (verbose > 1)
2913 		printbuf(rsp->data, rsp->data_len, "FRU DATA");
2914 
2915 	memcpy(&header, rsp->data + 1, 8);
2916 
2917 	if (header.version != 1) {
2918 		lprintf(LOG_ERR, " Unknown FRU header version 0x%02x",
2919 			header.version);
2920 		return -1;
2921 	}
2922 
2923 	/* offsets need converted to bytes
2924 	* but that conversion is not done to the structure
2925 	* because we may end up with offset > 255
2926 	* which would overflow our 1-byte offset field */
2927 
2928 	lprintf(LOG_DEBUG, "fru.header.version:         0x%x",
2929 		header.version);
2930 	lprintf(LOG_DEBUG, "fru.header.offset.internal: 0x%x",
2931 		header.offset.internal * 8);
2932 	lprintf(LOG_DEBUG, "fru.header.offset.chassis:  0x%x",
2933 		header.offset.chassis * 8);
2934 	lprintf(LOG_DEBUG, "fru.header.offset.board:    0x%x",
2935 		header.offset.board * 8);
2936 	lprintf(LOG_DEBUG, "fru.header.offset.product:  0x%x",
2937 		header.offset.product * 8);
2938 	lprintf(LOG_DEBUG, "fru.header.offset.multi:    0x%x",
2939 		header.offset.multi * 8);
2940 
2941 	/*
2942 	* rather than reading the entire part
2943 	* only read the areas we'll format
2944 	*/
2945 	/* chassis area */
2946 	if ((header.offset.chassis*8) >= sizeof(struct fru_header))
2947 		fru_area_print_chassis(intf, &fru, id, header.offset.chassis*8);
2948 
2949 	/* board area */
2950 	if ((header.offset.board*8) >= sizeof(struct fru_header))
2951 		fru_area_print_board(intf, &fru, id, header.offset.board*8);
2952 
2953 	/* product area */
2954 	if ((header.offset.product*8) >= sizeof(struct fru_header))
2955 		fru_area_print_product(intf, &fru, id, header.offset.product*8);
2956 
2957 	/* multirecord area */
2958 	if( verbose==0 ) /* scipp parsing multirecord */
2959 		return 0;
2960 
2961 	if ((header.offset.multi*8) >= sizeof(struct fru_header))
2962 		fru_area_print_multirec(intf, &fru, id, header.offset.multi*8);
2963 
2964 	return 0;
2965 }
2966 
2967 /* ipmi_fru_print  -  Print a FRU from its SDR locator record
2968 *
2969 * @intf:   ipmi interface
2970 * @fru: SDR FRU Locator Record
2971 *
2972 * returns -1 on error
2973 */
2974 int
2975 ipmi_fru_print(struct ipmi_intf * intf, struct sdr_record_fru_locator * fru)
2976 {
2977 	char desc[17];
2978 	uint8_t  bridged_request = 0;
2979 	uint32_t save_addr;
2980 	uint32_t save_channel;
2981 	int rc = 0;
2982 
2983 	if (fru == NULL)
2984 		return __ipmi_fru_print(intf, 0);
2985 
2986 	/* Logical FRU Device
2987 	*  dev_type == 0x10
2988 	*  modifier
2989 	*   0x00 = IPMI FRU Inventory
2990 	*   0x01 = DIMM Memory ID
2991 	*   0x02 = IPMI FRU Inventory
2992 	*   0x03 = System Processor FRU
2993 	*   0xff = unspecified
2994 	*
2995 	* EEPROM 24C01 or equivalent
2996 	*  dev_type >= 0x08 && dev_type <= 0x0f
2997 	*  modifier
2998 	*   0x00 = unspecified
2999 	*   0x01 = DIMM Memory ID
3000 	*   0x02 = IPMI FRU Inventory
3001 	*   0x03 = System Processor Cartridge
3002 	*/
3003 	if (fru->dev_type != 0x10 &&
3004 		(fru->dev_type_modifier != 0x02 ||
3005 		fru->dev_type < 0x08 || fru->dev_type > 0x0f))
3006 		return -1;
3007 
3008 	if (fru->dev_slave_addr == IPMI_BMC_SLAVE_ADDR &&
3009 		fru->device_id == 0)
3010 		return 0;
3011 
3012 	memset(desc, 0, sizeof(desc));
3013 	memcpy(desc, fru->id_string, fru->id_code & 0x01f);
3014 	desc[fru->id_code & 0x01f] = 0;
3015 	printf("FRU Device Description : %s (ID %d)\n", desc, fru->device_id);
3016 
3017 	switch (fru->dev_type_modifier) {
3018 	case 0x00:
3019 	case 0x02:
3020 		if (BRIDGE_TO_SENSOR(intf, fru->dev_slave_addr,
3021 					   fru->channel_num)) {
3022 			bridged_request = 1;
3023 			save_addr = intf->target_addr;
3024 			intf->target_addr = fru->dev_slave_addr;
3025 			save_channel = intf->target_channel;
3026 			intf->target_channel = fru->channel_num;
3027 		}
3028 		/* print FRU */
3029 		rc = __ipmi_fru_print(intf, fru->device_id);
3030 		if (bridged_request) {
3031 			intf->target_addr = save_addr;
3032 			intf->target_channel = save_channel;
3033 		}
3034 		break;
3035 	case 0x01:
3036 		rc = ipmi_spd_print_fru(intf, fru->device_id);
3037 		break;
3038 	default:
3039 		if (verbose)
3040 			printf(" Unsupported device 0x%02x "
3041 					"type 0x%02x with modifier 0x%02x\n",
3042 					fru->device_id, fru->dev_type,
3043 					fru->dev_type_modifier);
3044 		else
3045 			printf(" Unsupported device\n");
3046 	}
3047 	printf("\n");
3048 
3049 	return rc;
3050 }
3051 
3052 /* ipmi_fru_print_all  -  Print builtin FRU + SDR FRU Locator records
3053 *
3054 * @intf:   ipmi interface
3055 *
3056 * returns -1 on error
3057 */
3058 static int
3059 ipmi_fru_print_all(struct ipmi_intf * intf)
3060 {
3061 	struct ipmi_sdr_iterator * itr;
3062 	struct sdr_get_rs * header;
3063 	struct sdr_record_fru_locator * fru;
3064 	int rc;
3065 	struct ipmi_rs * rsp;
3066 	struct ipmi_rq req;
3067 	struct ipm_devid_rsp *devid;
3068 	struct sdr_record_mc_locator * mc;
3069 	uint32_t save_addr;
3070 
3071 	printf("FRU Device Description : Builtin FRU Device (ID 0)\n");
3072 	/* TODO: Figure out if FRU device 0 may show up in SDR records. */
3073 
3074 	/* Do a Get Device ID command to determine device support */
3075 	memset (&req, 0, sizeof(req));
3076 	req.msg.netfn = IPMI_NETFN_APP;
3077 	req.msg.cmd = BMC_GET_DEVICE_ID;
3078 	req.msg.data_len = 0;
3079 
3080 	rsp = intf->sendrecv(intf, &req);
3081 	if (rsp == NULL) {
3082 		lprintf(LOG_ERR, "Get Device ID command failed");
3083 		return -1;
3084 	}
3085 	if (rsp->ccode > 0) {
3086 		lprintf(LOG_ERR, "Get Device ID command failed: %s",
3087 			val2str(rsp->ccode, completion_code_vals));
3088 		return -1;
3089 	}
3090 
3091 	devid = (struct ipm_devid_rsp *) rsp->data;
3092 
3093 	/* Check the FRU inventory device bit to decide whether various */
3094 	/* FRU commands can be issued to FRU device #0 LUN 0		*/
3095 
3096 	if (devid->adtl_device_support & 0x08) {	/* FRU Inventory Device bit? */
3097 		rc = ipmi_fru_print(intf, NULL);
3098 		printf("\n");
3099 	}
3100 
3101 	if ((itr = ipmi_sdr_start(intf, 0)) == NULL)
3102 		return -1;
3103 
3104 	/* Walk the SDRs looking for FRU Devices and Management Controller Devices. */
3105 	/* For FRU devices, print the FRU from the SDR locator record.		    */
3106 	/* For MC devices, issue FRU commands to the satellite controller to print  */
3107 	/* FRU data.								    */
3108 	while ((header = ipmi_sdr_get_next_header(intf, itr)) != NULL)
3109 	{
3110 		if (header->type == SDR_RECORD_TYPE_MC_DEVICE_LOCATOR ) {
3111 			/* Check the capabilities of the Management Controller Device */
3112 			mc = (struct sdr_record_mc_locator *)
3113 				ipmi_sdr_get_record(intf, header, itr);
3114 			/* Does this MC device support FRU inventory device? */
3115 			if (mc && (mc->dev_support & 0x08) && /* FRU inventory device? */
3116 				intf->target_addr != mc->dev_slave_addr) {
3117 				/* Yes. Prepare to issue FRU commands to FRU device #0 LUN 0  */
3118 				/* using the slave address specified in the MC record.	      */
3119 
3120 				/* save current target address */
3121 				save_addr = intf->target_addr;
3122 
3123 				/* set new target address to satellite controller */
3124 				intf->target_addr = mc->dev_slave_addr;
3125 
3126 				printf("FRU Device Description : %-16s\n", mc->id_string);
3127 
3128 				/* print the FRU by issuing FRU commands to the satellite     */
3129 				/* controller.						      */
3130 				rc = __ipmi_fru_print(intf, 0);
3131 
3132 				printf("\n");
3133 
3134 				/* restore previous target */
3135 				intf->target_addr = save_addr;
3136 			}
3137 
3138 			if (mc) {
3139 				free(mc);
3140 				mc = NULL;
3141 			}
3142 
3143 			continue;
3144 		}
3145 
3146 		if (header->type != SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR)
3147 			continue;
3148 
3149 		/* Print the FRU from the SDR locator record. */
3150 		fru = (struct sdr_record_fru_locator *)
3151 			ipmi_sdr_get_record(intf, header, itr);
3152 		if (fru == NULL || !fru->logical) {
3153 			if (fru) {
3154 				free(fru);
3155 				fru = NULL;
3156 			}
3157 			continue;
3158 		}
3159 		rc = ipmi_fru_print(intf, fru);
3160 		free(fru);
3161 		fru = NULL;
3162 	}
3163 
3164 	ipmi_sdr_end(intf, itr);
3165 
3166 	return rc;
3167 }
3168 
3169 /* ipmi_fru_read_help() - print help text for 'read'
3170  *
3171  * returns void
3172  */
3173 void
3174 ipmi_fru_read_help()
3175 {
3176 	lprintf(LOG_NOTICE, "fru read <fru id> <fru file>");
3177 	lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified.");
3178 	lprintf(LOG_NOTICE, "Example: ipmitool fru read 0 /root/fru.bin");
3179 } /* ipmi_fru_read_help() */
3180 
3181 static void
3182 ipmi_fru_read_to_bin(struct ipmi_intf * intf,
3183 			char * pFileName,
3184 			uint8_t fruId)
3185 {
3186 	struct ipmi_rs * rsp;
3187 	struct ipmi_rq req;
3188 	struct fru_info fru;
3189 	uint8_t msg_data[4];
3190 	uint8_t * pFruBuf;
3191 
3192 	msg_data[0] = fruId;
3193 
3194 	memset(&req, 0, sizeof(req));
3195 	req.msg.netfn = IPMI_NETFN_STORAGE;
3196 	req.msg.cmd = GET_FRU_INFO;
3197 	req.msg.data = msg_data;
3198 	req.msg.data_len = 1;
3199 
3200 	rsp = intf->sendrecv(intf, &req);
3201 	if (!rsp)
3202 		return;
3203 
3204 	if (rsp->ccode > 0) {
3205 		if (rsp->ccode == 0xc3)
3206 			printf ("  Timeout accessing FRU info. (Device not present?)\n");
3207 		return;
3208 	}
3209 
3210 	memset(&fru, 0, sizeof(fru));
3211 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
3212 	fru.access = rsp->data[2] & 0x1;
3213 
3214 	if (verbose) {
3215 		printf("Fru Size   = %d bytes\n",fru.size);
3216 		printf("Fru Access = %xh\n", fru.access);
3217 	}
3218 
3219 	pFruBuf = malloc(fru.size);
3220 	if (pFruBuf != NULL) {
3221 		printf("Fru Size         : %d bytes\n",fru.size);
3222 		read_fru_area(intf, &fru, fruId, 0, fru.size, pFruBuf);
3223 	} else {
3224 		lprintf(LOG_ERR, "Cannot allocate %d bytes\n", fru.size);
3225 		return;
3226 	}
3227 
3228 	if(pFruBuf != NULL)
3229 	{
3230 		FILE * pFile;
3231 		pFile = fopen(pFileName,"wb");
3232 		if (pFile) {
3233 			fwrite(pFruBuf, fru.size, 1, pFile);
3234 			printf("Done\n");
3235 		} else {
3236 			lprintf(LOG_ERR, "Error opening file %s\n", pFileName);
3237 			free(pFruBuf);
3238 			pFruBuf = NULL;
3239 			return;
3240 		}
3241 		fclose(pFile);
3242 	}
3243 	free(pFruBuf);
3244 	pFruBuf = NULL;
3245 }
3246 
3247 static void
3248 ipmi_fru_write_from_bin(struct ipmi_intf * intf,
3249 			char * pFileName,
3250 			uint8_t fruId)
3251 {
3252 	struct ipmi_rs *rsp;
3253 	struct ipmi_rq req;
3254 	struct fru_info fru;
3255 	uint8_t msg_data[4];
3256 	uint8_t *pFruBuf;
3257 	uint16_t len = 0;
3258 	FILE *pFile;
3259 
3260 	msg_data[0] = fruId;
3261 
3262 	memset(&req, 0, sizeof (req));
3263 	req.msg.netfn = IPMI_NETFN_STORAGE;
3264 	req.msg.cmd = GET_FRU_INFO;
3265 	req.msg.data = msg_data;
3266 	req.msg.data_len = 1;
3267 
3268 	rsp = intf->sendrecv(intf, &req);
3269 	if (!rsp)
3270 		return;
3271 
3272 	if (rsp->ccode) {
3273 		if (rsp->ccode == 0xc3)
3274 			printf("  Timeout accessing FRU info. (Device not present?)\n");
3275 		return;
3276 	}
3277 
3278 	memset(&fru, 0, sizeof(fru));
3279 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
3280 	fru.access = rsp->data[2] & 0x1;
3281 
3282 	if (verbose) {
3283 		printf("Fru Size   = %d bytes\n", fru.size);
3284 		printf("Fru Access = %xh\n", fru.access);
3285 	}
3286 
3287 	pFruBuf = malloc(fru.size);
3288 	if (pFruBuf == NULL) {
3289 		lprintf(LOG_ERR, "Cannot allocate %d bytes\n", fru.size);
3290 		return;
3291 	}
3292 
3293 		pFile = fopen(pFileName, "rb");
3294 		if (pFile != NULL) {
3295 			len = fread(pFruBuf, 1, fru.size, pFile);
3296 			printf("Fru Size         : %d bytes\n", fru.size);
3297 			printf("Size to Write    : %d bytes\n", len);
3298 			fclose(pFile);
3299 		} else {
3300 		lprintf(LOG_ERR, "Error opening file %s\n", pFileName);
3301 		}
3302 
3303 		if (len != 0) {
3304 			write_fru_area(intf, &fru, fruId,0, 0, len, pFruBuf);
3305 			lprintf(LOG_INFO,"Done");
3306 		}
3307 
3308 	free(pFruBuf);
3309 	pFruBuf = NULL;
3310 }
3311 
3312 /* ipmi_fru_write_help() - print help text for 'write'
3313  *
3314  * retruns void
3315  */
3316 void
3317 ipmi_fru_write_help()
3318 {
3319 	lprintf(LOG_NOTICE, "fru write <fru id> <fru file>");
3320 	lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified.");
3321 	lprintf(LOG_NOTICE, "Example: ipmitool fru write 0 /root/fru.bin");
3322 } /* ipmi_fru_write_help() */
3323 
3324 /* ipmi_fru_edit_help - print help text for 'fru edit' command
3325  *
3326  * returns void
3327  */
3328 void
3329 ipmi_fru_edit_help()
3330 {
3331 	lprintf(LOG_NOTICE,
3332 			"fru edit <fruid> field <section> <index> <string> - edit FRU string");
3333 	lprintf(LOG_NOTICE,
3334 			"fru edit <fruid> oem iana <record> <format> <args> - limited OEM support");
3335 } /* ipmi_fru_edit_help() */
3336 
3337 /* ipmi_fru_edit_multirec  -  Query new values to replace original FRU content
3338 *
3339 * @intf:   interface to use
3340 * @id:  FRU id to work on
3341 *
3342 * returns: nothing
3343 */
3344 /* Work in progress, copy paste most of the stuff for other functions in this
3345 	file ... not elegant yet */
3346 static int
3347 ipmi_fru_edit_multirec(struct ipmi_intf * intf, uint8_t id ,
3348 												int argc, char ** argv)
3349 {
3350 
3351 	struct ipmi_rs * rsp;
3352 	struct ipmi_rq req;
3353 	struct fru_info fru;
3354 	struct fru_header header;
3355 	uint8_t msg_data[4];
3356 
3357 	uint16_t retStatus = 0;
3358 	uint32_t offFruMultiRec;
3359 	uint32_t fruMultiRecSize = 0;
3360 	struct fru_info fruInfo;
3361 	retStatus = ipmi_fru_get_multirec_location_from_fru(intf, id, &fruInfo,
3362 								&offFruMultiRec,
3363 								&fruMultiRecSize);
3364 
3365 
3366 	lprintf(LOG_DEBUG, "FRU Size        : %lu\n", fruMultiRecSize);
3367 	lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec);
3368 
3369 	{
3370 
3371 
3372 	memset(&fru, 0, sizeof(struct fru_info));
3373 	memset(&header, 0, sizeof(struct fru_header));
3374 
3375 	/*
3376 	* get info about this FRU
3377 	*/
3378 	memset(msg_data, 0, 4);
3379 	msg_data[0] = id;
3380 
3381 	memset(&req, 0, sizeof(req));
3382 	req.msg.netfn = IPMI_NETFN_STORAGE;
3383 	req.msg.cmd = GET_FRU_INFO;
3384 	req.msg.data = msg_data;
3385 	req.msg.data_len = 1;
3386 
3387 	rsp = intf->sendrecv(intf, &req);
3388 	if (rsp == NULL) {
3389 		printf(" Device not present (No Response)\n");
3390 		return -1;
3391 	}
3392 	if (rsp->ccode > 0) {
3393 		printf(" Device not present (%s)\n",
3394 			val2str(rsp->ccode, completion_code_vals));
3395 		return -1;
3396 	}
3397 
3398 	memset(&fru, 0, sizeof(fru));
3399 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
3400 	fru.access = rsp->data[2] & 0x1;
3401 
3402 	lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)",
3403 		fru.size, fru.access ? "words" : "bytes");
3404 
3405 	if (fru.size < 1) {
3406 		lprintf(LOG_ERR, " Invalid FRU size %d", fru.size);
3407 		return -1;
3408 	}
3409 	}
3410 
3411 	{
3412 		uint8_t * fru_data;
3413 		uint32_t fru_len, i;
3414 		uint32_t offset= offFruMultiRec;
3415 		struct fru_multirec_header * h;
3416 		uint32_t last_off, len;
3417 		uint8_t error=0;
3418 
3419 		i = last_off = offset;
3420 		fru_len = 0;
3421 
3422 		memset(&fru, 0, sizeof(fru));
3423 		fru_data = malloc(fru.size + 1);
3424 		if (fru_data == NULL) {
3425 			lprintf(LOG_ERR, " Out of memory!");
3426 			return -1;
3427 		}
3428 		memset(fru_data, 0, fru.size + 1);
3429 
3430 		do {
3431 			h = (struct fru_multirec_header *) (fru_data + i);
3432 
3433 			/* read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time */
3434 			if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len)))
3435 			{
3436 				len = fru.size - last_off;
3437 				if (len > FRU_MULTIREC_CHUNK_SIZE)
3438 					len = FRU_MULTIREC_CHUNK_SIZE;
3439 
3440 				if (read_fru_area(intf, &fru, id, last_off, len, fru_data) < 0)
3441 					break;
3442 
3443 				last_off += len;
3444 			}
3445 			if( h->type ==  FRU_RECORD_TYPE_OEM_EXTENSION ){
3446 
3447 				struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *)
3448 										&fru_data[i + sizeof(struct fru_multirec_header)];
3449 				uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16;
3450 
3451 				uint32_t suppliedIana = 0 ;
3452 				/* Now makes sure this is really PICMG record */
3453 
3454 				/* Default to PICMG for backward compatibility */
3455 				if( argc <=2 ) {
3456 					suppliedIana =  IPMI_OEM_PICMG;
3457 				}  else {
3458 					if( !strncmp( argv[2] , "oem" , 3 )) {
3459 						/* Expect IANA number next */
3460 						if( argc <= 3 ) {
3461 							lprintf(LOG_ERR, "oem iana <record> <format> [<args>]");
3462 							error = 1;
3463 						} else {
3464 							if (str2uint(argv[3], &suppliedIana) == 0) {
3465 								lprintf(LOG_DEBUG,
3466 										"using iana: %d",
3467 										suppliedIana);
3468 							} else {
3469 								lprintf(LOG_ERR,
3470 										"Given IANA '%s' is invalid.",
3471 										argv[3]);
3472 								error = 1;
3473 							}
3474 						}
3475 					}
3476 				}
3477 
3478 				if( suppliedIana == iana ) {
3479 					lprintf(LOG_DEBUG, "Matching record found" );
3480 
3481 					if( iana == IPMI_OEM_PICMG ){
3482 						if( ipmi_fru_picmg_ext_edit(fru_data,
3483 						i + sizeof(struct fru_multirec_header),
3484 						h->len, h, oh )){
3485 							/* The fru changed */
3486 							write_fru_area(intf,&fru,id, i,i,
3487 						h->len+ sizeof(struct fru_multirec_header), fru_data);
3488 						}
3489 					}
3490 					else if( iana == IPMI_OEM_KONTRON ) {
3491 						if( ipmi_fru_oemkontron_edit( argc,argv,fru_data,
3492 						i + sizeof(struct fru_multirec_header),
3493 						h->len, h, oh )){
3494 							/* The fru changed */
3495 							write_fru_area(intf,&fru,id, i,i,
3496 						h->len+ sizeof(struct fru_multirec_header), fru_data);
3497 						}
3498 					}
3499 					/* FIXME: Add OEM record support here */
3500 					else{
3501 						printf("  OEM IANA (%s) Record not support in this mode\n",
3502 															val2str( iana,  ipmi_oem_info));
3503 						error = 1;
3504 					}
3505 				}
3506 			}
3507 			i += h->len + sizeof (struct fru_multirec_header);
3508 		} while (!(h->format & 0x80) && (error != 1));
3509 
3510 		free(fru_data);
3511 		fru_data = NULL;
3512 	}
3513 	return 0;
3514 }
3515 
3516 /* ipmi_fru_get_help - print help text for 'fru get'
3517  *
3518  * returns void
3519  */
3520 void
3521 ipmi_fru_get_help()
3522 {
3523 	lprintf(LOG_NOTICE,
3524 			"fru get <fruid> oem iana <record> <format> <args> - limited OEM support");
3525 } /* ipmi_fru_get_help() */
3526 
3527 void
3528 ipmi_fru_internaluse_help()
3529 {
3530 	lprintf(LOG_NOTICE,
3531 			"fru internaluse <fru id> info             - get internal use area size");
3532 	lprintf(LOG_NOTICE,
3533 			"fru internaluse <fru id> print            - print internal use area in hex");
3534 	lprintf(LOG_NOTICE,
3535 			"fru internaluse <fru id> read  <fru file> - read internal use area to file");
3536 	lprintf(LOG_NOTICE,
3537 			"fru internaluse <fru id> write <fru file> - write internal use area from file");
3538 } /* void ipmi_fru_internaluse_help() */
3539 
3540 /* ipmi_fru_get_multirec   -  Query new values to replace original FRU content
3541 *
3542 * @intf:   interface to use
3543 * @id:  FRU id to work on
3544 *
3545 * returns: nothing
3546 */
3547 /* Work in progress, copy paste most of the stuff for other functions in this
3548 	file ... not elegant yet */
3549 static int
3550 ipmi_fru_get_multirec(struct ipmi_intf * intf, uint8_t id ,
3551 												int argc, char ** argv)
3552 {
3553 
3554 	struct ipmi_rs * rsp;
3555 	struct ipmi_rq req;
3556 	struct fru_info fru;
3557 	struct fru_header header;
3558 	uint8_t msg_data[4];
3559 
3560 	uint16_t retStatus = 0;
3561 	uint32_t offFruMultiRec;
3562 	uint32_t fruMultiRecSize = 0;
3563 	struct fru_info fruInfo;
3564 	retStatus = ipmi_fru_get_multirec_location_from_fru(intf, id, &fruInfo,
3565 								&offFruMultiRec,
3566 								&fruMultiRecSize);
3567 
3568 
3569 	lprintf(LOG_DEBUG, "FRU Size        : %lu\n", fruMultiRecSize);
3570 	lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec);
3571 
3572 	{
3573 
3574 
3575 	memset(&fru, 0, sizeof(struct fru_info));
3576 	memset(&header, 0, sizeof(struct fru_header));
3577 
3578 	/*
3579 	* get info about this FRU
3580 	*/
3581 	memset(msg_data, 0, 4);
3582 	msg_data[0] = id;
3583 
3584 	memset(&req, 0, sizeof(req));
3585 	req.msg.netfn = IPMI_NETFN_STORAGE;
3586 	req.msg.cmd = GET_FRU_INFO;
3587 	req.msg.data = msg_data;
3588 	req.msg.data_len = 1;
3589 
3590 	rsp = intf->sendrecv(intf, &req);
3591 	if (rsp == NULL) {
3592 		printf(" Device not present (No Response)\n");
3593 		return -1;
3594 	}
3595 	if (rsp->ccode > 0) {
3596 		printf(" Device not present (%s)\n",
3597 			val2str(rsp->ccode, completion_code_vals));
3598 		return -1;
3599 	}
3600 
3601 	memset(&fru, 0, sizeof(fru));
3602 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
3603 	fru.access = rsp->data[2] & 0x1;
3604 
3605 	lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)",
3606 		fru.size, fru.access ? "words" : "bytes");
3607 
3608 	if (fru.size < 1) {
3609 		lprintf(LOG_ERR, " Invalid FRU size %d", fru.size);
3610 		return -1;
3611 	}
3612 	}
3613 
3614 	{
3615 		uint8_t * fru_data;
3616 		uint32_t fru_len, i;
3617 		uint32_t offset= offFruMultiRec;
3618 		struct fru_multirec_header * h;
3619 		uint32_t last_off, len;
3620 		uint8_t error=0;
3621 
3622 		i = last_off = offset;
3623 		fru_len = 0;
3624 
3625 		fru_data = malloc(fru.size + 1);
3626 		if (fru_data == NULL) {
3627 			lprintf(LOG_ERR, " Out of memory!");
3628 			return -1;
3629 		}
3630 		memset(fru_data, 0, fru.size + 1);
3631 
3632 		do {
3633 			h = (struct fru_multirec_header *) (fru_data + i);
3634 
3635 			/* read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time */
3636 			if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len)))
3637 			{
3638 				len = fru.size - last_off;
3639 				if (len > FRU_MULTIREC_CHUNK_SIZE)
3640 					len = FRU_MULTIREC_CHUNK_SIZE;
3641 
3642 				if (read_fru_area(intf, &fru, id, last_off, len, fru_data) < 0)
3643 					break;
3644 
3645 				last_off += len;
3646 			}
3647 			if( h->type ==  FRU_RECORD_TYPE_OEM_EXTENSION ){
3648 
3649 				struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *)
3650 										&fru_data[i + sizeof(struct fru_multirec_header)];
3651 				uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16;
3652 
3653 				uint32_t suppliedIana = 0 ;
3654 				/* Now makes sure this is really PICMG record */
3655 				if( !strncmp( argv[2] , "oem" , 3 )) {
3656 					/* Expect IANA number next */
3657 					if( argc <= 3 ) {
3658 						lprintf(LOG_ERR, "oem iana <record> <format>");
3659 						error = 1;
3660 					} else {
3661 						if (str2uint(argv[3], &suppliedIana) == 0) {
3662 							lprintf(LOG_DEBUG,
3663 									"using iana: %d",
3664 									suppliedIana);
3665 						} else {
3666 							lprintf(LOG_ERR,
3667 									"Given IANA '%s' is invalid.",
3668 									argv[3]);
3669 							error = 1;
3670 						}
3671 					}
3672 				}
3673 
3674 				if( suppliedIana == iana ) {
3675 					lprintf(LOG_DEBUG, "Matching record found" );
3676 
3677 				if( iana == IPMI_OEM_KONTRON ) {
3678 						ipmi_fru_oemkontron_get( argc,argv,fru_data,
3679 						i + sizeof(struct fru_multirec_header),
3680 						h->len, h, oh );
3681 					}
3682 					/* FIXME: Add OEM record support here */
3683 					else{
3684 						printf("  OEM IANA (%s) Record not supported in this mode\n",
3685 															val2str( iana,  ipmi_oem_info));
3686 						error = 1;
3687 					}
3688 				}
3689 			}
3690 			i += h->len + sizeof (struct fru_multirec_header);
3691 		} while (!(h->format & 0x80) && (error != 1));
3692 
3693 		free(fru_data);
3694 		fru_data = NULL;
3695 	}
3696 	return 0;
3697 }
3698 
3699 static int
3700 ipmi_fru_upg_ekeying(struct ipmi_intf * intf,
3701 			char * pFileName,
3702 			uint8_t fruId)
3703 {
3704 	struct fru_info fruInfo = {0};
3705 	uint8_t *buf = NULL;
3706 	uint32_t offFruMultiRec = 0;
3707 	uint32_t fruMultiRecSize = 0;
3708 	uint32_t offFileMultiRec = 0;
3709 	uint32_t fileMultiRecSize = 0;
3710 	if (pFileName == NULL) {
3711 		lprintf(LOG_ERR, "File expected, but none given.");
3712 		return (-1);
3713 	}
3714 	if (ipmi_fru_get_multirec_location_from_fru(intf, fruId, &fruInfo,
3715 							&offFruMultiRec, &fruMultiRecSize) != 0) {
3716 		lprintf(LOG_ERR, "Failed to get multirec location from FRU.");
3717 		return (-1);
3718 	}
3719 	lprintf(LOG_DEBUG, "FRU Size        : %lu\n", fruMultiRecSize);
3720 	lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec);
3721 	if (ipmi_fru_get_multirec_size_from_file(pFileName, &fileMultiRecSize,
3722 				&offFileMultiRec) != 0) {
3723 		lprintf(LOG_ERR, "Failed to get multirec size from file '%s'.", pFileName);
3724 		return (-1);
3725 	}
3726 	buf = malloc(fileMultiRecSize);
3727 	if (buf == NULL) {
3728 		lprintf(LOG_ERR, "ipmitool: malloc failure");
3729 		return (-1);
3730 	}
3731 	if (ipmi_fru_get_multirec_from_file(pFileName, buf, fileMultiRecSize,
3732 				offFileMultiRec) != 0) {
3733 		lprintf(LOG_ERR, "Failed to get multirec from file '%s'.", pFileName);
3734 		if (buf != NULL) {
3735 			free(buf);
3736 			buf = NULL;
3737 		}
3738 		return (-1);
3739 	}
3740 	if (ipmi_fru_get_adjust_size_from_buffer(buf, &fileMultiRecSize) != 0) {
3741 		lprintf(LOG_ERR, "Failed to adjust size from buffer.");
3742 		if (buf != NULL) {
3743 			free(buf);
3744 			buf = NULL;
3745 		}
3746 		return (-1);
3747 	}
3748 	if (write_fru_area(intf, &fruInfo, fruId, 0, offFruMultiRec,
3749 				fileMultiRecSize, buf) != 0) {
3750 		lprintf(LOG_ERR, "Failed to write FRU area.");
3751 		if (buf != NULL) {
3752 			free(buf);
3753 			buf = NULL;
3754 		}
3755 		return (-1);
3756 	}
3757 	if (buf != NULL) {
3758 		free(buf);
3759 		buf = NULL;
3760 	}
3761 	lprintf(LOG_INFO, "Done upgrading Ekey.");
3762 	return 0;
3763 }
3764 
3765 /* ipmi_fru_upgekey_help - print help text for 'upgEkey'
3766  *
3767  * returns void
3768  */
3769 void
3770 ipmi_fru_upgekey_help()
3771 {
3772 	lprintf(LOG_NOTICE, "fru upgEkey <fru id> <fru file>");
3773 	lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified.");
3774 	lprintf(LOG_NOTICE, "Example: ipmitool fru upgEkey 0 /root/fru.bin");
3775 } /* ipmi_fru_upgekey_help() */
3776 
3777 static int
3778 ipmi_fru_get_multirec_size_from_file(char * pFileName,
3779 					uint32_t * pSize,
3780 					uint32_t * pOffset)
3781 {
3782 	struct fru_header header;
3783 	FILE * pFile;
3784 	uint8_t len = 0;
3785 	uint32_t end = 0;
3786 	*pSize = 0;
3787 
3788 	pFile = fopen(pFileName,"rb");
3789 	if (pFile) {
3790 		rewind(pFile);
3791 		len = fread(&header, 1, 8, pFile);
3792 		fseek(pFile, 0, SEEK_END);
3793 		end = ftell(pFile);
3794 		fclose(pFile);
3795 	}
3796 
3797 	lprintf(LOG_DEBUG, "File Size = %lu\n", end);
3798 	lprintf(LOG_DEBUG, "Len = %u\n", len);
3799 
3800 	if (len != 8) {
3801 		printf("Error with file %s in getting size\n", pFileName);
3802 		return -1;
3803 	}
3804 
3805 	if (header.version != 0x01) {
3806 		printf ("Unknown FRU header version %02x.\n", header.version);
3807 		return -1;
3808 	}
3809 
3810 	/* Retreive length */
3811 	if (((header.offset.internal * 8) > (header.offset.internal * 8)) &&
3812 		((header.offset.internal * 8) < end))
3813 		end = (header.offset.internal * 8);
3814 
3815 	if (((header.offset.chassis * 8) > (header.offset.chassis * 8)) &&
3816 		((header.offset.chassis * 8) < end))
3817 		end = (header.offset.chassis * 8);
3818 
3819 	if (((header.offset.board * 8) > (header.offset.board * 8)) &&
3820 		((header.offset.board * 8) < end))
3821 		end = (header.offset.board * 8);
3822 
3823 	if (((header.offset.product * 8) > (header.offset.product * 8)) &&
3824 		((header.offset.product * 8) < end))
3825 		end = (header.offset.product * 8);
3826 
3827 	*pSize = end - (header.offset.multi * 8);
3828 	*pOffset = (header.offset.multi * 8);
3829 
3830 	return 0;
3831 }
3832 
3833 int
3834 ipmi_fru_get_adjust_size_from_buffer(uint8_t * fru_data, uint32_t *pSize)
3835 {
3836 	struct fru_multirec_header * head;
3837 	int status = 0;
3838 	uint8_t checksum = 0;
3839 	uint8_t counter = 0;
3840 	uint16_t count = 0;
3841 	do {
3842 		checksum = 0;
3843 		head = (struct fru_multirec_header *) (fru_data + count);
3844 		if (verbose) {
3845 			printf("Adding (");
3846 		}
3847 		for (counter = 0; counter < sizeof(struct fru_multirec_header); counter++) {
3848 			if (verbose) {
3849 				printf(" %02X", *(fru_data + count + counter));
3850 			}
3851 			checksum += *(fru_data + count + counter);
3852 		}
3853 		if (verbose) {
3854 			printf(")");
3855 		}
3856 		if (checksum != 0) {
3857 			lprintf(LOG_ERR, "Bad checksum in Multi Records");
3858 			status = (-1);
3859 			if (verbose) {
3860 				printf("--> FAIL");
3861 			}
3862 		} else if (verbose) {
3863 			printf("--> OK");
3864 		}
3865 		if (verbose > 1 && checksum == 0) {
3866 			for (counter = 0; counter < head->len; counter++) {
3867 				printf(" %02X", *(fru_data + count + counter
3868 							+ sizeof(struct fru_multirec_header)));
3869 			}
3870 		}
3871 		if (verbose) {
3872 			printf("\n");
3873 		}
3874 		count += head->len + sizeof (struct fru_multirec_header);
3875 	} while ((!(head->format & 0x80)) && (status == 0));
3876 
3877 	*pSize = count;
3878 	lprintf(LOG_DEBUG, "Size of multirec: %lu\n", *pSize);
3879 	return status;
3880 }
3881 
3882 static int
3883 ipmi_fru_get_multirec_from_file(char * pFileName, uint8_t * pBufArea,
3884 		uint32_t size, uint32_t offset)
3885 {
3886 	FILE * pFile;
3887 	uint32_t len = 0;
3888 	if (pFileName == NULL) {
3889 		lprintf(LOG_ERR, "Invalid file name given.");
3890 		return (-1);
3891 	}
3892 
3893 	errno = 0;
3894 	pFile = fopen(pFileName, "rb");
3895 	if (!pFile) {
3896 		lprintf(LOG_ERR, "Error opening file '%s': %i -> %s.", pFileName, errno,
3897 				strerror(errno));
3898 		return (-1);
3899 	}
3900 	errno = 0;
3901 	if (fseek(pFile, offset, SEEK_SET) != 0) {
3902 		lprintf(LOG_ERR, "Failed to seek in file '%s': %i -> %s.", pFileName, errno,
3903 				strerror(errno));
3904 		fclose(pFile);
3905 		return (-1);
3906 	}
3907 	len = fread(pBufArea, size, 1, pFile);
3908 	fclose(pFile);
3909 
3910 	if (len != 1) {
3911 		lprintf(LOG_ERR, "Error in file '%s'.", pFileName);
3912 		return (-1);
3913 	}
3914 	return 0;
3915 }
3916 
3917 static int
3918 ipmi_fru_get_multirec_location_from_fru(struct ipmi_intf * intf,
3919 					uint8_t fruId,
3920 												struct fru_info *pFruInfo,
3921 					uint32_t * pRetLocation,
3922 					uint32_t * pRetSize)
3923 {
3924 	struct ipmi_rs * rsp;
3925 	struct ipmi_rq req;
3926 	uint8_t msg_data[4];
3927 	uint32_t end;
3928 	struct fru_header header;
3929 
3930 	*pRetLocation = 0;
3931 
3932 	msg_data[0] = fruId;
3933 
3934 	memset(&req, 0, sizeof(req));
3935 	req.msg.netfn = IPMI_NETFN_STORAGE;
3936 	req.msg.cmd = GET_FRU_INFO;
3937 	req.msg.data = msg_data;
3938 	req.msg.data_len = 1;
3939 
3940 	rsp = intf->sendrecv(intf, &req);
3941 	if (!rsp) {
3942 		if (verbose > 1)
3943 			printf("no response\n");
3944 		return -1;
3945 	}
3946 
3947 	if (rsp->ccode > 0) {
3948 		if (rsp->ccode == 0xc3)
3949 			printf ("  Timeout accessing FRU info. (Device not present?)\n");
3950 		else
3951 			printf ("   CCODE = 0x%02x\n", rsp->ccode);
3952 		return -1;
3953 	}
3954 	pFruInfo->size = (rsp->data[1] << 8) | rsp->data[0];
3955 	pFruInfo->access = rsp->data[2] & 0x1;
3956 
3957 	if (verbose > 1)
3958 		printf("pFruInfo->size = %d bytes (accessed by %s)\n",
3959 				pFruInfo->size, pFruInfo->access ? "words" : "bytes");
3960 
3961 	if (!pFruInfo->size)
3962 		return -1;
3963 
3964 	msg_data[0] = fruId;
3965 	msg_data[1] = 0;
3966 	msg_data[2] = 0;
3967 	msg_data[3] = 8;
3968 
3969 	memset(&req, 0, sizeof(req));
3970 	req.msg.netfn = IPMI_NETFN_STORAGE;
3971 	req.msg.cmd = GET_FRU_DATA;
3972 	req.msg.data = msg_data;
3973 	req.msg.data_len = 4;
3974 
3975 	rsp = intf->sendrecv(intf, &req);
3976 
3977 	if (!rsp)
3978 		return -1;
3979 	if (rsp->ccode > 0) {
3980 		if (rsp->ccode == 0xc3)
3981 			printf ("  Timeout while reading FRU data. (Device not present?)\n");
3982 		return -1;
3983 	}
3984 
3985 	if (verbose > 1)
3986 		printbuf(rsp->data, rsp->data_len, "FRU DATA");
3987 
3988 	memcpy(&header, rsp->data + 1, 8);
3989 
3990 	if (header.version != 0x01) {
3991 		printf ("  Unknown FRU header version %02x.\n", header.version);
3992 		return -1;
3993 	}
3994 
3995 	end = pFruInfo->size;
3996 
3997 	/* Retreive length */
3998 	if (((header.offset.internal * 8) > (header.offset.internal * 8)) &&
3999 		((header.offset.internal * 8) < end))
4000 		end = (header.offset.internal * 8);
4001 
4002 	if (((header.offset.chassis * 8) > (header.offset.chassis * 8)) &&
4003 		((header.offset.chassis * 8) < end))
4004 		end = (header.offset.chassis * 8);
4005 
4006 	if (((header.offset.board * 8) > (header.offset.board * 8)) &&
4007 		((header.offset.board * 8) < end))
4008 		end = (header.offset.board * 8);
4009 
4010 	if (((header.offset.product * 8) > (header.offset.product * 8)) &&
4011 		((header.offset.product * 8) < end))
4012 		end = (header.offset.product * 8);
4013 
4014 	*pRetSize = end;
4015 	*pRetLocation = 8 * header.offset.multi;
4016 
4017 	return 0;
4018 }
4019 
4020 /* ipmi_fru_get_internal_use_offset -  Retreive internal use offset
4021 *
4022 * @intf:   ipmi interface
4023 * @id:     fru id
4024 *
4025 * returns -1 on error
4026 * returns 0 if successful
4027 * returns 1 if device not present
4028 */
4029 static int
4030 ipmi_fru_get_internal_use_info(  struct ipmi_intf * intf,
4031 											uint8_t id,
4032 											struct fru_info * fru,
4033 											uint16_t * size,
4034 											uint16_t * offset)
4035 {
4036 	struct ipmi_rs * rsp;
4037 	struct ipmi_rq req;
4038 	struct fru_header header;
4039 	uint8_t msg_data[4];
4040 
4041 	// Init output value
4042 	* offset = 0;
4043 	* size = 0;
4044 
4045 	memset(fru, 0, sizeof(struct fru_info));
4046 	memset(&header, 0, sizeof(struct fru_header));
4047 
4048 	/*
4049 	* get info about this FRU
4050 	*/
4051 	memset(msg_data, 0, 4);
4052 	msg_data[0] = id;
4053 
4054 	memset(&req, 0, sizeof(req));
4055 	req.msg.netfn = IPMI_NETFN_STORAGE;
4056 	req.msg.cmd = GET_FRU_INFO;
4057 	req.msg.data = msg_data;
4058 	req.msg.data_len = 1;
4059 
4060 	rsp = intf->sendrecv(intf, &req);
4061 	if (rsp == NULL) {
4062 		printf(" Device not present (No Response)\n");
4063 		return -1;
4064 	}
4065 	if (rsp->ccode > 0) {
4066 		printf(" Device not present (%s)\n",
4067 			val2str(rsp->ccode, completion_code_vals));
4068 		return -1;
4069 	}
4070 
4071 	memset(&fru, 0, sizeof(fru));
4072 	fru->size = (rsp->data[1] << 8) | rsp->data[0];
4073 	fru->access = rsp->data[2] & 0x1;
4074 
4075 	lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)",
4076 		fru->size, fru->access ? "words" : "bytes");
4077 
4078 	if (fru->size < 1) {
4079 		lprintf(LOG_ERR, " Invalid FRU size %d", fru->size);
4080 		return -1;
4081 	}
4082 
4083 	/*
4084 	* retrieve the FRU header
4085 	*/
4086 	msg_data[0] = id;
4087 	msg_data[1] = 0;
4088 	msg_data[2] = 0;
4089 	msg_data[3] = 8;
4090 
4091 	memset(&req, 0, sizeof(req));
4092 	req.msg.netfn = IPMI_NETFN_STORAGE;
4093 	req.msg.cmd = GET_FRU_DATA;
4094 	req.msg.data = msg_data;
4095 	req.msg.data_len = 4;
4096 
4097 	rsp = intf->sendrecv(intf, &req);
4098 	if (rsp == NULL) {
4099 		printf(" Device not present (No Response)\n");
4100 		return 1;
4101 	}
4102 	if (rsp->ccode > 0) {
4103 		printf(" Device not present (%s)\n",
4104 				val2str(rsp->ccode, completion_code_vals));
4105 		return 1;
4106 	}
4107 
4108 	if (verbose > 1)
4109 		printbuf(rsp->data, rsp->data_len, "FRU DATA");
4110 
4111 	memcpy(&header, rsp->data + 1, 8);
4112 
4113 	if (header.version != 1) {
4114 		lprintf(LOG_ERR, " Unknown FRU header version 0x%02x",
4115 			header.version);
4116 		return -1;
4117 	}
4118 
4119 	lprintf(LOG_DEBUG, "fru.header.version:         0x%x",
4120 		header.version);
4121 	lprintf(LOG_DEBUG, "fru.header.offset.internal: 0x%x",
4122 		header.offset.internal * 8);
4123 	lprintf(LOG_DEBUG, "fru.header.offset.chassis:  0x%x",
4124 		header.offset.chassis * 8);
4125 	lprintf(LOG_DEBUG, "fru.header.offset.board:    0x%x",
4126 		header.offset.board * 8);
4127 	lprintf(LOG_DEBUG, "fru.header.offset.product:  0x%x",
4128 		header.offset.product * 8);
4129 	lprintf(LOG_DEBUG, "fru.header.offset.multi:    0x%x",
4130 		header.offset.multi * 8);
4131 
4132 	if((header.offset.internal*8) == 0)
4133 	{
4134 		* size = 0;
4135 		* offset = 0;
4136 	}
4137 	else
4138 	{
4139 		(* offset) = (header.offset.internal*8);
4140 
4141 		if(header.offset.chassis != 0)
4142 		{
4143 			(* size) = ((header.offset.chassis*8)-(* offset));
4144 		}
4145 		else if(header.offset.board != 0)
4146 		{
4147 			(* size) = ((header.offset.board*8)-(* offset));
4148 		}
4149 		else if(header.offset.product != 0)
4150 		{
4151 			(* size) = ((header.offset.product*8)-(* offset));
4152 		}
4153 		else if(header.offset.multi != 0)
4154 		{
4155 			(* size) = ((header.offset.multi*8)-(* offset));
4156 		}
4157 		else
4158 		{
4159 			(* size) = (fru->size - (* offset));
4160 		}
4161 	}
4162 	return 0;
4163 }
4164 
4165 /* ipmi_fru_info_internal_use -  print internal use info
4166 *
4167 * @intf:   ipmi interface
4168 * @id:     fru id
4169 *
4170 * returns -1 on error
4171 * returns 0 if successful
4172 * returns 1 if device not present
4173 */
4174 static int
4175 ipmi_fru_info_internal_use(struct ipmi_intf * intf, uint8_t id)
4176 {
4177 	struct fru_info fru;
4178 	uint16_t size;
4179 	uint16_t offset;
4180 	int rc = 0;
4181 
4182 	rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset);
4183 
4184 	if(rc == 0)
4185 	{
4186 		lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset);
4187 		printf(          "Internal Use Area Size  : %i\n", size);
4188 	}
4189 	else
4190 	{
4191 		lprintf(LOG_ERR, "Cannot access internal use area");
4192 		return -1;
4193 	}
4194 	return 0;
4195 }
4196 
4197 /* ipmi_fru_help - print help text for FRU subcommand
4198  *
4199  * returns void
4200  */
4201 void
4202 ipmi_fru_help()
4203 {
4204 	lprintf(LOG_NOTICE,
4205 			"FRU Commands:  print read write upgEkey edit internaluse get");
4206 } /* ipmi_fru_help() */
4207 
4208 /* ipmi_fru_read_internal_use -  print internal use are in hex or file
4209 *
4210 * @intf:   ipmi interface
4211 * @id:     fru id
4212 *
4213 * returns -1 on error
4214 * returns 0 if successful
4215 * returns 1 if device not present
4216 */
4217 static int
4218 ipmi_fru_read_internal_use(struct ipmi_intf * intf, uint8_t id, char * pFileName)
4219 {
4220 	struct fru_info fru;
4221 	uint16_t size;
4222 	uint16_t offset;
4223 	int rc = 0;
4224 
4225 	rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset);
4226 
4227 	if(rc == 0)
4228 	{
4229 		uint8_t * frubuf;
4230 
4231 		lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset);
4232 		printf(          "Internal Use Area Size  : %i\n", size);
4233 
4234 		frubuf = malloc( size );
4235 		if(frubuf)
4236 		{
4237 			rc = read_fru_area_section(intf, &fru, id, offset, size, frubuf);
4238 
4239 			if(rc == 0)
4240 			{
4241 				if(pFileName == NULL)
4242 				{
4243 					uint16_t counter;
4244 					for(counter = 0; counter < size; counter ++)
4245 					{
4246 						if((counter % 16) == 0)
4247 							printf("\n%02i- ", (counter / 16));
4248 						printf("%02X ", frubuf[counter]);
4249 					}
4250 				}
4251 				else
4252 				{
4253 					FILE * pFile;
4254 					pFile = fopen(pFileName,"wb");
4255 					if (pFile)
4256 					{
4257 						fwrite(frubuf, size, 1, pFile);
4258 						printf("Done\n");
4259 					}
4260 					else
4261 					{
4262 						lprintf(LOG_ERR, "Error opening file %s\n", pFileName);
4263 						free(frubuf);
4264 						frubuf = NULL;
4265 						return -1;
4266 					}
4267 					fclose(pFile);
4268 				}
4269 			}
4270 			printf("\n");
4271 
4272 			free(frubuf);
4273 			frubuf = NULL;
4274 		}
4275 
4276 	}
4277 	else
4278 	{
4279 		lprintf(LOG_ERR, "Cannot access internal use area");
4280 	}
4281 	return 0;
4282 }
4283 
4284 /* ipmi_fru_write_internal_use   -  print internal use are in hex or file
4285 *
4286 * @intf:   ipmi interface
4287 * @id:     fru id
4288 *
4289 * returns -1 on error
4290 * returns 0 if successful
4291 * returns 1 if device not present
4292 */
4293 static int
4294 ipmi_fru_write_internal_use(struct ipmi_intf * intf, uint8_t id, char * pFileName)
4295 {
4296 	struct fru_info fru;
4297 	uint16_t size;
4298 	uint16_t offset;
4299 	int rc = 0;
4300 
4301 	rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset);
4302 
4303 	if(rc == 0)
4304 	{
4305 		uint8_t * frubuf;
4306 		FILE * fp;
4307 		uint32_t fileLength = 0;
4308 
4309 		lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset);
4310 		printf(            "Internal Use Area Size  : %i\n", size);
4311 
4312 		fp = fopen(pFileName, "r");
4313 
4314 		if(fp)
4315 		{
4316 			/* Retreive file length, check if it's fits the Eeprom Size */
4317 			fseek(fp, 0 ,SEEK_END);
4318 			fileLength = ftell(fp);
4319 
4320 			lprintf(LOG_ERR, "File Size: %i", fileLength);
4321 			lprintf(LOG_ERR, "Area Size: %i", size);
4322 			if(fileLength != size)
4323 			{
4324 				lprintf(LOG_ERR, "File size does not fit Eeprom Size");
4325 				fclose(fp);
4326 				fp = NULL;
4327 			}
4328 			else
4329 			{
4330 				fseek(fp, 0 ,SEEK_SET);
4331 			}
4332 		}
4333 
4334 		if(fp)
4335 		{
4336 			frubuf = malloc( size );
4337 			if(frubuf)
4338 			{
4339 				uint16_t fru_read_size;
4340 				fru_read_size = fread(frubuf, 1, size, fp);
4341 
4342 				if(fru_read_size == size)
4343 				{
4344 					rc = write_fru_area(intf, &fru, id, 0, offset, size, frubuf);
4345 
4346 					if(rc == 0)
4347 					{
4348 						lprintf(LOG_INFO, "Done\n");
4349 					}
4350 				}
4351 				else
4352 				{
4353 					lprintf(LOG_ERR, "Unable to read file: %i\n", fru_read_size);
4354 				}
4355 
4356 				free(frubuf);
4357 				frubuf = NULL;
4358 			}
4359 			fclose(fp);
4360 			fp = NULL;
4361 		}
4362 	}
4363 	else
4364 	{
4365 		lprintf(LOG_ERR, "Cannot access internal use area");
4366 	}
4367 	return 0;
4368 }
4369 
4370 int
4371 ipmi_fru_main(struct ipmi_intf * intf, int argc, char ** argv)
4372 {
4373 	int rc = 0;
4374 	uint8_t fru_id = 0;
4375 
4376 	if (argc < 1) {
4377 		rc = ipmi_fru_print_all(intf);
4378 	}
4379 	else if (strncmp(argv[0], "help", 4) == 0) {
4380 		ipmi_fru_help();
4381 		return 0;
4382 	}
4383 	else if (strncmp(argv[0], "print", 5) == 0 ||
4384 		strncmp(argv[0], "list", 4) == 0) {
4385 		if (argc > 1) {
4386 			if (strcmp(argv[1], "help") == 0) {
4387 				lprintf(LOG_NOTICE, "fru print [fru id] - print information about FRU(s)");
4388 				return 0;
4389 			}
4390 
4391 			if (is_fru_id(argv[1], &fru_id) != 0)
4392 				return (-1);
4393 
4394 			rc = __ipmi_fru_print(intf, fru_id);
4395 		} else {
4396 			rc = ipmi_fru_print_all(intf);
4397 		}
4398 	}
4399 	else if (!strncmp(argv[0], "read", 5)) {
4400 		if (argc > 1 && strcmp(argv[1], "help") == 0) {
4401 			ipmi_fru_read_help();
4402 			return 0;
4403 		} else if (argc < 3) {
4404 			lprintf(LOG_ERR, "Not enough parameters given.");
4405 			ipmi_fru_read_help();
4406 			return (-1);
4407 		}
4408 
4409 		if (is_fru_id(argv[1], &fru_id) != 0)
4410 			return (-1);
4411 
4412 		/* There is a file name in the parameters */
4413 		if (is_valid_filename(argv[2]) != 0)
4414 				return (-1);
4415 
4416 		if (verbose) {
4417 			printf("FRU ID           : %d\n", fru_id);
4418 			printf("FRU File         : %s\n", argv[2]);
4419 		}
4420 		/* TODO - rc is missing */
4421 		ipmi_fru_read_to_bin(intf, argv[2], fru_id);
4422 	}
4423 	else if (!strncmp(argv[0], "write", 5)) {
4424 		if (argc > 1 && strcmp(argv[1], "help") == 0) {
4425 			ipmi_fru_write_help();
4426 			return 0;
4427 		} else if (argc < 3) {
4428 			lprintf(LOG_ERR, "Not enough parameters given.");
4429 			ipmi_fru_write_help();
4430 			return (-1);
4431 		}
4432 
4433 		if (is_fru_id(argv[1], &fru_id) != 0)
4434 			return (-1);
4435 
4436 		/* There is a file name in the parameters */
4437 		if (is_valid_filename(argv[2]) != 0)
4438 				return (-1);
4439 
4440 		if (verbose) {
4441 			printf("FRU ID           : %d\n", fru_id);
4442 			printf("FRU File         : %s\n", argv[2]);
4443 		}
4444 		/* TODO - rc is missing */
4445 		ipmi_fru_write_from_bin(intf, argv[2], fru_id);
4446 	}
4447 	else if (!strncmp(argv[0], "upgEkey", 7)) {
4448 		if (argc > 1 && strcmp(argv[1], "help") == 0) {
4449 			ipmi_fru_upgekey_help();
4450 			return 0;
4451 		} else if (argc < 3) {
4452 			lprintf(LOG_ERR, "Not enough parameters given.");
4453 			ipmi_fru_upgekey_help();
4454 			return (-1);
4455 		}
4456 
4457 		if (is_fru_id(argv[1], &fru_id) != 0)
4458 			return (-1);
4459 
4460 		/* There is a file name in the parameters */
4461 		if (is_valid_filename(argv[2]) != 0)
4462 				return (-1);
4463 
4464 		rc = ipmi_fru_upg_ekeying(intf, argv[2], fru_id);
4465 	}
4466 	else if (!strncmp(argv[0], "internaluse", 11)) {
4467 		if (argc > 1 && strcmp(argv[1], "help") == 0) {
4468 			ipmi_fru_internaluse_help();
4469 			return 0;
4470 		}
4471 
4472 		if ( (argc >= 3) && (!strncmp(argv[2], "info", 4)) ) {
4473 
4474 			if (is_fru_id(argv[1], &fru_id) != 0)
4475 				return (-1);
4476 
4477 			rc = ipmi_fru_info_internal_use(intf, fru_id);
4478 		}
4479 		else if ( (argc >= 3) && (!strncmp(argv[2], "print", 5)) ) {
4480 
4481 			if (is_fru_id(argv[1], &fru_id) != 0)
4482 				return (-1);
4483 
4484 			rc = ipmi_fru_read_internal_use(intf, fru_id, NULL);
4485 		}
4486 		else if ( (argc >= 4) && (!strncmp(argv[2], "read", 4)) ) {
4487 
4488 			if (is_fru_id(argv[1], &fru_id) != 0)
4489 				return (-1);
4490 
4491 			/* There is a file name in the parameters */
4492 			if (is_valid_filename(argv[3]) != 0)
4493 					return (-1);
4494 
4495 			lprintf(LOG_DEBUG, "FRU ID           : %d", fru_id);
4496 			lprintf(LOG_DEBUG, "FRU File         : %s", argv[3]);
4497 
4498 			rc = ipmi_fru_read_internal_use(intf, fru_id, argv[3]);
4499 		}
4500 		else if ( (argc >= 4) && (!strncmp(argv[2], "write", 5)) ) {
4501 
4502 			if (is_fru_id(argv[1], &fru_id) != 0)
4503 				return (-1);
4504 
4505 			/* There is a file name in the parameters */
4506 			if (is_valid_filename(argv[3]) != 0)
4507 					return (-1);
4508 
4509 			lprintf(LOG_DEBUG, "FRU ID           : %d", fru_id);
4510 			lprintf(LOG_DEBUG, "FRU File         : %s", argv[3]);
4511 
4512 			rc = ipmi_fru_write_internal_use(intf, fru_id, argv[3]);
4513 		} else {
4514 			lprintf(LOG_ERR,
4515 					"Either unknown command or not enough parameters given.");
4516 			ipmi_fru_internaluse_help();
4517 			return (-1);
4518 		}
4519 	}
4520 	else if (!strncmp(argv[0], "edit", 4)) {
4521 		if (argc > 1 && strcmp(argv[1], "help") == 0) {
4522 			ipmi_fru_edit_help();
4523 			return 0;
4524 		} else if (argc < 2) {
4525 			lprintf(LOG_ERR, "Not enough parameters given.");
4526 			ipmi_fru_edit_help();
4527 			return (-1);
4528 		}
4529 
4530 		if (argc >= 2) {
4531 			if (is_fru_id(argv[1], &fru_id) != 0)
4532 				return (-1);
4533 
4534 			if (verbose) {
4535 				printf("FRU ID           : %d\n", fru_id);
4536 			}
4537 		} else {
4538 			printf("Using default FRU ID: %d\n", fru_id);
4539 		}
4540 
4541 		if (argc >= 3) {
4542 			if (!strncmp(argv[2], "field", 5)) {
4543 				if (argc != 6) {
4544 					lprintf(LOG_ERR, "Not enough parameters given.");
4545 					ipmi_fru_edit_help();
4546 					return (-1);
4547 				}
4548 				rc = ipmi_fru_set_field_string(intf, fru_id, *argv[3], *argv[4],
4549 						(char *) argv[5]);
4550 			} else if (!strncmp(argv[2], "oem", 3)) {
4551 				rc = ipmi_fru_edit_multirec(intf, fru_id, argc, argv);
4552 			} else {
4553 				lprintf(LOG_ERR, "Invalid command: %s", argv[2]);
4554 				ipmi_fru_edit_help();
4555 				return (-1);
4556 			}
4557 		} else {
4558 			rc = ipmi_fru_edit_multirec(intf, fru_id, argc, argv);
4559 		}
4560 	}
4561 	else if (!strncmp(argv[0], "get", 4)) {
4562 		if (argc > 1 && (strncmp(argv[1], "help", 4) == 0)) {
4563 			ipmi_fru_get_help();
4564 			return 0;
4565 		} else if (argc < 2) {
4566 			lprintf(LOG_ERR, "Not enough parameters given.");
4567 			ipmi_fru_get_help();
4568 			return (-1);
4569 		}
4570 
4571 		if (argc >= 2) {
4572 			if (is_fru_id(argv[1], &fru_id) != 0)
4573 				return (-1);
4574 
4575 			if (verbose) {
4576 				printf("FRU ID           : %d\n", fru_id);
4577 			}
4578 		} else {
4579 			printf("Using default FRU ID: %d\n", fru_id);
4580 		}
4581 
4582 		if (argc >= 3) {
4583 			if (!strncmp(argv[2], "oem", 3)) {
4584 				rc = ipmi_fru_get_multirec(intf, fru_id, argc, argv);
4585 			} else {
4586 				lprintf(LOG_ERR, "Invalid command: %s", argv[2]);
4587 				ipmi_fru_get_help();
4588 				return (-1);
4589 			}
4590 		} else {
4591 			rc = ipmi_fru_get_multirec(intf, fru_id, argc, argv);
4592 		}
4593 	}
4594 	else {
4595 		lprintf(LOG_ERR, "Invalid FRU command: %s", argv[0]);
4596 		ipmi_fru_help();
4597 		return (-1);
4598 	}
4599 
4600 	return rc;
4601 }
4602 
4603 /* ipmi_fru_set_field_string -  Set a field string to a new value, Need to be the same size.  If
4604 *                              size if not equal, the function ipmi_fru_set_field_string_rebuild
4605 *                              will be called.
4606 *
4607 * @intf:       ipmi interface
4608 * @id:         fru id
4609 * @f_type:    Type of the Field : c=Chassis b=Board p=Product
4610 * @f_index:   findex of the field, zero indexed.
4611 * @f_string:  NULL terminated string
4612 *
4613 * returns -1 on error
4614 * returns 1 if successful
4615 */
4616 static int
4617 ipmi_fru_set_field_string(struct ipmi_intf * intf, uint8_t fruId, uint8_t
4618 f_type, uint8_t f_index, char *f_string)
4619 {
4620 	struct ipmi_rs *rsp;
4621 	struct ipmi_rq req;
4622 
4623 	struct fru_info fru;
4624 	struct fru_header header;
4625 	uint8_t msg_data[4];
4626 	uint8_t checksum;
4627 	int i = 0;
4628 	int rc = 1;
4629 	uint8_t *fru_data = NULL;
4630 	uint8_t *fru_area = NULL;
4631 	uint32_t fru_field_offset, fru_field_offset_tmp;
4632 	uint32_t fru_section_len, header_offset;
4633 
4634 	memset(msg_data, 0, 4);
4635 	msg_data[0] = fruId;
4636 
4637 	memset(&req, 0, sizeof(req));
4638 	req.msg.netfn = IPMI_NETFN_STORAGE;
4639 	req.msg.cmd = GET_FRU_INFO;
4640 	req.msg.data = msg_data;
4641 	req.msg.data_len = 1;
4642 
4643 	rsp = intf->sendrecv(intf, &req);
4644 	if (rsp == NULL) {
4645 		printf(" Device not present (No Response)\n");
4646 		rc = (-1);
4647 		goto ipmi_fru_set_field_string_out;
4648 	}
4649 	if (rsp->ccode > 0) {
4650 		printf(" Device not present (%s)\n",
4651 			val2str(rsp->ccode, completion_code_vals));
4652 		rc = (-1);
4653 		goto ipmi_fru_set_field_string_out;
4654 	}
4655 
4656 	memset(&fru, 0, sizeof(fru));
4657 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
4658 	fru.access = rsp->data[2] & 0x1;
4659 
4660 	if (fru.size < 1) {
4661 		printf(" Invalid FRU size %d", fru.size);
4662 		rc = (-1);
4663 		goto ipmi_fru_set_field_string_out;
4664 	}
4665 	/*
4666 	* retrieve the FRU header
4667 	*/
4668 	msg_data[0] = fruId;
4669 	msg_data[1] = 0;
4670 	msg_data[2] = 0;
4671 	msg_data[3] = 8;
4672 
4673 	memset(&req, 0, sizeof(req));
4674 	req.msg.netfn = IPMI_NETFN_STORAGE;
4675 	req.msg.cmd = GET_FRU_DATA;
4676 	req.msg.data = msg_data;
4677 	req.msg.data_len = 4;
4678 
4679 	rsp = intf->sendrecv(intf, &req);
4680 	if (rsp == NULL)
4681 	{
4682 		printf(" Device not present (No Response)\n");
4683 		rc = (-1);
4684 		goto ipmi_fru_set_field_string_out;
4685 	}
4686 	if (rsp->ccode > 0)
4687 	{
4688 		printf(" Device not present (%s)\n",
4689 				val2str(rsp->ccode, completion_code_vals));
4690 		rc = (-1);
4691 		goto ipmi_fru_set_field_string_out;
4692 	}
4693 
4694 	if (verbose > 1)
4695 		printbuf(rsp->data, rsp->data_len, "FRU DATA");
4696 
4697 	memcpy(&header, rsp->data + 1, 8);
4698 
4699 	if (header.version != 1)
4700 	{
4701 		printf(" Unknown FRU header version 0x%02x",
4702 			header.version);
4703 		rc = (-1);
4704 		goto ipmi_fru_set_field_string_out;
4705 	}
4706 
4707 	fru_data = malloc( fru.size );
4708 
4709 	if( fru_data == NULL )
4710 	{
4711 		printf("Out of memory!\n");
4712 		rc = (-1);
4713 		goto ipmi_fru_set_field_string_out;
4714 	}
4715 
4716 	/* Setup offset from the field type */
4717 
4718 	/* Chassis type field */
4719 	if (f_type == 'c' ) {
4720 		header_offset = (header.offset.chassis * 8);
4721 		read_fru_area(intf ,&fru, fruId, header_offset , 3 , fru_data);
4722 		fru_field_offset = 3;
4723 		fru_section_len = *(fru_data + 1) * 8;
4724 	}
4725 	/* Board type field */
4726 	else if (f_type == 'b' ) {
4727 		header_offset = (header.offset.board * 8);
4728 		read_fru_area(intf ,&fru, fruId, header_offset , 3 , fru_data);
4729 		fru_field_offset = 6;
4730 		fru_section_len = *(fru_data + 1) * 8;
4731 	}
4732 	/* Product type field */
4733 	else if (f_type == 'p' ) {
4734 		header_offset = (header.offset.product * 8);
4735 		read_fru_area(intf ,&fru, fruId, header_offset , 3 , fru_data);
4736 		fru_field_offset = 3;
4737 		fru_section_len = *(fru_data + 1) * 8;
4738 	}
4739 	else
4740 	{
4741 		printf("Wrong field type.");
4742 		rc = (-1);
4743 		goto ipmi_fru_set_field_string_out;
4744 	}
4745 	memset(fru_data, 0, fru.size);
4746 	if( read_fru_area(intf ,&fru, fruId, header_offset ,
4747 					fru_section_len , fru_data) < 0 )
4748 	{
4749 		rc = (-1);
4750 		goto ipmi_fru_set_field_string_out;
4751 	}
4752 	/* Convert index from character to decimal */
4753 	f_index= f_index - 0x30;
4754 
4755 	/*Seek to field index */
4756 	for (i=0; i <= f_index; i++) {
4757 		fru_field_offset_tmp = fru_field_offset;
4758 		if (fru_area != NULL) {
4759 			free(fru_area);
4760 			fru_area = NULL;
4761 		}
4762 		fru_area = (uint8_t *) get_fru_area_str(fru_data, &fru_field_offset);
4763 	}
4764 
4765 	if( (fru_area == NULL )  || strlen((const char *)fru_area) == 0 ) {
4766 		printf("Field not found !\n");
4767 		rc = (-1);
4768 		goto ipmi_fru_set_field_string_out;
4769 	}
4770 
4771 	if ( strlen((const char *)fru_area) == strlen((const char *)f_string) )
4772 	{
4773 		printf("Updating Field '%s' with '%s' ...\n", fru_area, f_string );
4774 		memcpy(fru_data + fru_field_offset_tmp + 1,
4775 								f_string, strlen(f_string));
4776 
4777 		checksum = 0;
4778 		/* Calculate Header Checksum */
4779 		for( i = header_offset; i < header_offset
4780 						+ fru_section_len - 1; i ++ )
4781 		{
4782 			checksum += fru_data[i];
4783 		}
4784 		checksum = (~checksum) + 1;
4785 		fru_data[header_offset + fru_section_len - 1] = checksum;
4786 
4787 		/* Write the updated section to the FRU data; source offset => 0 */
4788 		if( write_fru_area(intf, &fru, fruId, 0,
4789 				header_offset, fru_section_len, fru_data) < 0 )
4790 		{
4791 			printf("Write to FRU data failed.\n");
4792 			rc = (-1);
4793 			goto ipmi_fru_set_field_string_out;
4794 		}
4795 	}
4796 	else {
4797 		printf("String size are not equal, resizing fru to fit new string\n");
4798 		if(
4799 				ipmi_fru_set_field_string_rebuild(intf,fruId,fru,header,f_type,f_index,f_string)
4800 		)
4801 		{
4802 			rc = (-1);
4803 			goto ipmi_fru_set_field_string_out;
4804 		}
4805 	}
4806 
4807 	ipmi_fru_set_field_string_out:
4808 	if (fru_data != NULL) {
4809 		free(fru_data);
4810 		fru_data = NULL;
4811 	}
4812 	if (fru_area != NULL) {
4813 		free(fru_area);
4814 		fru_area = NULL;
4815 	}
4816 
4817 	return rc;
4818 }
4819 
4820 /*
4821 	This function can update a string within of the following section when the size is not equal:
4822 
4823 	Chassis
4824 	Product
4825 	Board
4826 */
4827 /* ipmi_fru_set_field_string_rebuild -  Set a field string to a new value, When size are not
4828 *                                      the same size.
4829 *
4830 *  This function can update a string within of the following section when the size is not equal:
4831 *
4832 *      - Chassis
4833 *      - Product
4834 *      - Board
4835 *
4836 * @intf:     ipmi interface
4837 * @fruId:    fru id
4838 * @fru:      info about fru
4839 * @header:   contain the header of the FRU
4840 * @f_type:   Type of the Field : c=Chassis b=Board p=Product
4841 * @f_index:  findex of the field, zero indexed.
4842 * @f_string: NULL terminated string
4843 *
4844 * returns -1 on error
4845 * returns 1 if successful
4846 */
4847 
4848 #define DBG_RESIZE_FRU
4849 static int
4850 ipmi_fru_set_field_string_rebuild(struct ipmi_intf * intf, uint8_t fruId,
4851 											struct fru_info fru, struct fru_header header,
4852 											uint8_t f_type, uint8_t f_index, char *f_string)
4853 {
4854 	int i = 0;
4855 	uint8_t *fru_data_old = NULL;
4856 	uint8_t *fru_data_new = NULL;
4857 	uint8_t *fru_area = NULL;
4858 	uint32_t fru_field_offset, fru_field_offset_tmp;
4859 	uint32_t fru_section_len, old_section_len, header_offset;
4860 	uint32_t chassis_offset, board_offset, product_offset;
4861 	uint32_t chassis_len, board_len, product_len, product_len_new;
4862 	int      num_byte_change = 0, padding_len = 0;
4863 	uint32_t counter;
4864 	unsigned char cksum;
4865 	int rc = 1;
4866 
4867 	fru_data_old = calloc( fru.size, sizeof(uint8_t) );
4868 
4869 	fru_data_new = malloc( fru.size );
4870 
4871 	if( fru_data_old == NULL || fru_data_new == NULL )
4872 	{
4873 		printf("Out of memory!\n");
4874 		rc = (-1);
4875 		goto ipmi_fru_set_field_string_rebuild_out;
4876 	}
4877 
4878 	/*************************
4879 	1) Read ALL FRU */
4880 	printf("Read All FRU area\n");
4881 	printf("Fru Size       : %u bytes\n", fru.size);
4882 
4883 	/* Read current fru data */
4884 	read_fru_area(intf ,&fru, fruId, 0, fru.size , fru_data_old);
4885 
4886 	#ifdef DBG_RESIZE_FRU
4887 	printf("Copy to new FRU\n");
4888 	#endif
4889 
4890 	/*************************
4891 	2) Copy all FRU to new FRU */
4892 	memcpy(fru_data_new, fru_data_old, fru.size);
4893 
4894 	/* Build location of all modifiable components */
4895 	chassis_offset = (header.offset.chassis * 8);
4896 	board_offset   = (header.offset.board   * 8);
4897 	product_offset = (header.offset.product * 8);
4898 
4899 	/* Retrieve length of all modifiable components */
4900 	chassis_len    =  *(fru_data_old + chassis_offset + 1) * 8;
4901 	board_len      =  *(fru_data_old + board_offset   + 1) * 8;
4902 	product_len    =  *(fru_data_old + product_offset + 1) * 8;
4903 	product_len_new = product_len;
4904 
4905 	/* Chassis type field */
4906 	if (f_type == 'c' )
4907 	{
4908 		header_offset    = chassis_offset;
4909 		fru_field_offset = chassis_offset + 3;
4910 		fru_section_len  = chassis_len;
4911 	}
4912 	/* Board type field */
4913 	else if (f_type == 'b' )
4914 	{
4915 		header_offset    = board_offset;
4916 		fru_field_offset = board_offset + 6;
4917 		fru_section_len  = board_len;
4918 	}
4919 	/* Product type field */
4920 	else if (f_type == 'p' )
4921 	{
4922 		header_offset    = product_offset;
4923 		fru_field_offset = product_offset + 3;
4924 		fru_section_len  = product_len;
4925 	}
4926 	else
4927 	{
4928 		printf("Wrong field type.");
4929 		rc = (-1);
4930 		goto ipmi_fru_set_field_string_rebuild_out;
4931 	}
4932 
4933 	/* Keep length for future old section display */
4934 	old_section_len = fru_section_len;
4935 
4936 	/*************************
4937 	3) Seek to field index */
4938 	for (i = 0;i <= f_index; i++) {
4939 		fru_field_offset_tmp = fru_field_offset;
4940 		if (fru_area != NULL) {
4941 			free(fru_area);
4942 			fru_area = NULL;
4943 		}
4944 		fru_area = (uint8_t *) get_fru_area_str(fru_data_old, &fru_field_offset);
4945 	}
4946 
4947 	if( (fru_area == NULL )  || strlen((const char *)fru_area) == 0 ) {
4948 		printf("Field not found (1)!\n");
4949 		rc = (-1);
4950 		goto ipmi_fru_set_field_string_rebuild_out;
4951 	}
4952 
4953 	#ifdef DBG_RESIZE_FRU
4954 	printf("Section Length: %u\n", fru_section_len);
4955 	#endif
4956 
4957 	/*************************
4958 	4) Check number of padding bytes and bytes changed */
4959 	for(counter = 2; counter < fru_section_len; counter ++)
4960 	{
4961 		if(*(fru_data_old + (header_offset + fru_section_len - counter)) == 0)
4962 			padding_len ++;
4963 		else
4964 			break;
4965 	}
4966 	num_byte_change = strlen(f_string) - strlen(fru_area);
4967 
4968 	#ifdef DBG_RESIZE_FRU
4969 	printf("Padding Length: %u\n", padding_len);
4970 	printf("NumByte Change: %i\n", num_byte_change);
4971 	printf("Start SecChnge: %x\n", *(fru_data_old + fru_field_offset_tmp));
4972 	printf("End SecChnge  : %x\n", *(fru_data_old + fru_field_offset_tmp + strlen(f_string) + 1));
4973 
4974 	printf("Start Section : %x\n", *(fru_data_old + header_offset));
4975 	printf("End Sec wo Pad: %x\n", *(fru_data_old + header_offset + fru_section_len - 2 - padding_len));
4976 	printf("End Section   : %x\n", *(fru_data_old + header_offset + fru_section_len - 1));
4977 	#endif
4978 
4979 	/* Calculate New Padding Length */
4980 	padding_len -= num_byte_change;
4981 
4982 	#ifdef DBG_RESIZE_FRU
4983 	printf("New Padding Length: %i\n", padding_len);
4984 	#endif
4985 
4986 	/*************************
4987 	5) Check if section must be resize.  This occur when padding length is not between 0 and 7 */
4988 	if( (padding_len < 0) || (padding_len >= 8))
4989 	{
4990 		uint32_t remaining_offset = ((header.offset.product * 8) + product_len);
4991 		int change_size_by_8;
4992 
4993 		if(padding_len >= 8)
4994 		{
4995 			/* Section must be set smaller */
4996 			change_size_by_8 = ((padding_len) / 8) * (-1);
4997 		}
4998 		else
4999 		{
5000 			/* Section must be set bigger */
5001 			change_size_by_8 = 1 + (((padding_len+1) / 8) * (-1));
5002 		}
5003 
5004 		/* Recalculate padding and section length base on the section changes */
5005 		fru_section_len += (change_size_by_8 * 8);
5006 		padding_len     += (change_size_by_8 * 8);
5007 
5008 		#ifdef DBG_RESIZE_FRU
5009 		printf("change_size_by_8: %i\n", change_size_by_8);
5010 		printf("New Padding Length: %i\n", padding_len);
5011 		printf("change_size_by_8: %i\n", change_size_by_8);
5012 		printf("header.offset.board: %i\n", header.offset.board);
5013 		#endif
5014 
5015 		/* Must move sections */
5016 		/* Section that can be modified are as follow
5017 			Chassis
5018 			Board
5019 			product */
5020 
5021 		/* Chassis type field */
5022 		if (f_type == 'c' )
5023 		{
5024 			printf("Moving Section Chassis, from %i to %i\n",
5025 						((header.offset.board) * 8),
5026 						((header.offset.board + change_size_by_8) * 8)
5027 					);
5028 			memcpy(
5029 						(fru_data_new + ((header.offset.board + change_size_by_8) * 8)),
5030 						(fru_data_old + (header.offset.board) * 8),
5031 						board_len
5032 					);
5033 			header.offset.board   += change_size_by_8;
5034 		}
5035 		/* Board type field */
5036 		if ((f_type == 'c' ) || (f_type == 'b' ))
5037 		{
5038 			printf("Moving Section Product, from %i to %i\n",
5039 						((header.offset.product) * 8),
5040 						((header.offset.product + change_size_by_8) * 8)
5041 					);
5042 			memcpy(
5043 						(fru_data_new + ((header.offset.product + change_size_by_8) * 8)),
5044 						(fru_data_old + (header.offset.product) * 8),
5045 						product_len
5046 					);
5047 			header.offset.product += change_size_by_8;
5048 		}
5049 
5050 		/* Adjust length of the section */
5051 		if (f_type == 'c')
5052 		{
5053 			*(fru_data_new + chassis_offset + 1) += change_size_by_8;
5054 		}
5055 		else if( f_type == 'b')
5056 		{
5057 			*(fru_data_new + board_offset + 1)   += change_size_by_8;
5058 		}
5059 		else if( f_type == 'p')
5060 		{
5061 			*(fru_data_new + product_offset + 1) += change_size_by_8;
5062 			product_len_new = *(fru_data_new + product_offset + 1) * 8;
5063 		}
5064 
5065 		/* Rebuild Header checksum */
5066 		{
5067 			unsigned char * pfru_header = (unsigned char *) &header;
5068 			header.checksum = 0;
5069 			for(counter = 0; counter < (sizeof(struct fru_header) -1); counter ++)
5070 			{
5071 				header.checksum += pfru_header[counter];
5072 			}
5073 			header.checksum = (0 - header.checksum);
5074 			memcpy(fru_data_new, pfru_header, sizeof(struct fru_header));
5075 		}
5076 
5077 		/* Move remaining sections in 1 copy */
5078 		printf("Moving Remaining Bytes (Multi-Rec , etc..), from %i to %i\n",
5079 					remaining_offset,
5080 					((header.offset.product) * 8) + product_len_new
5081 				);
5082 		if(((header.offset.product * 8) + product_len_new - remaining_offset) < 0)
5083 		{
5084 			memcpy(
5085 						fru_data_new + (header.offset.product * 8) + product_len_new,
5086 						fru_data_old + remaining_offset,
5087 						fru.size - remaining_offset
5088 					);
5089 		}
5090 		else
5091 		{
5092 			memcpy(
5093 						fru_data_new + (header.offset.product * 8) + product_len_new,
5094 						fru_data_old + remaining_offset,
5095 						fru.size - ((header.offset.product * 8) + product_len_new)
5096 					);
5097 		}
5098 	}
5099 
5100 	/* Update only if it's fits padding length as defined in the spec, otherwise, it's an internal
5101 	error */
5102 	/*************************
5103 	6) Update Field and sections */
5104 	if( (padding_len >=0) && (padding_len < 8))
5105 	{
5106 		/* Do not requires any change in other section */
5107 
5108 		/* Change field length */
5109 		printf(
5110 			"Updating Field : '%s' with '%s' ... (Length from '%d' to '%d')\n",
5111 			fru_area, f_string,
5112 			(int)*(fru_data_old + fru_field_offset_tmp),
5113 			(int)(0xc0 + strlen(f_string)));
5114 		*(fru_data_new + fru_field_offset_tmp) = (0xc0 + strlen(f_string));
5115 		memcpy(fru_data_new + fru_field_offset_tmp + 1, f_string, strlen(f_string));
5116 
5117 		/* Copy remaing bytes in section */
5118 #ifdef DBG_RESIZE_FRU
5119 		printf("Copying remaining of sections: %d \n",
5120 		 (int)((fru_data_old + header_offset + fru_section_len - 1) -
5121 		 (fru_data_old + fru_field_offset_tmp + strlen(f_string) + 1)));
5122 #endif
5123 
5124 		memcpy((fru_data_new + fru_field_offset_tmp + 1 +
5125 			strlen(f_string)),
5126 			(fru_data_old + fru_field_offset_tmp + 1 +
5127 			strlen(fru_area)),
5128 		((fru_data_old + header_offset + fru_section_len - 1) -
5129 		(fru_data_old + fru_field_offset_tmp + strlen(f_string) + 1)));
5130 
5131 		/* Add Padding if required */
5132 		for(counter = 0; counter < padding_len; counter ++)
5133 		{
5134 			*(fru_data_new + header_offset + fru_section_len - 1 -
5135 			  padding_len + counter) = 0;
5136 		}
5137 
5138 		/* Calculate New Checksum */
5139 		cksum = 0;
5140 		for( counter = 0; counter <fru_section_len-1; counter ++ )
5141 		{
5142 			cksum += *(fru_data_new + header_offset + counter);
5143 		}
5144 		*(fru_data_new + header_offset + fru_section_len - 1) = (0 - cksum);
5145 
5146 		#ifdef DBG_RESIZE_FRU
5147 		printf("Calculate New Checksum: %x\n", (0 - cksum));
5148 		#endif
5149 
5150 		/****** ENABLE to show section modified before and after ********/
5151 		#if 0
5152 		printf("Section: ");
5153 		for( counter = 0; counter <old_section_len; counter ++ )
5154 		{
5155 			if((counter %16) == 0)
5156 			{
5157 				printf("\n");
5158 			}
5159 			printf( "%02X ", *(fru_data_old + header_offset + counter) );
5160 		}
5161 		printf("\n");
5162 
5163 		printf("Section: ");
5164 		for( counter = 0; counter <fru_section_len; counter ++ )
5165 		{
5166 			if((counter %16) == 0)
5167 			{
5168 				printf("\n");
5169 			}
5170 			printf( "%02X ", *(fru_data_new + header_offset + counter) );
5171 		}
5172 		printf("\n");
5173 		#endif
5174 	}
5175 	else
5176 	{
5177 		printf( "Internal error, padding length %i (must be from 0 to 7) ", padding_len );
5178 		rc = (-1);
5179 		goto ipmi_fru_set_field_string_rebuild_out;
5180 	}
5181 
5182 	/*************************
5183 	7) Finally, write new FRU */
5184 	printf("Writing new FRU.\n");
5185 	if( write_fru_area( intf, &fru, fruId, 0, 0, fru.size, fru_data_new ) < 0 )
5186 	{
5187 		printf("Write to FRU data failed.\n");
5188 		rc = (-1);
5189 		goto ipmi_fru_set_field_string_rebuild_out;
5190 	}
5191 
5192 	printf("Done.\n");
5193 
5194 	ipmi_fru_set_field_string_rebuild_out:
5195 	if (fru_area != NULL) {
5196 		free(fru_area);
5197 		fru_area = NULL;
5198 	}
5199 	if (fru_data_new != NULL) {
5200 		free(fru_data_new);
5201 		fru_data_new = NULL;
5202 	}
5203 	if (fru_data_old != NULL) {
5204 		free(fru_data_old);
5205 		fru_data_old = NULL;
5206 	}
5207 
5208 	return rc;
5209 }
5210