/*******************************************************************************
*                                                                              *
* Copyright 2010 Rheinmetall Canada Inc.                                  	   *
*                                                                              *
* No part of this document may be reproduced, stored in                        *
* a retrieval system, or transmitted, in any form or by any means,             *
* electronic, mechanical, photocopying, recording, or otherwise,               *
* without the prior written permission of Rheinmetall Canada Inc.              *
*                                                                              *
*******************************************************************************/


/*---- example include file ---------------------------------------------------*/
#include "define.h"
#include "BootloaderProtocol.h"
#include "BootloaderStateMachine.h"
#include "internaluart.h"
//#include <string.h>


enum eBootloaderProtocolStates ProtocolState;
unsigned int RxCounter;
unsigned int WordBuf;
unsigned short DataSize;
unsigned int CurCmd;
char *DataBufPtr;
char Checksum;
int ByteCount;
int *RecordDataPtr;

Bool e_CommandPending;
Bool e_FlashUpdateMode;

//#pragma DATA_ALIGN ( RxDataBuffer,16);
//#pragma DATA_SECTION(RxDataBuffer,"bootloaderbuffer");
unsigned char RxDataBuffer[MAX_RX_BUFF_SIZE];	//Rx buffer

//#pragma DATA_SECTION(TxBuffer,"bootloaderbuffer");
char TxBuffer[MAX_TX_BUFF_SIZE];	//Tx buffer



stDataSector_t CurrentDataSector;


void ProtocolInit(void)
{
	ProtocolResetSM();
	e_CommandPending = false;
	e_FlashUpdateMode = false;
	ByteCount = 0;
}

void ProtocolRxData( char mData )
{

	if(e_CommandPending == true)
	{
		ProtocolSendAck(PROTOCOL_NACK,RET_PROTOCOL_ERR_COMMAND_PENDING);
		return;
	}
	
	if(e_FlashUpdateMode == true)
	{
	//	DownloadFlash(0x328000, 0x32FFFF);
		e_FlashUpdateMode = false;
		return;
	}


	switch(ProtocolState)
	{
		case PROTOCOL_RX_HEADER:
		{
			if((unsigned char)mData == PROTOCOL_HEADER)
				ProtocolState = PROTOCOL_RX_CMD;

			break;
		}
		case PROTOCOL_RX_CMD:
		{
			CurCmd = ((unsigned int)mData & 0x00FF);
			ProtocolState = PROTOCOL_RX_DATA_SIZE_1;
			break;
		}
		case PROTOCOL_RX_DATA_SIZE_1:
		{
	 		DataSize = ((unsigned int)mData & 0x00FF); //((mData << 8) & 0xFF00);
	 		ProtocolState = PROTOCOL_RX_DATA_SIZE_2;
			break;
		}
		case PROTOCOL_RX_DATA_SIZE_2:
		{	
			DataSize |= ((mData << 8) & 0xFF00); //(mData & 0x00FF);
			if(DataSize > MAX_SECTOR_SIZE*4)	//4 bytes per word...
			{
				ProtocolResetSM();
				ProtocolSendAck(PROTOCOL_NACK,RET_PROTOCOL_ERR_DATA_OVERSIZE);
			}
			else
			{
				if(DataSize == 0)
					ProtocolState = PROTOCOL_RX_CHECKSUM;
				else
				{	
					if(CurCmd == SEND_RECORD_REQUEST_CMD && ((DataSize-6) % 4 != 0))// data must be 32 bits word) aligned when receiving a data record.
					{
						ProtocolResetSM();
						ProtocolSendAck(PROTOCOL_NACK,RET_PROTOCOL_ERR_DATA_BYTES_MISALIGNED);
						break;
					}
					else
						ProtocolState = PROTOCOL_RX_DATA_BYTE4;
			 			//ProtocolState = PROTOCOL_RX_DATA_BYTE1;
			 	}		
	
			}
			
			break;
		}
/*		case PROTOCOL_RX_DATA_BYTE1:		//This compiler does not support 8 bits alignment
		{
			RxCounter++;
			Checksum ^= mData;									
		
			if(CurCmd == SEND_RECORD_REQUEST_CMD)	//for this command, we must concatenate 4 bytes in 1 word
			{
				WordBuf = (((unsigned int)mData << 24) & 0xFF000000);
				
				if(RxCounter >= DataSize)
				{
					ProtocolResetSM();
					ProtocolSendAck(PROTOCOL_NACK,RET_PROTOCOL_ERR_DATA_BYTES_MISALIGNED);
				}
				else
					ProtocolState = PROTOCOL_RX_DATA_BYTE2;
			}
			else
			{
				*DataBufPtr++ = mData;	//Store new byte in buffer.	
				if(RxCounter >= DataSize)	//all bytes received
				{
					ProtocolState = PROTOCOL_RX_CHECKSUM;	
				}
			}
			break;
		}
		case PROTOCOL_RX_DATA_BYTE2:
		{
			WordBuf |= (((unsigned int)mData << 16) & 0x00FF0000);
			RxCounter++;
			
			*DataBufPtr++ = WordBuf;	//Store new word in buffer.
			Checksum ^= mData;
			
			if(RxCounter >= DataSize)
			{
				ProtocolResetSM();
				ProtocolSendAck(PROTOCOL_NACK,RET_PROTOCOL_ERR_DATA_BYTES_MISALIGNED);
			}
			else
			ProtocolState = PROTOCOL_RX_DATA_BYTE3;
		}
		case PROTOCOL_RX_DATA_BYTE3:
		{
			WordBuf |= (((unsigned int)mData << 8) & 0x0000FF00);
			RxCounter++;
			
			*DataBufPtr++ = WordBuf;	//Store new word in buffer.
			Checksum ^= mData;
			
			if(RxCounter >= DataSize)
			{
				ProtocolResetSM();
				ProtocolSendAck(PROTOCOL_NACK,RET_PROTOCOL_ERR_DATA_BYTES_MISALIGNED);
			}
			else
				ProtocolState = PROTOCOL_RX_DATA_BYTE4;
		}	
		case PROTOCOL_RX_DATA_BYTE4:
		{
			WordBuf |= ((unsigned int)mData & 0x000000FF);
			RxCounter++;
			
			*DataBufPtr++ = WordBuf;	//Store new word in buffer.
			Checksum ^= mData;

			if(RxCounter >= DataSize)
			{
				ProtocolState = PROTOCOL_RX_CHECKSUM;	//all data received
			}
			else
			{
				ProtocolState = PROTOCOL_RX_DATA_BYTE1; //get next word
			}

			break;
		}*/
		
		case PROTOCOL_RX_DATA_BYTE4:
		{
			
			Checksum ^= mData;
			*DataBufPtr++ = mData;
		
			if(CurCmd == SEND_RECORD_REQUEST_CMD)	//for this command, we must concatenate 4 bytes in 1 word
			{
				if(RxCounter == 3)	//Start address..
				{
					CurrentDataSector.SectorStartAddress = *(unsigned int*)(&RxDataBuffer[0]);
					DataBufPtr = &RxDataBuffer[0];
				}
				if(RxCounter == 5)	//Record Size..
				{
					CurrentDataSector.SectorSize = *(unsigned short*)(&RxDataBuffer[0]);
					DataBufPtr = &RxDataBuffer[0];
				}
				if(RxCounter > 5)
				{
					if(ByteCount == 3)
					{
						ByteCount = 0;
						*RecordDataPtr++ = *(unsigned int*)(&RxDataBuffer[0]);
						DataBufPtr = &RxDataBuffer[0];
						
					}
					else
					{
						ByteCount++;
					}		
				}
					
				
			}

			
			RxCounter++;

			if(RxCounter >= DataSize)
			{
				ProtocolState = PROTOCOL_RX_CHECKSUM;	//all data received
			}
			else
			{
				ProtocolState = PROTOCOL_RX_DATA_BYTE4; //get next word
			}

			break;
		}
		case PROTOCOL_RX_CHECKSUM:
		{
			if(mData == Checksum)
			{
				//execute command...
				e_CommandPending = true;
			//	ProtocolExecCmd((enum eBootloaderProtocolCmds) CurCmd);
			}
			else
			{
				//Error, bad checksum...
				ProtocolSendAck(PROTOCOL_NACK,RET_PROTOCOL_ERR_BAD_CHECKSUM);
				ProtocolResetSM();
			}		

			break;
		}
		default:
		{
			ProtocolResetSM();
			break;
		}
	}

}

void ProtocolExecCmd()
{
	switch(CurCmd)
	{
		case CONNECT_REQUEST_CMD:		//Sent by the PTE in answer to BOOTLOADER_READY_CMD	to enter boot mode
		case BOOTLOADER_UNLOCK_REQUEST_CMD:
		case HEARTBEAT_REQUEST_CMD:
		case START_UPDATE_REQUEST_CMD:
		case SEND_RECORD_REQUEST_CMD:
		case END_UPDATE_REQUEST_CMD:
		case FLASH_API_VER_REQUEST_CMD:
		case SCRUB_FLASH_REQUEST_CMD:
		{
			BootloaderSM(SM_RX_CMD_EVENT,CurCmd);
			break;
		}
		case FIRMWARE_RESET_REQUEST:
		{
			SoftReset();
			break;
		}
		case DOWNLOAD_FLASH_REQUEST_CMD:
		{
			e_FlashUpdateMode = true;
			break;
		}
		default:
		{
			ProtocolSendAck(PROTOCOL_NACK,RET_PROTOCOL_ERR_UNKNOWN_COMMAND);
			break;
		}
	}

	ProtocolResetSM();
}


void ProtocolResetSM()
{
	ProtocolState = PROTOCOL_RX_HEADER;
	WordBuf = 0;
	RxCounter = 0;
	DataSize = 0;
	CurCmd = 0;
	DataBufPtr = &RxDataBuffer[0];
	Checksum = 0;
	RecordDataPtr = &CurrentDataSector.SectorData[0];
}

unsigned int ProtocolSendCmd(enum eBootloaderProtocolCmds mCmd, char *mData, unsigned  mDataSize)
{
	int i;
	char mChecksum = 0;
	if(mDataSize + 5 > MAX_TX_BUFF_SIZE)
		return 0;

	TxBuffer[0] = PROTOCOL_HEADER;
	TxBuffer[1] = (char)mCmd;
	TxBuffer[2] = LSB8(mDataSize);
	TxBuffer[3] = MSB8(mDataSize);

	for(i = 0; i < mDataSize; i++)
	{	
		mChecksum ^= *mData;
		TxBuffer[i+4] = *mData++;
	}


	TxBuffer[mDataSize + 4] = mChecksum;

	
	SendInternalUartData(TxBuffer,mDataSize+5);

	return mDataSize + 5;


}

void ProtocolSendAck(enum eBootloaderAcknowledgement mAck, enum eBootloaderProtocolRet mErr)
{
	char mAckBufferData[2];
	mAckBufferData[0] = (char) mAck;
	mAckBufferData[1] = (char) mErr;

	ProtocolSendCmd(ACKNOWLEDGE_CMD, mAckBufferData, 2);
}



