/**************************************************************
$Workfile:   ifftp.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 includes routines for IKanos Fast File Transfer Protocol (iFFTP)
	
	
*****************************************************************************/	

// #define	TEST																						 
																		  
															  
#include <stdio.h>
#include <string.h>

#include 	"ipos.h"
#include	"rfmdef.h"
#include	"rfmerr.h"
#include	"rfmstruct.h"
#include	"rfmifco.h"
#include	"rfmifcpe.h"
#include	"rfmlocal.h"

#include	"oamptask.h"


#define		TID_RFM			0			/* tentatively use 	OAMFUN Task	*/

int	TXSPEED=15;							/* to control the tx speed	*/
int	iposrfm_file_expiration_tick = IPOSRFM_FILE_EXPIRATION_TICK;

                         
						
//#ifdef	VTU_O						  
/**************************************************************************
				 Local CO functon prototypes
				 
**************************************************************************/				 
 
/* send a file packet	*/						

static int	CoSendDataPkt(ipos_rfm_co *pCoPe, iposrfm_data_block *pData, U16	BlkIndex);

static void CoSendAckHandler(ipos_port_id portId,  U8 UID, U32 uPar, int status, void	*par);

static	int	CoAckHandler(ipos_rfm_co	*pCoPe, int status, void *par);

static	int	CoCompleteHandler(ipos_rfm_co *pCoPe,int status);

static 	int	CoErrorHandler(ipos_rfm_co	*pCoPe, int status);

static	int CoStart(ipos_rfm_co *pCoPe);

static int	CoGetDataBlk(ipos_rfm_co	*pCoPe);

static	int	CoPetTimer(ipos_rfm_co	*pCpePe);

static	void CoTmrExpired(U32 arg1, U32 arg2);



//#endif


//#ifdef	VTU_R						  
/**************************************************************************
				 Local CPE functon prototypes
				 
**************************************************************************/				 
 
/* send a file packet	*/						
/* static	int	CpeAckHandler(ipos_rfm_cpe	*pCpePe, int status); */

/* static	int	CpeErrorHandler(ipos_rfm_cpe	*pCpePe, int status); */

static int	CpeSendAck(ipos_rfm_cpe	*pCpePe, U32 ACK1, void *par);

static	int	CpePetTimer(ipos_rfm_cpe	*pCpePe);

static	void CpeTmrExpired(U32 arg1, U32 arg2);


//#endif



//#ifdef	VTU_O						  
/**************************************************************************
				 CO functons
				 
**************************************************************************/				 
                                         

/* send	a packet	*/
static	int	CoSendDataPkt(ipos_rfm_co *pCoPe, iposrfm_data_block *pData, U16	BlkIndex)
{

    U8	eocMsg[MAX_MSG_SIZE];

	int	intLevel;
	int	errCode=IPOS_SUCCESS;

	
	/* check the ackflag	*/
	if(pCoPe->flgAck[BlkIndex]>=0)
	{
		
		/* construct a msg buffer	*/
		eocMsg[IPOSRFM_MSGHDR_PT]=0x20;
		eocMsg[IPOSRFM_MSGHDR_MSGTYPE]=pData->genericHdr.msgType;
		eocMsg[IPOSRFM_MSGHDR_MSGLEN]=IPOS_DATA_MSG_LEN;
		eocMsg[IPOSRFM_FILE_SID]=pCoPe->sid;
		eocMsg[IPOSRFM_FILE_SEQ]=(pData->seq&0xff00)>>8;
		eocMsg[IPOSRFM_FILE_SEQ+1]=(pData->seq&0xff);

		memcpy(&(eocMsg[IPOSRFM_FILE_DATA]),pData->pData,IPOS_DATA_BLK_SIZE);

#ifndef	TEST
		errCode= iposEocSendDataStream(pCoPe->pid,IPOS_DATA_MSG_LEN, eocMsg, 
							  0, (U32) pCoPe,CoSendAckHandler); 
		if(pCoPe->displayLevel>0)
		{
			if(!(pCoPe->cntDataBlock%pCoPe->displayLevel))
			{
				printf(".");
			}
		}
#else
		
		errCode= tstFileCo2Cpe(pCoPe->pid,IPOS_DATA_MSG_LEN, eocMsg, 
							  0, (U32) pCoPe,CoSendAckHandler); 
#endif


		/* increment the counter	*/
		intLevel=iposIntLock();		/* TT ID 1159 */
		pCoPe->flgAck[BlkIndex]++;
		pCoPe->cntDataBlock++;
		iposIntUnlock(intLevel);		/* TT ID 1159 */
	}

	return	errCode;
    
}


/* Send ACK handler	*/
static void CoSendAckHandler(ipos_port_id portId,  U8 UID, U32 uPar, int status, void	*par)
{

	ipos_rfm_co	*pCoPe;
	/* cast the uPar to ipos_rfm_co_pe	*/ /* for safety reason not use for now	*/

	pCoPe=iposRFMGetCoPe(portId);

	if(pCoPe==NULL)
	{
		printf("ERROR: InValid port ID in SENDACK	\n");
		return;

	}
	/* check the COPe state		*/

	if(pCoPe->stateiFftp==IPOSRFM_IFFTP_CO_ACTIVE)
	{

#if	0
		CoAckHandler(pCoPe,status);
#endif
		appOampJobAdd(0,(appOampFunc) CoAckHandler,(U32) pCoPe,status,NULL);


	}

	return;

}



static	int	CoAckHandler(ipos_rfm_co	*pCoPe, int status, void	*par)
{

	int	errCode;
	U16	currBlkIdx;

	/* if NACK, send the data again	*/
	if(status!=IPOS_SUCCESS)
	{

		 errCode= CoSendDataPkt(pCoPe,&(pCoPe->currBlk),pCoPe->currBlkIdx);
		 return	errCode;

	}

	/* if ACK, advance the index:	*/
	iposTaskDelay(TXSPEED);						/* Speed adjustment	*/
	pCoPe->currBlkIdx++;

    
	while(pCoPe->currBlkIdx<pCoPe->lastBlkIdx+1)  /* index to the end	?	*/
	{
		currBlkIdx=pCoPe->currBlkIdx;		
		if(pCoPe->flgAck[currBlkIdx]>=0)
		{

			if(pCoPe->flgAck[currBlkIdx]>IPOSRFM_DATABLK_RESEND)
			{

				printf("\nIndx=%d, cnt=%d\n", currBlkIdx,pCoPe->flgAck[currBlkIdx]); 
				CoErrorHandler(pCoPe, 0);
                return	IPOSRFM_ERROR_MAX_RESEND;
			}

			CoGetDataBlk(pCoPe);
			CoSendDataPkt(pCoPe, &(pCoPe->currBlk), pCoPe->currBlkIdx);
			return	IPOS_SUCCESS;
		}

		pCoPe->currBlkIdx++;

	}
	
	/* if nothing to send: restart	*/
	if(pCoPe->currBlkIdx==pCoPe->lastBlkIdx+1)
	{

		CoStart(pCoPe);			/* add job here?	*/

	}

	return	IPOS_SUCCESS;
        
}



static	int CoStart(ipos_rfm_co	*pCoPe)
{

	int	errCode;
	U16	i;
    

	for(i=0;i<pCoPe->lastBlkIdx+1;i++)
	{

		if(pCoPe->flgAck[i]>=0)				/* check the first block not acked	*/
		{

			if(pCoPe->flgAck[i]>IPOSRFM_DATABLK_RESEND)
			{

				printf("\nCoSTart: Indx=%d, cnt=%d\n", i,pCoPe->flgAck[i]); 
				CoErrorHandler(pCoPe, 0);
				return	IPOSRFM_ERROR_MAX_RESEND;
			}
			
			pCoPe->currBlkIdx=i;
	   		
			CoGetDataBlk(pCoPe);
			
			errCode=CoSendDataPkt(pCoPe,&(pCoPe->currBlk), pCoPe->currBlkIdx);
			
			return	errCode;				/* exit	*/
		}

	}

	 errCode=CoCompleteHandler(pCoPe, IPOS_SUCCESS);
     return	errCode;

}


static int	CoGetDataBlk(ipos_rfm_co	*pCoPe)
{

	pCoPe->currBlk.genericHdr.pt=0x20;
	pCoPe->currBlk.genericHdr.msgType=IPOSRFM_MSG_DATA_BLOCK;
	pCoPe->currBlk.genericHdr.length= IPOS_DATA_MSG_LEN;
	pCoPe->currBlk.sid=pCoPe->sid;
	pCoPe->currBlk.seq=pCoPe->currBlkIdx;
	(U32) (pCoPe->currBlk.pData) = (U32) (pCoPe->firmwareAddr) + (U32) (pCoPe->currBlkIdx)*IPOS_DATA_BLK_SIZE;
	return	IPOS_SUCCESS;															

}


static	void	CoCompletionDone(ipos_port_id pid, U8 uid, int status, iposrfm_transfer_complete_resp *uPar)
{

	ipos_rfm_co	*pCoPe;
	int	errCode;

	pCoPe=iposRFMGetCoPe(pid);

	errCode=iposResetTmr(pCoPe->ptmrExp);
	if(errCode!=IPOS_SUCCESS)
	{

		printf("CO RFM: Failed to reset the timer \n");
	}
	
	if(pCoPe->pEvntTransferComplete!=NULL)
	{

		pCoPe->pEvntTransferComplete(pid, pCoPe->uidEvntTransferComplete, uPar);

	}

}
/* the filetransfer complete handler running under co rfm task	*/
static	int	CoCompleteHandler(ipos_rfm_co *pCoPe,int status)
{


		int errCode;
		iposrfm_transfer_info	info;

	/* stop the error detection timer	*/
        errCode=iposResetTmr(pCoPe->ptmrExp);
		if(errCode!=IPOS_SUCCESS)
		{

			printf("CO RFM: Failed to reset the timer \n");
		}


	/* send the completion to CPE */
		info.dataBlockSent=pCoPe->lastBlkIdx+1;
		info.byteSent=(pCoPe->lastBlkIdx+1) * IPOS_DATA_BLK_SIZE;
		errCode=iposRFMCOiFftpComplete(pCoPe->pid, 1,&info, CoCompletionDone); 
		return	errCode;
    
}





/* the filetransfer error handler running in the co rfm task	*/

static 	int	CoErrorHandler(ipos_rfm_co	*pCoPe, int status)
{

		printf(" CO iFftp: Connection error dectected \n");
		printf(" CO iFftp: File transfer aborted \n");
        
		return	IPOS_SUCCESS;

}




/* CO error detection timer function */

/* timeout mechanism	*/
static	int	CoPetTimer(ipos_rfm_co	*pCoPe)
{
	
	int	errCode;

	errCode=iposSetTmr(pCoPe->ptmrExp,iposrfm_file_expiration_tick,CoTmrExpired,(U32) pCoPe, (U32) 0);
	
	return	errCode;

}
/* File expiration handler	*/

static	void CoTmrExpired(U32 arg1, U32 arg2)
{
	ipos_rfm_co		*pCoPe;

	pCoPe=(ipos_rfm_co *) arg1;
	
	printf("CO RFM: File transfer connection error detected \n"); 

    pCoPe->stateiFftp=IPOSRFM_IFFTP_CO_IDLE;	/* file transfer state = IDLE Ready for restart */

	/* error callback function TBD	*/

}






/****************************************************************
	CO iFftp initialization
*****************************************************************/

int	iposRFMCOiFftpInit(ipos_rfm_co		*pCoPe)
{

	int i;
	int	errCode=IPOS_SUCCESS;
	/* 	 sid=0: initial value	*/
	/* 	currBlkIdx=0; lastBlkIdx=0	*/
		
	pCoPe->sid=0;
	pCoPe->currBlkIdx=0;
	pCoPe->lastBlkIdx=0;

	/* state=IDLE	*/
	pCoPe->stateiFftp=IPOSRFM_IFFTP_CO_IDLE;

	/* initilize flgAck	*/
	for(i=0; i<MAX_NUM_DATA_BLOCKS;i++)
	{
		pCoPe->flgAck[i]=0;
	}

	/* firmware addr: NULL	*/
	pCoPe->firmwareAddr=NULL;

	/* cnts=0	*/
	pCoPe->cntAck=0;
	pCoPe->cntDataBlock=0;
	pCoPe->displayLevel=4;

	/* Get the expiration timer	*/
	errCode=iposCreateTmr(&(pCoPe->ptmrExp));


	return	IPOS_SUCCESS;

}



/****************************************************************
	CO iFftp start
*****************************************************************/
int	iposRFMCOiFftpStart(ipos_port_id   pid, U8	sid)
{

	int	i, errCode;
	U32	fileLen;
	ipos_rfm_co	*pCoPe;


	pCoPe=iposRFMGetCoPe(pid);
	/* set	sid	*/
	/* currBlkIdx=0; Derive lastBlkIdex from the filesize	*/
	pCoPe->sid=sid;

	pCoPe->currBlkIdx=0;

	fileLen=pCoPe->fileInfo.fileLength;
	if(fileLen>=(U32)MAX_NUM_DATA_BLOCKS*(U32)IPOS_DATA_BLK_SIZE)
	{
		printf("file size exceeds the maximum size \n");
		return	IPOSRFM_ERROR_FILE_SIZE;
	}
	pCoPe->lastBlkIdx=(fileLen/IPOS_DATA_BLK_SIZE);
	
	/* state=IDLE	*/
	pCoPe->stateiFftp=IPOSRFM_IFFTP_CO_IDLE;

	/* initilize flgAck	*/
	for(i=0; i<MAX_NUM_DATA_BLOCKS;i++)
	{
		pCoPe->flgAck[i]=0;
	}

	/* cnts=0	*/
	pCoPe->cntAck=0;
	pCoPe->cntDataBlock=0;

	/* start the transfering	*/
	pCoPe->stateiFftp=IPOSRFM_IFFTP_CO_ACTIVE;
	errCode=CoStart(pCoPe);
	
	/* start the error detection timer	*/
	errCode=iposSetTmr(pCoPe->ptmrExp,iposrfm_file_expiration_tick,CoTmrExpired,(U32) pCoPe, (U32) 0);
	
	return	errCode;

}


/****************************************************************
	CO iFftp getDataPkt
*****************************************************************/
int	iposRFMCOiFftpHandler(ipos_rfm_co	*pCoPe, U8	*pEocData)
{

	U16	i;
	U16	ack1, ack2;
	int	intLevel;
	/* msgType=Msg Ack?	*/


	if(pCoPe->stateiFftp!=IPOSRFM_IFFTP_CO_ACTIVE)
	{
		return	IPOS_SUCCESS;			/* wrong state: ignored	*/

	}

	if(pEocData[IPOSRFM_MSGHDR_MSGTYPE]!=IPOSRFM_MSG_ACK)
	{
		return	IPOS_SUCCESS;			/* other packets received: ignored	*/
	}

	/* sid match?	*/
	if(pEocData[IPOSRFM_FILE_SID]!=pCoPe->sid)
	{
        return	IPOS_SUCCESS;			/* session iD does not match: ignored	*/
     }

    
	/* ack1, ack2 in range?	*/
	ack1=((U16)pEocData[IPOSRFM_FILE_ACK1]<<8) | ( ((U16) pEocData[IPOSRFM_FILE_ACK1+1]) & 0x0ff); 

	ack2=( (U16)pEocData[IPOSRFM_FILE_ACK2]<<8) |( ((U16) pEocData[IPOSRFM_FILE_ACK2+1]) & 0x0ff); 
	
	if((ack1<=ack2)&&(ack2<MAX_NUM_DATA_BLOCKS))
    {												/* valid packet	*/



		intLevel=iposIntLock();		/* TT ID 1159 */
		
		for(i=ack1;i<=ack2;i++)
		{

			pCoPe->flgAck[i]=-1;					/* -1: the packet acked	*/

		}
		pCoPe->cntAck++;							/* ack counter ++	*/

		iposIntUnlock(intLevel);		/* TT ID 1159 */

		/* pet the error detection timer	*/
		CoPetTimer(pCoPe);

	}
	return	IPOS_SUCCESS;							/* opther packet ignored	*/

   
}


//#endif



//#ifdef	VTU_R						  
/**************************************************************************
				 CPE functons
				 
**************************************************************************/				 
 




/****************************************************************
	CPE iFftp initialization
*****************************************************************/
int	iposRFMCPEiFftpInit(ipos_rfm_cpe		*pCpePe)
{
	int errCode=IPOS_SUCCESS;

	/*	state=idle	*/
	pCpePe->stateiFftp=	IPOSRFM_IFFTP_CPE_IDLE;

	/* allocate the timer TBD  */

	/* 	cleatr the addr	*/
	pCpePe->addrFirmware=NULL;
	pCpePe->displayLevel=4;

	/* clear the counter	*/
	pCpePe->cntDataBlk=0;
	
	/* Get the expiration timer	*/
	errCode=iposCreateTmr(&(pCpePe->ptmrExp));

	return	errCode;

}



/* CPE iFFTP start	*/
int iposRFMCPEiFftpStart(ipos_port_id	pid, U8	sid, U8	*addrFirmware)
{
	
	ipos_rfm_cpe	*pCpePe;
	int errCode=IPOS_SUCCESS;
	
	
	pCpePe=iposRFMGetCpePe(pid);

	pCpePe->sid=sid;
	/* change the state	*/
	pCpePe->stateiFftp=IPOSRFM_IFFTP_CPE_ACTIVE;

	/* start the expiration timer*/
	errCode=iposSetTmr(pCpePe->ptmrExp,iposrfm_file_expiration_tick,CpeTmrExpired,(U32) pCpePe, (U32) 0);

	/* reset the counter, addr	*/
	pCpePe->addrFirmware=addrFirmware;

	pCpePe->cntDataBlk=0;
	return	IPOS_SUCCESS;

}


int	iposRFMCPEiFftpComplete(ipos_port_id	pid)
{
	
	ipos_rfm_cpe	*pCpePe;
	int errCode;

	pCpePe=iposRFMGetCpePe(pid);

	/* stop the error detection timer	*/
		errCode=iposResetTmr(pCpePe->ptmrExp);
		if(errCode!=IPOS_SUCCESS)
		{

			printf("CO RFM: Failed to reset the timer \n");
		}

	/* change teh state	*/
	pCpePe->stateiFftp=IPOSRFM_IFFTP_CPE_IDLE;
	return	errCode;

}

/* rcv DataBlk		*/
int	iposRFMCPEiFftpHandler(ipos_rfm_cpe	*pCpePe, U8	*pEocMsg)
{

	int	errCode;
	U8	sid;
	U16	seq;
	U8	*pProg;

	/* check the state active?	*/

	if(pCpePe->stateiFftp!=IPOSRFM_IFFTP_CPE_ACTIVE)
	{
		return	IPOS_SUCCESS;			/* cpe pe not in the right state: ignored the packet	*/

	}

	/* check msgType	*/
	if(pEocMsg[IPOSRFM_MSGHDR_MSGTYPE]!=IPOSRFM_MSG_DATA_BLOCK)
	{
		return	IPOS_SUCCESS;			/* other packets received: ignored	*/
	}


	/* check the session id	*/
	sid=pEocMsg[IPOSRFM_FILE_SID];
	if(sid!=pCpePe->sid)	 			/* not for this session, ignore the packet	*/
	{
		return	IPOS_SUCCESS;
	}                                                                                 

    /*  save the block	*/
	seq=( (U16)pEocMsg[IPOSRFM_FILE_SEQ]<<8) | ( ((U16) pEocMsg[IPOSRFM_FILE_SEQ+1]) & 0x0ff); 
	
	if(seq>=MAX_NUM_DATA_BLOCKS)
	{
		return	IPOSRFM_ERROR_SEQ;

	}
	
	/* clear the timer	*/
	errCode=CpePetTimer(pCpePe);
	if(errCode!=IPOS_SUCCESS)
	{
		return	errCode;
	}

    pProg=pCpePe->addrFirmware+seq*IPOS_DATA_BLK_SIZE;
    memcpy(pProg, pEocMsg+IPOSRFM_FILE_DATA, IPOS_DATA_BLK_SIZE);

	/* send the Ack	*/
	CpeSendAck(pCpePe, (U32) seq, NULL);
#if 0
	appOampJobAdd(0,(appOampFunc) CpeSendAck,(U32) pCpePe,seq,NULL);
#endif

	return	IPOS_SUCCESS;
}


/* EOC complete function: do nothing 	*/

static void	CpeSendComplete(ipos_port_id portId,  U8 UID, U32 uPar, int status, void	*par)
{

	return;
}

/* send the ACK		*/
static int	CpeSendAck(ipos_rfm_cpe	*pCpePe, U32 ack, void	*par)
{

	U8	EocMsg[MAX_MSG_SIZE];
	int	errCode;
	U16 ACK1, ACK2;
	
	
	ACK1=(U16) ack;
	ACK2=(U16) ack;
	/* generate the header	*/
	EocMsg[IPOSRFM_MSGHDR_PT]=0x20;
	EocMsg[IPOSRFM_MSGHDR_MSGTYPE]=IPOSRFM_MSG_ACK;
	EocMsg[IPOSRFM_MSGHDR_MSGLEN]= 0x8;
	/* ack1=ack2=seq	*/
	EocMsg[IPOSRFM_FILE_SID]=pCpePe->sid;
	EocMsg[IPOSRFM_FILE_ACK1]=(U8) ((ACK1&0xff00)>>8);
	EocMsg[IPOSRFM_FILE_ACK1+1]=(U8) (ACK1&0xff);

	EocMsg[IPOSRFM_FILE_ACK2]=(U8) ((ACK1&0xff00)>>8);
	EocMsg[IPOSRFM_FILE_ACK2+1]=(U8) (ACK1&0xff);

	/* send the EOC msg	*/
#ifndef	TEST	
	errCode= iposEocSendDataStream(pCpePe->pid, 0x08, EocMsg, 
						  0, (U32) pCpePe,CpeSendComplete); 
	
	pCpePe->cntDataBlk++;
	if(pCpePe->displayLevel>0)
	{
		if(!(pCpePe->cntDataBlk%pCpePe->displayLevel))
		{
			printf(".");
		}
	}

#else
	errCode= tstFileCpe2Co(pCpePe->pid, 0x08, EocMsg, 
						  0, (U32) pCpePe,CpeSendComplete); 
#endif

	return	errCode;
}



/* timeout mechanism	*/
static	int	CpePetTimer(ipos_rfm_cpe	*pCpePe)
{

	int	errCode=IPOS_SUCCESS;   
	/* start the expiration timer*/
	errCode=iposSetTmr(pCpePe->ptmrExp,iposrfm_file_expiration_tick,CpeTmrExpired,(U32) pCpePe, (U32) 0);
	return	errCode;
}


/* File expiration handler	*/

static	void CpeTmrExpired(U32 arg1, U32 arg2)
{
	ipos_rfm_cpe	*pCpePe;

	pCpePe=(ipos_rfm_cpe *) arg1;
	
	printf("CPE RFM: File transfer connection error detected \n"); 

    pCpePe->stateiFftp=IPOSRFM_IFFTP_CPE_IDLE;	/* file transfer state = IDLE Ready for restart */

	/* error callback function TBD	*/

}

//#endif


