/**
 * @file eps_btlr_driver.c
 *
 * @brief This file has the file transfer functions for EPS firmware update
 *
 * @copyright Copyright 2023 Antaris, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *
 */


#ifndef LINUX_TEMP_PORT

/* Includes ------------------------------------------------------------------*/
#include "stdio.h"
#include "string.h"
#include "stm32f7xx_hal_ext.h"
#include "eps_btlr_driver.h"
#include "stdlib.h"
#include "exo_io_al_i2c_common.h"
#include "nvm.h"
#include "eps_ftm.h"
#include "logger.h"
#include "eps.h"
#include "exo_io_al_crc_common.h"

#define BOOT_STARTUP_WAITING_TIME 150 //85

/* Allocated SDRAM for EPS module  1mb*/
extern uint8_t eps_ftm_file_dwnlod_storage[200*1024] PLACE_IN_EXT_RAM_MEM;

#ifdef EPS_NOR_FLASH_MEM_ENABLED
/* Allocated FMC NOR for EPS module  1mb*/
uint8_t eps_fw_storage[200*1024] PLACE_IN_EXT_FMC_FLASH_MEM;
#endif

/*
 *  sector 0 - 500ms
 *  sector 1 - 500ms
 *  sector 2 - 500ms
 *  sector 3 - 500ms
 *  sector 4 - 1400ms
 *  sector 5 - 4050ms
 *  sector 6 - 4050ms
 *  sector 7 - 4050ms
 */
const uint32_t sector_erase_delay_time[] = {500,500,500,500,1400,4050,4050,4050};

extern ioal_i2c_hdle ioal_hi2c3;

/*BOOTMODE XOR Calculation API Declaration */

static uint8_t xor_calculation(uint8_t *data_buff,uint16_t data_len);

/*BOOTMODE XOR Calculation API
 * This API is used to calculate the XOR value for given data
 */
static uint8_t xor_calculation(uint8_t *data_buff,uint16_t data_len)
{
	uint8_t xor = 0;
	xor = data_buff[0] ^ data_buff[1];
	for(int i=2;i<data_len;i++)
	{
		xor ^= data_buff[i];
	}
	return xor;
}


/**
 * @brief This function used to identify which sector the input address belongs
 */

static uint8_t get_sector(uint32_t address)
{
 uint8_t sector = 0;


 if((address < 0x8004000) && (address >= 0x08000000))
 {
   sector = 0x00;
 }
 else if((address < 0x08008000) && (address >= 0x08004000))
 {
   sector = 0x01;
 }
 else if((address < 0x0800C000) && (address >= 0x08008000))
 {
   sector = 0x02;
 }
 else if((address < 0x08010000) && (address >= 0x0800C000))
 {
   sector = 0x03;
 }
 else if((address < 0x08020000) && (address >= 0x08010000))
 {
   sector = 0x04;
 }
 else if((address < 0x08040000) && (address >= 0x08020000))
 {
   sector = 0x05;
 }
 else if((address < 0x08060000) && (address >= 0x08040000))
 {
   sector = 0x06;
 }
 else if((address < 0x08080000) && (address >= 0x08060000))
 {
   sector = 0x07;
 }
 return sector;
}


/*BOOTMODE WRITE API
 * This API is used to write in boot mode
 */
static uint8_t write_memory(uint32_t write_address, uint8_t *write_dt, uint16_t write_dt_len, uint8_t *write_data_buff);

/*BOOTMODE READ API
 * This API is used to write in boot mode
*/
static uint8_t read_memory(uint32_t read_address, uint8_t *read_buff, uint16_t read_dt_len);

/* MCU RESET API
 * This API is used to reset the slave my keeping NRST pin low for 50ms and releasing
 * it to high. Initially by default it will in high position
 */
#if 0
void eps_mcu_reset(void)
{
	  HAL_GPIO_WritePin(NRST_PORT, MCU_RESET_PIN, GPIO_PIN_RESET);
	  HAL_Delay(100);
	  HAL_GPIO_WritePin(NRST_PORT, MCU_RESET_PIN, GPIO_PIN_SET);
}
#endif
/*BOOT STARTUP API
 * This API is used to make the MCU enter into bootmode and check whether it enters into bootmode
 * by sending GET_ID command. This API performs following functions
 * * 1.Setting Boot0 pin  to enable System Bootloader
 * 2.Reset EPS
 * 3.Wait for 85ms for EPS Bootloader startup
 * 4.Read device ID and verify
 */

uint8_t eps_boot_startup(void)
{
	uint8_t sts;
	uint8_t rx_buff_local[5];
	uint8_t read_len_cnt_temp;
	HAL_Delay(BOOT_STARTUP_WAITING_TIME);

	sts = eps_boot_get_command(GET_ID, rx_buff_local, &read_len_cnt_temp);
	if(sts == 0)
	{
	  eps_boot_rx_buf_print(rx_buff_local, read_len_cnt_temp);
	  if((rx_buff_local[1] == 0x04) && (rx_buff_local[2] == 0x33))
	  {
		    DEBUG_CPRINT(("Boot Startup Success\r\n"));
	        log_id(LOG_INFO_L1, LOG_EPS, FW_BOOT_STARTUP_SUCCESS);
			sts = BOOT_OK;
	  }
	  else
	  {
			DEBUG_CPRINT(("Boot Startup Fail - PID1=0x%x PID2=x%x Invalid\n",rx_buff_local[1], rx_buff_local[2]));
			log_id_with_two_arg(LOG_ERROR, LOG_EPS, FW_BOOT_STARTUP_FAIL_PID1__hex__PID2__hex__INVALID, rx_buff_local[1], rx_buff_local[2]);
			sts = BOOT_STARTUP_FAIL;
	  }

	}
	return sts;
}

/*BOOT MODE GET COMMAD API
 * This API is used to get information from the slave MCU after making it to enter bootmode and
 * sending following get commands.
 * 1.Get command - This command can be sent to get commands for which the MCU will support and respond.
 * 2.Get ID      - This command can be sent to get product ID of the MCU.
 * 3.Get version - This command can be sent to get the version details.
 */
uint8_t eps_boot_get_command(uint8_t command, uint8_t *rx_buff, uint8_t *read_bytes_count)
{

	eps_boot_error sts  = BOOT_ERROR;
	uint8_t ack_read_byte;
	HAL_StatusTypeDef i2c_ret= 1;

	switch(command)
	{

	case GET_COMMAND:
		*read_bytes_count = 18 + 1; // added 1 byte for length field   GET command read byte count
	    break;
	case GET_VERSION:
		*read_bytes_count = 1;
		break;
	case GET_ID:
		*read_bytes_count = 2 + 1; // added 1 byte for length field
		break;
	default:
		DEBUG_CPRINT(("Improper command\n"));
        log_id(LOG_INFO_L1, LOG_EPS, FW_IMPROPER_COMMAND);
		sts = BOOT_IMPROPER_COMMAND;
		return sts;
		break;
	}

	sts = boot_send_command(command);
	if(sts == BOOT_OK)
	{
			// Read Data bytes
			i2c_ret = io_hal_i2c_receive(&ioal_hi2c3, SLAVE_ADDRESS, &rx_buff[0] ,*read_bytes_count ,EPS_I2C_BUS_TIMOUT_DELAY);
			if(i2c_ret == HAL_OK)
			{
				//Read ACK for Data Frame
				i2c_ret = io_hal_i2c_receive(&ioal_hi2c3,SLAVE_ADDRESS,&ack_read_byte,ACK_READ_FRAME_SIZE,EPS_I2C_BUS_TIMOUT_DELAY);
				if(i2c_ret == HAL_OK && ack_read_byte == ACK_BYTE)
				{
					sts = BOOT_OK;
				}
				else
				{
					DEBUG_CPRINT(("NACK for Data Read for Command 0x%02X\n",command));
					log_id_with_one_arg(LOG_ERROR, LOG_EPS, FW_NACK_FOR_DATA_READ_COMMAND__hex_, command);
					sts = BOOT_DATA_NACK;
				}

			}
			else
			{
				DEBUG_CPRINT(("Read Not success for Command 0x%02X\n",command));
				log_id_with_one_arg(LOG_ERROR, LOG_EPS, FW_READ_NOT_SUCCESS_FOR_COMMAND__hex_, command);
				sts = BOOT_COMMAND_READ_FAIL;
			}

		}
	 return sts;
}

/* BOOTMODE WRITE API
 * This API is used to write the data in boot mode
 */
uint8_t write_memory(uint32_t write_address, uint8_t *write_dt, uint16_t write_dt_len, uint8_t *write_data_buff)
{
	eps_boot_error sts  = BOOT_ERROR;
	uint8_t ack_read_byte, command;
	HAL_StatusTypeDef i2c_ret= 1;

	command =  WRITE_COMMAND;
	write_data_buff[0] = write_dt_len - 1;
	memcpy(&write_data_buff[1], write_dt, write_dt_len);
	write_data_buff[write_dt_len+1] = xor_calculation(write_data_buff, write_dt_len+1);
	 uint8_t wr_delay = 5;
	sts = boot_send_command(command);
	if(sts == BOOT_OK)
	{
		sts = boot_send_address(write_address);
		if(sts == BOOT_OK)
		{
			i2c_ret = io_hal_i2c_transmit(&ioal_hi2c3, SLAVE_ADDRESS, write_data_buff, write_dt_len+2, EPS_I2C_BUS_TIMOUT_DELAY);
			if(i2c_ret == HAL_OK)
			{
				HAL_Delay(wr_delay);
				i2c_ret = io_hal_i2c_receive(&ioal_hi2c3, SLAVE_ADDRESS, &ack_read_byte, ACK_READ_FRAME_SIZE, EPS_I2C_BUS_TIMOUT_DELAY); //ACK_READ_FRAME_SIZE

				if(i2c_ret != HAL_OK) //ACK_READ_BYTE
				{
					return BOOT_I2C_COMM_FAIL;
				}
				if(ack_read_byte == ACK_BYTE) //ACK_READ_BYTE
				{
					sts = BOOT_OK;
	              //  DEBUG_CPRINT(("PACKET WRITE SUCCESS\r\n"));
				}
				else if(ack_read_byte == NACK_BYTE)
				{
					sts = BOOT_WRITE_NACK;
				}
				else
				{
					sts = BOOT_WRITE_INVLD_ACK;
					DEBUG_CPRINT(("NACK received for write - WRITE FAIL\r\n"));
			        log_id(LOG_ERROR, LOG_EPS, FW_NACK_RECEIVED_FOR_WRITE);

				}

			}
			else
			{
				DEBUG_CPRINT(("Data frame transmit Failed\n"));
		        log_id(LOG_ERROR, LOG_EPS, FW_DATA_FRAME_TRANSMIT_FAILED);
				sts = BOOT_DATA_TRANSMIT_FAIL;
			}
		}
	}
	return sts;
}


/* BOOTMODE WRITE API
 * This API is used to read the data in boot mode
 */
uint8_t read_memory(uint32_t read_address, uint8_t *read_buff, uint16_t read_dt_len)
{
	eps_boot_error sts  = BOOT_ERROR;
    uint8_t read_len[2], ack_read_byte, command;
    HAL_StatusTypeDef i2c_ret= HAL_ERROR;

    command =  READ_COMMAND;

    sts = boot_send_command(command);
    if(sts == BOOT_OK)
    {
    	sts = boot_send_address(read_address);
    	if(sts == BOOT_OK)
    	{
    		// Read byte length frame
    		read_len[0] = read_dt_len -1;
    		read_len[1] = ~(read_dt_len -1);
			i2c_ret = io_hal_i2c_transmit(&ioal_hi2c3, SLAVE_ADDRESS, read_len, 2, EPS_I2C_BUS_TIMOUT_DELAY);
			if(i2c_ret==HAL_OK)
			{
				i2c_ret = io_hal_i2c_receive(&ioal_hi2c3, SLAVE_ADDRESS, &ack_read_byte, ACK_READ_FRAME_SIZE, EPS_I2C_BUS_TIMOUT_DELAY);
				if(i2c_ret != HAL_OK )
				{
					return BOOT_I2C_COMM_FAIL;
				}
				if(ack_read_byte == ACK_BYTE)
				{
					i2c_ret = io_hal_i2c_receive(&ioal_hi2c3, SLAVE_ADDRESS, &read_buff[0], read_dt_len, EPS_I2C_BUS_TIMOUT_DELAY);
					if(i2c_ret==HAL_OK)
					{
			        //   DEBUG_CPRINT(("Packet read Success\r\n"));
						sts = BOOT_OK;
					}
					else
					{
						DEBUG_CPRINT(("Read Memory Failure\r\n"));
				        log_id(LOG_ERROR, LOG_EPS, FW_READ_MEMORY_FAILURE);
						sts = BOOT_READ_FAIL;
					}
				}
				else if(ack_read_byte == NACK_BYTE)
				{
					sts = BOOT_READ_NACK;
				}
				else
				{
					DEBUG_CPRINT(("Received NACK for Read length frame\n"));
			        log_id(LOG_ERROR, LOG_EPS, FW_NACK_RECEIVED_FOR_READ_LENGTH_FRAME);
					sts = BOOT_READ_INVLD_ACK;
				}
			}
			else
			{
				DEBUG_CPRINT(("Read length frame transmit Failed\n"));
		        log_id(LOG_ERROR, LOG_EPS, FW_READ_LENGTH_FRAME_TRANSMIT_FAILED);
				sts = BOOT_DATA_TRANSMIT_FAIL;
			}
    	}
	}
 return sts;
}

/* BOOTMODE ERASE API
 * This API is used to erase memory sectors in boot mode
 */
uint8_t eps_btlr_sector_erase(uint8_t sector_id)
{
     //Delay required sector wise
	eps_boot_error sts  = BOOT_ERROR;
	uint8_t sector_cnt = 0x00;
    uint8_t ack_read_byte, command, sector_cnt_frame[3], sector_id_frame[3];
    HAL_StatusTypeDef i2c_ret= 1;

    command =  ERASE_COMMAND;
    sector_cnt_frame[0] = 0x00;
    sector_cnt_frame[1] = sector_cnt;
    sector_cnt_frame[2] = sector_cnt_frame[0] ^ sector_cnt_frame[1];
    sector_id_frame[0] = 0x00;
    sector_id_frame[1] = sector_id;
    sector_id_frame[2] = sector_id_frame[0] ^ sector_id_frame[1];

    sts = boot_send_command(command);
    if(sts == BOOT_OK)
    {
		i2c_ret = io_hal_i2c_transmit(&ioal_hi2c3, SLAVE_ADDRESS, sector_cnt_frame, SECTOR_CNT_FRAME_SIZE, EPS_I2C_BUS_TIMOUT_DELAY);
		if(i2c_ret == HAL_OK)
		{
			HAL_Delay(50);
			ack_read_byte = 0x00;
			i2c_ret = io_hal_i2c_receive(&ioal_hi2c3, SLAVE_ADDRESS, &ack_read_byte, ACK_READ_FRAME_SIZE, EPS_I2C_BUS_TIMOUT_DELAY);

			if(i2c_ret != HAL_OK)
			{
				return BOOT_I2C_COMM_FAIL;
			}


			if(ack_read_byte == ACK_BYTE)
			{
				i2c_ret = io_hal_i2c_transmit(&ioal_hi2c3, SLAVE_ADDRESS, sector_id_frame, SECTOR_ID_FRAME_SIZE, EPS_I2C_BUS_TIMOUT_DELAY);
				if(i2c_ret==HAL_OK)
				{
				  HAL_Delay(sector_erase_delay_time[sector_id]);
					i2c_ret = io_hal_i2c_receive(&ioal_hi2c3, SLAVE_ADDRESS, &ack_read_byte, ACK_READ_FRAME_SIZE, EPS_I2C_BUS_TIMOUT_DELAY);
					if(i2c_ret != HAL_OK)
					{
						return BOOT_SECTOR_ID_FAIL;
					}

					if(ack_read_byte == ACK_BYTE)
					{
					//	CPRINT_DEBUG(("ACK received for erase sector \r\n"));
					//	CPRINT_DEBUG(("Erase successful\r\n"));
						sts = BOOT_OK;
					}
					else if(ack_read_byte == NACK_BYTE)
					{
						sts = BOOT_SECTOR_ID_NACK;
					}
					else
					{
						DEBUG_CPRINT(("NACK received for erase sector \r\n"));
				        log_id(LOG_ERROR, LOG_EPS, FW_NACK_RECEIVED_FOR_ERASE_SECTOR);
						sts = BOOT_SECTOR_ID_INVALID_ACK;
					}
				}
				else
				{
					DEBUG_CPRINT(("Sector ID transmit Fail \r\n"));
			        log_id(LOG_ERROR, LOG_EPS, FW_SECTOR_ID_TRANSMIT_FAIL);
					sts = BOOT_SECTOR_ID_FAIL;
				}
			}
			else if(ack_read_byte == NACK_BYTE)
			{
				DEBUG_CPRINT(("NACK received for sector count transmit \r\n"));
		        log_id(LOG_ERROR, LOG_EPS, FW_NACK_RECEIVED_FOR_SECTOR_COUNT_TRANSMIT);
				sts = BOOT_SECTOR_CNT_NACK;
			}
			else
			{
				sts = BOOT_SECTOR_CNT_INVLD_ACK;
			}
		}
		else
		{
			DEBUG_CPRINT(("Sector count transmit Fail \r\n"));
	        log_id(LOG_ERROR, LOG_EPS, FW_SECTOR_COUNT_TRANSMIT_FAIL);
			sts = BOOT_SECTOR_CNT_FAIL;
		}
	}
 return sts;
}

/* BOOT ERASE PAGE API
 * This API is used to erase sectors specified from the start address till the size mentioned in the firmware size
 */

uint8_t eps_btlr_page_erase(uint32_t erase_start_address, uint32_t firmware_size)

{
	uint8_t start_erase_secotr, end_erase_sector, cnt;
	uint32_t erase_end_address;
	eps_boot_error sts  = BOOT_ERROR;

    start_erase_secotr = get_sector(erase_start_address);
    erase_end_address  = erase_start_address + firmware_size;
    end_erase_sector   = get_sector(erase_end_address);

    for(cnt = start_erase_secotr; cnt<=end_erase_sector; cnt++)
    {
    	sts = eps_btlr_sector_erase(cnt);
    	if(sts == BOOT_OK)
    	{
    	  // DEBUG_CPRINT(("Sector erased successfully\r\n"));
    	}
    	else
    	{
    		break;
    		//do nothing
    	}
    }
    return sts;
}
#if 0
/* BOOT GO COMMAND API
 * This API is used to jump MCU to the memory location specified in the address by using GO command
 */
uint8_t eps_btlr_run_firmware(uint32_t go_address)
{
    uint8_t command;
    eps_boot_error sts = BOOT_ERROR;
    command = GO_COMMAND;

	sts = boot_send_command(command);
	if(sts == BOOT_OK)
	{
		sts = boot_send_address(go_address);
		if(sts == BOOT_OK)
		{
			DEBUG_CPRINT(("GO command successful\r\n"));
	        log_id(LOG_INFO_L1, LOG_EPS, FW_GO_COMMAND_SUCCESSFUL);
		}
	}
	return sts;
}
#endif
/* BOOT SEND COMMAND API
 * This API is send command and checks for the acknowledgment for the command sent
 */

uint8_t boot_send_command(uint8_t boot_command)
{
	uint8_t send_cmd[2], ack_read_byte;
	HAL_StatusTypeDef i2c_ret= 1;
	eps_boot_error sts = BOOT_ERROR;

	send_cmd[0] = boot_command;
	send_cmd[1] = ~boot_command;

	i2c_ret = io_hal_i2c_transmit(&ioal_hi2c3, SLAVE_ADDRESS, send_cmd, COMMAND_FRAME_SIZE, EPS_I2C_BUS_TIMOUT_DELAY);
	if(i2c_ret==0)
	{
	//	CPRINT_DEBUG(("Command 0x%02X Accepted\r\n", boot_command));
		// Read ACK for Command Frame
		HAL_Delay(5);
		i2c_ret = io_hal_i2c_receive(&ioal_hi2c3, SLAVE_ADDRESS, &ack_read_byte, ACK_READ_FRAME_SIZE, EPS_I2C_BUS_TIMOUT_DELAY);
		if(i2c_ret == HAL_OK && ack_read_byte == ACK_BYTE)
		{
		//	CPRINT_DEBUG(("Received ACK for Command 0x%02X\r\n", boot_command));
			sts = BOOT_OK;
		}
		else
		{
			DEBUG_CPRINT(("Received NACK for Command 0x%02X\r\n", boot_command));
			log_id_with_one_arg(LOG_ERROR, LOG_EPS, FW_RECEIVED_NACK_FOR_COMMAND__hex_, boot_command);
		    sts = BOOT_COMMAND_NACK;
		}

	}
	else
	{
		DEBUG_CPRINT(("Command rejected - 0x%02X\r\n", boot_command));
		log_id_with_one_arg(LOG_ERROR, LOG_EPS, FW_COMMAND_REJECTED__hex_, boot_command);
		sts = BOOT_CMD_REJECTED;
	}
	return sts;
}

/* BOOT SEND ADDRESS COMMAND API
 * This API is used to send address location to MCU
 */

uint8_t boot_send_address(uint32_t address)
{
	uint8_t xor_add, address_frame[5], ack_read_byte, address_len = 4, sts;
	HAL_StatusTypeDef i2c_ret= 1;

	address_frame[0] = address>>24;
	address_frame[1] = (address>>16)&0xff;
	address_frame[2] = (address>>8)&0xff;
	address_frame[3] = (address)&0xff;
	//xor calculation for address
	xor_add = xor_calculation(address_frame, address_len);
	address_frame[4] = xor_add;
	//send address+checksum frame
	i2c_ret = io_hal_i2c_transmit(&ioal_hi2c3, SLAVE_ADDRESS, address_frame, ADDRESS_FRAME_SIZE, EPS_I2C_BUS_TIMOUT_DELAY);
	if(i2c_ret==0)
	{
	    i2c_ret = io_hal_i2c_receive(&ioal_hi2c3, SLAVE_ADDRESS, &ack_read_byte, ACK_READ_FRAME_SIZE, EPS_I2C_BUS_TIMOUT_DELAY);
	    if(i2c_ret == HAL_OK && ack_read_byte == ACK_BYTE)
	    {
	    //	CPRINT_DEBUG(("Received ACK for Address frame\r\n"));
	    	sts = BOOT_OK;
	    }
	    else
	    {
	    	DEBUG_CPRINT(("Received NACK for Address frame\r\n"));
			log_id(LOG_ERROR, LOG_EPS, FW_RECEIVED_NACK_FOR_ADDRESS_FRAME);
	       	sts = BOOT_ADDRESS_NACK;
	    }
	}
	else
	{
		DEBUG_CPRINT(("Address frame transmit Failed\r\n"));
		log_id(LOG_ERROR, LOG_EPS, FW_ADDRESS_FRAME_TRANSMIT_FAILED);
		sts = BOOT_ADDRESS_TRANSMIT_FAIL;
	}
	return sts;
}

/*
 * @brief This API write the data in flash memory
 */
uint8_t eps_btlr_program_page_write(uint32_t memory_address, uint8_t *write_data, uint32_t write_length)
{
	uint32_t cnt, page_write_cnt, residue_length;
	uint32_t data_cnt = 0;
	uint8_t *data_frame_buff;
	eps_boot_error sts = BOOT_ERROR;

	// checking input address is byte alligned
	if((memory_address % FOUR_BYTE_ALLIGNMENT) == 0)
	{
		page_write_cnt = write_length / PAGE_WRITE_SIZE;
		residue_length = write_length % PAGE_WRITE_SIZE;

		// TODO malloc 256 bytes
		data_frame_buff = os_malloc(PAGE_WRITE_SIZE+2);

		for(cnt=0 ; cnt<page_write_cnt; cnt++)
		{

			sts = write_memory(memory_address, &write_data[data_cnt], PAGE_WRITE_SIZE, data_frame_buff);
			if(sts == BOOT_OK)
			{
			memory_address +=PAGE_WRITE_SIZE;
			data_cnt +=PAGE_WRITE_SIZE;
			}
			else
			{
				return sts;
			}
			os_delay(10);
		}

		if(residue_length !=0 )
		{
			sts = write_memory(memory_address, &write_data[data_cnt], residue_length, data_frame_buff);
		}
		else
		{
			// do nothing
		}

		os_free(data_frame_buff);
	}
	else
	{
		DEBUG_CPRINT(("Write address not aligned in 4bytes-Write fail\r\n"));
		log_id(LOG_ERROR, LOG_EPS, FW_WRITE_ADDRESS_NOT_ALIGNED_IN_4BYTES_WRITE_FAIL);
		sts = BOOT_WRITE_ADDRESS_NOT_ALIGNED;
	}
	return sts;
}

/*
 * @brief This API read the data from flash memory
 */
uint8_t eps_btlr_page_read(uint32_t memory_address, uint8_t *read_data, uint32_t read_length)
{
	uint32_t cnt, page_read_cnt, residue_length;
	uint32_t data_cnt = 0;
	eps_boot_error sts = BOOT_ERROR;

	// checking input address is byte alligned
	if((memory_address % FOUR_BYTE_ALLIGNMENT) == 0)
	{
		page_read_cnt = read_length / PAGE_READ_SIZE;
		residue_length = read_length % PAGE_READ_SIZE;
		for(cnt=0 ; cnt<page_read_cnt; cnt++)
		{
			sts = read_memory(memory_address, &read_data[data_cnt], PAGE_READ_SIZE);
			if(sts == BOOT_OK)
			{
			memory_address +=PAGE_READ_SIZE;
			data_cnt +=PAGE_READ_SIZE;
			}
			else
			{
				return sts;
			}
			os_delay(5);
		}
		if(residue_length !=0 )
		{
			sts = read_memory(memory_address, &read_data[data_cnt], residue_length);
		}
		else
		{
			// do nothing
		}

	}
	else
	{
		DEBUG_CPRINT(("Read address not aligned in 4bytes\r\n"));
		log_id(LOG_ERROR, LOG_EPS, FW_READ_ADDRESS_NOT_ALIGNED_IN_4BYTES_READ_FAIL);
		sts = BOOT_READ_ADDRESS_NOT_ALIGNED;
	}
	return sts;
}

/*
 * @brief This API update the firmware in boot mode
 */
uint8_t eps_firmware_update(uint32_t source_address, uint32_t destination_address, uint32_t firmware_size, uint32_t fw_crc)
{
	eps_boot_error sts = BOOT_ERROR;

	/*
	 *  if startup fails ? we can try for 3 times
	 */
	sts = eps_boot_startup();
	if(sts == BOOT_OK)
	{
		/* The following operation processed
		 * Firmware Write
		 * Read firmware back and check integrity
		 *
		 * Questions:
		 * 1. How to handle if write failed ?
		 * 2. Integrity fail ?
		 */
		sts = eps_firmware_loading(source_address, destination_address, firmware_size, fw_crc);
		if(sts == BOOT_OK)
		{
			/* how can we ensure that EPS is working after firmware upadte ? */
			DEBUG_CPRINT(("FIRMWARE LOADING SUCCESS\r\n"));
			log_id(LOG_INFO_L1, LOG_EPS, FW_FIRMWARE_LOADING_SUCCESS);
		}
		else
		{
			/*
			 * we can send go command again
			 */
			DEBUG_CPRINT(("FIRMWARE LOADING FAILURE\r\n"));
			log_id(LOG_ERROR, LOG_EPS, FW_FIRMWARE_LOADING_FAILURE);
		}
	}
	else
	{
		//do nothing
	}
	return sts;
}

/*
 * @brief This API load the firmware in flash memory.
 * Readback the data from flash memory for CRC calculation
 */
uint8_t eps_firmware_loading(uint32_t source_address, uint32_t destination_address, uint32_t firmware_size, uint32_t fw_crc)
{
	eps_boot_error sts = BOOT_ERROR;
	uint32_t read_buff_crc;


	sts = eps_btlr_page_erase(destination_address, firmware_size);
	if(sts == BOOT_OK)
	{
		DEBUG_CPRINT(("ERASE SECTORS SUCCESSFUL\r\n"));
		log_id(LOG_INFO_L1, LOG_EPS, FW_ERASE_SECTORS_SUCCESSFUL);
		sts = eps_btlr_program_page_write(destination_address, (uint8_t*)source_address, firmware_size);
		if(sts == BOOT_OK)
		{
			sts = eps_btlr_page_read(destination_address, eps_ftm_file_dwnlod_storage, firmware_size);
			if(sts == BOOT_OK)
			{
				      read_buff_crc = exo_io_hal_crc_calculate(EXO_HARDWARE_CRC_MODE,eps_ftm_file_dwnlod_storage, (firmware_size/4)*4);

					  if(read_buff_crc == fw_crc)
					  {
						DEBUG_CPRINT(("Read and write data match success\r\n"));
						log_id(LOG_INFO_L1, LOG_EPS, FW_READ_AND_WRITE_DATA_MATCH_SUCCESS);
						sts = BOOT_OK;
					  }
					  else
					  {
						 DEBUG_CPRINT(("Read and write data match fail"));
						 log_id(LOG_ERROR, LOG_EPS, FW_READ_AND_WRITE_DATA_MATCH_FAIL);
						 sts = BOOT_READ_WRITE_MISMATCH;
					  }
			}
			else
			{
				//do nothing
			}
		}
		else
		{
			//do nothing
		}
	}
	else
	{
		//do nothing
	}
	return sts;
}
#if 0
/*
 * @brief
 */
uint8_t eps_run_firmware(uint32_t destination_address)
{
	eps_boot_error sts = BOOT_ERROR;

	sts = eps_btlr_run_firmware(destination_address);
	if(sts == BOOT_OK)
	{
		DEBUG_CPRINT(("FIRMWARE UPDATE SUCCESS\r"));
		log_id(LOG_INFO_L1, LOG_EPS, FW_FIRMWARE_UPDATE_SUCCESS);
	}
	else
	{
		//do nothing
	}
return sts;
}
#endif

/*BOOT PRINT API
 * This API is used to print the buffer received while sending commands in boot mode
 */
void eps_boot_rx_buf_print(uint8_t *rx_buf_ptr, uint8_t rx_buf_len)
{
    uint8_t u8Cntr;

	for(u8Cntr = 0; u8Cntr < (rx_buf_len); u8Cntr++)
	{
//	  DEBUG_CPRINT(("0x%02X, ",rx_buf_ptr[u8Cntr]));
	}
//	DEBUG_CPRINT(("\r\n"));
}



#endif
