/**************************************************************
$Workfile:   elmapi.c  $
***************************************************************
**
** FIRMWARE, Copyright (C) 2000-2002
** Ikanos Communications, Fremont, CA
** All rights reserved. 
**
** The information and contents of this file are the
** proprietary information of Ikanos Communication and may not
** be disclosed or used without the formal written approval of
** Ikanos Communication
**
$Revision: 1.1 $
$Date: 2006/10/16 04:34:50 $
***************************************************************/


/**************************************************************

	This file contains functions for 
		ELM related Apps:
		
**************************************************************/

#include <stdio.h>
#include <string.h>

#include "ipos.h"
#include "appconfig.h"
#include "apperrCode.h"
#include "apposmacro.h"
#include "portmap.h"

#include "elmlocal.h"
#include "elmdeferred.h"
#include "elmapp.h"


U32	iposSizeofEventLog( char *pEventLog, U32 eventLogSize );


#define IPOSELM_GET_EVENT_LOG_TIMER_DELAY	16	/* wait at least 3 seconds */

int
iposGetRemoteEventLog
(
	ipos_port_id pid,
	char * pEventLog,
	unsigned int eventLogSize,
	eELMTypeEventLog typeOfLog,
	U8 uid,
	U32 uPar,
	iposELMCmdCb pCompleteFunc
)
{
#ifdef VTU_O
	int			 errCode;
	ipos_elm_co		*pCoPe;
	ipos_elm_cmd_handler	*pCmd;

	iposelm_select_event_log_req	msgStruct;

	pCoPe = iposELMGetCoPe( pid );
	pCoPe->pCbEventLogComplete = pCompleteFunc;	/* callback when event log received */
	pCoPe->eventLogAddr = pEventLog;

	iposELMCmdSetCtrlHdr( &(msgStruct.ctrlHdr), IPOSELM_MSG_GET_EVENT_LOG, IPOSELM_ACK_CMD, uid );
	msgStruct.eventLogChoice.eventLogOption = (U8) typeOfLog;

	pCmd = &(pCoPe->cmdHandler);

/* wait 3 seconds before resend (IPOSELM_GET_EVENT_LOG_TIMER_DELAY) ... 
   getting the BME event log takes a while */

/* for now we pass a different callback to iposELMCmdCoreReq from the one passed
   to this method.  If the initial send received an ACK, this side's response is
   to send a subsequent message to actually begin the transfer.  Do we want to
   keep passing the callback argument to this routine down the chain or just
   store that callback within iPOS for future reference.			*/

	errCode = iposELMCmdCoreReq( pCmd, IPOSELM_MSG_GET_EVENT_LOG,
	   (void *) &msgStruct, IPOSELM_GET_EVENT_LOG_TIMER_DELAY, uid, NULL );
	return( IPOS_SUCCESS );
#else
#ifdef ELM_DIAGNOSTIC_DEBUG
	printf( "\nLogic error, in iposGetRemoteEventLog on non-CO modem\n" );
#endif
	return( IPOS_ERROR );
#endif
}

#define IPOSELM_BME_WAS_RESET_TIMER_DELAY	6

int
iposNotifyBmeCrash( ipos_port_id pid, int bmeErrorCode, U8 uid, U32 uPar, iposELMCmdCb pCompleteFunc )
{
	int			 errCode;
	ipos_elm_cmd_handler	*pCmd;

	iposelm_bme_crash_req	 msgStruct;

#ifdef	VTU_O
	ipos_elm_co		*pCoPe = iposELMGetCoPe( pid );

	pCmd = &(pCoPe->cmdHandler);
#endif
#ifdef	VTU_R
	ipos_elm_cpe		*pCpePe = iposELMGetCpePe( pid );

	pCmd = &(pCpePe->cmdHandler);
#endif

	if (pCmd == NULL)
	{
#ifdef ELM_DIAGNOSTIC_DEBUG
		printf(
	   "\nProgramming problem in iposNotifyBmeCrash, pCmd is NULL for port b%dp%d\n",
	      pid.bmeNum, pid.portNum
		);
#endif
		return( IPOS_ERROR );
	}

	iposELMCmdSetCtrlHdr( &(msgStruct.ctrlHdr), IPOSELM_MSG_BME_WAS_RESET, IPOSELM_ACK_CMD, uid );
	msgStruct.crashReport.bmeErrorCode = bmeErrorCode;

	errCode = iposELMCmdCoreReq( pCmd, IPOSELM_MSG_BME_WAS_RESET,
	   (void *) &msgStruct, IPOSELM_BME_WAS_RESET_TIMER_DELAY, uid, pCompleteFunc );

	return( IPOS_SUCCESS );
}


/* only one ELM transfer should be in progress at one time
   if the event log memory is rearranged to allocate a separate buffer
   for each CPE modem in the CO modem, then this requirement can be
   relaxed to one ELM transfer per port at a time.			*/

int
iposELMXferInProgress( int portIndex )
{
	int	iter, startingPort, endingPort;
	int	retval = 0;

	if (portIndex == 0)
	{
		startingPort = 1;			/* do not check for index 0 */
		endingPort = IPOS_MAX_PORTS;
	}
	else {
		startingPort = portIndex;
		endingPort = portIndex;
	}

/* BEWARE:  No semaphore guards the ELM states.  The actual state
            may have changed by the time the application examines
	    the result from this routine.			*/

	for (iter = startingPort; iter <= endingPort; iter++)
	{
		ipos_elm_cmd_handler	*pCmd = NULL;		/* this is common, CO and CPE */
#ifdef	VTU_O
		ipos_elm_co		*pCoPe = iposELMIndxGetCo( iter - 1 );
		if (pCoPe == NULL)
		  continue;

		if (pCoPe->stateiFftp != IPOSELM_IFFTP_CO_IDLE)
		{
			printf( "ELM transfer in progress on port %d\n", iter );
			retval = 1;
		}
		pCmd = &(pCoPe->cmdHandler);
#endif
#ifdef	VTU_R
		ipos_elm_cpe	*pCpePe = iposELMIndxGetCpe( iter - 1 );

		if (pCpePe == NULL)
		  continue;
		if (pCpePe->stateiFftp != IPOSELM_IFFTP_CPE_IDLE)
		{
			printf( "ELM transfer in progress on port %d\n", iter );
			retval = 1;
		}

		pCmd = &(pCpePe->cmdHandler);
#endif

/* common, CO and CPE */

		if (retval == 0)
		  if (pCmd != NULL)				/* sanity check */
		    if (pCmd->stateCmd != IPOSELM_STATE_CMD_IDLE)
		    {
			printf( "ELM command in progress on port %d\n", iter );
			retval = 1;
		    }

		if (retval != 0)
		  return( retval );
	}

	return( 0 );
}

static S32
findchar( char *caddr, U32 maxsize, char tchar )
{
	U32	iter = 0;

	for (iter = 0; iter < maxsize; iter++)
	{
		if (*caddr == tchar)
		  return( iter );
		caddr++;
	}

	return( -1 );
}

#define  COUNT_UNINITIALIZED_BYTES	2

static int
compareWithUnitialized( char *caddr )
{
	int	retval = 0;
	char	uninitializedFlashMemory[ COUNT_UNINITIALIZED_BYTES ] = { 0377, 0377 };

	if (memcmp( caddr, uninitializedFlashMemory, COUNT_UNINITIALIZED_BYTES ) == 0)
	 retval = 1;

	return( retval );
}

static int
atEndEventLog( char *caddr )
{
	int	retval = 0;
	char	twoNulls[ 2 ] = { 0, 0 };

	if (memcmp( caddr, twoNulls, 2 ) == 0)
	 retval = 1;

	return( retval );
}

/* buffer to store an entry is 512 chars (ikanos/ispos/bmedebug/bmeEventLog.c)
   subtract 1 to account for the null byte expected at the end of the (max) 512 chars */

#define  MAX_ENTRY_LENGTH	511

U16
iposCountofEntriesInEventLog( char *pEventLog, U32 eventLogSize )
{
	char	*currAddr = pEventLog;
	U32	 remainingSize = eventLogSize;
	U16	 countofEntries = 0;
	int	 remainingLen = MAX_ENTRY_LENGTH;	/* maximum size of this variable */
	S32	 indexNull = 0;

/* event log with no entries is a special case ... */

	if (atEndEventLog( pEventLog ))
	  return( 0 );

	for (;;)
	{
		if (compareWithUnitialized( currAddr ))
		{
			break;
		}

		if (remainingSize <  MAX_ENTRY_LENGTH)
		  remainingLen = remainingSize;
		else
		  remainingLen = MAX_ENTRY_LENGTH;
		indexNull = findchar( currAddr, remainingLen, '\0' );
		if (indexNull < 1)
		{
			break;
		}

		countofEntries++;
		currAddr += indexNull;

		if (atEndEventLog( currAddr ))
		{
			break;
		}

		currAddr++;	/* skip over NULL *after* testing for end of event log */
		remainingSize = remainingSize - (indexNull + 1);
	}

	return( countofEntries );
}

U32
iposSizeofEventLog( char *pEventLog, U32 eventLogSize )
{
	char	*currAddr = pEventLog;
	U32	 remainingSize = eventLogSize;
	int	 remainingLen = MAX_ENTRY_LENGTH;	/* maximum size of this variable */
	S32	 indexNull = 0;
	U32	 currLen = 0;

/* event log with no entries is a special case ... */

	if (atEndEventLog( pEventLog ))
	  return( 0 );

	for (;;)
	{
		if (compareWithUnitialized( currAddr ))
		{
			break;
		}

		if (remainingSize <  MAX_ENTRY_LENGTH)
		  remainingLen = remainingSize;
		else
		  remainingLen = MAX_ENTRY_LENGTH;
		indexNull = findchar( currAddr, remainingLen, '\0' );

	   /* indexNull = -1    ==>  ran off the end of the event log w/o finding the end
	      indexNull = 0     ==>  at end of event log.  Should have been detected
				     on the previous iteration.				*/
		
		if (indexNull < 1)
		{
			break;
		}

		currAddr += indexNull;
		currLen += indexNull;

		if (atEndEventLog( currAddr ))
		{
			currLen += 2;	/* at end of event log.  account for BOTH nulls */
			break;
		}

		currAddr++;	/* skip over NULL *after* testing for end of event log */
		currLen++;
		remainingSize = remainingSize - (indexNull + 1);
	}

	return( currLen );
}
