/**************************************************************
$Workfile:   bmeEventLog.c  $
***************************************************************
**
** FIRMWARE, Copyright (C) 2000-2002
** Ikanos Communication, 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:51 $
***************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include "ipos.h"
#include "mem.h"
#include "dapi.h"
#include "apilocal.h"
#include "elmdef.h"
#include "host_ver.h"

/*******  Defines               ***************************************/

#define NUM_OF_EVENTS           1000    /* number of events in database */
#define MAX_EVENT_PARAMS        8       /* number of parameters */
#define NUM_OF_EVENTS_TO_PRINT  20      /* default number of events to print */

/* Get a seconds tick */
extern unsigned int getTimerSeconds ();
extern unsigned int getSysClkTicks(void);
extern tBmeStruct  	BmeStruct[IPOS_MAX_BME];	  		 /* BME objects for direct API */
extern ipos_api_bme	apiBme[];
extern HostCpuEndianType;

#define GET_TIME_STAMP()      getSysClkTicks()

/*
    Stub macros for now.
    Need to replace with real functions.
 */
#define GET_RET_ADDR()        (void *)0xFFFFFFFF


/*******  Constants             ***************************************/

/*******  Local Data Structures ***************************************/

/* Time of day structure */
typedef struct
{
	UINT8 hours;
	UINT8 minutes;
	UINT8 seconds;
} tTime;

/* Structure to hold an event */
typedef struct
{
    UINT8    	logType;        /* type of log event (info, debug, etc )*/
    UINT32      timeStamp;      /* system time stamp*/
    void *      retAddr;        /* return address to calling function */
    char *      eventString;    /* the string related to the event */
    int         varList[MAX_EVENT_PARAMS]; /* the variables for the string */
} tEvent;

/* Structure to hold the event database */
typedef struct
{
    UINT16    	current;                /* points to the head of queue*/
    tEvent      entry[NUM_OF_EVENTS];
} tEventLogDB;

/*******  External Functions    ***************************************/

/*******  Global Data           ***************************************/

/*******  Local Data            ***************************************/
static char varStr[7][256];
static int globalBmeNum=1;

/*
    Event Log Database
    This holds all the event entries.  This database is a circular
    queue.  The head of the queue is pointed to by the 'current' pointer.
*/
tEventLogDB eventLogDB;

/*******  Local Functions       ***************************************/

void secondsToTime(UINT16 sec, tTime *retTime);
void setEvlogBme (BYTE eBme);
int getDataPIO (unsigned long addr, unsigned int len, char *buf);
extern void doDmaRead (int bmeNum, UINT32 srcAddr, UINT32 codeLen,
                       BYTE * BmeCodeArrayPtr, int destFifoFlag);

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

          L O C A L    F U N C T I O N S

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

/******************************************************************************
* Function      : printOneEvent
* Parameters    : num - the event to print.
* Return        : Nothing
* Description   : This function prints out an entry in the event logger
*                 database.  The first entry start with the index '0'.
******************************************************************************/
void printOneEvent(int num)
{
  char buf[256];
  tEvent *prEvent;
  char *ptr;
  int i;

  /* Check to see if the entry is valid */
  if (eventLogDB.entry[num].eventString)
  {
    /* point to entry specified by num */
    prEvent = &eventLogDB.entry[num];

		printf("%10lu [0x%08x] -> ", (unsigned long)prEvent->timeStamp,
           (int)(prEvent->retAddr));

    if (prEvent->retAddr)
    {
      /* print event string and parameters */
      doDmaRead (globalBmeNum, (unsigned long)prEvent->eventString, 255, buf, 0);
      buf[255] = 0;
      i = 0;
      ptr = buf;
      while((i < 7) && ((ptr = strchr(ptr, '%')) != NULL))
      {
        ptr++;
        if ((*ptr == 's') || (*ptr == 'S'))
        {
          doDmaRead (globalBmeNum, prEvent->varList[i], 255, varStr[i], 0);
          varStr[i][255] = 0;
          prEvent->varList[i] = (int)varStr[i];
          i++;
        }
        else
        {
          if (*ptr != '%')
            i++;
        }
      }

	    printf(buf,
               prEvent->varList[0],
               prEvent->varList[1],
               prEvent->varList[2],
               prEvent->varList[3],
               prEvent->varList[4],
               prEvent->varList[5],
               prEvent->varList[6],
               prEvent->varList[7]);
    }


    printf("\n");
  }

} /* End of PrintOneEvent() */


/******************************************************************************
* Function      : printEvents
* Parameters    : num - number of events to show ending with the lastest.
*                        A '0' will print all the events currently in the
*                        database.
* Return        : Nothing
* Description   : This command prints out events in the database.  If a zero is
*                 given as the parameter then it will print out the last 'n'
*                 events.  'n' is specified by the variable 'eventsToDisplay'.
*                 If any other number is given as the paramter than it will
*                 print out that many events.
*                 Events are printed in reverse order.
******************************************************************************/
void printEvents(unsigned int num)
{
  unsigned int i;

  if (num == 0)
    num = NUM_OF_EVENTS;
  i = eventLogDB.current;

  do
  {
    printOneEvent (i);
    if (i > 0)
      i--;
    else
      i = NUM_OF_EVENTS - 1;
  } while (--num > 0);
} /* End of printEvents() */

/******************************************************************************
* Function      : printLastEvent
* Parameters    :
* Return        : Nothing
* Description   : This command prints the last event that was entered into the
*                 database.
******************************************************************************/
void printLastEvent(void)
{
    printOneEvent(eventLogDB.current);

} /* End of printLastEvent() */


/******************************************************************************
* Function      : secondToTime
* Parameters    : sec - seconds to convert to time
* Return        : retTime - time in hrs, mins, secs.  Second parameter.
* Description   : This function converts seconds into a time format.
******************************************************************************/
void secondsToTime(UINT16 sec, tTime *retTime)
{
	retTime->seconds = sec % 60;
	retTime->minutes = abs(((sec)/60) % 60);
	retTime->hours   = abs((sec/60/60));

} /* End of secondToTime() */

/******************************************************************************
* Function      : dump Event log
* Parameters    : number of entries
* Return        : None
* Description   : This function dump (short cut) the event log
******************************************************************************/
extern void iPosPrintVer(int bmeNum);

void dump (int iNum)
{
  iPosPrintVer(globalBmeNum);

	iposSemTake( apiBme[globalBmeNum].semBme ,IPOS_WAIT_FOREVER);

  if ( iNum )
	{
		printEvents ((unsigned int) iNum);
  }
	else
	{
		printEvents (100);
	}
	iposSemGive (apiBme[globalBmeNum].semBme);
}


#define RCPU_DBG_EVENT_LOG				(0x40008328)
#define RCPU_DBG_EVENT_LOG_SIZE			(0x4000832c)

int getdump (int bmeNum)
{
	UINT32 srcAddr;
	UINT32 codeLen;
	unsigned char *buff;
	UINT32 i;

	if(bmeNum){
		setEvlogBme(bmeNum);
	}

	iposSemTake( apiBme[globalBmeNum].semBme ,IPOS_WAIT_FOREVER);

	getDataPIO (RCPU_DBG_EVENT_LOG,      4, (char *) &srcAddr);
	getDataPIO (RCPU_DBG_EVENT_LOG_SIZE, 4, (char *) &codeLen);

	if(srcAddr > 0x7FFFFFFF || codeLen > 0x20000){
		printf("Cannot retrieve eventLog from 0x%08lx size 0x%lx\n", srcAddr, codeLen);

		iposSemGive (apiBme[globalBmeNum].semBme);
		return -1;
	}

	buff = (unsigned char *)(&eventLogDB);
	printf("Uploading Log from 0x%lx  size 0x%lx:\n", srcAddr, codeLen);
	while (codeLen > 0)  {
		printf(".");
		if (codeLen > 256)
			i = 256;
		else
			i = codeLen;
		codeLen -= i;
    doDmaRead (globalBmeNum, srcAddr, i, buff, 0);
		srcAddr += i;
		buff += i;
	}
	printf("\nDone\n");
	iposSemGive (apiBme[globalBmeNum].semBme);
	return 0;
}

void setEvlogBme (BYTE eBme)
{
  globalBmeNum = eBme;
}

int getEvlogBme (void)
{
  return (globalBmeNum);
}

int getDataPIO (unsigned long addr, unsigned int len, char *buf)
{
	HWAPI_RET_VAL retVal = HWAPI_FAILED;
        union{
          UINT32 data32;
          char buf[4];
        } data;
	int i;
	BmeStruct[globalBmeNum].BMEbaseAddr = BME_BASE_ADDR;
	BmeStruct[globalBmeNum].BmeNum	  = globalBmeNum;


    if ((i = (addr & 0x3)) != 0)
    {
      addr &= ~0x3;
      retVal = bmePioRead (&BmeStruct[globalBmeNum],  addr, (U32 *)&(data.data32));
      switch (i)
      {
        case 1: *buf++ = data.buf[1]; len--;
        case 2: *buf++ = data.buf[2]; len--;
        case 3: *buf++ = data.buf[3]; len--;
        default: break;
      }
      addr += 4;
    }

    while (len > 0)
    {
	/* PIO read from DMA_SAR reg */
	retVal = bmePioRead (&BmeStruct[globalBmeNum],  addr, (U32 *)&(data.data32));
        addr += 4;
	i = 0;
	do
        {
	  *buf++ = data.buf[i++];
        } while ((--len > 0) && (i < 4));
    }

  return(retVal);
}


static int getOneEvent( unsigned int num, char *memAddr, size_t memSize )
{
	char	 buf[256];
	tEvent	*prEvent;
	char	*ptr;
	int	 i;
	int	 estimatedSize = 0;
	int	 retval = IPOS_SUCCESS;

    /*printf( "Getting BME event %d\n", num );*/

  /* Check to see if the entry is valid */

	if (eventLogDB.entry[num].eventString)
	{

    /* point to entry specified by num */

		prEvent = &eventLogDB.entry[num];

		sprintf( buf, "%10lu [0x%08x] -> ",
			       (unsigned long)prEvent->timeStamp, (int)(prEvent->retAddr)
		);
		strcpy( memAddr, buf );
		memAddr += strlen( buf );
		estimatedSize += strlen( buf );
		*memAddr = '\0';		/* should already be NUL, but let's be sure */

		if (prEvent->retAddr)
		{

	      /* get the format string for printf */

			doDmaRead (globalBmeNum, (unsigned long)prEvent->eventString, 255, buf, 0);
			buf[255] = 0;
			estimatedSize += strlen( buf );
			i = 0;
			ptr = buf;

	      /* scan through the string for up to 7 conversion operators */

			while((i < 7) && ((ptr = strchr(ptr, '%')) != NULL))
			{
				ptr++;
				if ((*ptr == 's') || (*ptr == 'S'))
				{

			    /* strings need to be read from the HIC -
			       all we have now is the address */

					doDmaRead (globalBmeNum, prEvent->varList[i], 255, varStr[i], 0);
					varStr[i][255] = 0;
		/*printf( "\tparameter %d is %d chars long\n", i, strlen( varStr[ i ] ) );*/
		/*printf( "\tparameter %d: %s\n", i, varStr[ i ] );*/
					prEvent->varList[i] = (int)varStr[i];
					i++;

			    /* estimated size increases by the length of the string,
			       less 2 for the %s conversion operator */

					estimatedSize += (strlen( varStr[ i ] ) - 2);
        			}
				else
				{

			    /* other parameters are already in the database */
			    /* %% is not a conversion operator, just the way to display % */

					if (*ptr != '%')
					{
						i++;
						estimatedSize += 10;
					}
				}
			}

			if (estimatedSize > memSize)
			{
				printf(
   "\nWarning, estimated size of %d exceeds memory size of %d in BME get event log\n",
      estimatedSize, memSize
				);
			}
			sprintf( memAddr, buf,
				    prEvent->varList[0],
				    prEvent->varList[1],
				    prEvent->varList[2],
				    prEvent->varList[3],
				    prEvent->varList[4],
				    prEvent->varList[5],
				    prEvent->varList[6],
				    prEvent->varList[7]
			);
		}
	}

	return( retval );
}

static void
dumpEventsToMem( unsigned int num, char *baseAddr, size_t size )
{
	unsigned int	 i;
	char		*currAddr = baseAddr;
	size_t		 remainingSize = size, eventSize = 0;

	static char localBuffer[ 512 ];

	if (num == 0)
	  num = NUM_OF_EVENTS;
	i = eventLogDB.current;
	memset( baseAddr, 0, size );

  /*printf( "dump BME events to memory starts at event %d\n", i );*/
  /*printf( "dump BME events to memory starts at address 0x%lx with size %d\n", baseAddr, size );*/

	do
	{
		int	status;

		status = getOneEvent(i, &localBuffer[ 0 ], sizeof( localBuffer ) );
		eventSize = strlen( &localBuffer[ 0 ] );
	   /*if (remainingSize < 200)
	     printf( "event %d, size: %d, remaining size: %d\n", i, eventSize, remainingSize );*/
	   /* 2 consecutive NULL bytes terminates the event log in memory.
	      Expressed differently, a 0-length entry terminates the event log.
	      So subtract 2 from remaining size to leave room for the additional
	      NULL byte to terminate.

	      event size       remaining size
                98                  100                OK
                99                  100                will not fit
	    */

		if (eventSize > remainingSize - 2)
		{
			break;			/* if this event would overflow, stop */
		}
		else
		{
			strcpy( currAddr, &localBuffer[ 0 ] );
			currAddr += (eventSize + 1);
			remainingSize -= (eventSize + 1);
    		}

		if (i > 0)
		  i--;
		else
		  i = NUM_OF_EVENTS - 1;
	} while (--num > 0);

   /*printf( "start at 0x%x, end at 0x%x, size: %d\n",
	    baseAddr, (currAddr + strlen( currAddr )), (currAddr + strlen( currAddr )) - baseAddr
   );*/
}

static void
dumpToMem( int iNum, char *baseAddr, size_t size )
{
	int	localNum;

	if (iNum > NUM_OF_EVENTS)
	  localNum = NUM_OF_EVENTS;
	else if (iNum < 1)
	  localNum = 100;
	else
	  localNum = iNum;

	iposSemTake( apiBme[globalBmeNum].semBme, IPOS_WAIT_FOREVER);
	  dumpEventsToMem((unsigned int) localNum, baseAddr, size );
	iposSemGive(apiBme[globalBmeNum].semBme);
}

/* Do not call this from the BME Handler or other time critical task
   Do not call this from an ISR
   Brief test showed it took over 500 ms to complete */

int iposGetBmeEventLog(
	int bmeNum,
	char *pEventLog,
	U32 eventLogSize,
	eELMBmeState eCurrentBmeState,
	U8 uid,			/* not currently used */
	U32 uPar,		/* not currently used */
	iposCbCtl pCompleteFunc	/* not currently used */
)
{
	int	countofEvents = 1000;
	/*U32	tick1, tick2;*/

	/*tick1 = iposTickGet();*/

/* getdump / dumpToMem MUST be followed if the BME is hung
   or has crashed.  (eCurrentBmeState == eELM_BME_IS_HUNG)		*/

	if (getdump( bmeNum ) != 0)
	  return( IPOS_ERROR );		/* model is iposEntryRcvPoll ... */

	dumpToMem( countofEvents, pEventLog, eventLogSize );

	/*tick2 = iposTickGet();*/
	/*printf( "iposGetBmeEventLog timing: %lu %lu, %lu\n", tick1, tick2, (tick2 - tick1) );*/
	return( IPOS_SUCCESS );
}
