/**
 * @file eps_fw_ctrlr.c
 *
 * @brief This file has the functions for handling telecommand and telemetry 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.
 *
 *
 */

#include "eps_btlr_driver.h"
#include "eps_fw_update.h"
#include "obc_tc_tm.h"
#include "nvm.h"
#include "eps_fw_ctrlr.h"
#include "eps_ftm.h"
#include "logger.h"
#include "exo_io_al_crc_common.h"
#include "exo_tctm_ipc.h"
#include "eps_ctrlr_init.h"
#include "eps_ctrlr.h"
#include "eps.h"

extern os_timer_handle_ptr  eps_rd_tmr;
extern sat_dev_sts_s sat_dev_sts[MAX_SAT_DEV_CNT];

eps_fw_data_s PLACE_IN_EXT_RAM_MEM fw_conv_status;
eps_fw_data_s PLACE_IN_EXT_RAM_MEM fw_op_chnl_status;
eps_fw_data_s PLACE_IN_EXT_RAM_MEM bckup_fw_conv_status;
eps_fw_data_s PLACE_IN_EXT_RAM_MEM bckup_fw_op_chnl_status;

firmware_status PLACE_IN_EXT_RAM_MEM fw_conv_upt_progress_status;
firmware_status PLACE_IN_EXT_RAM_MEM fw_op_chnl_upt_progress_status;
extern uint8_t eps_fw_start_flag;

eps_get_fw_s eps_bckup_get_nvm_fw[2];

#define EPS_FW_UPDATE_TRIAL_COUNT 3
#define TC_CMD_FIRMWARE_AVAILABILITY_CHECK             01
#define TC_CMD_FIRMWARE_UPDATE                         02
#define TC_CMD_FIRMWARE_UPDATE_STATUS_CHECK            03
#define TM_CMD_FIRMWARE_NOT_AVAILABLE                  04
#define TM_BCKUP_FW_INTR_FAIL						   05
#define TC_CMD_BCKUP_FIRMWARE_AVAILABILITY_CHECK       04
#define TC_CMD_BCKUP_FIRMWARE_UPDATE                   05

#define TM_CMD_FIRMWARE_SIZE_0R_CRC_MISMATCH           03
#define TM_CMD_DOWNLOADED_FIRMWARE_INTEGRITY_FAIL      02

#define TM_CMD_FIRMWARE_UPDATE_FAILED                  01
#define TM_CMD_FIRMWARE_UPDATE_PASSED                  00
#define TM_CMD_BOOT_STARTUP_FAIL					   06

#define CONVERTOR_BOARD_ID                             01
#define OUTPUT_CHNL_BOARD_ID                           02

uint8_t eps_fw_update_fail_flag=0;
uint8_t bckup_fw_intr_status;
uint16_t eps_chnl_ste;      	//This var include one reset bit and remaining as chnl bit
uint8_t eps_chnl_pwr_dwn_flg;

/*
 * @brief 1.This API will check the availablity of the firmware of both convertor and output channel board.
 * 2. Pack the telemetry data of the firmware status
 */
void eps_fw_avail_check(eps_fw_data_s *fw_status, obc_pck_data_eps *fw_info)
{
	if((fw_status->fw_status == FIRMWARE_AVAILABLE) && (fw_status->fw_size != 0) && (fw_status->fw_crc!= 0))
	{
		memcpy(&fw_info->rx_data[2],(uint8_t*)fw_status,sizeof(eps_fw_data_s));
	}
	else
	{
		fw_status->fw_status = NO_FIRMWARE;
		memcpy(&fw_info->rx_data[2],(uint8_t*)fw_status,sizeof(eps_fw_data_s));
	}
}

/*
 * @brief 1.This API will check the availablity of the backup firmware of both convertor and output channel board.
 * 2. Pack the telemetry data of the firmware status
 */
void eps_bckup_fw_avail_check(eps_fw_data_s *fw_status, obc_pck_data_eps *fw_info)
{
	if((fw_status->fw_status == FIRMWARE_AVAILABLE) && (fw_status->fw_size != 0) && (fw_status->fw_crc!= 0))
	{
		memcpy(&fw_info->rx_data[2],(uint8_t*)fw_status,sizeof(eps_fw_data_s));
	}
	else
	{
		fw_status->fw_status = NO_FIRMWARE;
		memcpy(&fw_info->rx_data[2],(uint8_t*)fw_status,sizeof(eps_fw_data_s));
	}
}

/*
 * @brief 1.This API will update the firmware of converter board and output channel board based on the given input
 * 2. Pack the telemetry data of firmware update status
 */
void eps_fw_update(eps_fw_data_s *fw_status, obc_pck_data_eps *fw_info, uint32_t *crc, uint32_t *len, uint8_t conv_batt_selc)
{
	uint32_t check_sum;
	uint32_t bckup_check_sum;
	eps_boot_error sts = BOOT_ERROR;
	uint8_t u8cntr;
	ptr_typ bckup_addr_sdram;
	ptr_typ addr_sdram;
	ptr_typ addr_norflash;
	uint8_t trial_failure_casue[EPS_FW_UPDATE_TRIAL_COUNT]={0xff,0xff,0xff};
	uint8_t status;

	/* EPS backup FW integrity check */
	bckup_addr_sdram = (ptr_typ)eps_get_sdram_storage_address();                 // gets address of sdram storage
	if(conv_batt_selc==1)
	{
		bckup_fw_intr_status = eps_bckup_fw_integrity_check(&bckup_fw_conv_status,&bckup_check_sum,conv_batt_selc,bckup_addr_sdram);
		if(bckup_fw_intr_status == CPY_OK)
		{
			if(bckup_check_sum == bckup_fw_conv_status.fw_crc)
			{
				bckup_fw_intr_status = BCKUP_CRC_PASS;
			}
			else
			{
				bckup_fw_intr_status = BCKUP_CRC_FAIL;
			}
		}
		else
		{
			bckup_fw_intr_status = CPY_ERR;
		}
	}
	else
	{
		bckup_fw_intr_status = eps_bckup_fw_integrity_check(&bckup_fw_op_chnl_status,&bckup_check_sum,conv_batt_selc,bckup_addr_sdram);
		if(bckup_fw_intr_status == CPY_OK)
		{
			if(bckup_check_sum == bckup_fw_op_chnl_status.fw_crc)
			{
				bckup_fw_intr_status = BCKUP_CRC_PASS;
			}
			else
			{
				bckup_fw_intr_status = BCKUP_CRC_FAIL;
			}
		}
		else
		{
			bckup_fw_intr_status = CPY_ERR;
		}
	}

	if(bckup_fw_intr_status == BCKUP_CRC_PASS)
	{
		if(fw_status->fw_status == FIRMWARE_UPDATE_IN_PROGRESS )
		{

			if((*len == fw_status->fw_size)&& (*crc == fw_status->fw_crc))
			{
				addr_sdram = (ptr_typ)eps_get_sdram_storage_address();                 // gets address of sdram storage

				addr_norflash = (ptr_typ)eps_get_nor_flash_address(conv_batt_selc);				// External Flash address(converter or output channel board storage)
				status = os_memcpy_extmem((uint8_t*)addr_sdram, (uint8_t*)addr_norflash, fw_status->fw_size, NULL, 1,1);
				if(status == CPY_OK)
				{
					check_sum =exo_io_hal_crc_calculate(EXO_HARDWARE_CRC_MODE,(uint8 *)addr_sdram, ((fw_status->fw_size/4)*4));
					if(check_sum == fw_status->fw_crc)
					{
						for (u8cntr = 0; u8cntr < EPS_FW_UPDATE_TRIAL_COUNT; u8cntr++)
						{
							if(conv_batt_selc == 1)
							{
								eps_fw_boot_en(0);
							}
							else if(conv_batt_selc == 2)
							{
								eps_fw_boot_en(1);
							}
							else
							{
								DEBUG_CPRINT(("Invalid MCU type\n"));
							}
#ifndef LINUX_TEMP_PORT
							sts = eps_firmware_update(addr_sdram, EPS_FIRMWARE_UPDATE_ADDRESS,fw_status->fw_size, fw_status->fw_crc);
#endif
							if(sts == BOOT_OK || sts == BOOT_STARTUP_FAIL)
							{
								eps_fw_update_fail_flag = 0;
								break;
							}
							else
							{
								trial_failure_casue[u8cntr] = sts;			//firmware update fail, retry again
								if(u8cntr == 3 && sts != 0)
									eps_fw_update_fail_flag=1;
							}
						}

						if(sts == BOOT_OK)
						{
							DEBUG_CPRINT(("Firmware Update Pass\n"));
							log_id(LOG_INFO_L1, LOG_EPS, FW_UPDATE_PASS);
							fw_info->rx_data[2] = TM_CMD_FIRMWARE_UPDATE_PASSED;
							if(conv_batt_selc == 1)
							{
								eps_fw_boot_en(EPS_CONV_BRD_DIS);
							}
							else
							{
								eps_fw_boot_en(EPS_OP_CHNL_DIS);
								eps_fw_mcu2_upd_check(fw_info);
							}
						}
						else if(sts == BOOT_STARTUP_FAIL)
						{
							fw_info->rx_data[2] = TM_CMD_BOOT_STARTUP_FAIL;
							log_id(LOG_ERROR, LOG_EPS, EPS_FW_BOOT_STARTUP_FAIL);
						}
						else
						{
							log_id(LOG_ERROR, LOG_EPS, EPS_FW_UPDATE_FAILED);
							fw_info->rx_data[2] = TM_CMD_FIRMWARE_UPDATE_FAILED;
							if(conv_batt_selc == 1)
							{
								fw_conv_upt_progress_status = FIRMWARE_UPDATE_FAIL;
								eps_fw_boot_en(EPS_CONV_BRD_DIS);
							}
							else
							{
								fw_op_chnl_upt_progress_status = FIRMWARE_UPDATE_FAIL;
								eps_fw_boot_en(EPS_OP_CHNL_DIS);
								eps_fw_mcu2_upd_check(fw_info);
							}
						}
					}
					else
					{
						fw_info->rx_data[2] = TM_CMD_DOWNLOADED_FIRMWARE_INTEGRITY_FAIL;
						log_id(LOG_ERROR, LOG_EPS, EPS_DOWNLOADED_FIRMWARE_INTEGRITY_FAIL);
					}

				}
				else
				{
					DEBUG_CPRINT(("Memcopy from NOR to SDRAM fail\n"));
					log_id(LOG_ERROR, LOG_EPS, FW_MEMCOPY_FROM_NOR_TO_SDRAM_FAIL);
				}

			}
			else
			{
				fw_info->rx_data[2] = TM_CMD_FIRMWARE_SIZE_0R_CRC_MISMATCH;
				log_id(LOG_ERROR, LOG_EPS, FW_SIZE_0R_CRC_MISMATCH);
			}
		}
		else
		{
			fw_info->rx_data[2] = TM_CMD_FIRMWARE_NOT_AVAILABLE;
			log_id(LOG_ERROR, LOG_EPS, EPS_FW_NOT_AVAILABLE);
		}
	}
	else
	{
		fw_info->rx_data[2] = TM_BCKUP_FW_INTR_FAIL;
		log_id(LOG_ERROR, LOG_EPS, EPS_BCKUP_FW_INTR_FAIL);
	}

	fw_info->rx_data[3] = sts;
	os_memcpy(&fw_info->rx_data[4],&trial_failure_casue[0],EPS_FW_UPDATE_TRIAL_COUNT);
}

/**
 * @brief This function is used to check the backup FW integrity check
 */
uint8_t eps_bckup_fw_integrity_check(eps_fw_data_s *fw_sts,uint32_t *check_sum,uint8_t conv_batt_selc,ptr_typ addr_sdram)
{
	ptr_typ bckup_addr_norflash;
	uint8_t status;
	bckup_addr_norflash = (ptr_typ)eps_get_nor_flash_bckup_address(conv_batt_selc);				// External Flash address(converter or output channel board storage)
	status = os_memcpy_extmem((uint8_t*)addr_sdram, (uint8_t*)bckup_addr_norflash, fw_sts->fw_size, NULL, 1,1);
	if(status == CPY_OK)
	{
		*check_sum =exo_io_hal_crc_calculate(EXO_HARDWARE_CRC_MODE,(uint8 *)addr_sdram, ((fw_sts->fw_size/4)*4));
	}
	return status;
}

/*
 * @brief 1.This API will update the backup firmware of converter board and output channel board based on the given input
 * 2. Pack the telemetry data of firmware update status
 */
void eps_bckup_fw_update(eps_fw_data_s *fw_status, obc_pck_data_eps *fw_info,uint8_t conv_batt_selc)
{
	uint32_t check_sum;
	eps_boot_error sts = BOOT_ERROR;
	uint8_t u8cntr;
	ptr_typ addr_sdram;
	uint8_t trial_failure_casue[EPS_FW_UPDATE_TRIAL_COUNT]={0xff,0xff,0xff};
	uint8_t status;

	if(fw_status->fw_status == FIRMWARE_UPDATE_IN_PROGRESS)
	{
		addr_sdram = (ptr_typ)eps_get_sdram_storage_address();                 // gets address of sdram storage
		status = eps_bckup_fw_integrity_check(fw_status,&check_sum,conv_batt_selc,addr_sdram);
		if(status == CPY_OK && check_sum == fw_status->fw_crc)
		{
			for (u8cntr = 0; u8cntr < EPS_FW_UPDATE_TRIAL_COUNT; u8cntr++)
			{
				if(conv_batt_selc == 1)
				{
					eps_fw_boot_en(0);
				}
				else if(conv_batt_selc == 2)
				{
					eps_fw_boot_en(1);
				}
				else
				{
					DEBUG_CPRINT(("Invalid MCU type\n"));
				}
#ifndef LINUX_TEMP_PORT
				sts = eps_firmware_update(addr_sdram, EPS_FIRMWARE_UPDATE_ADDRESS,fw_status->fw_size, fw_status->fw_crc);
#endif
				if(sts == BOOT_OK || sts == BOOT_STARTUP_FAIL)
				{
					eps_fw_update_fail_flag = 0;
					break;
				}
				else
				{
					trial_failure_casue[u8cntr] = sts;			//firmware update fail, retry again
					if(u8cntr == 3 && sts != 0)
						eps_fw_update_fail_flag=1;
				}

			}
			if(sts == BOOT_OK)
			{
				DEBUG_CPRINT(("Firmware Update Pass\n"));
				log_id(LOG_INFO_L1, LOG_EPS, FW_UPDATE_PASS);
				fw_info->rx_data[2] = TM_CMD_FIRMWARE_UPDATE_PASSED;
				if(conv_batt_selc == 1)
				{
					eps_fw_boot_en(EPS_CONV_BRD_DIS);
				}
				else
				{
					eps_fw_boot_en(EPS_OP_CHNL_DIS);
					eps_fw_mcu2_upd_check(fw_info);
				}
			}
			else if(sts == BOOT_STARTUP_FAIL)
			{
				fw_info->rx_data[2] = TM_CMD_BOOT_STARTUP_FAIL;
				log_id(LOG_ERROR, LOG_EPS, EPS_BCKUP_FW_BOOT_STARTUP_FAIL);
			}
			else
			{
				log_id(LOG_ERROR, LOG_EPS, EPS_BCKUP_FW_UPDATE_FAILED);
				fw_info->rx_data[2] = TM_CMD_FIRMWARE_UPDATE_FAILED;
				if(conv_batt_selc == 1)
				{
					fw_conv_upt_progress_status = BCKUP_FIRMWARE_UPDATE_FAIL;
				}
				else
				{
					fw_op_chnl_upt_progress_status = BCKUP_FIRMWARE_UPDATE_FAIL;
				}
			}
		}
		else
		{
			if(status == CPY_OK)
			{
				DEBUG_CPRINT(("CRC check fail"));
				fw_info->rx_data[2] =TM_CMD_DOWNLOADED_FIRMWARE_INTEGRITY_FAIL;
			}
			else
			{
				DEBUG_CPRINT(("Memcopy from NOR to SDRAM fail\n"));
				log_id(LOG_ERROR, LOG_EPS, BCKUP_FW_MEMCOPY_FROM_NOR_TO_SDRAM_FAIL);
			}

		}
	}
	else
	{
		DEBUG_CPRINT(("FIRMWARE_NOT_AVAILABLE"));
		fw_info->rx_data[2] =TM_CMD_FIRMWARE_NOT_AVAILABLE;
		log_id(LOG_ERROR, LOG_EPS, EPS_BCKUP_FW_NOT_AVAILABLE);
	}
	fw_info->rx_data[3] = sts;
	os_memcpy(&fw_info->rx_data[4],&trial_failure_casue[0],EPS_FW_UPDATE_TRIAL_COUNT);
}

/**
 * @brief This function is used to handle generate telemetry depending on the telecommand given
 *
 * It performs the following functions based on telecommand
 * 1.Check whether firmware is available. Send response whether firmware available or not.
 * 2.Trigger the firmware update process after checking the integrity.
 * 3.Send the status whether is firmware update process is pass or fail.
 *
 */
uint8_t eps_fw_update_tc_decode(obc_pck_data_eps *eps_fw_info)
{
#ifndef LINUX_TEMP_PORT
	uint32_t *len_temp;
	uint32_t *crc_temp;
#endif
	uint8_t status;
	uint8_t *data = eps_fw_info->tx_data;

	if(data[0] == TC_CMD_FIRMWARE_AVAILABILITY_CHECK)
	{
		eps_fw_info->rx_data = (uint8_t*)os_malloc(sizeof(eps_fw_data_s)+2);
		if(eps_fw_info->rx_data != NULL)
		{
			if(data[1] == CONVERTOR_BOARD_ID)
			{
				eps_fw_avail_check(&fw_conv_status, eps_fw_info);
			}
			else if(data[1]== OUTPUT_CHNL_BOARD_ID)
			{
				eps_fw_avail_check(&fw_op_chnl_status, eps_fw_info);
			}
			else
			{
				DEBUG_CPRINT(("Invalid MCU type\n"));
			}
			eps_fw_info->rx_data_len = sizeof(eps_fw_data_s)+2;
		}
		else
		{
			//do nothing
		}
	}
#ifndef LINUX_TEMP_PORT
	else if(data[0] == TC_CMD_FIRMWARE_UPDATE)
	{
		eps_deinit_other_process_before_fw_upd();
		crc_temp = (uint32_t*)(&data[6]);
		len_temp = (uint32_t*)(&data[2]);
		eps_fw_info->rx_data_len = 7;
		eps_fw_info->rx_data = (uint8_t*)os_malloc(eps_fw_info->rx_data_len);
		if(eps_fw_info->rx_data != NULL)
		{
			if (data[1]== CONVERTOR_BOARD_ID)
			{
				//add infor log-low pri
				log_id_with_three_arg(LOG_INFO_L2, LOG_EPS, EPS_MCU1_SEC_FW_VERS__u8_pt_u8_pt_u8,data[10],data[11],data[12]);
				nvm_parameter_write(NVM_DATA, NVM_EPS_MCU1_SEC_FW_MJR_VERS, (uint8_t *)&data[10]);
				nvm_parameter_write(NVM_DATA, NVM_EPS_MCU1_SEC_FW_MNR_VERS, (uint8_t *)&data[11]);
				nvm_parameter_write(NVM_DATA, NVM_EPS_MCU1_SEC_FW_PATCH_VERS, (uint8_t *)&data[12]);
				nvm_commit();
				fw_conv_status.fw_status = FIRMWARE_UPDATE_IN_PROGRESS;
				eps_fw_update(&fw_conv_status, eps_fw_info, crc_temp, len_temp, data[1]);
			}
			else if(data[1]== OUTPUT_CHNL_BOARD_ID)
			{
				//add infor log low pri
				log_id_with_three_arg(LOG_INFO_L2, LOG_EPS, EPS_MCU2_SEC_FW_VERS__u8_pt_u8_pt_u8,data[10],data[11],data[12]);
				nvm_parameter_write(NVM_DATA, NVM_EPS_MCU2_SEC_FW_MJR_VERS, (uint8_t *)&data[10]);
				nvm_parameter_write(NVM_DATA, NVM_EPS_MCU2_SEC_FW_MNR_VERS, (uint8_t *)&data[11]);
				nvm_parameter_write(NVM_DATA, NVM_EPS_MCU2_SEC_FW_PATCH_VERS, (uint8_t *)&data[12]);
				nvm_commit();
				fw_op_chnl_status.fw_status = FIRMWARE_UPDATE_IN_PROGRESS;
				eps_fw_update(&fw_op_chnl_status, eps_fw_info, crc_temp, len_temp, data[1]);
			}
			else
			{
				DEBUG_CPRINT(("Invalid MCU type\n"));
			}


		}
		else
		{
			//do nothing
		}
	}
	else if(data[0] == TC_CMD_FIRMWARE_UPDATE_STATUS_CHECK)
	{
		if (data[1]== CONVERTOR_BOARD_ID)
		{
			eps_fw_info->rx_data_len = 3;
			eps_fw_info->rx_data = (uint8_t*)os_malloc(sizeof(fw_conv_status.fw_status)+2);
			if(eps_fw_info->rx_data != NULL)
			{
				eps_fw_info->rx_data[2] = fw_conv_upt_progress_status;
			}
			else
			{
				//have to check
			}
		}
		else if (data[1]== OUTPUT_CHNL_BOARD_ID)
		{
			eps_fw_info->rx_data_len = 3;
			eps_fw_info->rx_data = (uint8_t*)os_malloc(sizeof(fw_op_chnl_status.fw_status)+2);
			if(eps_fw_info->rx_data != NULL)
			{
				eps_fw_info->rx_data[2] = fw_op_chnl_upt_progress_status;
			}
			else
			{
				//have to check
			}
		}
		else
		{
			DEBUG_CPRINT(("Invalid MCU type\n"));
		}

	}
#endif
	else if(data[0] == TC_CMD_BCKUP_FIRMWARE_AVAILABILITY_CHECK)
	{
		eps_fw_info->rx_data = (uint8_t*)os_malloc(sizeof(eps_fw_data_s)+2);
		if(eps_fw_info->rx_data != NULL)
		{
			if(data[1] == CONVERTOR_BOARD_ID)
			{
				eps_bckup_fw_avail_check(&bckup_fw_conv_status, eps_fw_info);
			}
			else if(data[1]== OUTPUT_CHNL_BOARD_ID)
			{
				eps_bckup_fw_avail_check(&bckup_fw_op_chnl_status, eps_fw_info);
			}
			else
			{
				DEBUG_CPRINT(("Invalid MCU type\n"));
			}
			eps_fw_info->rx_data_len = sizeof(eps_fw_data_s)+2;
		}
		else
		{
			//do nothing
		}

	}
#ifndef LINUX_TEMP_PORT
	else if(data[0] == TC_CMD_BCKUP_FIRMWARE_UPDATE)
	{
		eps_deinit_other_process_before_fw_upd();
		crc_temp = (uint32_t*)(&data[6]);
		len_temp = (uint32_t*)(&data[2]);
		eps_fw_info->rx_data_len = 7;
		eps_fw_info->rx_data = (uint8_t*)os_malloc(eps_fw_info->rx_data_len);
		if(eps_fw_info->rx_data != NULL)
		{
			if (data[1]== CONVERTOR_BOARD_ID)
			{
				//add infor log-low pri
				log_id_with_three_arg(LOG_INFO_L2, LOG_EPS, EPS_MCU1_SEC_FW_VERS_BCKUP_FW__u8_pt_u8_pt_u8,data[10],data[11],data[12]);
				nvm_parameter_write(NVM_DATA, NVM_EPS_MCU1_SEC_FW_MJR_VERS, (uint8_t *)&data[10]);
				nvm_parameter_write(NVM_DATA, NVM_EPS_MCU1_SEC_FW_MNR_VERS, (uint8_t *)&data[11]);
				nvm_parameter_write(NVM_DATA, NVM_EPS_MCU1_SEC_FW_PATCH_VERS, (uint8_t *)&data[12]);
				nvm_commit();
				bckup_fw_conv_status.fw_status = FIRMWARE_UPDATE_IN_PROGRESS;
				eps_bckup_fw_update(&bckup_fw_conv_status, eps_fw_info,data[1]);

			}
			else if(data[1]== OUTPUT_CHNL_BOARD_ID)
			{
				log_id_with_three_arg(LOG_INFO_L2, LOG_EPS, EPS_MCU2_SEC_FW_VERS_BCKUP_FW__u8_pt_u8_pt_u8,data[10],data[11],data[12]);
				nvm_parameter_write(NVM_DATA, NVM_EPS_MCU2_SEC_FW_MJR_VERS, (uint8_t *)&data[10]);
				nvm_parameter_write(NVM_DATA, NVM_EPS_MCU2_SEC_FW_MNR_VERS, (uint8_t *)&data[11]);
				nvm_parameter_write(NVM_DATA, NVM_EPS_MCU2_SEC_FW_PATCH_VERS, (uint8_t *)&data[12]);
				nvm_commit();
				bckup_fw_op_chnl_status.fw_status = FIRMWARE_UPDATE_IN_PROGRESS;
				eps_bckup_fw_update(&bckup_fw_op_chnl_status, eps_fw_info, data[1]);
			}
			else
			{
				DEBUG_CPRINT(("Invalid MCU type\n"));
			}

		}
		else
		{
			//do nothing

		}
	}

	else
	{
		DEBUG_CPRINT(("Invalid command ID\n"));
	}
	if(eps_fw_info->rx_data != NULL)
	{
		memcpy(eps_fw_info->rx_data,data,2);
		eps_fw_info->tm_rsp_id = TM_EPS_FW_UPDATE;
		obc_prepare_ipc_msg(eps_fw_info->rx_data, eps_fw_info->rx_data_len,eps_fw_info->tm_rsp_id ,TM_SRC_EPS);
	}

#endif
#ifndef LINUX_TEMP_PORT
	DEBUG_CPRINT(("Convertor Board Firmware status:\n"));
	DEBUG_CPRINT(("Status = %d\n", fw_conv_status.fw_status));
	DEBUG_CPRINT(("Size = %lu\n", fw_conv_status.fw_size));
	DEBUG_CPRINT(("Crc= %lu\n", fw_conv_status.fw_crc));
	DEBUG_CPRINT(("Battery Board Firmware status:\n"));
	DEBUG_CPRINT(("Status = %d\n", fw_op_chnl_status.fw_status));
	DEBUG_CPRINT(("Size = %lu\n", fw_op_chnl_status.fw_size));
	DEBUG_CPRINT(("Crc= %lu\n", fw_op_chnl_status.fw_crc));
#endif
	status = TCTM_OK;
	return status;
}

/*
 * @brief Application API to write firmware CRC, size and status of converter board in NVM
 */
void eps_fw_conv_info_nvm_write(uint8_t fr_status, uint32_t fr_size, uint32_t fr_crc)
{
	fw_conv_status.fw_size = fr_size;
	fw_conv_status.fw_crc = fr_crc;
	fw_conv_status.fw_status = fr_status;
	nvm_parameter_write(NVM_DATA, NVM_EPS_MCU1_FW_SIZE, (uint8_t *)&fw_conv_status.fw_size);
	nvm_parameter_write(NVM_DATA, NVM_EPS_MCU1_FW_CHECKSUM, (uint8_t *)&fw_conv_status.fw_crc);
	nvm_parameter_write(NVM_DATA, NVM_EPS_MCU1_FW_AVAILABLITY, (uint8_t *)&fw_conv_status.fw_status);
}

/*
 * @brief Application API to write backup firmware CRC, size and status of converter board in NVM
 */
void eps_bckup_fw_conv_info_nvm_write(uint8_t fr_status, uint32_t fr_size, uint32_t fr_crc)
{
	bckup_fw_conv_status.fw_size = fr_size;
	bckup_fw_conv_status.fw_crc = fr_crc;
	bckup_fw_conv_status.fw_status = fr_status;
	nvm_parameter_write(NVM_DATA, NVM_EPS_MCU1_BCKUP_FW_SIZE, (uint8_t *)&bckup_fw_conv_status.fw_size);
	nvm_parameter_write(NVM_DATA, NVM_EPS_MCU1_BCKUP_FW_CHECKSUM, (uint8_t *)&bckup_fw_conv_status.fw_crc);
	nvm_parameter_write(NVM_DATA, NVM_EPS_MCU1_BCKUP_FW_AVAILABLITY, (uint8_t *)&bckup_fw_conv_status.fw_status);
}

/*
 * @brief Application API to write firmware CRC, size and status of output channel board in NVM
 */
void eps_fw_op_chnl_info_nvm_write(uint8_t fr_status, uint32_t fr_size, uint32_t fr_crc)
{
	fw_op_chnl_status.fw_size = fr_size;
	fw_op_chnl_status.fw_crc = fr_crc;
	fw_op_chnl_status.fw_status = fr_status;
	nvm_parameter_write(NVM_DATA, NVM_EPS_MCU2_FW_SIZE, (uint8_t *)&fw_op_chnl_status.fw_size);
	nvm_parameter_write(NVM_DATA, NVM_EPS_MCU2_FW_CHECKSUM, (uint8_t *)&fw_op_chnl_status.fw_crc);
	nvm_parameter_write(NVM_DATA, NVM_EPS_MCU2_FW_AVAILABLITY, (uint8_t *)&fw_op_chnl_status.fw_status);
}

/*
 * @brief Application API to write backup firmware CRC, size and status of output channel board in NVM
 */
void eps_bckupfw_op_chnl_info_nvm_write(uint8_t fr_status, uint32_t fr_size, uint32_t fr_crc)
{
	bckup_fw_op_chnl_status.fw_size = fr_size;
	bckup_fw_op_chnl_status.fw_crc = fr_crc;
	bckup_fw_op_chnl_status.fw_status = fr_status;
	nvm_parameter_write(NVM_DATA, NVM_EPS_MCU2_BCKUP_FW_SIZE, (uint8_t *)&bckup_fw_op_chnl_status.fw_size);
	nvm_parameter_write(NVM_DATA, NVM_EPS_MCU2_BCKUP_FW_CHECKSUM, (uint8_t *)&bckup_fw_op_chnl_status.fw_crc);
	nvm_parameter_write(NVM_DATA, NVM_EPS_MCU2_BCKUP_FW_AVAILABLITY, (uint8_t *)&bckup_fw_op_chnl_status.fw_status);
}

/*
 * @brief Application API called during eps_init to read firmware availablity, checksum and size.
 */
void eps_fw_upt_init(void)
{
	ft_register_receiver_app(TC_DST_FTM_OBC_EPS, eps_ft_receiver_notificataion_clbk);
	DEBUG_CPRINT(("EPS FTM REGISTRATION"));
	log_id(LOG_INFO_L1, LOG_EPS, FW_EPS_FTM_REGISTRATION);

	nvm_parameter_read(NVM_DATA, NVM_EPS_MCU1_FW_SIZE, (uint8_t *)&fw_conv_status.fw_size);
	nvm_parameter_read(NVM_DATA, NVM_EPS_MCU1_FW_CHECKSUM, (uint8_t *)&fw_conv_status.fw_crc);
	nvm_parameter_read(NVM_DATA, NVM_EPS_MCU1_FW_AVAILABLITY, (uint8_t *)&fw_conv_status.fw_status);
	fw_conv_upt_progress_status = fw_conv_status.fw_status;

	nvm_parameter_read(NVM_DATA, NVM_EPS_MCU2_FW_SIZE, (uint8_t *)&fw_op_chnl_status.fw_size);
	nvm_parameter_read(NVM_DATA, NVM_EPS_MCU2_FW_CHECKSUM, (uint8_t *)&fw_op_chnl_status.fw_crc);
	nvm_parameter_read(NVM_DATA, NVM_EPS_MCU2_FW_AVAILABLITY, (uint8_t *)&fw_op_chnl_status.fw_status);
	fw_op_chnl_upt_progress_status = fw_op_chnl_status.fw_status;

	nvm_parameter_read(NVM_DATA, NVM_EPS_MCU1_BCKUP_FW_SIZE, (uint8_t *)&bckup_fw_conv_status.fw_size);
	nvm_parameter_read(NVM_DATA, NVM_EPS_MCU1_BCKUP_FW_CHECKSUM, (uint8_t *)&bckup_fw_conv_status.fw_crc);
	fw_conv_upt_progress_status=bckup_fw_conv_status.fw_status;

	nvm_parameter_read(NVM_DATA, NVM_EPS_MCU2_BCKUP_FW_SIZE, (uint8_t *)&bckup_fw_op_chnl_status.fw_size);
	nvm_parameter_read(NVM_DATA, NVM_EPS_MCU2_BCKUP_FW_CHECKSUM, (uint8_t *)&bckup_fw_op_chnl_status.fw_crc);
	fw_op_chnl_upt_progress_status=bckup_fw_op_chnl_status.fw_status;

	nvm_parameter_read(NVM_DATA,NVM_EPS_MCU1_BCKUP_FW_MJR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[0].fw_mjr_vers);
	nvm_parameter_read(NVM_DATA,NVM_EPS_MCU1_BCKUP_FW_MNR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[0].fw_mnr_vers);
	nvm_parameter_read(NVM_DATA,NVM_EPS_MCU1_BCKUP_FW_PATCH_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[0].fw_patch_vers);

	nvm_parameter_read(NVM_DATA,NVM_EPS_MCU2_BCKUP_FW_MJR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[1].fw_mjr_vers);
	nvm_parameter_read(NVM_DATA,NVM_EPS_MCU2_BCKUP_FW_MNR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[1].fw_mnr_vers);
	nvm_parameter_read(NVM_DATA,NVM_EPS_MCU2_BCKUP_FW_PATCH_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[1].fw_patch_vers);
}

/*
 * @brief This API power down all the channels before uploading FW and
 * save the powered ON channel state in NVM
 */
void eps_pwr_dwn_all_chnl()
{
	eps_chnl_ste = 1;

	for(uint8_t i = 0; i < 14 ; i++)
	{
		if(i != HW_OBC_PS_GPS_STD_PL_BRD && i!= HW_HRM && sat_dev_sts[i].hw_sts == PWR_ON)
		{
			eps_chnl_ste |= sat_dev_sts[i].hw_sts << (i+1);   //only ON specified
			eps_ctrl_en_chnl(i,0);
		}
	}
	nvm_parameter_write(NVM_DATA,EPS_CHNL_PWR_STE,(uint8_t*)&eps_chnl_ste);
}

/*
 * @brief This API check the channel state whether it is in ON state and trigger the NVM commit
 */
void eps_check_chnl_pwr_dwn_state()
{
	eps_chnl_pwr_dwn_flg=0;
	for(uint8_t j = 0; j < 14 ; j++)
	{
		if(sat_dev_sts[j].hw_sts == PWR_OFF)
		{
			eps_chnl_pwr_dwn_flg++;
		}
	}
	if(eps_chnl_pwr_dwn_flg == 12)
	{
		eps_fw_conv_info_nvm_write(FIRMWARE_UPDATE_IN_PROGRESS, fw_conv_status.fw_size, fw_conv_status.fw_crc);
		nvm_commit();
	}
}

/*
 * @brief This function power up the channel which was in ON state before FW update
 */
void eps_pwr_up_chnl_after_fw_upd()
{
	nvm_parameter_read(NVM_DATA,EPS_CHNL_PWR_STE,(uint8_t*)&eps_chnl_ste);
	if(eps_chnl_ste & 0x0001)
	{
		for(uint8_t i = 0; i< 14; i++)
		{
			if((eps_chnl_ste >> (i+1)) & 0x01)
			{
				if(sat_dev_sts[i].hw_sts != PWR_ON)
					eps_ctrl_en_chnl(i,1);
			}
		}
		eps_chnl_ste = 0;
		nvm_parameter_write(NVM_DATA,EPS_CHNL_PWR_STE,(uint8_t*)&eps_chnl_ste);
		if(eps_fw_start_flag)
		{
			os_timer_start(eps_rd_tmr,(os_tick_time_t)RD_TMR_SECS);
			eps_init_com_time_out();
			eps_fw_start_flag=0;
		}
		nvm_commit();
	}
	else
	{
		DEBUG_CPRINT(("Reset dosen't happen\n"));
	}
}

/*
 * @brief This function read the EPS secondary FW version stored from the NVM
 */
void eps_read_sec_fw_vers(eps_get_fw_s *get_nvm_fw_vers)
{
	nvm_parameter_read(NVM_DATA,NVM_EPS_MCU1_SEC_FW_MJR_VERS,(uint8_t*)&get_nvm_fw_vers[0].fw_mjr_vers);
	nvm_parameter_read(NVM_DATA,NVM_EPS_MCU1_SEC_FW_MNR_VERS,(uint8_t*)&get_nvm_fw_vers[0].fw_mnr_vers);
	nvm_parameter_read(NVM_DATA,NVM_EPS_MCU1_SEC_FW_PATCH_VERS,(uint8_t*)&get_nvm_fw_vers[0].fw_patch_vers);

	nvm_parameter_read(NVM_DATA,NVM_EPS_MCU2_SEC_FW_MJR_VERS,(uint8_t*)&get_nvm_fw_vers[1].fw_mjr_vers);
	nvm_parameter_read(NVM_DATA,NVM_EPS_MCU2_SEC_FW_MNR_VERS,(uint8_t*)&get_nvm_fw_vers[1].fw_mnr_vers);
	nvm_parameter_read(NVM_DATA,NVM_EPS_MCU2_SEC_FW_PATCH_VERS,(uint8_t*)&get_nvm_fw_vers[1].fw_patch_vers);
}

/*
 * @brief This API check the communication status for mcu2 after FW upload
 * If communication fails, it leads to backup firmware update
 */
void eps_fw_mcu2_upd_check(obc_pck_data_eps *eps_fw)
{
	ptr_typ bckup_addr_norflash;
	ptr_typ addr_norflash;
	if(!eps_fw_update_fail_flag)
	{
		eps_get_fw_s get_fw[2];
		eps_get_fw_s get_sec_nvm_fw_vers[2];
		eps_read_sec_fw_vers(get_sec_nvm_fw_vers);
		eps_get_fw_update(get_fw);
		if(get_fw[0].fw_mjr_vers == get_sec_nvm_fw_vers[1].fw_mjr_vers && get_fw[0].fw_mnr_vers == get_sec_nvm_fw_vers[1].fw_mnr_vers && get_fw[0].fw_patch_vers == get_sec_nvm_fw_vers[1].fw_patch_vers)
		{
			if(fw_op_chnl_status.fw_status == FIRMWARE_UPDATE_IN_PROGRESS)
			{
				fw_op_chnl_upt_progress_status = FIRMWARE_UPDATE_PASS;
				addr_norflash = (ptr_typ)eps_get_nor_flash_address(1);
				bckup_addr_norflash=(ptr_typ)eps_get_nor_flash_bckup_address(1);
				os_memcpy_extmem((uint8_t*)bckup_addr_norflash, (uint8_t*)addr_norflash, fw_op_chnl_status.fw_size, NULL, 1,1);
			}
			else
			{
				fw_op_chnl_upt_progress_status = BCKUP_FIRMWARE_UPDATE_PASS;
				eps_bckupfw_op_chnl_info_nvm_write(FIRMWARE_AVAILABLE,bckup_fw_op_chnl_status.fw_size,bckup_fw_op_chnl_status.fw_crc);
			}
			eps_fw_op_chnl_info_nvm_write(NO_FIRMWARE, 0, 0);
		}
		else
		{
			//add error log for bckup fw update previo uploaded FW vers numb
			//add war log bckup fw vers num
			log_id(LOG_ERROR, LOG_EPS, EPS_MCU2_INCORRECT_FW_UPLOADED_BCKUP_FW_TRIGGERED);
			log_id_with_three_arg(LOG_WARNING,LOG_EPS,EPS_MCU2_BCKUP_FW_VERS__u8_pt_u8_pt_u8,eps_bckup_get_nvm_fw[1].fw_mjr_vers,eps_bckup_get_nvm_fw[1].fw_mnr_vers,eps_bckup_get_nvm_fw[1].fw_patch_vers);
			nvm_parameter_write(NVM_DATA,NVM_EPS_MCU2_SEC_FW_MJR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[1].fw_mjr_vers);
			nvm_parameter_write(NVM_DATA,NVM_EPS_MCU2_SEC_FW_MNR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[1].fw_mnr_vers);
			nvm_parameter_write(NVM_DATA,NVM_EPS_MCU2_SEC_FW_PATCH_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[1].fw_patch_vers);
			nvm_parameter_write(NVM_DATA,EPS_OP_CHNL_FW_MJR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[1].fw_mjr_vers);
			nvm_parameter_write(NVM_DATA,EPS_OP_CHNL_FW_MNR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[1].fw_mnr_vers);
			nvm_parameter_write(NVM_DATA,EPS_OP_CHNL_FW_PATCH_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[1].fw_patch_vers);
			nvm_commit();
			fw_op_chnl_status.fw_status = FIRMWARE_UPDATE_FAIL;
			bckup_fw_op_chnl_status.fw_status = FIRMWARE_UPDATE_IN_PROGRESS;
			eps_bckup_fw_update(&bckup_fw_op_chnl_status, eps_fw,2);

		}
	}
	else
	{
		log_id(LOG_ERROR, LOG_EPS, EPS_MCU2_FW_UPLOAD_FAILED_BCKUP_FW_TRIGGERED);
		nvm_parameter_write(NVM_DATA,NVM_EPS_MCU2_SEC_FW_MJR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[1].fw_mjr_vers);
		nvm_parameter_write(NVM_DATA,NVM_EPS_MCU2_SEC_FW_MNR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[1].fw_mnr_vers);
		nvm_parameter_write(NVM_DATA,NVM_EPS_MCU2_SEC_FW_PATCH_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[1].fw_patch_vers);
		nvm_parameter_write(NVM_DATA,EPS_OP_CHNL_FW_MJR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[1].fw_mjr_vers);
		nvm_parameter_write(NVM_DATA,EPS_OP_CHNL_FW_MNR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[1].fw_mnr_vers);
		nvm_parameter_write(NVM_DATA,EPS_OP_CHNL_FW_PATCH_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[1].fw_patch_vers);
		nvm_commit();
		fw_op_chnl_status.fw_status = FIRMWARE_UPDATE_FAIL;
		bckup_fw_op_chnl_status.fw_status = FIRMWARE_UPDATE_IN_PROGRESS;
		eps_bckup_fw_update(&bckup_fw_op_chnl_status, eps_fw,2);
	}
}

/*
 * @brief This API check the communication status for mcu1 after FW upload
 * If communication fails, it leads to backup firmware update
 */
void eps_fw_mcu1_upd_check()
{
	ptr_typ bckup_addr_norflash;
	ptr_typ addr_norflash;
	obc_pck_data_eps eps_fw;
	eps_fw.rx_data_len = 3;
	eps_fw.rx_data = (uint8_t*)os_malloc(sizeof(fw_conv_status.fw_status)+2);
	if(eps_fw.rx_data != NULL)
	{
		nvm_parameter_read(NVM_DATA,EPS_CHNL_PWR_STE,(uint8_t*)&eps_chnl_ste);
		if(eps_chnl_ste & 0x0001)
		{
			eps_get_fw_s get_fw[2];
			eps_get_fw_update(get_fw);
			eps_get_fw_s get_sec_nvm_fw_vers[2];
			eps_read_sec_fw_vers(get_sec_nvm_fw_vers);
			if(get_fw[1].fw_mjr_vers == get_sec_nvm_fw_vers[0].fw_mjr_vers && get_fw[1].fw_mnr_vers == get_sec_nvm_fw_vers[0].fw_mnr_vers && get_fw[1].fw_patch_vers == get_sec_nvm_fw_vers[0].fw_patch_vers)
			{
				if(fw_conv_status.fw_status == FIRMWARE_UPDATE_IN_PROGRESS)
				{
					fw_conv_upt_progress_status = FIRMWARE_UPDATE_PASS;
					addr_norflash = (ptr_typ)eps_get_nor_flash_address(1);
					bckup_addr_norflash= (ptr_typ)eps_get_nor_flash_bckup_address(1);
					os_memcpy_extmem((uint8_t*)bckup_addr_norflash, (uint8_t*)addr_norflash, fw_conv_status.fw_size, NULL, 1,1);
				}
				else
				{
					fw_conv_upt_progress_status = BCKUP_FIRMWARE_UPDATE_PASS;
					eps_bckup_fw_conv_info_nvm_write(FIRMWARE_AVAILABLE,bckup_fw_conv_status.fw_size,bckup_fw_conv_status.fw_crc );    //nvm write
				}

				eps_fw_conv_info_nvm_write(NO_FIRMWARE, 0, 0);
			}
			else
			{
				log_id(LOG_ERROR, LOG_EPS, EPS_MCU2_INCORRECT_FW_UPLOADED_BCKUP_FW_TRIGGERED);
				log_id_with_three_arg(LOG_WARNING,LOG_EPS,EPS_MCU2_BCKUP_FW_VERS__u8_pt_u8_pt_u8,eps_bckup_get_nvm_fw[1].fw_mjr_vers,eps_bckup_get_nvm_fw[1].fw_mnr_vers,eps_bckup_get_nvm_fw[1].fw_patch_vers);
				nvm_parameter_write(NVM_DATA,NVM_EPS_MCU1_SEC_FW_MJR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[0].fw_mjr_vers);
				nvm_parameter_write(NVM_DATA,NVM_EPS_MCU1_SEC_FW_MNR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[0].fw_mnr_vers);
				nvm_parameter_write(NVM_DATA,NVM_EPS_MCU1_SEC_FW_PATCH_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[0].fw_patch_vers);
				nvm_parameter_write(NVM_DATA,EPS_CONV_BRD_FW_MJR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[0].fw_mjr_vers);
				nvm_parameter_write(NVM_DATA,EPS_CONV_BRD_FW_MNR_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[0].fw_mnr_vers);
				nvm_parameter_write(NVM_DATA,EPS_CONV_BRD_FW_PATCH_VERS,(uint8_t*)&eps_bckup_get_nvm_fw[0].fw_patch_vers);
				eps_bckup_fw_conv_info_nvm_write(FIRMWARE_UPDATE_IN_PROGRESS,bckup_fw_conv_status.fw_size,bckup_fw_conv_status.fw_crc );    //nvm write
				eps_fw_conv_info_nvm_write(FIRMWARE_UPDATE_FAIL, 0,0);
				nvm_commit();
				eps_bckup_fw_update(&bckup_fw_conv_status, &eps_fw,1);
			}
		}
	}
}


