/**************************************************************
$Workfile:   elmpe.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 protocol entity:
		
**************************************************************/

#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"


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

/***************************************************************
	core command/response handler for both CPE and CO	
 ***************************************************************/

int
iposELMCmdInit(ipos_elm_cmd_handler *pCmd, ipos_port_id pid)
{
	
	int	errCode=IPOS_SUCCESS;
	/* PID	*/
	pCmd->pid=pid;

	/* allocate the semphore	*/
	pCmd->semCmd=iposSemCreate();

	/* initial command state 	*/
	pCmd->stateCmd=IPOSELM_STATE_CMD_IDLE;

	/* counters	*/
	pCmd->cntRsnd=0;

	/* create the resend timer	*/
	errCode=iposCreateTmr(&(pCmd->ptmrRsnd));

	/* others	*/
	pCmd->resendTick=1;
	pCmd->msgLen=MAX_MSG_SIZE-1;
	pCmd->pCbCmd=NULL;

	return	errCode;
}

/* message utilities shared between CPE and CO	*/

int
msgCheckElmClass(U8 *pEocMsg)
{
	U8	msgType, ack;

	msgType=pEocMsg[ IPOSELM_MSGHDR_MSGTYPE ];
	ack=pEocMsg[ IPOSELM_MSGHDR_ACK ];

	/*printf( "in msgCheckElmClass, type: %d, at index %d, ack: %d, at index %d\n",
		 msgType, IPOSELM_MSGHDR_MSGTYPE, ack, IPOSELM_MSGHDR_ACK
	);*/

	if (msgType==IPOSELM_MSG_DATA_BLOCK || msgType==IPOSELM_MSG_ACK)
	{
		return	IPOSELM_MSG_FILE;
	}

	if ((msgType>= IPOSELM_MSG_CMDRESP_START) && (msgType<=IPOSELM_MSG_CMDRESP_END))
	{
		if(ack==IPOSELM_ACK_CMD)
		{
			return IPOSELM_MSG_COMMAND;
		}

		if(ack==IPOSELM_ACK_ACK || ack==IPOSELM_ACK_NACK)
		{
			return IPOSELM_MSG_RESPONSE;
		}

		/*return	IPOSELM_ERROR_UNKNOWN_MSG;*/
		return( -1 );

	}
	return( 0 );
}

/* setup the control header	*/

int
iposELMCmdSetCtrlHdr(iposelm_ctrl_hdr *pHdr, U8	msgType, U8 ackType, U8 uid)
{
	pHdr->genericHdr.pt=EOC_ELM_PROTOCOL;
	pHdr->genericHdr.msgType=msgType;
						/* length always filled by the parser	*/
	pHdr->uid=uid;
	pHdr->ack=ackType;

	return	IPOS_SUCCESS;
}

/*****************************************************************/
/*   message to struct programs - decoding, decoding, decoding   */
/*****************************************************************/

static int
decodeElmCtrlHeader(U8 *msg, iposelm_ctrl_hdr *ctrlHdr)
{
  int size = 0 ;
  
  /* msg header - protocol type */
  ctrlHdr->genericHdr.pt = *msg ;
  msg++ ;
  size++ ;

  /* msg header - message type */
  ctrlHdr->genericHdr.msgType = *msg ;
  msg++ ;
  size++ ;

  /* msg header - length of the message */
  ctrlHdr->genericHdr.length = *msg ;
  msg++ ;
  size++ ;

  /* User Id */
  ctrlHdr->uid = *msg ;
  msg++ ;
  size++ ;

  /* Ack */
  ctrlHdr->ack = *msg ;
  msg++ ;
  size++ ;

  return size ;

} /* decodeElMCtrlHeader() */

static int
decodeEventLogSelectionInfo( U8 *msg, iposelm_event_log_choice *pEventLogChoice )
{
	int size = 0;

	pEventLogChoice->eventLogOption = *msg;
	size++;

	return( size );
}

static int
decodeEventLogInfo( U8 *msg, iposelm_event_log_info *pEventLogInfo )
{
	int size = 0;

	memcpy( pEventLogInfo, msg, sizeof( iposelm_event_log_info ) );
	size += sizeof( iposelm_event_log_info );

	return( size );
}

static int
decodeSidInfo( U8 *msg, U8 *sid )
{
	int size = 0;

	*sid = *msg;
	size += sizeof( U8 );

	return( size );
}

static int
decodeXferCompleteInfo( U8 *msg, iposelm_transfer_info *pTransferInfo )
{
	int	size = 0;

	memcpy( pTransferInfo, msg, sizeof( iposelm_transfer_info ) );
	size += sizeof( iposelm_transfer_info );

	return( size );
}

static int
decodeBmeResetInfo( U8 *msg, iposelm_bme_crash_report *pCrashReport )
{
	int	size = 0;

	memcpy( pCrashReport, msg, sizeof( iposelm_bme_crash_report ) );
	size += sizeof( iposelm_bme_crash_report );

	return( size );
}

/*  decoding  ...  decoding  ...  decoding  */

int
iposELMMsg2Struct( U8 *pMsgBuf, void *pStructBuf, U8 *pMsgType)
{
  U8 ack = 0;
  int size = 0;
  int size2 = 0;
  int ieType = 0;
  int ieLen = 0;
  int decodedLen = 0;
  int msgLen = 0;

  *pMsgType = *(pMsgBuf+1) ;  /* Always 2nd byte in any message in message type */
  ack=pMsgBuf[ IPOSELM_MSGHDR_ACK ];

  msgLen = pMsgBuf[ IPOSELM_MSGHDR_MSGLEN ];

  decodedLen = 0 ;
  switch (*pMsgType)
  {  
    case IPOSELM_MSG_GET_EVENT_LOG:
    {
	if (ack == IPOSELM_ACK_CMD)
	{
		iposelm_select_event_log_req	*pSelectEventLogReq =
		  (iposelm_select_event_log_req *) pStructBuf;

	        size = decodeElmCtrlHeader(pMsgBuf, &(pSelectEventLogReq->ctrlHdr)) ;
	        pMsgBuf += size ;
	        decodedLen += size ;

	        ieType = *pMsgBuf ;
	        pMsgBuf += 1 ;
	        ieLen = *pMsgBuf ;
	        pMsgBuf += 1 ;
	        decodedLen += 2 ;

	    /*printf( "ie type is %d, ie len is %d in iposELMMsg2Struct\n", ieType, ieLen );*/

		if (ieType == EVENT_LOG_SELECTION_INFO)
		{
			size2 = decodeEventLogSelectionInfo(
					pMsgBuf, &(pSelectEventLogReq->eventLogChoice)
			);
			size += size2;
			pMsgBuf += size2;
			decodedLen += size2;
	        }
		else {
#ifdef ELM_DIAGNOSTIC_DEBUG
			printf( "Unexpected ie type %d for IPOSELM_MSG_GET_EVENT_LOG / command\n",
				 ieType
			);
#endif
			return( IPOS_ERROR );
		}
	}
	else {
		iposelm_select_event_log_resp	*pSelectEventLogResp =
			(iposelm_select_event_log_resp *) pStructBuf;

          /* Decode Ctrl Header */

		size = decodeElmCtrlHeader(pMsgBuf, &(pSelectEventLogResp->ctrlHdr)) ;
		pMsgBuf += size ;
		decodedLen += size ;

	        ieType = *pMsgBuf ;
	        pMsgBuf += 1 ;
	        ieLen = *pMsgBuf ;
	        pMsgBuf += 1 ;
	        decodedLen += 2 ;

	    /*printf( "ie type is %d, ie len is %d in iposELMMsg2Struct\n", ieType, ieLen );*/

		if (ieType == EVENT_LOG_INFO)
		{
			size2 = decodeEventLogInfo(
					pMsgBuf, &(pSelectEventLogResp->eventLogInfo)
			);
		}
	}
    }
    break;

    case IPOSELM_MSG_FILE_TRANSFER:
    {
	if (ack == IPOSELM_ACK_CMD)
	{
		iposelm_xfer_event_log_req	*pXferEventLogReq =
			(iposelm_xfer_event_log_req *) pStructBuf;

	        size = decodeElmCtrlHeader(pMsgBuf, &(pXferEventLogReq->ctrlHdr)) ;
	        pMsgBuf += size ;
	        decodedLen += size ;

		ieType = *pMsgBuf++;
		ieLen = *pMsgBuf++;
	        decodedLen += 2;

		if (ieType == SID_INFO)
		{
			size2 = decodeSidInfo( pMsgBuf, &(pXferEventLogReq->sid) );
			size += size2;
			pMsgBuf += size2;
			decodedLen += size2;
		}
		else
		{
#ifdef ELM_DIAGNOSTIC_DEBUG
			printf(
		   "Unexpected ie type of %d for IPOSELM_MSG_FILE_TRANSFER / command\n", ieType
			);
#endif
			return( IPOS_ERROR );
		}
    	}
	else if (ack == IPOSELM_ACK_NACK)
	{
		iposelm_xfer_event_log_req	*pXferEventLogReq =
			(iposelm_xfer_event_log_req *) pStructBuf;

	        size = decodeElmCtrlHeader(pMsgBuf, &(pXferEventLogReq->ctrlHdr)) ;
	        pMsgBuf += size ;
	        decodedLen += size ;
	    /*printf( "Decoding NACK for IPOSELM_MSG_FILE_TRANSFER\n" );*/

	   /* The session ID is irrelevent if CPE says no */

		pXferEventLogReq->sid  = 0;
	}

   /* the CPE is expected to ACK an IPOSELM_MSG_FILE_TRANSFER with data blocks */

#ifdef ELM_DIAGNOSTIC_DEBUG
	else
	  printf( "Unrecognized ACK of %d for IPOSELM_MSG_FILE_TRANSFER\n", ack );
#endif
    }
    break;

    case IPOSELM_MSG_FILE_TR_COMPLETE:
    {
	if (ack == IPOSELM_ACK_CMD)
	{
		iposelm_xfer_complete_req	*pXferCompleteReq =
			(iposelm_xfer_complete_req *) pStructBuf;

	        size = decodeElmCtrlHeader(pMsgBuf, &(pXferCompleteReq->ctrlHdr));
	        pMsgBuf += size ;
	        decodedLen += size ;

		ieType = *pMsgBuf;
		pMsgBuf++;
		ieLen = *pMsgBuf;
		pMsgBuf++;
		decodedLen += 2;

		if (ieType == XFER_COMPLETE_CMD_INFO)
		{
			size2 = decodeXferCompleteInfo( pMsgBuf, &(pXferCompleteReq->transferInfo));
		        pMsgBuf += size2;
		        decodedLen += size2;
			size += size2;
		}
		else
		{
#ifdef ELM_DIAGNOSTIC_DEBUG
			printf(
		   "Unexpected IE type %d for IPOSELM_MSG_FILE_TR_COMPLETE / command\n", ieType
			);
#endif
			return( IPOS_ERROR );
		}
	}
	else
	{
		iposelm_xfer_complete_resp	*pXferCompleteResp =
			(iposelm_xfer_complete_resp *) pStructBuf;

	        size = decodeElmCtrlHeader(pMsgBuf, &(pXferCompleteResp->ctrlHdr));
	        pMsgBuf += size ;
	        decodedLen += size ;

		ieType = *pMsgBuf;
		pMsgBuf++;
		ieLen = *pMsgBuf;
		pMsgBuf++;
		decodedLen += 2;

		if (ieType == XFER_COMPLETE_RESP_INFO)
		{
			size2 = decodeXferCompleteInfo( pMsgBuf, &(pXferCompleteResp->transferInfo));
		        pMsgBuf += size2;
		        decodedLen += size2;
			size += size2;

			size2 = decodeEventLogInfo(
					pMsgBuf, &(pXferCompleteResp->rcvdEventLog)
			);
		}
		else
		{
#ifdef ELM_DIAGNOSTIC_DEBUG
			printf(
		   "Unexpected IE type %d for IPOSELM_MSG_FILE_TR_COMPLETE / ack\n", ieType
			);
#endif
			return( IPOS_ERROR );
		}
	}
    }
    break;

    case IPOSELM_MSG_BME_WAS_RESET:
    {
	if (ack == IPOSELM_ACK_CMD)
	{
		iposelm_bme_crash_req	*pBmeResetReport =
				(iposelm_bme_crash_req *) pStructBuf;

	        size = decodeElmCtrlHeader(pMsgBuf, &(pBmeResetReport->ctrlHdr));
	        pMsgBuf += size ;
	        decodedLen += size ;

		ieType = *pMsgBuf;
		pMsgBuf++;
		ieLen = *pMsgBuf;
		pMsgBuf++;
		decodedLen += 2;

		if (ieType == BME_ERROR_CODE_INFO)
		{
			size2 = decodeBmeResetInfo( pMsgBuf, &(pBmeResetReport->crashReport) );
		}
		else
		{
#ifdef ELM_DIAGNOSTIC_DEBUG
			printf(
		   "Unexpected IE type %d for IPOSELM_MSG_BME_WAS_RESET / command\n", ieType
			);
#endif
			return( IPOS_ERROR );
		}
	}
	else
	{
		iposelm_ctrl_hdr	*pCtrlHdr = (iposelm_ctrl_hdr *) pStructBuf;

		size = decodeElmCtrlHeader(pMsgBuf, pCtrlHdr );
	}
    }

    default:
	break;
  }

  return IPOS_SUCCESS;
}

/*****************************************************************/
/*   struct to message programs - encoding, encoding, encoding   */
/*****************************************************************/

static int encodeElmCtrlHeader(iposelm_ctrl_hdr ctrlHdr, U8 **msg)
{
  int size = 0 ;

  /* Mesg header - Protocol Type */
  **msg = ctrlHdr.genericHdr.pt ;
  *msg += 1 ;
  size++ ;

  /* Mesg header - Message Type */
  **msg = ctrlHdr.genericHdr.msgType ;
  *msg += 1 ;
  size++ ;
    
  /* Mesg header - Length */
  **msg = ctrlHdr.genericHdr.length ;
  *msg += 1 ;
  size++ ;

  /* User Id */
  **msg = ctrlHdr.uid ;
  *msg += 1 ;
  size++ ;

  /* Ack */
  **msg = ctrlHdr.ack ;
  *msg += 1 ;
  size++ ;

  return size ;
} /* encodeElmCtrlHeader() */

static int
encodeEventLogSelectionInfo( iposelm_event_log_choice eventLogChoice, U8 **msg)
{
	int	size = 0;

	**msg = eventLogChoice.eventLogOption;
	*msg  += 1 ;
	size++ ;

	return( size );
}

static int
encodeEventLogInfo( iposelm_event_log_info *pEventLogInfo, U8 **msg)
{
	int	size = 0;

	memcpy( *msg, pEventLogInfo, sizeof( iposelm_event_log_info ) );
	*msg  += sizeof( iposelm_event_log_info );
	size += sizeof( iposelm_event_log_info );

	return( size );
}

static int
encodeSidInfo( U8 sid, U8 **msg )
{
	int	size = 0;

	memcpy( *msg, &sid, sizeof( U8 ) );
	*msg += sizeof( U8 );
	size += sizeof( U8 );

	return( size );
}

static int
encodeXferCompleteInfo( iposelm_transfer_info *pTransferInfo, U8 **msg)
{
	int	size = 0;

	memcpy( *msg, pTransferInfo, sizeof( iposelm_transfer_info ) );
	*msg  += sizeof( iposelm_transfer_info );
	size += sizeof( iposelm_transfer_info );

	return( size );
}

static int
encodeBmeResetInfo( iposelm_bme_crash_report *pCrashReport, U8 **msg )
{
	int	size = 0;

	memcpy( *msg, pCrashReport, sizeof( iposelm_bme_crash_report ) );
	*msg  += sizeof( iposelm_bme_crash_report );
	size += sizeof( iposelm_bme_crash_report );

	return( size );
}

/*  encoding  ...  encoding  ...  encoding
    preparing to send out a message ...    */

int
iposELMStruct2Msg(void *pStructBuf, U8 msgType, U8 *pBuf )
{
	int	 size = 0;
	int	 size2 = 0;
	U8	 ack = 0;
	U8	*pCurrBuf = pBuf;
	U8	**pMsgBuf = &pCurrBuf;

	switch (msgType)
	{
	  case IPOSELM_MSG_GET_EVENT_LOG:
	  {
		iposelm_select_event_log_req	*pSelectEventLogReq =
			(iposelm_select_event_log_req *) pStructBuf;

		ack = pSelectEventLogReq->ctrlHdr.ack;
		if (ack == IPOSELM_ACK_CMD)
		{
			size2 = encodeElmCtrlHeader( pSelectEventLogReq->ctrlHdr, pMsgBuf );
			size += size2;

			**pMsgBuf = EVENT_LOG_SELECTION_INFO;
			*pMsgBuf += 1;			/* autoincrement causes compiler warning */
			size++;

			*pMsgBuf += 1;
			size++;

			size2 = encodeEventLogSelectionInfo(
					pSelectEventLogReq->eventLogChoice, pMsgBuf
			);
			size += size2;
			*(*pMsgBuf - size2 - 1) = size2;
		}
		else
		{
			iposelm_select_event_log_resp	*pSelectEventLogResp =
				(iposelm_select_event_log_resp *) pStructBuf;

			size2 = encodeElmCtrlHeader( pSelectEventLogResp->ctrlHdr, pMsgBuf );
			size += size2;

			**pMsgBuf = EVENT_LOG_INFO;
			*pMsgBuf += 1;			/* autoincrement causes compiler warning */
			size++;

			*pMsgBuf += 1;			/* room for secondary size, */
			size++;				/* about to be calculated, below */

			size2 = encodeEventLogInfo(
					&pSelectEventLogResp->eventLogInfo, pMsgBuf
			);
			size += size2;
			*(*pMsgBuf - size2 - 1) = size2;
		}
	  }
	  break;

	  case IPOSELM_MSG_FILE_TRANSFER:
	  {
		iposelm_ctrl_hdr	*pCtrlHdr = (iposelm_ctrl_hdr *) pStructBuf;

	  /* No additional data at this time.  The CO is requesting a file from the CPE
	     It is expected to have verified it can receive the file before requesting it */

		ack = pCtrlHdr->ack;
		if (ack == IPOSELM_ACK_CMD)
		{
			iposelm_xfer_event_log_req	*pXferEventLogReq =
				(iposelm_xfer_event_log_req *) pStructBuf;

			size2 = encodeElmCtrlHeader( pXferEventLogReq->ctrlHdr, pMsgBuf );
			size += size2;

			**pMsgBuf = SID_INFO;		/* ie type */
			(*pMsgBuf)++;
			size++;

			(*pMsgBuf)++;			/* room for ie length */
			size++;

			size2 = encodeSidInfo( pXferEventLogReq->sid, pMsgBuf );
			size += size2;
			*(*pMsgBuf - size2 - 1) = size2;	/* ie length */
		}

	   /* no ACK_ACK - the CPE is to send data blocks instead
	      a ACK_NACK is possible to tell the CO it ain't gonna happen */

		else if (ack == IPOSELM_ACK_NACK)
		{
			iposelm_xfer_event_log_req	*pXferEventLogReq =
				(iposelm_xfer_event_log_req *) pStructBuf;

			size2 = encodeElmCtrlHeader( pXferEventLogReq->ctrlHdr, pMsgBuf );
			size += size2;
		}
	  }
	  break;

	  case IPOSELM_MSG_FILE_TR_COMPLETE:
	  {
		iposelm_ctrl_hdr	*pCtrlHdr = (iposelm_ctrl_hdr *) pStructBuf;

		ack = pCtrlHdr->ack;
		if (ack == IPOSELM_ACK_CMD)		/* CPE */
		{
			iposelm_xfer_complete_req	*pXferCompleteReq =
				(iposelm_xfer_complete_req *) pStructBuf;

			size2 = encodeElmCtrlHeader( pXferCompleteReq->ctrlHdr, pMsgBuf );
			size += size2;

			**pMsgBuf = XFER_COMPLETE_CMD_INFO;
			*pMsgBuf += 1;
			size++;

			*pMsgBuf += 1;	/* room for el size, inserted below */
			size++;

			size2 = encodeXferCompleteInfo(
					&(pXferCompleteReq->transferInfo), pMsgBuf
			);

			size += size2;
			*(*pMsgBuf - size2 - 1) = size2;
		}
		else					/* CO */
		{					/* NACK not expected here */
			iposelm_xfer_complete_resp	*pXferCompleteResp =
				(iposelm_xfer_complete_resp *) pStructBuf;

			size2 = encodeElmCtrlHeader( pXferCompleteResp->ctrlHdr, pMsgBuf );
			size += size2;

			**pMsgBuf = XFER_COMPLETE_RESP_INFO;
			*pMsgBuf += 1;
			size++;

			*pMsgBuf += 1;
			size++;

			size2 = encodeXferCompleteInfo(
					&(pXferCompleteResp->transferInfo), pMsgBuf
			);

			size2 += encodeEventLogInfo(
 					&(pXferCompleteResp->rcvdEventLog), pMsgBuf
			);

			size += size2;
			*(*pMsgBuf - size2 - 1) = size2;
		}
	  }
	  break;

	  case IPOSELM_MSG_BME_WAS_RESET:
	  {
		iposelm_ctrl_hdr	*pCtrlHdr = (iposelm_ctrl_hdr *) pStructBuf;

		ack = pCtrlHdr->ack;
		if (ack == IPOSELM_ACK_CMD)		/* CPE */
		{
			iposelm_bme_crash_req	*pBmeResetReport =
				(iposelm_bme_crash_req *) pStructBuf;

			size2 = encodeElmCtrlHeader( pBmeResetReport->ctrlHdr, pMsgBuf );
			size += size2;

			**pMsgBuf = BME_ERROR_CODE_INFO;
			*pMsgBuf += 1;
			size++;

			*pMsgBuf += 1;	/* room for el size, inserted below */
			size++;

			size2 = encodeBmeResetInfo(
					&(pBmeResetReport->crashReport), pMsgBuf
			);

			size += size2;
			*(*pMsgBuf - size2 - 1) = size2;
	  	}
		else
		{
			size2 = encodeElmCtrlHeader( *pCtrlHdr, pMsgBuf );
			size += size2;
		}
	  }
	  break;

	  default:
#ifdef ELM_DIAGNOSTIC_DEBUG
		printf( "Unknown message type %d in iposELMStruct2Msg\n", msgType );
#endif
		break;
	}

	pBuf[ IPOSELM_MSGHDR_MSGLEN ] = size;
	return size + 1;
}

/****************************************************************************
	command/response state machine
****************************************************************************/
static U8
iposELMCmdGetState(ipos_elm_cmd_handler *pCmd)
{
	return	pCmd->stateCmd;
}

int
iposELMCmdStateUpdate(ipos_elm_cmd_handler *pCmd, int	Event)
{
	U8	curState;

	curState=pCmd->stateCmd;

	switch(curState)
	{

	  case IPOSELM_STATE_CMD_IDLE:

		if(Event==ELM_EVNT_CMD_SENT)
		{
			 pCmd->stateCmd=IPOSELM_STATE_CMD_INPROGRESS;
		}

		break;

	  case IPOSELM_STATE_CMD_INPROGRESS :

		if(Event==ELM_EVNT_RESP_RCVD || Event==ELM_EVNT_RESP_EXPIRED)
		{
			pCmd->stateCmd=IPOSELM_STATE_CMD_IDLE;
		}

		break;

	default:

		break;
	}

	return	IPOS_SUCCESS;
}

int
iposELMCmdCoreResp( ipos_elm_cmd_handler *pCmd, U8 *pMsgRcvd )
{
	U8	msgType, state;
	int	errCode;

	state = iposELMCmdGetState( pCmd );
	if (state == IPOSELM_STATE_CMD_IDLE )
	{
#ifdef ELM_DIAGNOSTIC_DEBUG
		printf( "\nport b%dp%d, response to command called in idle state\n",
			 pCmd->pid.bmeNum, pCmd->pid.portNum
		);
#endif
		return( IPOS_SUCCESS );
	}

	errCode = msgMatchCmdResp( pMsgRcvd, pCmd->msgRsn );
	if (errCode != IPOS_SUCCESS)
	{
		return( IPOS_ERROR );
	}

    /* change the state	*/

	iposELMCmdStateUpdate(pCmd, ELM_EVNT_RESP_RCVD);

    /* reset the timer	*/

	errCode=iposResetTmr(pCmd->ptmrRsnd);
	if(errCode!=IPOS_SUCCESS)
	{
		return IPOS_ERROR;
	}

   /* Give the semaphore here ... for some commands,
      the response to a response is another command */

	errCode = iposSemGive( pCmd->semCmd );

	errCode = iposELMMsg2Struct( pMsgRcvd, &(pCmd->respStruct), &msgType );
    /*printf( "port b%dp%d, received ack for %d\n",
	       pCmd->pid.bmeNum, pCmd->pid.portNum, msgType );*/
	if(errCode!=IPOS_SUCCESS)
	{
		return( IPOS_ERROR );
	}

	if (msgType == IPOSELM_MSG_GET_EVENT_LOG)
	{
#ifdef VTU_O
		iposELMCOGetEventLogResult( pCmd );
#else
#ifdef ELM_DIAGNOSTIC_DEBUG
		printf(
	   "\nELM message type of %d (IPOSELM_MSG_GET_EVENT_LOG) not supported on non-CO modem\n",
	    msgType
		);
#endif
#endif
	}

/* CPE comes here when the CO acks the File Transfer Complete.
   callback completes the transfer, sets state to idle  */

	else if (msgType == IPOSELM_MSG_FILE_TR_COMPLETE)
	{
		/* issue the callback	*/
		if (pCmd->pCbCmd != NULL) 
		{
			pCmd->pCbCmd(pCmd->pid, pCmd->uidResp, IPOS_SUCCESS, (void *)pCmd->respStruct);
		}
	}

	return( IPOS_SUCCESS );
}

static void
iposELMCmdCoreResend(U32 arg1, U32 arg2)
{
	int			 errCode;
	int			 msgLen;
	ipos_elm_cmd_handler	*pCmd;

	pCmd=(ipos_elm_cmd_handler *) arg1;

	msgLen=pCmd->msgLen;
	/*printf(
   "iposELMCmdCoreResend called, current tick: %d, message length: %d, resend count: %d\n",
    iposTickGet(), msgLen, pCmd->cntRsnd );*/

	if(pCmd->cntRsnd>0)
	{
		pCmd->cntRsnd--;
		
        /* resend the command	*/

		errCode=iposELMSendMsg(pCmd->pid, pCmd->msgRsn, msgLen );

		if(errCode!=IPOS_SUCCESS)
		{
#ifdef ELM_DIAGNOSTIC_DEBUG
			printf( "ELM: cmd resend error for port b%dp%d\n",
				 pCmd->pid.bmeNum, pCmd->pid.portNum );
#endif
			return;
		}
		
	/* set the resend timer	*/

		errCode=iposSetTmr(pCmd->ptmrRsnd,pCmd->resendTick,iposELMCmdCoreResend,(U32) pCmd, (U32) 0);
		if(errCode!=IPOS_SUCCESS)
		{
#ifdef ELM_DIAGNOSTIC_DEBUG
			printf( "ELM: cmd resend timer error for port b%dp%d\n",
				 pCmd->pid.bmeNum, pCmd->pid.portNum );
#endif
			return;
		}
	}
	else 		/* time out: callback with error */
	{
		/* issue the callback	*/
		if (pCmd->pCbCmd!=NULL) 
		{
			pCmd->pCbCmd(pCmd->pid, pCmd->uidResp, IPOS_ERROR, (void *)pCmd->respStruct);
		}

	/* change the state		*/

		iposELMCmdStateUpdate(pCmd, ELM_EVNT_RESP_EXPIRED);

	/* give the semaphore	*/

		errCode=iposSemGive(pCmd->semCmd);
		if(errCode!=OS_SUCCESS)
		{
#ifdef ELM_DIAGNOSTIC_DEBUG
			printf( "ELM: sem give error on resend timeout for port b%dp%d\n",
				 pCmd->pid.bmeNum, pCmd->pid.portNum );
#endif
		}

	   /*printf("resend ends for port b%dp%d ...\n", pCmd->pid.bmeNum, pCmd->pid.portNum );*/

		if (pCmd->msgRsn[ IPOSELM_MSGHDR_MSGTYPE ] == IPOSELM_MSG_GET_EVENT_LOG)
		{
#ifdef VTU_O
			ipos_elm_co	*pCoPe = iposELMGetCoPe( pCmd->pid );

		    /*printf( "IPOSELM_MSG_GET_EVENT_LOG timed out, checking callback\n" );*/
			if (pCoPe != NULL)
			  if (pCoPe->pCbEventLogComplete != NULL)
			  {
			  	(pCoPe->pCbEventLogComplete)(
					pCmd->pid, pCmd->uidResp, IPOS_ERROR, NULL
				);
			  }
#else
#ifdef ELM_DIAGNOSTIC_DEBUG
			printf(
	   "\nLogic error:  in ELM CmdCoreResend with message type of %d"
	   "\n(IPOSELM_MSG_GET_EVENT_LOG), not supported on non-CO modem\n",
	    pCmd->msgRsn[ IPOSELM_MSGHDR_MSGTYPE ]
			);
#endif
#endif /* VTU_O */
		}
	}
}

/***************************************************************
		 send a EOC packet	
 ***************************************************************/

/* Unlike the RFM version, this one has the memory address 2nd, length 3rd */

int
iposELMSendMsg(ipos_port_id pid, U8 *pMsg, U8 msgLen )
{
	/*int	iter;			used for debug print only */
	int	errCode;

	errCode = iposEocSendDataStream(pid, msgLen+1, pMsg, 0, 0, NULL);

	/*printf( "for b%dp%d at %ld", pid.bmeNum, pid.portNum, iposTickGet() );
	for (iter = 0; iter <= 10; iter++)
	  printf( " %d", *(pMsg + iter ) );
	printf( "\n" );*/

	return( errCode );
}

int
iposELMCmdCoreReq(
	ipos_elm_cmd_handler *pCmd,
	int cmdType,
	void *pMsgStruct,
	int resendTimes,
	U8 uid,
	void *pCb
)
{
	U8	msgLen;
	int	errCode;

	errCode = iposSemTake( pCmd->semCmd, IPOS_WAIT_FOREVER );
	if (errCode != OS_SUCCESS)
	{
		return( -1 );
	}

	pCmd->uPar=uid;
	pCmd->pCbCmd= (iposELMCmdCb) pCb;

	msgLen = iposELMStruct2Msg( pMsgStruct, cmdType, pCmd->msgRsn );

	errCode = iposELMSendMsg( pCmd->pid, pCmd->msgRsn, msgLen );

    /* reset the resend counter	*/
    /* not all commands require resend on lack of ack */

	if (resendTimes > 0)
	{
		pCmd->cntRsnd=IPOSELM_DATABLK_RESEND;	/* max count to try resend */
		pCmd->resendTick=resendTimes;		/* num of ticks before resend	*/
		errCode=iposSetTmr(pCmd->ptmrRsnd,resendTimes,iposELMCmdCoreResend,(U32)pCmd,(U32)0);
	}

    /* change the state */

	iposELMCmdStateUpdate(pCmd, ELM_EVNT_CMD_SENT);

	return( IPOS_SUCCESS );
}
