/*
 * @brief Secondary loader I2C host interface protocol handler
 *
 * @note
 * Copyright(C) NXP Semiconductors, 2014
 * All rights reserved.
 *
 * @par
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * LPC products.  This software is supplied "AS IS" without any warranties of
 * any kind, and NXP Semiconductors and its licensor disclaim any and
 * all warranties, express or implied, including all implied warranties of
 * merchantability, fitness for a particular purpose and non-infringement of
 * intellectual property rights.  NXP Semiconductors assumes no responsibility
 * or liability for the use of the software, conveys no license or rights under any
 * patent, copyright, mask work right, or any other intellectual property rights in
 * or to any products. NXP Semiconductors reserves the right to make changes
 * in the software without notification. NXP Semiconductors also makes no
 * representation or warranty that such application will be suitable for the
 * specified use without further testing or modification.
 *
 * @par
 * Permission to use, copy, modify, and distribute this software and its
 * documentation is hereby granted, under NXP Semiconductors' and its
 * licensor's relevant copyrights in the software, without fee, provided that it
 * is used in conjunction with NXP Semiconductors microcontrollers.  This
 * copyright, permission, and disclaimer notice must appear in all copies of
 * this code.
 */

#include "chip.h"
#include "sl_common.h"
#include "sl_flash.h"
#include <string.h>
#include "sl_protocol.h"

/*****************************************************************************
 * Private types/enumerations/variables
 ****************************************************************************/

extern uint32_t *FW_VERSION1;
extern uint32_t *FW_VERSION2;
static uint8_t FW_INDEX=0;

typedef struct {
	uint16_t cmd;
	uint16_t cmd_param_size;
	uint16_t (*doCmd)(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost);
} CMD_HANDLER_T;

/*****************************************************************************
 * Public types/enumerations/variables
 ****************************************************************************/

SL_IFCONFIG_T slIfConfig;
int buffXferPending;
uint32_t g_spiTxPending = 0;
uint8_t g_SubBlockWrData[SL_FLASH_BLOCK_SZ];
uint16_t g_SubBlockWrOffset = 0;

extern void CleanUpSystem(void);

/*****************************************************************************
 * Private functions
 ****************************************************************************/
uint16_t doCmdWhoAmI(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost)
{
	/* nothing to do */
	return 0;
}

uint16_t doCmdGetVersion(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost)
{
	pToHost->buffer[SH_RESP_HEADER_SIZE + 0] = CFG_MAJOR_VERSION;
	pToHost->buffer[SH_RESP_HEADER_SIZE + 1] = CFG_MINOR_VERSION;

	return SH_RESP_VERSION_SZ;
}

uint16_t doCmdReset(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost)
{
	/* do Chip reset */
	NVIC_SystemReset();
	return 0;
}

uint16_t doCmdWriteBlock(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost)
{
	CmdRWBlockParam_t *pParam = (CmdRWBlockParam_t *) &pFromHost->buffer[0];
	CmdDataResp_t *pResp = (CmdDataResp_t *) &pToHost->buffer[0];
	uint32_t crc = 0;
	uint16_t retval = 0;
	Bool APP1_CRC=FALSE;
	Bool APP2_CRC=FALSE;
	

	if (pParam->crc_check != SH_RW_PARAM_SKIP_CRC) {
		crc = computeCrc32((uint32_t *) &pFromHost->buffer[0], pFromHost->bytes - SH_RESP_CRC32_SZ);
	}
	
	/*Judge the current latest app*/
	if(pParam->block_nr==16)
	{
		if(*FW_VERSION1!=0)
		{
			if(checkAppCRC(1)==0)
				APP1_CRC=TRUE;
		}
		if(*FW_VERSION2!=0)
		{
			if(checkAppCRC(2)==0)
				APP2_CRC=TRUE;
		}
		
		if(APP1_CRC && APP2_CRC)
		{
			/*if FW_VERSION1== FW_VERSION2, write new app to 0X2000*/
			if(*FW_VERSION1>*FW_VERSION2)	
				FW_INDEX=1;
			else
				FW_INDEX=2;
		}
		else if(APP1_CRC)			
			FW_INDEX=1;		
		else if(APP2_CRC)			
			FW_INDEX=2;			
	}
	
	/*If firmware1(0X2000) is the latest one, then update new firmware to address2 (0x5000)*/
	if(FW_INDEX==1)
		pParam->block_nr += 24;
		
	/* check CRC of the block received */
	if (crc == pParam->crc32) {
		pResp->data[0] = flashWriteBlock(&pParam->data[0], pParam->block_nr);
	}
	else {
		pResp->data[0] = ERR_API_INVALID_PARAMS;
	}
	/* Update response length if error code needs to be sent */
	if (pResp->data[0] != LPC_OK) {

		retval = SH_RESP_ERROR_CODE_SZ;
	}
	else
	{
		retval = 4;
		pResp->data[1] = 0;
	}

	return retval;
}

uint16_t doCmdReadBlock(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost)
{
	CmdRWBlockParam_t *pParam = (CmdRWBlockParam_t *) &pFromHost->buffer[0];
	CmdDataResp_t *pResp = (CmdDataResp_t *) &pToHost->buffer[0];
	int32_t res = LPC_OK;
	uint16_t retval = 0;

	res = flashReadBlock(&pResp->data[0], pParam->block_nr);
	retval = sizeof(CmdDataResp_t);
	/* check CRC of the block to be sent */
	if (res == LPC_OK) {
		pResp->crc32 = 0;
		pResp->hdr.length = retval;
		pResp->crc32 = computeCrc32((uint32_t *) &pToHost->buffer[0], retval - SH_RESP_CRC32_SZ);
	}
	else {
		/* return error code */
		pResp->data[0] = res;
		retval = SH_RESP_ERROR_CODE_SZ;
	}
	return retval;
}

uint16_t doCmdWriteSubBlock(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost)
{
	CmdRWBlockParam_t *pParam = (CmdRWBlockParam_t *) &pFromHost->buffer[0];
	CmdDataResp_t *pResp = (CmdDataResp_t *) &pToHost->buffer[0];
	uint32_t crc = 0;
	uint16_t retval = 0;
	uint32_t len = 0;
	uint32_t *pCrc = (uint32_t *) &pFromHost->buffer[pFromHost->bytes - SH_RESP_CRC32_SZ];

	if ((pParam->crc_check & SH_RW_PARAM_SKIP_CRC) == 0) {
		crc = computeCrc32((uint32_t *) &pFromHost->buffer[0], pFromHost->bytes - SH_RESP_CRC32_SZ);
	}
	/* check CRC of the block received */
	if (crc == *pCrc) {
		/* Fill 0xFF the block memory if it is first sub-block. */
		if (g_SubBlockWrOffset == 0) {
			memset(&g_SubBlockWrData[0], 0xFF, SL_FLASH_BLOCK_SZ);
		}
		/* set positive response */
		pResp->data[0] = LPC_OK;
		/* calculate the sub-block length */
		len = pFromHost->bytes - SH_RESP_CRC32_SZ - SH_RESP_HEADER_SIZE;
		/* copy only the remaining block data even if large sub-block is sent */
		if (len > (SL_FLASH_BLOCK_SZ - g_SubBlockWrOffset)) {
			len = (SL_FLASH_BLOCK_SZ - g_SubBlockWrOffset);
		}
		memcpy(&g_SubBlockWrData[g_SubBlockWrOffset], &pParam->data[0], len);
		g_SubBlockWrOffset += len;
		/* check if block buffer is full. If full write the block */
		if (g_SubBlockWrOffset >= SL_FLASH_BLOCK_SZ) {
			pParam->block_nr &= 0x1FFF;
			pResp->data[0] = flashWriteBlock((uint32_t *) &g_SubBlockWrData[0], pParam->block_nr);
			g_SubBlockWrOffset = 0;
		}
	}
	else {
		pResp->data[0] = ERR_API_INVALID_PARAMS;
	}
	/* Update response length if error code needs to be sent */
	if (pResp->data[0] != LPC_OK) {

		retval = SH_RESP_ERROR_CODE_SZ;
	}
	else
	{
		retval = 4;
		pResp->data[1] = 0;
	}
	return retval;
}

uint16_t doCmdReadSubBlock(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost)
{
	CmdReadSubBlockParam_t *pParam = (CmdReadSubBlockParam_t *) &pFromHost->buffer[0];
	CmdDataResp_t *pResp = (CmdDataResp_t *) &pToHost->buffer[0];
	int32_t res = LPC_OK;
	uint16_t retval = 0;
	uint32_t offset = 0;
	uint32_t subBlockSz = 1 << (((pParam->sub_block_nr >> 6) & 0x3) + 5);

	res = flashReadBlock(&pResp->data[0], pParam->block_nr);
	/* check CRC of the block to be sent */
	if (res == LPC_OK) {
		offset = (((pParam->sub_block_nr >> 1) & 0x1F) * subBlockSz);
		memcpy(&pResp->data[0], &pResp->data[offset / sizeof(uint32_t)], subBlockSz);
		retval = SH_RESP_HEADER_SIZE + subBlockSz + SH_RESP_CRC32_SZ;
		pResp->hdr.length = retval;
		pResp->data[subBlockSz / sizeof(uint32_t)] = computeCrc32((uint32_t *) &pToHost->buffer[0],
																  retval - SH_RESP_CRC32_SZ);
	}
	else {
		/* return error code */
		pResp->data[0] = res;
		retval = SH_RESP_ERROR_CODE_SZ;
	}
	return retval;
}

uint16_t doCmdBoot(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost)
{
	/* Clean-up SBL resources and boot application */

//	if(*FW_VERSION1>=*FW_VERSION2)
//		doCleanBoot(SL_BOOTAPP_ADDR1);
//	else
//		doCleanBoot(SL_BOOTAPP_ADDR2);
	
	doCleanBoot(SL_BOOTAPP_ADDR1);
	return 0;
}

uint16_t doCmdCheckImage(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost)
{
	CmdDataResp_t *pResp = (CmdDataResp_t *) &pToHost->buffer[0];
	uint16_t retval = SH_RESP_CRC32_SZ;
	/* Does CRC32 image integrity check on flash */
	
	
	pResp->data[0] = checkAppCRC(1);

	return retval;
}

uint16_t doCmdProbe(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost)
{
	/* Parses the initial packet and determines pin mapping based on the data */
	SL_PINSETUP_T *pPinCfg = (SL_PINSETUP_T *) &pFromHost->buffer[0];

	if (pinCheckValidHostif(pPinCfg) == FALSE) {
		/* reset the part */
		NVIC_SystemReset();
	}

	/* Parse host IRQ information if a valid packet */
	parsePortData(&slIfConfig.hostIrqPortPin, pPinCfg->hostIrqPortPin);
	/* Valid SPI packet received, parse MOSI, MISO, SSEL */
	parsePortData(&slIfConfig.hostMisoPortPin, pPinCfg->hostMisoPortPin);
	parsePortData(&slIfConfig.hostMosiPortPin, pPinCfg->hostMosiPortPin);
	parsePortData(&slIfConfig.hostSckPortPin, pPinCfg->hostSckPortPin);
	parsePortData(&slIfConfig.hostSselPortPin, pPinCfg->hostSselPortPin);
	
	Hostif_DeAssertIRQ();


	if ((pPinCfg->ifSel == SL_SPI0) || (pPinCfg->ifSel == SL_SPI1)) {
		slIfConfig.selectedHostSpi = 1;

	}
	else {
		
	}

	return 0;
}

uint16_t doCmdPageErase(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost)
{
	CmdRWPageParam_t *pParam = (CmdRWPageParam_t *) &pFromHost->buffer[0];
	CmdDataResp_t *pResp = (CmdDataResp_t *) &pToHost->buffer[0];
	int32_t res = LPC_OK;
	uint16_t retval = 0;
	uint32_t page_addr;

	/* compute page address */
	page_addr = (pParam->page_nr * SL_FLASH_PAGE_SZ);
	/* erase page */
	res = flashErasePage(page_addr, page_addr);
	/* check if erase was successful */
	if (res != LPC_OK) {
		pResp->data[0] = res;
		retval = SH_RESP_ERROR_CODE_SZ;
	}
	return retval;
}

uint16_t doCmdPageWrite(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost)
{
	CmdRWPageParam_t *pParam = (CmdRWPageParam_t *) &pFromHost->buffer[0];
	CmdDataResp_t *pResp = (CmdDataResp_t *) &pToHost->buffer[0];
	uint16_t retval = 0;
	uint32_t crc = 0;
	uint32_t page_addr;

	/* compute page address */
	page_addr = (pParam->page_nr * SL_FLASH_PAGE_SZ);
	/* check CRC32 before proceeding */
	if (pParam->crc_check != SH_RW_PARAM_SKIP_CRC) {
		crc = computeCrc32((uint32_t *) &pFromHost->buffer[0], pFromHost->bytes - SH_RESP_CRC32_SZ);
	}
	/* check CRC of the block received */
	if (crc == pParam->crc32) {
		/* program page */
		pResp->data[0] = flashWritePage(page_addr, &pParam->data[0]);
	}
	else {
		pResp->data[0] = ERR_API_INVALID_PARAMS;
	}

	/* Update response length if error code needs to be sent */
	if (pResp->data[0] != LPC_OK) {

		retval = SH_RESP_ERROR_CODE_SZ;
	}
	return retval;
}

uint16_t doCmdPageRead(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost)
{
	CmdRWPageParam_t *pParam = (CmdRWPageParam_t *) &pFromHost->buffer[0];
	CmdDataResp_t *pResp = (CmdDataResp_t *) &pToHost->buffer[0];
	int32_t res = LPC_OK;
	uint16_t retval = 0;
	uint32_t crc = 0;

	res = flashReadPage(&pResp->data[0], pParam->page_nr);
	retval = SH_RESP_HEADER_SIZE + SL_FLASH_PAGE_SZ + SH_RESP_CRC32_SZ;
	/* check CRC of the block to be sent */
	if (res == LPC_OK) {
		pResp->hdr.length = retval;
		crc = computeCrc32((uint32_t *) &pToHost->buffer[0], retval - SH_RESP_CRC32_SZ);
		pResp->data[SL_FLASH_PAGE_SZ / 4] = crc;
	}
	else {
		/* return error code */
		pResp->data[0] = res;
		retval = SH_RESP_ERROR_CODE_SZ;
	}
	return retval;
}

uint16_t doCmdSectorErase(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost)
{
	CmdEraseSectorParam_t *pParam = (CmdEraseSectorParam_t *) &pFromHost->buffer[0];
	CmdDataResp_t *pResp = (CmdDataResp_t *) &pToHost->buffer[0];
	int32_t res = LPC_OK;
	uint16_t retval = 0;

	/* erase sector */
	res = flashEraseSector(pParam->sec_nr, pParam->sec_nr);
	/* check if erase was successful */
	if (res != LPC_OK) {
		/* return error code */
		pResp->data[0] = res;
		retval = SH_RESP_ERROR_CODE_SZ;
	}
	return retval;
}

static const CMD_HANDLER_T g_CmdHdlr[] = {
	/* common app commands */
	{SH_CMD_WHO_AM_I, 1, doCmdWhoAmI},
	{SH_CMD_GET_VERSION, 1, doCmdGetVersion},
	{SH_CMD_RESET, 1, doCmdReset},
	/* boot loader specific commands */
	{SH_CMD_BOOT, 1, doCmdBoot},
	{SH_CMD_CHECK_IMAGE, 1, doCmdCheckImage},
	{SH_CMD_PROBE, 8, doCmdProbe},
	{SH_CMD_WRITE_BLOCK, sizeof(CmdRWBlockParam_t), doCmdWriteBlock},
	{SH_CMD_READ_BLOCK, 4, doCmdReadBlock},
	{SH_CMD_SECTOR_ERASE, 4, doCmdSectorErase},
	{SH_CMD_PAGE_ERASE, 4, doCmdPageErase},
	{SH_CMD_PAGE_WRITE, (4 + SL_FLASH_PAGE_SZ + SH_RESP_CRC32_SZ), doCmdPageWrite},
	{SH_CMD_PAGE_READ, 4, doCmdPageRead},
	{SH_CMD_WRITE_SUBBLOCK, 0, doCmdWriteSubBlock},
	{SH_CMD_READ_SUBBLOCK, 4, doCmdReadSubBlock},

};

/*****************************************************************************
 * Public functions
 ****************************************************************************/
void IRQ_Pin_Config(SL_PORTPIN_T *pPortInfo)
{
	pPortInfo->port = 0;  
	pPortInfo->pin  = 14; //--Adision changed  ??/
}

/* Parses port and pin data from a byte */
void parsePortData(SL_PORTPIN_T *pPortInfo, uint8_t packetByte)
{
	pPortInfo->port = (packetByte >> 5) & 0x7;  
	pPortInfo->pin  = packetByte & 0x1F; 
}

/* Assert interrupt line to Host/AP */
void processHostIfPacket(SL_HOSTIF_PACKET_T *pFromHost, SL_HOSTIF_PACKET_T *pToHost)
{
	uint32_t i = 0;
	int32_t cmdProcessed = 0;
	const CMD_HANDLER_T *pHdlr;
	CmdDataResp_t *resp = (CmdDataResp_t *) &pToHost->buffer[0];

	/* reset response buffers */
	pToHost->bytes = 0;

	for (i = 0; i < sizeof(g_CmdHdlr) / (sizeof(CMD_HANDLER_T)); i++) {
		/* get the handler pointer */
		pHdlr = &g_CmdHdlr[i];
		/* check if we received this command */
		if (pFromHost->buffer[0] == pHdlr->cmd) {
			/* check if packet size matches the command params */
			if ((pHdlr->cmd_param_size == 0) || (pFromHost->bytes == pHdlr->cmd_param_size)) {

				/* reset sub-block offset if any other command is issued in between */
				if ((g_SubBlockWrOffset != 0) && (pHdlr->cmd != SH_CMD_WRITE_SUBBLOCK)) {
					g_SubBlockWrOffset = 0;
				}

				/* pre-fill response header. Common for all commands. */
				resp->hdr.sop = CFG_BOOT_WHO_AM_I;
				resp->hdr.cmd = pHdlr->cmd;
				
				GPIOSetDir(slIfConfig.hostIrqPortPin.port,slIfConfig.hostIrqPortPin.pin,1 );
				Hostif_DeAssertIRQ();

				/* execute the command */
				resp->hdr.length = pHdlr->doCmd(pFromHost, pToHost);
				/* update transfer length */
				pToHost->bytes = SH_RESP_HEADER_SIZE + resp->hdr.length;
				cmdProcessed = 1;
				/* SPI transfer will happen only if SPI inreface is active */
				//g_spiTxPending = 1;
				break;
			}
		}
	}

	/* If command has response packet start transmission */
	if (cmdProcessed) {
		/* Assert IRQ to host and send response */
		Hostif_AssertIRQ();
	}
}

/* De-assert interrupt line to Host/AP */
void Hostif_AssertIRQ(void)
{
	/* Don't attempt to assert IRQ until the port is assigned */
	if (slIfConfig.hostIrqPortPin.pin != 0xFF) {

		GPIOSetBitValue( slIfConfig.hostIrqPortPin.port, slIfConfig.hostIrqPortPin.pin, 0);
	}
}

/* De-assert interrupt line to Host/AP */
void Hostif_DeAssertIRQ(void)
{
	/* Don't attempt to de-assert IRQ until the port is assigned */
	if (slIfConfig.hostIrqPortPin.pin != 0xFF) {

		GPIOSetBitValue( slIfConfig.hostIrqPortPin.port, slIfConfig.hostIrqPortPin.pin, 1);
	}
}

/* Initialize host interface subsystem */
void Hostif_Init(void)
{
	/* Initially auto */
	slIfConfig.ifSel = SL_I2C0;

	/* Unmapped host IRQ mapping */
	slIfConfig.hostIrqPortPin.port = 0xFF;		//--??
	slIfConfig.hostIrqPortPin.pin = 0xFF;
}

/* Is a host packet send or reeive done? */
bool hostIfPacketPending(void)
{
	bool pending = (bool) (buffXferPending != 0);

	buffXferPending = 0;
	return pending;
}
