/* BEGIN_ICS_COPYRIGHT7 ****************************************

Copyright (c) 2015-2020, Intel Corporation

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of Intel Corporation nor the names of its contributors
      may be used to endorse or promote products derived from this software
      without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

** END_ICS_COPYRIGHT7   ****************************************/

/* [ICS VERSION STRING: unknown] */

#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <limits.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>

/* work around conflicting names */

#include "iba/ib_types.h"
#include "iba/ib_sm_priv.h"
#include "iba/ib_helper.h"
#include "opamgt_sa_priv.h"
#include <iba/ibt.h>
#include "opaswcommon.h"
#include "opaswmetadata.h"
#include "zlib.h"

/* defines */

#define LIST_FILE_SUPPORTED 0

#define CR						'\r'
#define NL						'\n'
#define NUL						'\0'
#define OB						'['
#define CB						']'
#define EQ						'='
#define QU						'\"'
#define SP						' '

#define TABLE_BLOCKSIZE			25
#define INBUFFERSIZE			4096

#define OPASW_INIBIN			"iniXedge.inibin"
#define VIPER_INIBIN			"iniViper.inibin"
#define PRR_INIBIN				"prrIniEdge.inibin"

#define INI_MAX_SIZE			(16 * 1024)

#define SIGNATURE				0x50724F6d
#define EEPROMSIZE				(64 * 1024)

#define FW_RESP_WAIT_TIME		2000

#ifndef min
#define min(a,b) (((a)<(b))?(a):(b))
#endif
#ifndef max
#define max(a,b) (((a)>(b))?(a):(b))
#endif

/* typedefs */

/* global variables */

int						g_debugMode = 0;
int						g_verbose = 0;
int						g_quiet = 0;
int						g_gotHfi = 0;
int						g_gotPort = 0;
int						g_gotModule = 0;
int						g_gotSecondary = 0;
int						g_fileParam = 0;
int						g_dirParam = 0;
int						g_respTimeout = FW_RESP_WAIT_TIME;

char					fwFileName[FNAME_SIZE];
char					inibinFileName[FNAME_SIZE];
char					dirName[DNAME_SIZE];

char					*cmdName;

uint8					switchNodeDesc[64];
EUI64					switchNodeGuid;
EUI64					switchSystemImageGuid;
uint32					newNumPorts;
uint32					fmPushButtonState;
uint32					newPortEntrySize;
uint32					oldNumPorts;
uint32					oldPortEntrySize;
uint32					*oldPortDataTable;
uint32					*oldPortMetaTable;
IB_PATH_RECORD			path;
VENDOR_MAD				mad;
uint16					sessionID = 0;
uint32					max_data_table_type;

#define DEBUG_USE_FILE	0

/* for PRR stuff */

// SYSTEM_TABLE definitions
#define SYSTEM_TABLE_FIELD_RESERVED                                  0 
#define SYSTEM_TABLE_FIELD_NODE_STRING                               1 
#define SYSTEM_TABLE_FIELD_SYSTEM_IMAGE_GUID                         2 
#define SYSTEM_TABLE_FIELD_NODE_GUID                                 3 
#define SYSTEM_TABLE_FIELD_NUM_PORTS								 8
#define SYSTEM_TABLE_FIELD_FM_PUSH_BUTTON_STATE                      33

// PORT_TABLE definitions
#define PORT_TABLE_FIELD_LINK_SPEED_SUPPORTED                       7
#define PORT_TABLE_FIELD_LINK_WIDTH_SUPPORTED                       8
#define PORT_TABLE_FIELD_CRC										22
#define PORT_TABLE_FIELD_FM_ENABLED									17
#define PORT_TABLE_FIELD_VCU										28
#define PORT_TABLE_FIELD_EXTERNAL_LOOPBACK_ALLOWED					30

// INI TABLE types
#define INI_TYPE_END_TABLE									0
#define INI_TYPE_SYSTEM_TABLE								1
#define INI_TYPE_PORT_TABLE									2
#define INI_TYPE_DFE_TABLE									3
#define INI_TYPE_FFE_TABLE									4
#define INI_TYPE_FFE_INDIRECT_TABLE							5
#define INI_TYPE_PORT_TYPE_QSFP_TABLE						6
#define INI_TYPE_PORT_TYPE_CUSTOM_TABLE						7
#define INI_TYPE_DATA_TABLE_MAX								(INI_TYPE_PORT_TYPE_CUSTOM_TABLE + 1)
#define INI_TYPE_META_DATA_TABLE_MAX						(INI_TYPE_FFE_INDIRECT_TABLE + 1)

#define S_S20INI_META_TABLE_ENTRY_START_BIT				0
#define L_S20INI_META_TABLE_ENTRY_START_BIT				15
#define S_S20INI_META_TABLE_ENTRY_PROTECTED				15
#define L_S20INI_META_TABLE_ENTRY_PROTECTED				1
#define S_S20INI_META_TABLE_ENTRY_BITS					16
#define L_S20INI_META_TABLE_ENTRY_BITS					16

#define S_S20INI_RECORD_HEADER_RECORD_IDX	0
#define L_S20INI_RECORD_HEADER_RECORD_IDX	6
#define M_S20INI_RECORD_HEADER_RECORD_IDX	(((1 << L_S20INI_RECORD_HEADER_RECORD_IDX) - 1) << S_S20INI_RECORD_HEADER_RECORD_IDX)
#define S_S20INI_RECORD_HEADER_DATA_WORDS	16
#define L_S20INI_RECORD_HEADER_DATA_WORDS	12
#define M_S20INI_RECORD_HEADER_DATA_WORDS	(((1 << L_S20INI_RECORD_HEADER_DATA_WORDS) - 1) << S_S20INI_RECORD_HEADER_DATA_WORDS)
#define S_S20INI_RECORD_HEADER_TABLE_TYPE	28
#define L_S20INI_RECORD_HEADER_TABLE_TYPE	4
#define M_S20INI_RECORD_HEADER_TABLE_TYPE	(((1 << L_S20INI_RECORD_HEADER_TABLE_TYPE) - 1) << S_S20INI_RECORD_HEADER_TABLE_TYPE)

#define FFE_INDIRECT_TABLE_RECORDS_PER_WORD	2

#define SimPrintf printf

typedef	uint8					U8_t;
typedef	uint32					U32_t;
typedef	uint64					U64_t;
typedef int						BOOL;

typedef struct {
	U32_t *ptr;
	U32_t entries;
	U32_t allocated;
	U32_t dataWords;
} S20INI_TABLE_REF;

typedef struct {
	struct S20 *s20;
	U32_t numPorts;
	U32_t tableType;
	U32_t field;
	U32_t *v;
	U32_t vLenInWords;
	U32_t vLenInBits;
	U32_t v32;
	U32_t recordIdx;
	U32_t recordBits;
	const char *argv;
	S20INI_TABLE_REF *metaDataRef;
	S20INI_TABLE_REF *dataRef;
	S20INI_TABLE_REF dataTables[INI_TYPE_DATA_TABLE_MAX];
	S20INI_TABLE_REF metaDataTables[INI_TYPE_META_DATA_TABLE_MAX];
} S20INI;

typedef struct {
	U32_t val;
	const char *text;
} S20INI_ENCODING;

struct s20iniHandler;

typedef struct {
	struct S20 *s20;
	const struct s20iniHandler *infoTable;
	const char *outputFn;
	U32_t line;
	const char *inputFn;
	int argi;
	int argc;
	int tableType; // this needs to be an int for the logic below to work correctly
	U32_t field;
	U32_t recordIdx;
	U32_t portWordsPerPort;
	U32_t wordsPerIdx;
	U32_t startBitIncrement;
	U32_t recordsPerWord;
	U32_t numPorts;
	U32_t *recordData;
	const U32_t *metaDataPtr;
	int result;
	U32_t scanIdx;
	S20INI_TABLE_REF *recordDataRef;
	S20INI_TABLE_REF metaData[INI_TYPE_DATA_TABLE_MAX]; // yes this should be INI_TYPE_DATA_TABLE_MAX
	S20INI_TABLE_REF tableData[INI_TYPE_DATA_TABLE_MAX];
	S20INI_TABLE_REF portCommon;
	BOOL portCommonCopied[256]; // assumes max of 255 ports
	char *argv[256];
	char src[2048];
	char dst[4096];
} S20INI_TOBIN;

typedef BOOL(*procFieldTextIn_t)(S20INI_TOBIN *, int idx, BOOL *insert, U32_t *value);
typedef BOOL(*procFieldTextOut_t)(S20INI *);

typedef struct s20iniHandler {
	procFieldTextIn_t procTextIn;
	procFieldTextOut_t procTextOut;
	const S20INI_ENCODING *encoding;
	const char *name;
} s20iniHandler_t;

#define CRC32_POLYNOMIAL 0x04C11DB7
#define CRC32_PRELOAD 0x46af6449
#define CRC32_REFLECT_POLYNOMIAL 0xEDB88320 // 1110-1101-1011-1000-1000-0011-0010-0000
#define CRC32_REFLECT_PRELOAD 0x9226f562    // 1001-0010-0010-0110-1111-0101-0110-0010

S20INI ini;

static U32_t crc32AddByte(U32_t crc, U8_t u8) {
	U32_t i = 8;
	do {
		crc = ((crc >> 1) | (u8 << 31)) ^ ((crc & 1) ? CRC32_REFLECT_POLYNOMIAL : 0);
		u8 >>= 1;
	} while(--i);
	return crc;
}

U32_t crc32AddBytes(U32_t crc, U32_t u32, U8_t bytes) {
	// note that we are doing this using big-endian byte ordering, since S20 is big-endian
	do {
		crc = crc32AddByte(crc, u32 >> 24);
		u32 <<= 8;
	} while(--bytes);
	return crc;
}

U32_t crc32Finish(U32_t crc) {
	crc = ~crc32AddBytes(crc, 0, 4);
	crc = (crc >> 24) | ((crc >> 8) & 0xff00) | (crc << 24) | ((crc << 8) & 0xff0000);
	return crc;
}

U32_t crc32Init() {
	return CRC32_REFLECT_PRELOAD;
}

U32_t crc32Calculate(U8_t *data, U32_t bytes) {
	U32_t crc = crc32Init();
	U32_t i;
	U32_t val;

	for(i = 0; i < bytes; i += 4) {
		val = (data[i] << 24) + (data[i + 1] << 16) + (data[i + 2] << 8) + data[i + 3];
		crc = crc32AddBytes(crc, val, 4);
	}
	return crc32Finish(crc);
}

void s20iniFieldInsert(const U32_t *metaData, U32_t *dst, U32_t entryIdx, U32_t data) {
	U32_t meta = metaData[entryIdx];
	U32_t start = (meta >> S_S20INI_META_TABLE_ENTRY_START_BIT) & ((1 << L_S20INI_META_TABLE_ENTRY_START_BIT) - 1);
	U32_t startBit = start & 0x1f;
	U32_t startIdx = start >> 5;
	U32_t bits = (meta >> S_S20INI_META_TABLE_ENTRY_BITS) & ((1 << L_S20INI_META_TABLE_ENTRY_BITS) - 1);
	U32_t mask = ((1 << bits) - 1) << startBit;
	dst[startIdx] = ((data << startBit) & mask) | (dst[startIdx] & ~mask);
}

int s20iniFieldIsolate(const U32_t *metaData, U32_t *src, U32_t entryIdx) {
	U32_t meta = metaData[entryIdx];
	U32_t start = (meta >> S_S20INI_META_TABLE_ENTRY_START_BIT) & ((1 << L_S20INI_META_TABLE_ENTRY_START_BIT) - 1);
	U32_t startBit = start & 0x1f;
	U32_t startIdx = start >> 5;
	U32_t bits = (meta >> S_S20INI_META_TABLE_ENTRY_BITS) & ((1 << L_S20INI_META_TABLE_ENTRY_BITS) - 1);
	return (src[startIdx] >> startBit) & ((1 << bits) - 1);
}

int readIniBinFile(char *fileName, U8_t *binBuf)
{
	int				totalRead = -1;
	int				nread;
	U8_t			*p;
	FILE			*fp;

	if ((fp = fopen(fileName, "r")) == NULL) {
		if (errno == ENOENT)
			fprintf(stderr, "Error opening file %s ... old style EMFW not supported\n", fileName);
		else
			fprintf(stderr, "Error opening file %s for input: %s\n", fileName, strerror(errno));
		return(-1);
	}

	p = binBuf;
	while ((nread = fread(p, 1, 1024, fp)) > 0) {
		totalRead += nread;
		p += nread;
	}

	fclose(fp);
	return(totalRead);
}

#if DEBUG_USE_FILE
int writeIniBinFile(char *fileName, U8_t *binBuf, int bufSize)
{
	int				totalWritten = 0;
	int				nwritten;
	int				chunkSize;
	U8_t			*p;
	FILE			*fp;

	if ((fp = fopen(fileName, "w")) == NULL) {
		fprintf(stderr, "Error opening file %s for output: %s\n", fileName, strerror(errno));
		return(-1);
	}

	p = binBuf;
	while (totalWritten < bufSize) {
		if ((bufSize - totalWritten) < 1024)
			chunkSize = bufSize - totalWritten;
		else
			chunkSize = 1024;
		nwritten = fwrite(p, 1, chunkSize, fp);
		totalWritten += nwritten;
		p += nwritten;
	}

	fclose(fp);
	return(totalWritten);
}
#endif

static U32_t S20iniMetaDataEntryLenGet(U32_t entry) {
	return (entry >> S_S20INI_META_TABLE_ENTRY_BITS) & ((1 << L_S20INI_META_TABLE_ENTRY_BITS) - 1);
}

static U32_t S20iniMetaDataEntryStartGet(U32_t entry) {
	return (entry >> S_S20INI_META_TABLE_ENTRY_START_BIT) & ((1 << L_S20INI_META_TABLE_ENTRY_START_BIT) - 1);
}

static U32_t S20iniMetaDataEntryProtectedGet(U32_t entry) {
	return (entry >> S_S20INI_META_TABLE_ENTRY_PROTECTED) & ((1 << L_S20INI_META_TABLE_ENTRY_PROTECTED) - 1);
}

void S20iniMetaDataDump(S20INI *ini) {
	U32_t i;
	U32_t entry;
	S20INI_TABLE_REF *tableRef = &ini->metaDataTables[ini->tableType];

	printf("Dumping table type %d\n", ini->tableType);

	for(i = 0; i < tableRef->entries; ++i) {
		entry = tableRef->ptr[i];
		printf("idx keyword %d len keyword %d start keyword %d protected keyword %d\n",
				i, S20iniMetaDataEntryLenGet(entry),
				   S20iniMetaDataEntryStartGet(entry),
				   S20iniMetaDataEntryProtectedGet(entry));
	}
}

BOOL S20iniFieldOut(S20INI *ini) {

	if (ini->vLenInBits <= 32) {
		char s[16];
		if (ini->vLenInBits <= 3) {
			strcpy(s, "field %d=%lu");
		} else {
			if (ini->vLenInBits <= 4) {
				strcpy(s, "field %d=0x%x");
			} else {
				snprintf(s, sizeof(s), "field %%d=0x%%0%dx", (int)((ini->vLenInBits + 3) >> 2));
			}
		}
		printf(s, ini->field, *ini->v);
	} else {
		printf("field %d=0x%08x", ini->field, *ini->v);
		while(--ini->vLenInWords) {
			++ini->v;
			printf("%08x", *ini->v);
		}
	}
	printf("\n");
	return FALSE;
}

BOOL S20iniFieldValue(S20INI *ini) {
	U32_t startBit;
	U32_t entry;

	entry = ini->metaDataRef->ptr[ini->field];
	ini->vLenInBits = S20iniMetaDataEntryLenGet(entry);
	if (ini->vLenInBits == 0)
		return TRUE;
	ini->vLenInWords = ((ini->vLenInBits + 31) >> 5);

	startBit = S20iniMetaDataEntryStartGet(entry) + (ini->recordIdx * ini->recordBits);

	if (ini->vLenInBits <= 32) {
		if (ini->recordBits & 31) {
			U32_t recordsPerU32 = 32 / ini->recordBits;
			U32_t modIdx = ini->recordIdx % recordsPerU32;
			startBit -= (modIdx * ini->recordBits);
			startBit += ((recordsPerU32 - modIdx - 1) * ini->recordBits);
		}
		ini->v32 = ini->dataRef->ptr[startBit >> 5];
		if (ini->vLenInBits < 32)
			ini->v32 = (ini->v32 >> (startBit & 0x1f)) & ((1 << ini->vLenInBits) - 1);
		ini->v = &ini->v32;
		ini->vLenInWords = 1;
		if ((ini->tableType == INI_TYPE_SYSTEM_TABLE) && (ini->field == SYSTEM_TABLE_FIELD_NUM_PORTS))
			ini->numPorts = ini->v32;
	} else {
		ini->v = &ini->dataRef->ptr[startBit >> 5];
		ini->vLenInWords = ((ini->vLenInBits + 31) >> 5);
	}
	return FALSE;
}

U32_t S20iniFieldValueOccurrences(S20INI *ini, U32_t value) {
	U32_t count = 0;

	for(ini->recordIdx = 0; ini->recordIdx < ini->numPorts; ++ini->recordIdx) {
		if (S20iniFieldValue(ini))
			break;
		if (ini->vLenInWords == 1)
			if (ini->v32 == value)
				++count;
	}
	return count;
}

static U32_t s20iniTableWords(S20INI_TABLE_REF *tref) {
	U32_t result = 0;
	U32_t i;
	U32_t entry;
	U32_t bits;
	U32_t val;

	for(i = 0; i < tref->entries; ++i) {
		entry = tref->ptr[i];
		bits = S20iniMetaDataEntryLenGet(entry);
		if (bits) {
			val = (S20iniMetaDataEntryStartGet(entry) + bits + 31) >> 5;
			if (val > result)
				result = val;
		}
	}
	return result;
}

void S20iniDataDump(S20INI *ini) {
	struct S20INI_COMMON {
		U32_t count;
		U32_t value;
		U32_t recordIdx;
	} *common;

	ini->recordBits = 0;
	ini->recordIdx = 0;
	ini->dataRef = &ini->dataTables[ini->tableType];

	switch(ini->tableType) {
	case INI_TYPE_SYSTEM_TABLE:
		ini->metaDataRef = &ini->metaDataTables[INI_TYPE_SYSTEM_TABLE];

		printf("dump of system data table type %d\n", ini->tableType);
		for(ini->field = 0; ini->field < ini->metaDataRef->entries; ++ini->field) {
			if (S20iniFieldValue(ini))
				continue;
			if (S20iniFieldOut(ini))
				return;
			printf("\n");
		}
		break;
	case INI_TYPE_PORT_TABLE:
		ini->metaDataRef = &ini->metaDataTables[INI_TYPE_PORT_TABLE];
		printf("dump of port data table type %d\n", ini->tableType);

		if (ini->numPorts)
			ini->recordBits = (ini->dataRef->entries / ini->numPorts) * 32;

		if (ini->metaDataRef->entries) {
			BOOL anyCommon = FALSE;
			common = (struct S20INI_COMMON *)malloc(ini->metaDataRef->entries * sizeof(*common));
			if (common != NULL) {
				memset(common, 0, ini->metaDataRef->entries * sizeof(*common));

				for(ini->field = 1; ini->field < ini->metaDataRef->entries; ++ini->field) {
					U32_t recordIdx;
					for(recordIdx = 0; recordIdx < ini->numPorts; ++recordIdx) {
						ini->recordIdx = recordIdx; // this gets corrupted by S20iniFieldValueOccurrences
						if (S20iniFieldValue(ini))
							continue;
						if (ini->vLenInWords == 1) {
							if ((common[ini->field].count == 0) || (common[ini->field].value != ini->v32)) {
								U32_t value = ini->v32;
								U32_t count = S20iniFieldValueOccurrences(ini, value);
								if ((count >= (ini->numPorts >> 2)) && (count > common[ini->field].count)) {
									common[ini->field].count = count;
									common[ini->field].value = value;
									common[ini->field].recordIdx = recordIdx;
									anyCommon = TRUE;
									if (count >= (ini->numPorts >> 1))
										break;
								}
							}
						}
					}
				}
				if (anyCommon) {
					printf("***port==Common\n");
					for(ini->field = 1; ini->field < ini->metaDataRef->entries; ++ini->field) {
						if (common[ini->field].count) {
							ini->recordIdx = common[ini->field].recordIdx;
							if (S20iniFieldValue(ini))
								continue;
							if (S20iniFieldOut(ini))
								return;
							printf("\n");
						}
					}
				}
			}
		} else
			common = NULL;

		for(ini->recordIdx = 0; ini->recordIdx < ini->numPorts; ++ini->recordIdx) {
			printf("\n");
			printf("***port=%lu\n", (long)(ini->recordIdx + 1));
			for(ini->field = 0; ini->field < ini->metaDataRef->entries; ++ini->field) {
				if (S20iniFieldValue(ini))
					continue;
				if (common && common[ini->field].count && (common[ini->field].value == ini->v32))
					continue;
				if (S20iniFieldOut(ini))
					return;
				printf("\n");
			}
		}
		if (common != NULL)
			free(common);
		break;
	case INI_TYPE_DFE_TABLE:
	case INI_TYPE_FFE_TABLE:
	case INI_TYPE_FFE_INDIRECT_TABLE:
		ini->metaDataRef = &ini->metaDataTables[ini->tableType];
		if (ini->metaDataRef->entries) {
			ini->recordBits = s20iniTableWords(ini->metaDataRef) * 32;
			if (ini->tableType == INI_TYPE_FFE_INDIRECT_TABLE)
				if (ini->recordBits)
					ini->recordBits = 32 / FFE_INDIRECT_TABLE_RECORDS_PER_WORD;

			if (ini->recordBits) {
				U32_t records = (ini->dataRef->entries * 32) / ini->recordBits;

				printf("table type %d\n\n", ini->tableType);
				for(ini->recordIdx = 0; ini->recordIdx < records; ++ini->recordIdx) {
					printf("IDX=%u", ini->recordIdx);
					for(ini->field = 0; ini->field < ini->metaDataRef->entries; ++ini->field) {
						if (S20iniFieldValue(ini))
							continue;
						printf(" ");
						if (S20iniFieldOut(ini))
							return;
					}
					printf("\n");
				}
			}
		}
		break;
	case INI_TYPE_PORT_TYPE_QSFP_TABLE:
	case INI_TYPE_PORT_TYPE_CUSTOM_TABLE:
		ini->metaDataRef = NULL;

		printf("table type %d\n\n", ini->tableType);
		ini->field = 0;

		U32_t i;
		for(i = 0; i < ini->dataRef->entries; ++i) {
			U32_t value = ini->dataRef->ptr[i];
			ini->v = &ini->v32;
			ini->vLenInWords = 1;
			ini->vLenInBits = 8;
			U32_t j;
			for(j = 0; j < 4; ++j) {
				printf("IDX_KEYWORD =%-4lu", (long)((i << 2) + j));
				ini->v32 = (value >> (24 - (j << 3))) & 0xff;
				S20iniFieldOut(ini);
				printf("\n");
			}
		}
		break;
	default:
		break;
	}
}

void S20iniBinToText(struct S20 *s20, U8_t *data, U32_t dataLen, BOOL show, const char *metaFn, const char *dataFn) {
	U32_t i = 0;
	S20INI_TABLE_REF *tableRef;

	memset(&ini, 0, sizeof(ini));

	while((i + 7) < dataLen) {
		U32_t header1;
		U32_t header2;
		U32_t dataWords;
		U32_t crc = 0;
		U32_t crcCheck;
		U32_t tableType;
		U32_t recordIdx;
		U32_t dataIdx;

		header1 = (data[i + 0] << 24) + (data[i + 1] << 16) + (data[i + 2] << 8) + data[i + 3];
		header2 = (data[i + 4] << 24) + (data[i + 5] << 16) + (data[i + 6] << 8) + data[i + 7];
		if (header1 != ~header2) {
			SimPrintf("Header check failed offset=%lu\n", (long)i);
			break;
		}
		i += 8;
		dataIdx = i;
		dataWords = (header1 & M_S20INI_RECORD_HEADER_DATA_WORDS) >> S_S20INI_RECORD_HEADER_DATA_WORDS;
		if (dataWords) {
			crcCheck = crc32Calculate(&data[i], dataWords << 2);
			i += (dataWords << 2);
			crc = (data[i + 0] << 24) + (data[i + 1] << 16) + (data[i + 2] << 8) + data[i + 3];
			i += 4;
			if (crcCheck != crc) {
				SimPrintf("crc check failed at offset %lu\n", (long)(i - 4));
				break;
			}
		}
		tableType = (header1 & M_S20INI_RECORD_HEADER_TABLE_TYPE) >> S_S20INI_RECORD_HEADER_TABLE_TYPE;
		recordIdx = (header1 & M_S20INI_RECORD_HEADER_RECORD_IDX) >> S_S20INI_RECORD_HEADER_RECORD_IDX;
		// SimPrintf("tableType=%lu recordIdx=%lu dataWords=%lu\n", (long)tableType, (long)recordIdx, (long)dataWords);
		if (recordIdx == 0) {
			if (tableType > INI_TYPE_META_DATA_TABLE_MAX) {
				SimPrintf("Unrecognized meta data tableType=%lu\n", (long)tableType);
				break;
			}
			tableRef = &ini.metaDataTables[tableType];
		} else {
			if (recordIdx == 1) {
				if (tableType >= INI_TYPE_DATA_TABLE_MAX) {
					SimPrintf("Unrecognized tableType=%lu\n", (long)tableType);
					break;
				}
				tableRef = &ini.dataTables[tableType];
				max_data_table_type = tableType + 1;
			} else {
				SimPrintf("Unrecognized recordIdx=%lu\n", (long)recordIdx);
				break;
			}
		}

		if (tableRef->ptr != NULL) {
			tableRef->ptr = NULL;
			free(tableRef->ptr);
		}

		tableRef->entries = 0;
		tableRef->ptr = 0;
		if (dataWords) {
			tableRef->dataWords = dataWords;
			if ((tableRef->ptr = (U32_t *)malloc(dataWords * sizeof(U32_t))) != NULL) {
				U32_t j;

				tableRef->entries = dataWords;
				for(j = 0; j < dataWords; ++j) {
					tableRef->ptr[j] = (data[dataIdx] << 24) + (data[dataIdx + 1] << 16) +
							(data[dataIdx + 2] << 8) + data[dataIdx + 3];
					dataIdx += 4;
				}
			} else {
				SimPrintf("Out of memory allocating dataWords=%lu\n", (long)dataWords);
				break;
			}
		}
	}

}

int buildNewIniBin(U8_t *binBuffer)
{
	U32_t header1;
	U32_t header2;
	U32_t crc = 0;
	U32_t recordIdx;
	U32_t bufSize = 0;
	U32_t *newSystemMetaTable;
	U32_t *newSystemDataTable;
	U32_t *newPortMetaTable;
	U32_t *newPortDataTable;

	U8_t *p = binBuffer;
	U8_t *q;
	int i;
	int j;
	U32_t ports;
	U32_t oldPortBaseIdx;
	U32_t newPortBaseIdx;
	U32_t startBit;
	U8_t segBuffer[2048];
	int segSize;
	U32_t nodeBuf[16];
	U32_t guidBuf[2];

	memset(binBuffer, 0, INI_MAX_SIZE);

	newPortMetaTable = ini.metaDataTables[INI_TYPE_PORT_TABLE].ptr;
	newPortDataTable = ini.dataTables[INI_TYPE_PORT_TABLE].ptr;

	oldPortBaseIdx = 0;
	newPortBaseIdx = 0;
	ports = min(oldNumPorts, newNumPorts);
	for (j = 0; j < ports; j++) {
		s20iniFieldInsert(newPortMetaTable, &newPortDataTable[newPortBaseIdx], PORT_TABLE_FIELD_LINK_WIDTH_SUPPORTED,
				s20iniFieldIsolate(oldPortMetaTable, &oldPortDataTable[oldPortBaseIdx], PORT_TABLE_FIELD_LINK_WIDTH_SUPPORTED));
		s20iniFieldInsert(newPortMetaTable, &newPortDataTable[newPortBaseIdx], PORT_TABLE_FIELD_LINK_SPEED_SUPPORTED,
				s20iniFieldIsolate(oldPortMetaTable, &oldPortDataTable[oldPortBaseIdx], PORT_TABLE_FIELD_LINK_SPEED_SUPPORTED));
		s20iniFieldInsert(newPortMetaTable, &newPortDataTable[newPortBaseIdx], PORT_TABLE_FIELD_FM_ENABLED,
				s20iniFieldIsolate(oldPortMetaTable, &oldPortDataTable[oldPortBaseIdx], PORT_TABLE_FIELD_FM_ENABLED));
		s20iniFieldInsert(newPortMetaTable, &newPortDataTable[newPortBaseIdx], PORT_TABLE_FIELD_CRC,
				s20iniFieldIsolate(oldPortMetaTable, &oldPortDataTable[oldPortBaseIdx], PORT_TABLE_FIELD_CRC));
		s20iniFieldInsert(newPortMetaTable, &newPortDataTable[newPortBaseIdx], PORT_TABLE_FIELD_VCU,
				s20iniFieldIsolate(oldPortMetaTable, &oldPortDataTable[oldPortBaseIdx], PORT_TABLE_FIELD_VCU));
		s20iniFieldInsert(newPortMetaTable, &newPortDataTable[newPortBaseIdx], PORT_TABLE_FIELD_EXTERNAL_LOOPBACK_ALLOWED,
				s20iniFieldIsolate(oldPortMetaTable, &oldPortDataTable[oldPortBaseIdx], PORT_TABLE_FIELD_EXTERNAL_LOOPBACK_ALLOWED));
		oldPortBaseIdx += oldPortEntrySize;
		newPortBaseIdx += newPortEntrySize;
	}

	newSystemMetaTable = ini.metaDataTables[INI_TYPE_SYSTEM_TABLE].ptr;
	newSystemDataTable = ini.dataTables[INI_TYPE_SYSTEM_TABLE].ptr;

	// convert node description to big endian format
	for (i = 0; i < 16; i++)
		nodeBuf[i] = ntoh32(*(U32_t *)&switchNodeDesc[i << 2]);

	// insert node desc
	startBit = S20iniMetaDataEntryStartGet(newSystemMetaTable[SYSTEM_TABLE_FIELD_NODE_STRING]);
	memcpy(&newSystemDataTable[(startBit >> 5)], nodeBuf, 64);

	// convert system image guid to big endian format
	for (i = 0; i < 2; i++) {
		U32_t val;
		memcpy(&val, ((char *)&switchSystemImageGuid) + (i * 4), 4);
		guidBuf[i] = ntoh32(val);
	}

	// insert system image GUID
	startBit = S20iniMetaDataEntryStartGet(newSystemMetaTable[SYSTEM_TABLE_FIELD_SYSTEM_IMAGE_GUID]);
	memcpy(&newSystemDataTable[(startBit >> 5)], guidBuf, 8);

	// convert node guid to big endian format
	for (i = 0; i < 2; i++) {
		U32_t val;
		memcpy(&val, ((char *)&switchNodeGuid) + (i * 4), 4);
		guidBuf[i] = ntoh32(val);
	}

	// insert node GUID
	startBit = S20iniMetaDataEntryStartGet(newSystemMetaTable[SYSTEM_TABLE_FIELD_NODE_GUID]);
	memcpy(&newSystemDataTable[(startBit >> 5)], guidBuf, 8);

	//insert FM Push Button State
	s20iniFieldInsert(newSystemMetaTable, &newSystemDataTable[0], SYSTEM_TABLE_FIELD_FM_PUSH_BUTTON_STATE, fmPushButtonState);

	for(ini.tableType = 1; ini.tableType < INI_TYPE_META_DATA_TABLE_MAX; ++ini.tableType) {
		S20INI_TABLE_REF *newMetaTableRef;

		//S20iniMetaDataDump(&ini);
		memset(segBuffer, 0, 1024);
		segSize = 0;
		q = segBuffer;
		newMetaTableRef = &ini.metaDataTables[ini.tableType];
		recordIdx = 0;
		header1 = 0;
		header1 |= (newMetaTableRef->dataWords << S_S20INI_RECORD_HEADER_DATA_WORDS);
		header1 |= (ini.tableType << S_S20INI_RECORD_HEADER_TABLE_TYPE);
		header1 |= (recordIdx << S_S20INI_RECORD_HEADER_RECORD_IDX);
		header1 = ntoh32(header1);
		header2 = ~header1;
		memcpy(q, &header1, sizeof(header1));
		q += sizeof(header1);
		segSize += sizeof(header1);
		memcpy(q, &header2, sizeof(header1));
		q += sizeof(header2);
		segSize += sizeof(header2);
		memcpy(q, newMetaTableRef->ptr, newMetaTableRef->dataWords * 4);
		for (i = 0; i < newMetaTableRef->dataWords; i++) {
			((U32_t*)q)[i] = ntoh32(((U32_t*)q)[i]);
		}
		q += newMetaTableRef->dataWords * 4;
		segSize += newMetaTableRef->dataWords * 4;
		crc = crc32Calculate(&segBuffer[2*sizeof(header1)], newMetaTableRef->dataWords << 2);
		crc = ntoh32(crc);
		memcpy(q, &crc, sizeof(crc));
		q += sizeof(crc);
		segSize += sizeof(crc);
		memcpy(p, segBuffer, segSize);
		p += segSize;
		bufSize += segSize;
	}

	for(ini.tableType = 1; ini.tableType < max_data_table_type; ++ini.tableType) {
		S20INI_TABLE_REF *newDataTableRef;

		//S20iniDataDump(&ini);
		memset(segBuffer, 0, 1024);
		segSize = 0;
		q = segBuffer;
		newDataTableRef = &ini.dataTables[ini.tableType];
		recordIdx = 1;
		header1 = 0;
		header1 |= (newDataTableRef->dataWords << S_S20INI_RECORD_HEADER_DATA_WORDS);
		header1 |= (ini.tableType << S_S20INI_RECORD_HEADER_TABLE_TYPE);
		header1 |= (recordIdx << S_S20INI_RECORD_HEADER_RECORD_IDX);
		header1 = ntoh32(header1);
		header2 = ~header1;
		memcpy(q, &header1, sizeof(header1));
		q += sizeof(header1);
		segSize += sizeof(header1);
		memcpy(q, &header2, sizeof(header1));
		q += sizeof(header2);
		segSize += sizeof(header2);
		memcpy(q, newDataTableRef->ptr, newDataTableRef->dataWords * 4);
		for (i = 0; i < newDataTableRef->dataWords; i++) {
			((U32_t*)q)[i] = ntoh32(((U32_t*)q)[i]);
		}
		q += newDataTableRef->dataWords * 4;
		segSize += newDataTableRef->dataWords * 4;
		crc = crc32Calculate(&segBuffer[2*sizeof(header1)], newDataTableRef->dataWords << 2);
		crc = ntoh32(crc);
		memcpy(q, &crc, sizeof(crc));
		q += sizeof(crc);
		segSize += sizeof(crc);
		memcpy(p, segBuffer, segSize);
		p += segSize;
		bufSize += segSize;
	}

	while (bufSize & 3) {
		binBuffer[bufSize] = 0;
		++bufSize;
	}

	return(bufSize);

}

void buildEEPROMTrailer(int dataLen, uint8 *trailerBuf) {
	uint32 i;

	i = hton32(SIGNATURE);
	memcpy(trailerBuf, (char *)&i, 4);
	i = dataLen >> 2;
	i = hton32(i);
	memcpy(&trailerBuf[4], (char *)&i, 4);
	i = 0;
	memcpy(&trailerBuf[8], (char *)&i, 4);
	i = crc32Calculate(trailerBuf, 12);
	i = hton32(i);
	memcpy(&trailerBuf[12], (char *)&i, 4);
}


static void usage(char *app_name)
{
	fprintf(stderr, "usage: %s -t target ", app_name);
#if LIST_FILE_SUPPORTED
	fprintf(stderr, " [-l list_file]");
#endif
	fprintf(stderr, " [-v|-q] [-h hfi] [-o port] [-f ini_file | -d ini_directory ]");
	fprintf(stderr, "\n");
	fprintf(stderr, "   -t - target on which to update fw\n");
#if LIST_FILE_SUPPORTED
	fprintf(stderr, "   -l - file that holds a list of targets to update\n");
#endif
	fprintf(stderr, "   -v - verbose output\n");
	fprintf(stderr, "   -q - no output\n");
	fprintf(stderr, "   -h - hfi, numbered 1..n, 0= -o port will be a\n");
	fprintf(stderr, "        system wide port num (default is 0)\n");
	fprintf(stderr, "   -o - port, numbered 1..n, 0=1st active (default\n");
	fprintf(stderr, "        is 1st active)\n");
	fprintf(stderr, "   -f - ini file\n");
	fprintf(stderr, "   -d - directory for ini file\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "The -h and -o options permit a variety of selections:\n");
	fprintf(stderr, "    -h 0       - 1st active port in system (this is the default)\n");
	fprintf(stderr, "    -h 0 -o 0  - 1st active port in system\n");
	fprintf(stderr, "    -h x       - 1st active port on HFI x\n");
	fprintf(stderr, "    -h x -o 0  - 1st active port on HFI x\n");
	fprintf(stderr, "    -h 0 -o y  - port y within system (irrespective of which ports are active)\n");
	fprintf(stderr, "    -h x -o y  - HFI x, port y\n");
#if 0 /* non-documented options */
	fprintf(stderr, "   -F - do secondary EEPROM (default if primary)\n");
#endif

	exit(2);
}

int s20ReadMem(struct omgt_port *port, U32_t addr, U32_t words, uint32 *data) {
	U32_t remainingWords = words;
	int status = FSUCCESS;
	U32_t xferWords;
	uint32 *p = data;

	while(remainingWords && (status == FSUCCESS)) {
		xferWords = 16;
		if (remainingWords < xferWords)
			xferWords = remainingWords;

		status = sendMemAccessGetMad(port, &path, &mad, sessionID, addr, xferWords * 4, (U8_t *)p);
		if (status == FSUCCESS) {
			U32_t i;
			for(i = 0; i < xferWords; ++i)
				p[i] = hton32(p[i]);
			p += xferWords;
			remainingWords -= xferWords;
			addr += xferWords;
		}
	}
	return status;
}

/* offsets need to be adjusted for multi EEPROM setup */
int adjustEepromOffset(int totalOffset, uint16 *newOffset, uint32 *newLocation) {
	*newOffset = totalOffset % STL_MAX_EEPROM_SIZE;
	int numEeprom = totalOffset / STL_MAX_EEPROM_SIZE;
	switch (numEeprom) {
	case 0:
		*newLocation = g_gotSecondary ? STL_PRR_SEC_EEPROM1_ADDR : STL_PRR_PRI_EEPROM1_ADDR;
		break;
	case 1:
		*newLocation = g_gotSecondary ? STL_PRR_SEC_EEPROM2_ADDR : STL_PRR_PRI_EEPROM2_ADDR;
		break;
	case 2:
		*newLocation = g_gotSecondary ? STL_PRR_SEC_EEPROM3_ADDR : STL_PRR_PRI_EEPROM3_ADDR;
		break;
	case 3:
		*newLocation = g_gotSecondary ? STL_PRR_SEC_EEPROM4_ADDR : STL_PRR_PRI_EEPROM4_ADDR;
		break;
	default:
		*newLocation = g_gotSecondary ? STL_PRR_SEC_EEPROM1_ADDR : STL_PRR_PRI_EEPROM1_ADDR;
		numEeprom = 0;
		break;
	}
	return numEeprom;
}

uint32 getLocationOfEeprom(int eeprom) {
	switch (eeprom) {
	case 0:
		return g_gotSecondary ? STL_PRR_SEC_EEPROM1_ADDR : STL_PRR_PRI_EEPROM1_ADDR;
	case 1:
		return g_gotSecondary ? STL_PRR_SEC_EEPROM2_ADDR : STL_PRR_PRI_EEPROM2_ADDR;
	case 2:
		return g_gotSecondary ? STL_PRR_SEC_EEPROM3_ADDR : STL_PRR_PRI_EEPROM3_ADDR;
	case 3:
		return g_gotSecondary ? STL_PRR_SEC_EEPROM4_ADDR : STL_PRR_PRI_EEPROM4_ADDR;
	default:
		return g_gotSecondary ? STL_PRR_SEC_EEPROM1_ADDR : STL_PRR_PRI_EEPROM1_ADDR;
	}
}

FSTATUS EepromRW(struct omgt_port *port, IB_PATH_RECORD *path, uint16 sessionID,
	void *mad, uint8 jumbo, uint8 method, int timeout, uint32 locationDescriptor,
	uint16 dataLen, uint16 dataOffset, uint8 *data)
{
	FSTATUS				status;
	uint32				maxXfer = 128;
	uint32				remainingLen;
	uint32				xferLen;
	uint32				secondary;

	remainingLen = dataLen;
	secondary = locationDescriptor & 0x00010000;

	if (secondary && (method == MMTHD_SET))
		locationDescriptor &= 0x7fffffff;

	while (remainingLen) {
		xferLen = MIN(remainingLen, maxXfer);

		status = sendI2CAccessMad(port, path, sessionID, mad, jumbo, method, timeout, locationDescriptor, xferLen, dataOffset, data);
		if (status != FSUCCESS) {
			return(status);
		}
		dataOffset += xferLen;
		data += xferLen;
		remainingLen -= xferLen;
	}

	return FSUCCESS;
}

int main(int argc, char *argv[])
{
	const char			*opts="DFvqKBbt:l:h:o:d:m:f:T:";
	char				parameter[100];
	char				*p;
	EUI64				destPortGuid = -1;
	int					c;
	uint8				hfi = 0;
	uint8				port = 0;
	FSTATUS				status;

	int					iniBinSize;
	int					iniBinSizeOut;
	U8_t				iniBinBuf[INI_MAX_SIZE];
	U8_t				iniBinBufOut[INI_MAX_SIZE];
	opasw_ini_descriptor_get_t tableDescriptors;
	uint8				trailer[16];
	uint8				eepromBuffer[INI_MAX_SIZE];
	uint8				eepromBuffer2[INI_MAX_SIZE];
	uint32				eepromReadSize;
	uint32				crcSize;
	uint16				eepromReadOffset;
	uint32				crcOut, crcIn;
	uint8				*ep;
	uint32				eepromWriteSize;
	uint16				eepromOffset;
	uint32				locationDescriptor;
	uint32				byteCount;
	int					primary = 1;
	int					overwriteFactoryDefaults = 0;
	struct stat			fileStat;
	struct              omgt_port *omgt_port_session = NULL;
	int					currentEeprom;


	// determine how we've been invoked
	cmdName = strrchr(argv[0], '/');			// Find last '/' in path
	if (cmdName != NULL) {
		cmdName++;								// Skip over last '/'
	} else {
		cmdName = argv[0];
	}

	// parse options and parameters
	while (-1 != (c = getopt(argc, argv, opts))) {
		switch (c) {
			case 'D':
				g_debugMode = 1;
				break;

			case 't':
				errno = 0;
				strncpy(parameter, optarg, sizeof(parameter)-1);
				parameter[sizeof(parameter)-1] = 0;
				if ((p = strchr(parameter, ',')) != NULL) {
					*p = '\0';
				}
				if (FSUCCESS != StringToUint64(&destPortGuid, parameter, NULL, 0, TRUE)) {
					fprintf(stderr, "%s: Error: Invalid GUID: %s\n", cmdName, optarg);
					usage(cmdName);
				}
				break;

			case 'l':
#if !LIST_FILE_SUPPORTED
				fprintf(stderr, "Error: l option is not supported at this time\n");
				exit(1);
#endif
				break;

			case 'v':
				g_verbose = 1;
				break;

			case 'q':
				g_quiet = 1;
				break;

			case 'h':
				if (FSUCCESS != StringToUint8(&hfi, optarg, NULL, 0, TRUE)) {
					fprintf(stderr, "%s: Error: Invalid HFI Number: %s\n", cmdName, optarg);
					usage(cmdName);
				}
				g_gotHfi = 1;
				break;

			case 'o':
				if (FSUCCESS != StringToUint8(&port, optarg, NULL, 0, TRUE)) {
					fprintf(stderr, "%s: Error: Invalid Port Number: %s\n", cmdName, optarg);
					usage(cmdName);
				}
				g_gotPort = 1;
				break;

			case 'f':
				g_fileParam = 1;
				strncpy(inibinFileName, optarg, FNAME_SIZE-1);
				inibinFileName[FNAME_SIZE-1] = 0;
				if (!inibinFileName[0]) {
					fprintf(stderr, "%s: Error: null input filename\n", cmdName);
					exit(1);
				}
				break;

			case 'd':
				g_dirParam = 1;
				strncpy(dirName, optarg, DNAME_SIZE-1);
				dirName[DNAME_SIZE-1]=0;
				break;

			case 'b':
				primary = 1 - primary;
				break;

			case 'B':
				overwriteFactoryDefaults = 1 - overwriteFactoryDefaults;
				break;

			case 'F':
				g_gotSecondary = 1;
				break;

			case 'T':
				if (FSUCCESS != StringToInt32(&g_respTimeout, optarg, NULL, 0, TRUE)
					|| g_respTimeout < 0) {
					fprintf(stderr, "%s: Error: Invalid delay value: %s\n", cmdName, optarg);
					usage(cmdName);
				}
				break;

			default:
				usage(cmdName);
				break;

		}
	}

	// user has requested display of help
	if (argc == 1) {
		usage(cmdName);
	}

	if (-1 == destPortGuid) {
		fprintf(stderr, "%s: Error: Must specify a target GUID\n", cmdName);
		exit(1);
	}

	if ((!g_fileParam) && (!g_dirParam)) {
		fprintf(stderr, "%s: Error: Must specify either a directory or file for the ini files\n", cmdName);
		exit(1);
	} else if ((g_fileParam) && (g_dirParam)) {
		fprintf(stderr, "%s: Error: Cannot specify both a directory and file for the ini files\n", cmdName);
		exit(1);
	}

	if (g_quiet && (g_debugMode || g_verbose)) {
		fprintf(stderr, "%s: Error: Can not specify both -q and -D|-v\n", cmdName);
		exit(1);
	}

	if (g_gotSecondary)
		primary = 0;

	locationDescriptor = primary ? STL_PRR_PRI_EEPROM1_ADDR : STL_PRR_SEC_EEPROM1_ADDR;

	// Get the path

	struct omgt_params params = {.debug_file = g_debugMode ? stderr : NULL};
	status = omgt_open_port_by_num(&omgt_port_session, hfi, port, &params);
	if (status != 0) {
		fprintf(stderr, "%s: Error: Unable to open fabric interface.\n", cmdName);
		exit(1);
	}

	if (getDestPath(omgt_port_session, destPortGuid, cmdName, &path) != FSUCCESS) {
		fprintf(stderr, "%s: Error: Failed to get destination path\n", cmdName);
		status = FERROR;
		goto err_exit;
	}

	// Send a ClassPortInfo to see if the switch is responding

	status = sendClassPortInfoMad(omgt_port_session, &path, &mad);
	if (status != FSUCCESS) {
		fprintf(stderr, "%s: Error: Failed to send/rcv ClassPortInfo\n", cmdName);
		goto err_exit;
	}

	// Get a session ID

	sessionID = getSessionID(omgt_port_session, &path);
	if (sessionID == (uint16)-1) {
		fprintf(stderr, "%s: Error: Failed to obtain sessionID\n", cmdName);
		status = FERROR;
		goto err_exit;
	}

	// Read in the inibin file and parse it

	if (g_dirParam) {
		if (chdir(dirName) < 0) {
			fprintf(stderr, "Error: cannot change directory to %s: %s\n", dirName, strerror(errno));
			if (sessionID>0) releaseSession(omgt_port_session, &path, sessionID);
			goto err_exit;
		}
		strcpy(inibinFileName, PRR_INIBIN);
		if (stat("emfwMapFile", &fileStat) < 0) {
			if (errno == ENOENT) {
				strcpy(inibinFileName, OPASW_INIBIN);
			} else {
				fprintf(stderr, "Error: cannot validate emfwMapFile: %s\n", strerror(errno));
				if (sessionID>0) releaseSession(omgt_port_session, &path, sessionID);
				status = FERROR;
				goto err_exit;
			}
		} else {
			getEMFWFileNames(omgt_port_session, &path, sessionID, fwFileName, inibinFileName);
		}
	}

	if (strstr(inibinFileName, ".inibin") == NULL) {
		fprintf(stderr, "Error: old style EMFW not valid with this release\n");
		if (sessionID>0) releaseSession(omgt_port_session, &path, sessionID);
		status = FERROR;
		goto err_exit;
	}

	iniBinSize = readIniBinFile(inibinFileName, iniBinBuf);
	if (iniBinSize < 0) {
		if (sessionID>0) releaseSession(omgt_port_session, &path, sessionID);
		goto err_exit;
	}

	S20iniBinToText(NULL, iniBinBuf, iniBinSize, 0, "metaFn", "dataFn");


	status = getNodeDescription(omgt_port_session, &path, sessionID, switchNodeDesc);
	if (status != FSUCCESS) {
		fprintf(stderr, "Error: Failed to acquire node description - status %d\n", status);
	}

	status = getFmPushButtonState(omgt_port_session, &path, sessionID, &fmPushButtonState);
        if (status != FSUCCESS) {
                fprintf(stderr, "Error: Failed to acquire FM Push Button Status - status %d\n", status);
        }

	status = getGuid(omgt_port_session, &path, sessionID, &switchSystemImageGuid, SYSTEM_IMAGE_GUID);
	if (status != FSUCCESS) {
		fprintf(stderr, "Error: Failed to acquire system image guid - status %d\n", status);
	}

	status = getGuid(omgt_port_session, &path, sessionID, &switchNodeGuid, NODE_GUID);
	if (status != FSUCCESS) {
		fprintf(stderr, "Error: Failed to acquire node guid - status %d\n", status);
	}

	status = sendIniDescriptorGetMad(omgt_port_session, &path, &mad, sessionID, &tableDescriptors);
	if (status != FSUCCESS) {
		fprintf(stderr, "%s: Error: Failed to get ini descriptors - status %d\n", cmdName, status);
		if (sessionID>0) releaseSession(omgt_port_session, &path, sessionID);
		goto err_exit;
	}
	oldNumPorts = getNumPorts(omgt_port_session, &path, sessionID);
	oldPortEntrySize = tableDescriptors.portDataLen / oldNumPorts;
	newNumPorts = s20iniFieldIsolate(ini.metaDataTables[INI_TYPE_SYSTEM_TABLE].ptr,
			ini.dataTables[INI_TYPE_SYSTEM_TABLE].ptr, SYSTEM_TABLE_FIELD_NUM_PORTS),
	newPortEntrySize = ini.dataTables[INI_TYPE_PORT_TABLE].dataWords / newNumPorts;

	// fetch the port meta data
	oldPortMetaTable = (uint32 *)malloc(tableDescriptors.portMetaDataLen * sizeof(uint32));
	status = s20ReadMem(omgt_port_session, tableDescriptors.portMetaDataAddr, tableDescriptors.portMetaDataLen, oldPortMetaTable);

	// fetch the port table data
	oldPortDataTable = (uint32 *)malloc(tableDescriptors.portDataLen * sizeof(uint32));
	status = s20ReadMem(omgt_port_session, tableDescriptors.portDataAddr, tableDescriptors.portDataLen, oldPortDataTable);

	if (g_verbose) {
		printf("Port Data dump:\n");
		opaswDisplayBuffer((char *)oldPortDataTable, tableDescriptors.portDataLen * 4);
	}

	int totalEepromOffset = STL_MAX_TOTAL_EEPROM_SIZE - 256 - sizeof(trailer);
	currentEeprom = adjustEepromOffset(totalEepromOffset, &eepromOffset, &locationDescriptor);

	if (overwriteFactoryDefaults == 0) {
		status = EepromRW(omgt_port_session, &path, sessionID, (void *)&mad, NOJUMBOMAD, MMTHD_GET, 
								  g_respTimeout, locationDescriptor, sizeof(trailer),
								  eepromOffset, trailer);
		if (status != FSUCCESS) {
			fprintf(stderr, "%s: Error: Failed to get eeprom trailer - status %d\n", cmdName, status);
			if (sessionID>0) releaseSession(omgt_port_session, &path, sessionID);
			goto err_exit;
		}
		U32_t signature = ntoh32(*(U32_t *)&trailer[0]);
		U32_t len = ntoh32(*(U32_t *)&trailer[4]);
		U32_t crc = ntoh32(*(U32_t *)&trailer[12]);

		if (signature == SIGNATURE) {
			if (crc32Calculate(trailer, 12) == crc) {
				// move backwards past the factory default INI setting to the user trailer
				eepromOffset -= ((len << 2) + sizeof(trailer));
			} else {
				overwriteFactoryDefaults = 1;
				printf("INI trailer crc mismatch expected=0x%x actual=0x%x\n",
						crc32Calculate(trailer, 12), crc);
			}
		} else {
			overwriteFactoryDefaults = 1;
			printf("INI trailer signature mismatch expected=0x%x actual=0x%x\n",
						SIGNATURE, signature);
		}
	}

	// format new inibin now that we have user-settable values

	iniBinSizeOut = buildNewIniBin(iniBinBufOut);
	totalEepromOffset -= iniBinSizeOut;
	currentEeprom = adjustEepromOffset(totalEepromOffset, &eepromOffset, &locationDescriptor);
	int totalEepromReadOffset = totalEepromOffset;

	// construct the trailer

	buildEEPROMTrailer(iniBinSizeOut, trailer);

	ep = eepromBuffer;
	eepromWriteSize = 0;
	if (overwriteFactoryDefaults) {
		// invalidate the previous trailer
		memset (ep, 0xff, sizeof(trailer));
		ep += sizeof(trailer);
		eepromWriteSize = sizeof(trailer);
		totalEepromOffset -= sizeof(trailer); // will also be updating the previous trailer
		currentEeprom = adjustEepromOffset(totalEepromOffset, &eepromOffset, &locationDescriptor);
		totalEepromReadOffset = totalEepromOffset;
	}
	
	memcpy(ep, iniBinBufOut, iniBinSizeOut);
	memcpy(ep + iniBinSizeOut, trailer, sizeof(trailer));
	eepromWriteSize += iniBinSizeOut + sizeof(trailer);
	eepromReadSize = eepromWriteSize;

	if (overwriteFactoryDefaults) {
		printf("overwriting factory defaults in %s eeprom\n", primary ? "primary" : "secondary");
	} else {
		if (!primary)
			printf("overwriting configuration in secondary eeprom\n");
	}

	// calculate checkout on outgoing buffer
	crcOut = crc32Calculate(eepromBuffer, eepromWriteSize);

	// write to EEPROM in loop

#if DEBUG_USE_FILE == 0
	ep = eepromBuffer;
	status = FSUCCESS;
	byteCount = 0;
	while ((status == FSUCCESS) && eepromWriteSize) {
		U32_t xferLen = I2C_DATA_SIZE;

		if (xferLen > eepromWriteSize)
			xferLen = eepromWriteSize;

		if (byteCount < STL_MAX_EEPROM_SIZE && (xferLen + eepromOffset) > STL_MAX_EEPROM_SIZE)
			xferLen = STL_MAX_EEPROM_SIZE - eepromOffset;

		if (g_verbose)
			printf("Sending offset %d (0x%04x)\n", eepromOffset, eepromOffset);
		status = EepromRW(omgt_port_session, &path, sessionID, (void *)&mad, NOJUMBOMAD, MMTHD_SET, 
								  g_respTimeout, locationDescriptor,
								  xferLen, eepromOffset, ep);
		if (status == FSUCCESS) {
			ep += xferLen;
			eepromWriteSize -= xferLen;
			if (byteCount < STL_MAX_EEPROM_SIZE && (xferLen + eepromOffset) > STL_MAX_EEPROM_SIZE)
				locationDescriptor = getLocationOfEeprom(--currentEeprom);
			eepromOffset += xferLen;
			byteCount += xferLen;
		} else {
			/* retry ??? */
			fprintf(stderr, "%s: Error sending MAD packet to switch\n", cmdName);
		}
	}
#else
	printf("eepromOffset=0x%x eepromWriteSize=0x%x\n", eepromOffset, eepromWriteSize);
	writeIniBinFile("eeprom.bin", eepromBuffer, eepromWriteSize);
#endif

	// Read back and calculate checksum
	usleep(250000);
	ep = eepromBuffer2;
	status = FSUCCESS;
	byteCount = 0;
	crcSize = eepromReadSize;

	currentEeprom = adjustEepromOffset(totalEepromReadOffset, &eepromReadOffset, &locationDescriptor);

	while ((status == FSUCCESS) && eepromReadSize) {
		U32_t xferLen = I2C_DATA_SIZE;
		if (xferLen > eepromReadSize)
			xferLen = eepromReadSize;

		if (byteCount < STL_MAX_EEPROM_SIZE && (xferLen + eepromReadOffset) > STL_MAX_EEPROM_SIZE)
			xferLen = STL_MAX_EEPROM_SIZE - eepromReadOffset;

		if (g_verbose)
			printf("Reading offset %d (0x%04x)\n", eepromReadOffset, eepromReadOffset);

		status = EepromRW(omgt_port_session, &path, sessionID, (void *)&mad, NOJUMBOMAD, MMTHD_GET, 
								  g_respTimeout, locationDescriptor,
								  xferLen, eepromReadOffset, ep);
		if (status == FSUCCESS) {
			ep += xferLen;
			eepromReadSize -= xferLen;
			if (byteCount < STL_MAX_EEPROM_SIZE && (xferLen + eepromReadOffset) > STL_MAX_EEPROM_SIZE)
				locationDescriptor = getLocationOfEeprom(--currentEeprom);
			eepromReadOffset += xferLen;
			byteCount += xferLen;
		} else {
			/* retry ??? */
			fprintf(stderr, "%s: Error reading MAD packet to switch\n", cmdName);
		}
	}
	crcIn = crc32Calculate(eepromBuffer2, crcSize);
	printf("%s: Config block in EEPROM is %s\n", cmdName, (crcOut == crcIn) ? "valid" : "invalid");

	if (sessionID>0) releaseSession(omgt_port_session, &path, sessionID);

	printf("opaswfwconfigure completed\n");

err_exit:
	omgt_close_port(omgt_port_session);

	if (status == FSUCCESS)
		exit(0);
	else
		exit(1);

}
