xref: /openbmc/ipmitool/lib/ipmi_fwum.c (revision 531569ec)
1 /*
2  * Copyright (c) 2004 Kontron Canada, Inc.  All Rights Reserved.
3  *
4  * Base on code from
5  * Copyright (c) 2003 Sun Microsystems, Inc.  All Rights Reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * Redistribution of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  *
14  * Redistribution in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in the
16  * documentation and/or other materials provided with the distribution.
17  *
18  * Neither the name of Sun Microsystems, Inc. or the names of
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * This software is provided "AS IS," without a warranty of any kind.
23  * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
24  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
25  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
26  * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
27  * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
28  * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
29  * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
30  * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
31  * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
32  * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
33  * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
34  */
35 
36 #include <string.h>
37 #include <math.h>
38 #include <time.h>
39 #include <unistd.h>
40 
41 #include <ipmitool/log.h>
42 #include <ipmitool/helper.h>
43 #include <ipmitool/ipmi.h>
44 #include <ipmitool/ipmi_fwum.h>
45 #include <ipmitool/ipmi_intf.h>
46 #include <ipmitool/ipmi_mc.h>
47 
48 extern int verbose;
49 unsigned char firmBuf[1024*512];
50 tKFWUM_SaveFirmwareInfo save_fw_nfo;
51 
52 int KfwumGetFileSize(const char *pFileName,
53 		unsigned long *pFileSize);
54 int KfwumSetupBuffersFromFile(const char *pFileName,
55 		unsigned long fileSize);
56 void KfwumShowProgress(const char *task, unsigned long current,
57 		unsigned long total);
58 unsigned short KfwumCalculateChecksumPadding(unsigned char *pBuffer,
59 		unsigned long totalSize);
60 int KfwumGetInfo(struct ipmi_intf *intf, unsigned char output,
61 		unsigned char *pNumBank);
62 int KfwumGetDeviceInfo(struct ipmi_intf *intf,
63 		unsigned char output, tKFWUM_BoardInfo *pBoardInfo);
64 int KfwumGetStatus(struct ipmi_intf *intf);
65 int KfwumManualRollback(struct ipmi_intf *intf);
66 int KfwumStartFirmwareImage(struct ipmi_intf *intf,
67 		unsigned long length, unsigned short padding);
68 int KfwumSaveFirmwareImage(struct ipmi_intf *intf,
69 		unsigned char sequenceNumber, unsigned long address,
70 		unsigned char *pFirmBuf, unsigned char *pInBufLength);
71 int KfwumFinishFirmwareImage(struct ipmi_intf *intf,
72 		tKFWUM_InFirmwareInfo firmInfo);
73 int KfwumUploadFirmware(struct ipmi_intf *intf,
74 		unsigned char *pBuffer, unsigned long totalSize);
75 int KfwumStartFirmwareUpgrade(struct ipmi_intf *intf);
76 int KfwumGetInfoFromFirmware(unsigned char *pBuf,
77 		unsigned long bufSize, tKFWUM_InFirmwareInfo *pInfo);
78 void KfwumFixTableVersionForOldFirmware(tKFWUM_InFirmwareInfo *pInfo);
79 int KfwumGetTraceLog(struct ipmi_intf *intf);
80 int ipmi_kfwum_checkfwcompat(tKFWUM_BoardInfo boardInfo,
81 		tKFWUM_InFirmwareInfo firmInfo);
82 
83 int ipmi_fwum_fwupgrade(struct ipmi_intf *intf, char *file, int action);
84 int ipmi_fwum_info(struct ipmi_intf *intf);
85 int ipmi_fwum_status(struct ipmi_intf *intf);
86 void printf_kfwum_help(void);
87 void printf_kfwum_info(tKFWUM_BoardInfo boardInfo,
88 		tKFWUM_InFirmwareInfo firmInfo);
89 
90 /* String table */
91 /* Must match eFWUM_CmdId */
92 const char *CMD_ID_STRING[] = {
93 	"GetFwInfo",
94 	"KickWatchdog",
95 	"GetLastAnswer",
96 	"BootHandshake",
97 	"ReportStatus",
98 	"CtrlIPMBLine",
99 	"SetFwState",
100 	"GetFwStatus",
101 	"GetSpiMemStatus",
102 	"StartFwUpdate",
103 	"StartFwImage",
104 	"SaveFwImage",
105 	"FinishFwImage",
106 	"ReadFwImage",
107 	"ManualRollback",
108 	"GetTraceLog"
109 };
110 
111 const char *EXT_CMD_ID_STRING[] = {
112 	"FwUpgradeLock",
113 	"ProcessFwUpg",
114 	"ProcessFwRb",
115 	"WaitHSAfterUpg",
116 	"WaitFirstHSUpg",
117 	"FwInfoStateChange"
118 };
119 
120 const char *CMD_STATE_STRING[] = {
121 	"Invalid",
122 	"Begin",
123 	"Progress",
124 	"Completed"
125 };
126 
127 const struct valstr bankStateValS[] = {
128 	{ 0x00, "Not programmed" },
129 	{ 0x01, "New firmware" },
130 	{ 0x02, "Wait for validation" },
131 	{ 0x03, "Last Known Good" },
132 	{ 0x04, "Previous Good" }
133 };
134 
135 /* ipmi_fwum_main  -  entry point for this ipmitool mode
136  *
137  * @intf: ipmi interface
138  * @arc: number of arguments
139  * @argv: point to argument array
140  *
141  * returns 0 on success
142  * returns -1 on error
143  */
144 int
145 ipmi_fwum_main(struct ipmi_intf *intf, int argc, char **argv)
146 {
147 	int rc = 0;
148 	printf("FWUM extension Version %d.%d\n", VER_MAJOR, VER_MINOR);
149 	if (argc < 1) {
150 		lprintf(LOG_ERR, "Not enough parameters given.");
151 		printf_kfwum_help();
152 		return (-1);
153 	}
154 	if (strncmp(argv[0], "help", 4) == 0) {
155 		printf_kfwum_help();
156 		rc = 0;
157 	} else if (strncmp(argv[0], "info", 4) == 0) {
158 		rc = ipmi_fwum_info(intf);
159 	} else if (strncmp(argv[0], "status", 6) == 0) {
160 		rc = ipmi_fwum_status(intf);
161 	} else if (strncmp(argv[0], "rollback", 8) == 0) {
162 		rc = KfwumManualRollback(intf);
163 	} else if (strncmp(argv[0], "download", 8) == 0) {
164 		if ((argc < 2) || (strlen(argv[1]) < 1)) {
165 			lprintf(LOG_ERR,
166 					"Path and file name must be specified.");
167 			return (-1);
168 		}
169 		printf("Firmware File Name         : %s\n", argv[1]);
170 		rc = ipmi_fwum_fwupgrade(intf, argv[1], 0);
171 	} else if (strncmp(argv[0], "upgrade", 7) == 0) {
172 		if ((argc >= 2) && (strlen(argv[1]) > 0)) {
173 			printf("Upgrading using file name %s\n", argv[1]);
174 			rc = ipmi_fwum_fwupgrade(intf, argv[1], 1);
175 		} else {
176 			rc = KfwumStartFirmwareUpgrade(intf);
177 		}
178 	} else if (strncmp(argv[0], "tracelog", 8) == 0) {
179 		rc = KfwumGetTraceLog(intf);
180 	} else {
181 		lprintf(LOG_ERR, "Invalid KFWUM command: %s", argv[0]);
182 		printf_kfwum_help();
183 		rc = (-1);
184 	}
185 	return rc;
186 }
187 
188 void
189 printf_kfwum_help(void)
190 {
191 	lprintf(LOG_NOTICE,
192 "KFWUM Commands:  info status download upgrade rollback tracelog");
193 }
194 
195 /*  private definitions and macros */
196 typedef enum eFWUM_CmdId
197 {
198 	KFWUM_CMD_ID_GET_FIRMWARE_INFO                         = 0,
199 	KFWUM_CMD_ID_KICK_IPMC_WATCHDOG                        = 1,
200 	KFWUM_CMD_ID_GET_LAST_ANSWER                           = 2,
201 	KFWUM_CMD_ID_BOOT_HANDSHAKE                            = 3,
202 	KFWUM_CMD_ID_REPORT_STATUS                             = 4,
203 	KFWUM_CMD_ID_GET_FIRMWARE_STATUS                       = 7,
204 	KFWUM_CMD_ID_START_FIRMWARE_UPDATE                     = 9,
205 	KFWUM_CMD_ID_START_FIRMWARE_IMAGE                      = 0x0a,
206 	KFWUM_CMD_ID_SAVE_FIRMWARE_IMAGE                       = 0x0b,
207 	KFWUM_CMD_ID_FINISH_FIRMWARE_IMAGE                     = 0x0c,
208 	KFWUM_CMD_ID_READ_FIRMWARE_IMAGE                       = 0x0d,
209 	KFWUM_CMD_ID_MANUAL_ROLLBACK                           = 0x0e,
210 	KFWUM_CMD_ID_GET_TRACE_LOG                             = 0x0f,
211 	KFWUM_CMD_ID_STD_MAX_CMD,
212 	KFWUM_CMD_ID_EXTENDED_CMD                              = 0xC0
213 }  tKFWUM_CmdId;
214 
215 int
216 ipmi_fwum_info(struct ipmi_intf *intf)
217 {
218 	tKFWUM_BoardInfo b_info;
219 	int rc = 0;
220 	unsigned char not_used;
221 	if (verbose) {
222 		printf("Getting Kontron FWUM Info\n");
223 	}
224 	if (KfwumGetDeviceInfo(intf, 1, &b_info) != 0) {
225 		rc = (-1);
226 	}
227 	if (KfwumGetInfo(intf, 1, &not_used) != 0) {
228 		rc = (-1);
229 	}
230 	return rc;
231 }
232 
233 int
234 ipmi_fwum_status(struct ipmi_intf *intf)
235 {
236 	if (verbose) {
237 		printf("Getting Kontron FWUM Status\n");
238 	}
239 	if (KfwumGetStatus(intf) != 0) {
240 		return (-1);
241 	}
242 	return 0;
243 }
244 
245 /* ipmi_fwum_fwupgrade - function implements download/upload of the firmware
246  * data received as parameters
247  *
248  * @file: fw file
249  * @action: 0 = download, 1 = upload/start upload
250  *
251  * returns 0 on success, otherwise (-1)
252  */
253 int
254 ipmi_fwum_fwupgrade(struct ipmi_intf *intf, char *file, int action)
255 {
256 	tKFWUM_BoardInfo b_info;
257 	tKFWUM_InFirmwareInfo fw_info = { 0 };
258 	unsigned short padding;
259 	unsigned long fsize = 0;
260 	unsigned char not_used;
261 	if (file == NULL) {
262 		lprintf(LOG_ERR, "No file given.");
263 		return (-1);
264 	}
265 	if (KfwumGetFileSize(file, &fsize) != 0) {
266 		return (-1);
267 	}
268 	if (KfwumSetupBuffersFromFile(file, fsize) != 0) {
269 		return (-1);
270 	}
271 	padding = KfwumCalculateChecksumPadding(firmBuf, fsize);
272 	if (KfwumGetInfoFromFirmware(firmBuf, fsize, &fw_info) != 0) {
273 		return (-1);
274 	}
275 	if (KfwumGetDeviceInfo(intf, 0, &b_info) != 0) {
276 		return (-1);
277 	}
278 	if (ipmi_kfwum_checkfwcompat(b_info, fw_info) != 0) {
279 		return (-1);
280 	}
281 	KfwumGetInfo(intf, 0, &not_used);
282 	printf_kfwum_info(b_info, fw_info);
283 	if (KfwumStartFirmwareImage(intf, fsize, padding) != 0) {
284 		return (-1);
285 	}
286 	if (KfwumUploadFirmware(intf, firmBuf, fsize) != 0) {
287 		return (-1);
288 	}
289 	if (KfwumFinishFirmwareImage(intf, fw_info) != 0) {
290 		return (-1);
291 	}
292 	if (KfwumGetStatus(intf) != 0) {
293 		return (-1);
294 	}
295 	if (action != 0) {
296 		if (KfwumStartFirmwareUpgrade(intf) != 0) {
297 			return (-1);
298 		}
299 	}
300 	return 0;
301 }
302 
303 /* KfwumGetFileSize  -  gets the file size
304  *
305  * @pFileName : filename ptr
306  * @pFileSize : output ptr for filesize
307  *
308  * returns 0 on success, otherwise (-1)
309  */
310 int
311 KfwumGetFileSize(const char *pFileName, unsigned long *pFileSize)
312 {
313 	FILE *pFileHandle = NULL;
314 	pFileHandle = fopen(pFileName, "rb");
315 	if (pFileHandle == NULL) {
316 		return (-1);
317 	}
318 	if (fseek(pFileHandle, 0L , SEEK_END) == 0) {
319 		*pFileSize = ftell(pFileHandle);
320 	}
321 	fclose(pFileHandle);
322 	if (*pFileSize != 0) {
323 		return 0;
324 	}
325 	return (-1);
326 }
327 
328 /* KfwumSetupBuffersFromFile  -  small buffers are used to store the file data
329  *
330  * @pFileName : filename ptr
331  * unsigned long : filesize
332  *
333  * returns 0 on success, otherwise (-1)
334  */
335 int
336 KfwumSetupBuffersFromFile(const char *pFileName, unsigned long fileSize)
337 {
338 	int rc = (-1);
339 	FILE *pFileHandle = NULL;
340 	int count;
341 	int modulus;
342 	int qty = 0;
343 
344 	pFileHandle = fopen(pFileName, "rb");
345 	if (pFileHandle == NULL) {
346 		lprintf(LOG_ERR, "Failed to open '%s' for reading.",
347 				pFileName);
348 		return (-1);
349 	}
350 	count = fileSize / MAX_BUFFER_SIZE;
351 	modulus = fileSize % MAX_BUFFER_SIZE;
352 
353 	rewind(pFileHandle);
354 	for (qty = 0; qty < count; qty++) {
355 		KfwumShowProgress("Reading Firmware from File",
356 				qty, count);
357 		if (fread(&firmBuf[qty * MAX_BUFFER_SIZE], 1,
358 					MAX_BUFFER_SIZE,
359 					pFileHandle) == MAX_BUFFER_SIZE) {
360 			rc = 0;
361 		}
362 	}
363 	if (modulus) {
364 		if (fread(&firmBuf[qty * MAX_BUFFER_SIZE], 1,
365 					modulus, pFileHandle) == modulus) {
366 			rc = 0;
367 		}
368 	}
369 	if (rc == 0) {
370 		KfwumShowProgress("Reading Firmware from File", 100, 100);
371 	}
372 	fclose(pFileHandle);
373 	return rc;
374 }
375 
376 /* KfwumShowProgress  -  helper routine to display progress bar
377  *
378  * Converts current/total in percent
379  *
380  * *task  : string identifying current operation
381  * current: progress
382  * total  : limit
383  */
384 void
385 KfwumShowProgress(const char *task, unsigned long current, unsigned long total)
386 {
387 # define PROG_LENGTH 42
388 	static unsigned long staticProgress=0xffffffff;
389 	unsigned char spaces[PROG_LENGTH + 1];
390 	unsigned short hash;
391 	float percent = ((float)current / total);
392 	unsigned long progress =  100 * (percent);
393 
394 	if (staticProgress == progress) {
395 		/* We displayed the same last time.. so don't do it */
396 		return;
397 	}
398 	staticProgress = progress;
399 	printf("%-25s : ", task); /* total 20 bytes */
400 	hash = (percent * PROG_LENGTH);
401 	memset(spaces, '#', hash);
402 	spaces[hash] = '\0';
403 
404 	printf("%s", spaces);
405 	memset(spaces, ' ', (PROG_LENGTH - hash));
406 	spaces[(PROG_LENGTH - hash)] = '\0';
407 	printf("%s", spaces );
408 
409 	printf(" %3ld %%\r", progress); /* total 7 bytes */
410 	if (progress == 100) {
411 		printf("\n");
412 	}
413 	fflush(stdout);
414 }
415 
416 /* KfwumCalculateChecksumPadding - TBD
417  */
418 unsigned short
419 KfwumCalculateChecksumPadding(unsigned char *pBuffer, unsigned long totalSize)
420 {
421 	unsigned short sumOfBytes = 0;
422 	unsigned short padding;
423 	unsigned long  counter;
424 	for (counter = 0; counter < totalSize; counter ++) {
425 		sumOfBytes += pBuffer[counter];
426 	}
427 	padding = 0 - sumOfBytes;
428 	return padding;
429 }
430 
431 /* KfwumGetInfo  -  Get Firmware Update Manager (FWUM) information
432  *
433  * *intf  : IPMI interface
434  * output  : when set to non zero, queried information is displayed
435  * pNumBank: output ptr for number of banks
436  *
437  * returns 0 on success, otherwise (-1)
438  */
439 int
440 KfwumGetInfo(struct ipmi_intf *intf, unsigned char output,
441 		unsigned char *pNumBank)
442 {
443 	int rc = 0;
444 	static struct KfwumGetInfoResp *pGetInfo;
445 	struct ipmi_rs *rsp;
446 	struct ipmi_rq req;
447 
448 	memset(&req, 0, sizeof(req));
449 	req.msg.netfn = IPMI_NETFN_FIRMWARE;
450 	req.msg.cmd = KFWUM_CMD_ID_GET_FIRMWARE_INFO;
451 	req.msg.data_len = 0;
452 
453 	rsp = intf->sendrecv(intf, &req);
454 	if (!rsp) {
455 		lprintf(LOG_ERR, "Error in FWUM Firmware Get Info Command.");
456 		return (-1);
457 	} else if (rsp->ccode != 0) {
458 		lprintf(LOG_ERR, "FWUM Firmware Get Info returned %x",
459 				rsp->ccode);
460 		return (-1);
461 	}
462 
463 	pGetInfo = (struct KfwumGetInfoResp *)rsp->data;
464 	if (output) {
465 		printf("\nFWUM info\n");
466 		printf("=========\n");
467 		printf("Protocol Revision         : %02Xh\n",
468 				pGetInfo->protocolRevision);
469 		printf("Controller Device Id      : %02Xh\n",
470 				pGetInfo->controllerDeviceId);
471 		printf("Firmware Revision         : %u.%u%u",
472 				pGetInfo->firmRev1, pGetInfo->firmRev2 >> 4,
473 				pGetInfo->firmRev2 & 0x0f);
474 		if (pGetInfo->byte.mode != 0) {
475 			printf(" - DEBUG BUILD\n");
476 		} else {
477 			printf("\n");
478 		}
479 		printf("Number Of Memory Bank     : %u\n", pGetInfo->numBank);
480 	}
481 	*pNumBank = pGetInfo->numBank;
482 	/* Determine wich type of download to use: */
483 	/* Old FWUM or Old IPMC fw (data_len < 7)
484 	 * --> Address with small buffer size
485 	 */
486 	if ((pGetInfo->protocolRevision) <= 0x05 || (rsp->data_len < 7 )) {
487 		save_fw_nfo.downloadType = KFWUM_DOWNLOAD_TYPE_ADDRESS;
488 		save_fw_nfo.bufferSize   = KFWUM_SMALL_BUFFER;
489 		save_fw_nfo.overheadSize = KFWUM_OLD_CMD_OVERHEAD;
490 		if (verbose) {
491 			printf("Protocol Revision          :");
492 			printf(" <= 5 detected, adjusting buffers\n");
493 		}
494 	} else {
495 		/* Both fw are using the new protocol */
496 		save_fw_nfo.downloadType = KFWUM_DOWNLOAD_TYPE_SEQUENCE;
497 		save_fw_nfo.overheadSize = KFWUM_NEW_CMD_OVERHEAD;
498 		/* Buffer size depending on access type (Local or remote) */
499 		/* Look if we run remote or locally */
500 		if (verbose) {
501 			printf("Protocol Revision          :");
502 			printf(" > 5 optimizing buffers\n");
503 		}
504 		if (strstr(intf->name,"lan") != NULL) {
505 			/* also covers lanplus */
506 			save_fw_nfo.bufferSize = KFWUM_SMALL_BUFFER;
507 			if (verbose) {
508 				printf("IOL payload size           : %d\n",
509 						save_fw_nfo.bufferSize);
510 			}
511 		} else if ((strstr(intf->name,"open")!= NULL)
512 				&& intf->target_addr != IPMI_BMC_SLAVE_ADDR
513 				&& (intf->target_addr !=  intf->my_addr)) {
514 			save_fw_nfo.bufferSize = KFWUM_SMALL_BUFFER;
515 			if (verbose) {
516 				printf("IPMB payload size          : %d\n",
517 						save_fw_nfo.bufferSize);
518 			}
519 		} else {
520 			save_fw_nfo.bufferSize = KFWUM_BIG_BUFFER;
521 			if (verbose) {
522 				printf("SMI payload size           : %d\n",
523 						save_fw_nfo.bufferSize);
524 			}
525 		}
526 	}
527 	return rc;
528 }
529 
530 /* KfwumGetDeviceInfo - Get IPMC/Board information
531  *
532  * *intf: IPMI interface
533  * output: when set to non zero, queried information is displayed
534  * tKFWUM_BoardInfo: output ptr for IPMC/Board information
535  *
536  * returns 0 on success, otherwise (-1)
537  */
538 int
539 KfwumGetDeviceInfo(struct ipmi_intf *intf, unsigned char output,
540 		tKFWUM_BoardInfo *pBoardInfo)
541 {
542 	struct ipm_devid_rsp *pGetDevId;
543 	struct ipmi_rs *rsp;
544 	struct ipmi_rq req;
545 	/* Send Get Device Id */
546 	memset(&req, 0, sizeof(req));
547 	req.msg.netfn = IPMI_NETFN_APP;
548 	req.msg.cmd = BMC_GET_DEVICE_ID;
549 	req.msg.data_len = 0;
550 
551 	rsp = intf->sendrecv(intf, &req);
552 	if (rsp == NULL) {
553 		lprintf(LOG_ERR, "Error in Get Device Id Command");
554 		return (-1);
555 	} else if (rsp->ccode != 0) {
556 		lprintf(LOG_ERR, "Get Device Id returned %x",
557 				rsp->ccode);
558 		return (-1);
559 	}
560 	pGetDevId = (struct ipm_devid_rsp *)rsp->data;
561 	pBoardInfo->iana = IPM_DEV_MANUFACTURER_ID(pGetDevId->manufacturer_id);
562 	pBoardInfo->boardId = buf2short(pGetDevId->product_id);
563 	if (output) {
564 		printf("\nIPMC Info\n");
565 		printf("=========\n");
566 		printf("Manufacturer Id           : %u\n",
567 				pBoardInfo->iana);
568 		printf("Board Id                  : %u\n",
569 				pBoardInfo->boardId);
570 		printf("Firmware Revision         : %u.%u%u",
571 				pGetDevId->fw_rev1, pGetDevId->fw_rev2 >> 4,
572 				pGetDevId->fw_rev2 & 0x0f);
573 		if (((pBoardInfo->iana == IPMI_OEM_KONTRON)
574 					&& (pBoardInfo->boardId == KFWUM_BOARD_KONTRON_5002))) {
575 			printf(" SDR %u", pGetDevId->aux_fw_rev[0]);
576 		}
577 		printf("\n");
578 	}
579 	return 0;
580 }
581 
582 /* KfwumGetStatus  -  Get (and prints) FWUM  banks information
583  *
584  * *intf  : IPMI interface
585  *
586  * returns 0 on success, otherwise (-1)
587  */
588 int
589 KfwumGetStatus(struct ipmi_intf * intf)
590 {
591 	int rc = 0;
592 	struct ipmi_rs *rsp;
593 	struct ipmi_rq req;
594 	struct KfwumGetStatusResp *pGetStatus;
595 	unsigned char numBank;
596 	unsigned char counter;
597 	unsigned long firmLength;
598 	if (verbose) {
599 		printf(" Getting Status!\n");
600 	}
601 	/* Retreive the number of bank */
602 	rc = KfwumGetInfo(intf, 0, &numBank);
603 	for(counter = 0;
604 			(counter < numBank) && (rc == 0);
605 			counter ++) {
606 		/* Retreive the status of each bank */
607 		memset(&req, 0, sizeof(req));
608 		req.msg.netfn = IPMI_NETFN_FIRMWARE;
609 		req.msg.cmd = KFWUM_CMD_ID_GET_FIRMWARE_STATUS;
610 		req.msg.data = &counter;
611 		req.msg.data_len = 1;
612 		rsp = intf->sendrecv(intf, &req);
613 		if (rsp == NULL) {
614 			lprintf(LOG_ERR,
615 					"Error in FWUM Firmware Get Status Command.");
616 			rc = (-1);
617 			break;
618 		} else if (rsp->ccode) {
619 			lprintf(LOG_ERR,
620 					"FWUM Firmware Get Status returned %x",
621 					rsp->ccode);
622 			rc = (-1);
623 			break;
624 		}
625 		pGetStatus = (struct KfwumGetStatusResp *) rsp->data;
626 		printf("\nBank State %d               : %s\n",
627 				counter,
628 				val2str(pGetStatus->bankState, bankStateValS));
629 		if (!pGetStatus->bankState) {
630 			continue;
631 		}
632 		firmLength  = pGetStatus->firmLengthMSB;
633 		firmLength  = firmLength << 8;
634 		firmLength |= pGetStatus->firmLengthMid;
635 		firmLength  = firmLength << 8;
636 		firmLength |= pGetStatus->firmLengthLSB;
637 		printf("Firmware Length            : %ld bytes\n",
638 				firmLength);
639 		printf("Firmware Revision          : %u.%u%u SDR %u\n",
640 				pGetStatus->firmRev1,
641 				pGetStatus->firmRev2 >> 4,
642 				pGetStatus->firmRev2 & 0x0f,
643 				pGetStatus->firmRev3);
644 	}
645 	printf("\n");
646 	return rc;
647 }
648 
649 /* KfwumManualRollback  -  Ask IPMC to rollback to previous version
650  *
651  * *intf  : IPMI interface
652  *
653  * returns 0 on success
654  * returns (-1) on error
655  */
656 int
657 KfwumManualRollback(struct ipmi_intf *intf)
658 {
659 	struct ipmi_rs *rsp;
660 	struct ipmi_rq req;
661 	struct KfwumManualRollbackReq thisReq;
662 
663 	memset(&req, 0, sizeof(req));
664 	req.msg.netfn = IPMI_NETFN_FIRMWARE;
665 	req.msg.cmd = KFWUM_CMD_ID_MANUAL_ROLLBACK;
666 	thisReq.type = 0; /* Wait BMC shutdown */
667 	req.msg.data = (unsigned char *)&thisReq;
668 	req.msg.data_len = 1;
669 
670 	rsp = intf->sendrecv(intf, &req);
671 	if (rsp == NULL) {
672 		lprintf(LOG_ERR, "Error in FWUM Manual Rollback Command.");
673 		return (-1);
674 	} else if (rsp->ccode != 0) {
675 		lprintf(LOG_ERR,
676 				"Error in FWUM Manual Rollback Command returned %x",
677 				rsp->ccode);
678 		return (-1);
679 	}
680 	printf("FWUM Starting Manual Rollback \n");
681 	return 0;
682 }
683 
684 int
685 KfwumStartFirmwareImage(struct ipmi_intf *intf, unsigned long length,
686 		unsigned short padding)
687 {
688 	struct ipmi_rs *rsp;
689 	struct ipmi_rq req;
690 	struct KfwumStartFirmwareDownloadResp *pResp;
691 	struct KfwumStartFirmwareDownloadReq thisReq;
692 
693 	thisReq.lengthLSB  = length         & 0x000000ff;
694 	thisReq.lengthMid  = (length >>  8) & 0x000000ff;
695 	thisReq.lengthMSB  = (length >> 16) & 0x000000ff;
696 	thisReq.paddingLSB = padding        & 0x00ff;
697 	thisReq.paddingMSB = (padding>>  8) & 0x00ff;
698 	thisReq.useSequence = 0x01;
699 	memset(&req, 0, sizeof(req));
700 	req.msg.netfn = IPMI_NETFN_FIRMWARE;
701 	req.msg.cmd = KFWUM_CMD_ID_START_FIRMWARE_IMAGE;
702 	req.msg.data = (unsigned char *) &thisReq;
703 	/* Look for download type */
704 	if (save_fw_nfo.downloadType == KFWUM_DOWNLOAD_TYPE_ADDRESS) {
705 		req.msg.data_len = 5;
706 	} else {
707 		req.msg.data_len = 6;
708 	}
709 	rsp = intf->sendrecv(intf, &req);
710 	if (rsp == NULL) {
711 		lprintf(LOG_ERR,
712 				"Error in FWUM Firmware Start Firmware Image Download Command.");
713 		return (-1);
714 	} else if (rsp->ccode) {
715 		lprintf(LOG_ERR,
716 				"FWUM Firmware Start Firmware Image Download returned %x",
717 				rsp->ccode);
718 		return (-1);
719 	}
720 	pResp = (struct KfwumStartFirmwareDownloadResp *)rsp->data;
721 	printf("Bank holding new firmware  : %d\n", pResp->bank);
722 	sleep(5);
723 	return 0;
724 }
725 
726 int
727 KfwumSaveFirmwareImage(struct ipmi_intf *intf, unsigned char sequenceNumber,
728 		unsigned long address, unsigned char *pFirmBuf,
729 		unsigned char *pInBufLength)
730 {
731 	int rc = 0;
732 	struct ipmi_rs *rsp;
733 	struct ipmi_rq req;
734 	struct KfwumSaveFirmwareAddressReq addr_req;
735 	struct KfwumSaveFirmwareSequenceReq seq_req;
736 	int retry = 0;
737 	int no_rsp = 0;
738 	do {
739 		memset(&req, 0, sizeof(req));
740 		req.msg.netfn = IPMI_NETFN_FIRMWARE;
741 		req.msg.cmd = KFWUM_CMD_ID_SAVE_FIRMWARE_IMAGE;
742 		if (save_fw_nfo.downloadType == KFWUM_DOWNLOAD_TYPE_ADDRESS) {
743 			addr_req.addressLSB  = address         & 0x000000ff;
744 			addr_req.addressMid  = (address >>  8) & 0x000000ff;
745 			addr_req.addressMSB  = (address >> 16) & 0x000000ff;
746 			addr_req.numBytes    = *pInBufLength;
747 			memcpy(addr_req.txBuf, pFirmBuf, *pInBufLength);
748 			req.msg.data = (unsigned char *)&addr_req;
749 			req.msg.data_len = *pInBufLength + 4;
750 		} else {
751 			seq_req.sequenceNumber = sequenceNumber;
752 			memcpy(seq_req.txBuf, pFirmBuf, *pInBufLength);
753 			req.msg.data = (unsigned char *)&seq_req;
754 			req.msg.data_len = *pInBufLength + sizeof(unsigned char);
755 			/* + 1 => sequenceNumber*/
756 		}
757 		rsp = intf->sendrecv(intf, &req);
758 		if (rsp == NULL) {
759 			lprintf(LOG_ERR,
760 					"Error in FWUM Firmware Save Firmware Image Download Command.");
761 			/* We don't receive "C7" on errors with IOL,
762 			 * instead we receive nothing
763 			 */
764 			if (strstr(intf->name, "lan") != NULL) {
765 				no_rsp++;
766 				if (no_rsp < FWUM_SAVE_FIRMWARE_NO_RESPONSE_LIMIT) {
767 					*pInBufLength -= 1;
768 					continue;
769 				}
770 				lprintf(LOG_ERR,
771 						"Error, too many commands without response.");
772 				*pInBufLength = 0;
773 				break;
774 			} /* For other interface keep trying */
775 		} else if (rsp->ccode != 0) {
776 			if (rsp->ccode == 0xc0) {
777 				sleep(1);
778 			} else if ((rsp->ccode == 0xc7)
779 					|| ((rsp->ccode == 0xc3)
780 						&& (sequenceNumber == 0))) {
781 				*pInBufLength -= 1;
782 				retry = 1;
783 			} else if (rsp->ccode == 0x82) {
784 				/* Double sent, continue */
785 				rc = 0;
786 				break;
787 			} else if (rsp->ccode == 0x83) {
788 				if (retry == 0) {
789 					retry = 1;
790 					continue;
791 				}
792 				rc = (-1);
793 				break;
794 			} else if (rsp->ccode == 0xcf) {
795 				/* Ok if receive duplicated request */
796 				retry = 1;
797 			} else if (rsp->ccode == 0xc3) {
798 				if (retry == 0) {
799 					retry = 1;
800 					continue;
801 				}
802 				rc = (-1);
803 				break;
804 			} else {
805 				lprintf(LOG_ERR,
806 						"FWUM Firmware Save Firmware Image Download returned %x",
807 						rsp->ccode);
808 				rc = (-1);
809 				break;
810 			}
811 		} else {
812 			break;
813 		}
814 	} while (1);
815 	return rc;
816 }
817 
818 int
819 KfwumFinishFirmwareImage(struct ipmi_intf *intf, tKFWUM_InFirmwareInfo firmInfo)
820 {
821 	struct ipmi_rs *rsp;
822 	struct ipmi_rq req;
823 	struct KfwumFinishFirmwareDownloadReq thisReq;
824 
825 	thisReq.versionMaj = firmInfo.versMajor;
826 	thisReq.versionMinSub = ((firmInfo.versMinor <<4)
827 			| firmInfo.versSubMinor);
828 	thisReq.versionSdr = firmInfo.sdrRev;
829 	thisReq.reserved = 0;
830 	/* Byte 4 reserved, write 0 */
831 	memset(&req, 0, sizeof(req));
832 	req.msg.netfn = IPMI_NETFN_FIRMWARE;
833 	req.msg.cmd = KFWUM_CMD_ID_FINISH_FIRMWARE_IMAGE;
834 	req.msg.data = (unsigned char *)&thisReq;
835 	req.msg.data_len = 4;
836 	/* Infinite loop if BMC doesn't reply or replies 0xc0 every time. */
837 	do {
838 		rsp = intf->sendrecv(intf, &req);
839 	} while (rsp == NULL || rsp->ccode == 0xc0);
840 
841 	if (rsp->ccode != 0) {
842 		lprintf(LOG_ERR,
843 				"FWUM Firmware Finish Firmware Image Download returned %x",
844 				rsp->ccode);
845 		return (-1);
846 	}
847 	return 0;
848 }
849 
850 int
851 KfwumUploadFirmware(struct ipmi_intf *intf, unsigned char *pBuffer,
852 		unsigned long totalSize)
853 {
854 	int rc = (-1);
855 	unsigned long address = 0x0;
856 	unsigned char writeSize;
857 	unsigned char oldWriteSize;
858 	unsigned long lastAddress = 0;
859 	unsigned char sequenceNumber = 0;
860 	unsigned char retry = FWUM_MAX_UPLOAD_RETRY;
861 	do {
862 		writeSize = save_fw_nfo.bufferSize - save_fw_nfo.overheadSize;
863 		/* Reach the end */
864 		if (address + writeSize > totalSize) {
865 			writeSize = (totalSize - address);
866 		} else if (((address % KFWUM_PAGE_SIZE)
867 					+ writeSize) > KFWUM_PAGE_SIZE) {
868 			/* Reach boundary end */
869 			writeSize = (KFWUM_PAGE_SIZE - (address % KFWUM_PAGE_SIZE));
870 		}
871 		oldWriteSize = writeSize;
872 		rc = KfwumSaveFirmwareImage(intf, sequenceNumber,
873 				address, &pBuffer[address], &writeSize);
874 		if ((rc != 0) && (retry-- != 0)) {
875 			address = lastAddress;
876 			rc = 0;
877 		} else if ( writeSize == 0) {
878 			rc = (-1);
879 		} else {
880 			if (writeSize != oldWriteSize) {
881 				printf("Adjusting length to %d bytes \n",
882 						writeSize);
883 				save_fw_nfo.bufferSize -= (oldWriteSize - writeSize);
884 			}
885 			retry = FWUM_MAX_UPLOAD_RETRY;
886 			lastAddress = address;
887 			address+= writeSize;
888 		}
889 		if (rc == 0) {
890 			if ((address % 1024) == 0) {
891 				KfwumShowProgress("Writing Firmware in Flash",
892 						address, totalSize);
893 			}
894 			sequenceNumber++;
895 		}
896 	} while ((rc == 0) && (address < totalSize));
897 	if (rc == 0) {
898 		KfwumShowProgress("Writing Firmware in Flash",
899 				100, 100);
900 	}
901 	return rc;
902 }
903 
904 int
905 KfwumStartFirmwareUpgrade(struct ipmi_intf *intf)
906 {
907 	int rc = 0;
908 	struct ipmi_rs *rsp;
909 	struct ipmi_rq req;
910 	/* Upgrade type, wait BMC shutdown */
911 	unsigned char upgType = 0 ;
912 
913 	memset(&req, 0, sizeof(req));
914 	req.msg.netfn = IPMI_NETFN_FIRMWARE;
915 	req.msg.cmd = KFWUM_CMD_ID_START_FIRMWARE_UPDATE;
916 	req.msg.data = (unsigned char *) &upgType;
917 	req.msg.data_len = 1;
918 
919 	rsp = intf->sendrecv(intf, &req);
920 	if (rsp == NULL) {
921 		lprintf(LOG_ERR,
922 				"Error in FWUM Firmware Start Firmware Upgrade Command");
923 		rc = (-1);
924 	} else if (rsp->ccode) {
925 		if (rsp->ccode == 0xd5) {
926 			lprintf(LOG_ERR,
927 					"No firmware available for upgrade.  Download Firmware first.");
928 		} else {
929 			lprintf(LOG_ERR,
930 					"FWUM Firmware Start Firmware Upgrade returned %x",
931 					rsp->ccode);
932 		}
933 		rc = (-1);
934 	}
935 	return rc;
936 }
937 
938 int
939 KfwumGetTraceLog(struct ipmi_intf *intf)
940 {
941 	int rc = 0;
942 	struct ipmi_rs *rsp;
943 	struct ipmi_rq req;
944 	unsigned char chunkIdx;
945 	unsigned char cmdIdx;
946 	if (verbose) {
947 		printf(" Getting Trace Log!\n");
948 	}
949 	for (chunkIdx = 0;
950 			(chunkIdx < TRACE_LOG_CHUNK_COUNT)
951 			&& (rc == 0);
952 			chunkIdx++) {
953 		/* Retreive each log chunk and print it */
954 		memset(&req, 0, sizeof(req));
955 		req.msg.netfn = IPMI_NETFN_FIRMWARE;
956 		req.msg.cmd = KFWUM_CMD_ID_GET_TRACE_LOG;
957 		req.msg.data = &chunkIdx;
958 		req.msg.data_len = 1;
959 
960 		rsp = intf->sendrecv(intf, &req);
961 		if (rsp == NULL) {
962 			lprintf(LOG_ERR,
963 					"Error in FWUM Firmware Get Trace Log Command");
964 			rc = (-1);
965 			break;
966 		} else if (rsp->ccode) {
967 			lprintf(LOG_ERR,
968 					"FWUM Firmware Get Trace Log returned %x",
969 					rsp->ccode);
970 			rc = (-1);
971 			break;
972 		}
973 		for (cmdIdx=0; cmdIdx < TRACE_LOG_CHUNK_SIZE; cmdIdx++) {
974 			/* Don't diplay commands with an invalid state */
975 			if ((rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 1] != 0)
976 					&& (rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx] < KFWUM_CMD_ID_STD_MAX_CMD)) {
977 				printf("  Cmd ID: %17s -- CmdState: %10s -- CompCode: %2x\n",
978 						CMD_ID_STRING[rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx]],
979 						CMD_STATE_STRING[rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 1]],
980 						rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 2]);
981 			} else if ((rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 1] != 0)
982 					&& (rsp->data[TRACE_LOG_ATT_COUNT*cmdIdx] >= KFWUM_CMD_ID_EXTENDED_CMD)) {
983 				printf("  Cmd ID: %17s -- CmdState: %10s -- CompCode: %2x\n",
984 						EXT_CMD_ID_STRING[rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx] - KFWUM_CMD_ID_EXTENDED_CMD],
985 						CMD_STATE_STRING[rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 1]],
986 						rsp->data[TRACE_LOG_ATT_COUNT * cmdIdx + 2]);
987 			}
988 		}
989 	}
990 	printf("\n");
991 	return rc;
992 }
993 
994 int
995 KfwumGetInfoFromFirmware(unsigned char *pBuf, unsigned long bufSize,
996 		tKFWUM_InFirmwareInfo *pInfo)
997 {
998 	unsigned long offset = 0;
999 	if (bufSize < (IN_FIRMWARE_INFO_OFFSET_LOCATION + IN_FIRMWARE_INFO_SIZE)) {
1000 		return (-1);
1001 	}
1002 	offset = IN_FIRMWARE_INFO_OFFSET_LOCATION;
1003 
1004 	/* Now, fill the structure with read informations */
1005 	pInfo->checksum = (unsigned short)KWUM_GET_BYTE_AT_OFFSET(pBuf,
1006 			offset + 0 + IN_FIRMWARE_INFO_OFFSET_CHECKSUM ) << 8;
1007 
1008 	pInfo->checksum|= (unsigned short)KWUM_GET_BYTE_AT_OFFSET(pBuf,
1009 			offset + 1 + IN_FIRMWARE_INFO_OFFSET_CHECKSUM);
1010 
1011 	pInfo->sumToRemoveFromChecksum = KWUM_GET_BYTE_AT_OFFSET(pBuf,
1012 			offset + IN_FIRMWARE_INFO_OFFSET_CHECKSUM);
1013 
1014 	pInfo->sumToRemoveFromChecksum+= KWUM_GET_BYTE_AT_OFFSET(pBuf,
1015 			offset + IN_FIRMWARE_INFO_OFFSET_CHECKSUM + 1);
1016 
1017 	pInfo->fileSize = KWUM_GET_BYTE_AT_OFFSET(pBuf,
1018 			offset + IN_FIRMWARE_INFO_OFFSET_FILE_SIZE + 0) << 24;
1019 
1020 	pInfo->fileSize|= (unsigned long)KWUM_GET_BYTE_AT_OFFSET(pBuf,
1021 			offset + IN_FIRMWARE_INFO_OFFSET_FILE_SIZE + 1) << 16;
1022 
1023 	pInfo->fileSize|= (unsigned long)KWUM_GET_BYTE_AT_OFFSET(pBuf,
1024 			offset + IN_FIRMWARE_INFO_OFFSET_FILE_SIZE + 2) << 8;
1025 
1026 	pInfo->fileSize|= (unsigned long)KWUM_GET_BYTE_AT_OFFSET(pBuf,
1027 			offset + IN_FIRMWARE_INFO_OFFSET_FILE_SIZE + 3);
1028 
1029 	pInfo->boardId = KWUM_GET_BYTE_AT_OFFSET(pBuf,
1030 			offset + IN_FIRMWARE_INFO_OFFSET_BOARD_ID + 0) << 8;
1031 
1032 	pInfo->boardId|= KWUM_GET_BYTE_AT_OFFSET(pBuf,
1033 			offset + IN_FIRMWARE_INFO_OFFSET_BOARD_ID + 1);
1034 
1035 	pInfo->deviceId = KWUM_GET_BYTE_AT_OFFSET(pBuf,
1036 			offset + IN_FIRMWARE_INFO_OFFSET_DEVICE_ID);
1037 
1038 	pInfo->tableVers = KWUM_GET_BYTE_AT_OFFSET(pBuf,
1039 			offset + IN_FIRMWARE_INFO_OFFSET_TABLE_VERSION);
1040 
1041 	pInfo->implRev = KWUM_GET_BYTE_AT_OFFSET(pBuf,
1042 			offset + IN_FIRMWARE_INFO_OFFSET_IMPLEMENT_REV);
1043 
1044 	pInfo->versMajor = (KWUM_GET_BYTE_AT_OFFSET(pBuf,
1045 				offset
1046 				+ IN_FIRMWARE_INFO_OFFSET_VER_MAJOROR)) & 0x0f;
1047 
1048 	pInfo->versMinor = (KWUM_GET_BYTE_AT_OFFSET(pBuf,
1049 				offset
1050 				+ IN_FIRMWARE_INFO_OFFSET_VER_MINORSUB) >> 4) & 0x0f;
1051 
1052 	pInfo->versSubMinor = (KWUM_GET_BYTE_AT_OFFSET(pBuf,
1053 				offset + IN_FIRMWARE_INFO_OFFSET_VER_MINORSUB)) & 0x0f;
1054 
1055 	pInfo->sdrRev = KWUM_GET_BYTE_AT_OFFSET(pBuf,
1056 			offset + IN_FIRMWARE_INFO_OFFSET_SDR_REV);
1057 
1058 	pInfo->iana = KWUM_GET_BYTE_AT_OFFSET(pBuf,
1059 			offset + IN_FIRMWARE_INFO_OFFSET_IANA2) << 16;
1060 
1061 	pInfo->iana|= (unsigned long)KWUM_GET_BYTE_AT_OFFSET(pBuf,
1062 			offset + IN_FIRMWARE_INFO_OFFSET_IANA1) << 8;
1063 
1064 	pInfo->iana|= (unsigned long)KWUM_GET_BYTE_AT_OFFSET(pBuf,
1065 			offset + IN_FIRMWARE_INFO_OFFSET_IANA0);
1066 
1067 	KfwumFixTableVersionForOldFirmware(pInfo);
1068 	return 0;
1069 }
1070 
1071 void
1072 KfwumFixTableVersionForOldFirmware(tKFWUM_InFirmwareInfo * pInfo)
1073 {
1074 	switch(pInfo->boardId) {
1075 	case KFWUM_BOARD_KONTRON_UNKNOWN:
1076 		pInfo->tableVers = 0xff;
1077 		break;
1078 	default:
1079 		/* pInfo->tableVers is already set for
1080 		 * the right version
1081 		 */
1082 		break;
1083 	}
1084 }
1085 
1086 /* ipmi_kfwum_checkfwcompat - check whether firmware we're about to upload is
1087  * compatible with board.
1088  *
1089  * @boardInfo:
1090  * @firmInfo:
1091  *
1092  * returns 0 if compatible, otherwise (-1)
1093  */
1094 int
1095 ipmi_kfwum_checkfwcompat(tKFWUM_BoardInfo boardInfo,
1096 		tKFWUM_InFirmwareInfo firmInfo)
1097 {
1098 	int compatible = 0;
1099 	if (boardInfo.iana != firmInfo.iana) {
1100 		lprintf(LOG_ERR,
1101 				"Board IANA does not match firmware IANA.");
1102 		compatible = (-1);
1103 	}
1104 	if (boardInfo.boardId != firmInfo.boardId) {
1105 		lprintf(LOG_ERR,
1106 				"Board IANA does not match firmware IANA.");
1107 		compatible = (-1);
1108 	}
1109 	if (compatible != 0) {
1110 		lprintf(LOG_ERR,
1111 				"Firmware invalid for target board. Download of upgrade aborted.");
1112 	}
1113 	return compatible;
1114 }
1115 
1116 void
1117 printf_kfwum_info(tKFWUM_BoardInfo boardInfo, tKFWUM_InFirmwareInfo firmInfo)
1118 {
1119 	printf(
1120 "Target Board Id            : %u\n", boardInfo.boardId);
1121 	printf(
1122 "Target IANA number         : %u\n", boardInfo.iana);
1123 	printf(
1124 "File Size                  : %lu bytes\n", firmInfo.fileSize);
1125 	printf(
1126 "Firmware Version           : %d.%d%d SDR %d\n", firmInfo.versMajor,
1127 firmInfo.versMinor, firmInfo.versSubMinor, firmInfo.sdrRev);
1128 }
1129